Imported from jakeoborn/Plursky (
AGENTS.md). Install upstream withnpx skills add jakeoborn/Plursky. Copyright stays with the author.
AGENTS.md — for AI agents working on Plursky
If you are an AI agent (Instinct, Claude Code, a GitHub bot, etc.) about to touch
this repo: read this first, then CLAUDE.md and RELEASE.md. They encode
rules that cause real breakage if ignored.
Instinct — start here:
docs/qa/INSTINCT-ROLE.md(standing brief, hard rules, what a simulator cannot prove) thendocs/qa/INSTINCT-QUEUE.md(the job queue and its status protocol). Instinct replaced Clicky on 2026-09-08; the olderQA-v208.mdandCLICKY-BRIEF-*.mdfiles are historical records of past release QA, not current instructions.
Plursky is a music-festival companion app: a no-bundler static SPA (plain
.jsx files loaded as <script type="text/babel">) that ships to the web
(plursky.com via GitHub Pages) and to iOS (App Store, via Capacitor).
0. Approval gate (HARD RULE — applies to EVERY agent)
No patching, version bumping, build sync, committing, pushing, or issue filing should happen until the user explicitly approves the next implementation step.
Diagnose, read, propose — freely. But STOP before any action that changes the
repo, the deploy, the build, or GitHub (edits, vNNN bumps, cap sync,
commits, pushes, PRs, issues). Surface the plan and wait for an explicit
"yes / go / do it" from the user for that specific step. One approval covers
the step it was given for, not the next one. When in doubt, ask.
Standing exception — merges (founder rule, 2026-09-10). A PR whose full verify gate is green on its exact head commit, and which has had its review, is merged by Claude Code without waiting for a further go-ahead. That covers the merge only. If the head moves, the gate and the review run again on the new head. iOS submission, prices and anything audience-facing still wait for Jake.
0b. Verify before you relay (HARD RULE — applies to EVERY agent)
Every factual claim about this repo — yours, another agent's, anyone's — gets verified against the CURRENT repo before it is acted on, merged, or relayed as fact.
Stale docs are not evidence. The code and data on main are. A doc, a backlog
row, a queue entry, a previous session's summary and a plan from another agent
are all hypotheses — each states something that was true when it was written,
which is not the same as true now. Run the cheap falsifying check first; it is
almost always one grep, one script, or one registry read.
This is not pedantry, it is the failure this project keeps hitting:
- A plan led with "ACL is live with 2025-estimated set times."
data.jsxsays the opposite in the file — an OFFICIAL grid that already replaced the 2025-estimated one. Acting on it would have redone finished work (2026-09-08). - A flip spec still instructed a georeference that a merged PR had forbidden; following it would have undone #97.
- A cited anchor residual was off by an order of magnitude, and the conclusion happened to survive for the opposite reason to the one given.
When you relay a claim you have not checked, say so in the same sentence. "Instinct reports X" and "X is true" are different statements — do not silently convert one into the other.
Findings over failure
"No surveyable feature" is a finding, not a failure. This generalises to all measurement work: a negative result that is measured and recorded is a deliverable, not a shortfall. Report what is true, including "this cannot be done and here is the evidence" — never pad a result to look like progress, and never invent data to fill a column. The set-time honesty and unplaced-stage gates exist because that padding has been shipped before.
1. Who owns what
| Agent | May do (after approval) | NEVER |
|---|---|---|
| Claude Code (terminal) | Repo surgery: multi-file edits, version bumps, the verify gate, commits/merges to main |
Act without the approval gate above |
| Instinct (on-screen) | Visual QA on the live app, on-device testing, official-source data research, design drafts, web-dashboard ops (App Store Connect, RevenueCat), Xcode triage. Code changes only via branch + PR | Commit or push to main; cache-bust or iOS version bumps; archive/upload; act without approval |
| Any other agent | Branches / PRs / issues | Commit or push to main |
main is the live deploy (push to main → plursky.com updates). Only Claude
Code commits there, and only after both the approval gate (§0, including its
standing merge exception) and the verify gate (§2) pass. Instinct has GitHub
write access but must NOT push to main — it opens a branch + PR (or files an issue) and lets Claude Code run the gate and
merge. Direct-to-main bypasses the only validation Plursky has. Note that Instinct
uses jakeoborn's shared credentials, so branch protection CANNOT tell it apart from
Claude Code: this rule is instruction-level only, which is exactly why it is here.
The handoff loop: Instinct sees a problem on screen → reports it privately to Jake or Claude Code → Claude Code implements + verifies + ships → Instinct re-checks on the real device. Not a GitHub issue: issues on this repo are public, so they fall under the open-defect rule below exactly like a PR body does.
Open defects stay out of this repo. It is public — it deploys plursky.com. A bug that is not yet fixed is reported to Jake or Claude Code directly, never described in a PR body, commit message, issue or doc here. Once it is fixed, describe it in full: that is what the "born from" comments are for.
2. Hard rules (these break the app if ignored)
-
iOS version train is
1.x, NOT1.0.x. Apple compares numerically, so1.0.7 < 1.6and gets REJECTED. The next marketing string must be numerically higher (e.g.1.8 → 1.9); the build number must be unique/higher. -
There is NO build validation.
scripts/build.mjsonly copies files — a green[build] dist/ readymeans nothing was validated. Changed.jsxmust pass: Babel transform (react preset) + a headless mount probe (#roothas children, key fns aretypeof function) + a screenshot for UI changes. (Claude Code has aplursky-verifyskill for exactly this.) -
Cache-bust
vNNNmoves in lockstep. On any shipped JS change, bump the version acrossindex.html+sw.js+app.jsxtogether (sed one-liner inRELEASE.md). A new.jsxfile must be added to both theindex.html<script>tags and thesw.jsprecache list. Docs-only changes (like this file) do not bump the version. -
No bundler — everything is global across
<script>s. Top-levelfunction/constdeclarations become global; cross-file calls work by bare name. Load order only matters for eval-time references. -
Don't hand-edit build output.
dist/andios/App/App/public/are generated bynode scripts/build.mjs && npx cap sync iosand are gitignored. Edit the source.jsxat the repo root, never the copies. -
Don't rebuild what exists. There's already a recap/share engine (
recap-engine.jsx), Spotify/Apple API (spotify-api.jsx), EXIF/auto-tag (photo-tag.jsx), map/meetup (map.jsx+supabase.jsx). Grep before building; integrate, don't duplicate. -
Secrets: never commit private keys (
.p8MusicKit key, Supabase service-role keys). The Spotify client ID and the MusicKit developer token are client-side-public by design and live in the code — that's expected.
3. Two trains, two deploys
- Web reaches plursky.com on push to
main. - iOS bundles
dist/locally, so a web/JS fix reaches the device only after a rebuild:node scripts/build.mjs && npx cap sync ios, then an Xcode archive + App Store submit. A web fix is not automatically on iOS.
Full ship flow + the pre-submit checklist live in RELEASE.md.
4. Definition of done
A change is "done" when: source .jsx edited → verify gate passed (Babel +
mount probe + screenshot) → vNNN bumped in lockstep (if it's a shipped JS
change) → committed to main → (for iOS) build.mjs + cap sync ios run.
Reporting status = stating what was measured on the artifact, not what was
intended.
Work reports
Write the report as a file in docs/reports/ (YYYY-MM-DD-<slug>.md), not
as an email and not only in chat. Lanes read the repo directly; a report that
lives in a mailbox or a terminal scrollback is not readable by the next agent.
Per-PR verification receipts still belong in the PR body or a PR comment,
on the PR they describe — docs/reports/ is for the cross-PR narrative.
⚠️ This repo is PUBLIC. Do NOT put in docs/reports/:
- an unfixed defect (a fixed one is fine — say so, and say what shipped),
- unreleased pricing, or App Store / RevenueCat material not yet public,
- partner, strategy or third-party material, or anyone's personal data.
That content goes to ~/Plursky-private. Note it is currently a plain local
folder, not a git repo — so nothing in it is readable by a lane. If a lane
needs to read it, it has to become a real private GitHub repo first; until
then, treat "private" as "founder's eyes on this machine only".
Docs-only, so no vNNN bump (§2 rule 3).
Festival flips (rescued from issue #18, 2026-08-22 — the issue is closed, this rule is not)
No public-facing flips without founder word. Every festival scaffold PR
lands available: false. A watch fires the flip session only when the
official set times publish, and each flip is its own PR for founder /
Claude Code review — never agent-merged, never folded into an unrelated PR.
The §0 merge exception does not cover flips: a flip is audience-facing, so a
green gate is necessary but the founder's word is still required.
A gated entry whose event date has passed never flips. Retire it or roll it
to next year — do not leave it sitting available: false forever, and check
whether it is still being published to search (/f/<id>/, sitemap.xml).