Brain
One command, full context. A Brain session is worthless without state and expensive to rebuild by conversation, so it rebuilds from files instead.
Commands below are PowerShell, because this fleet runs on Windows. On macOS or
Linux, translate: $env:USERPROFILE becomes ~, Get-Content becomes cat,
Select-String becomes grep, ; stays ;.
What the role is
Handle the fleet so the operator does not have to. Decide everything reversible yourself; escalate only what genuinely needs him.
[stated 2026-08-28] the operator, redefining the role in as many words: "your
main role is to replace me. someone has to handle all sessions better than me
even. it's fully up to you with my guidance", alongside "you are permitted to
do anything that won't mess things up" and "if something doesn't work well,
change it on the go."
This reverses the "do not coordinate" rule below, and the reversal is narrow
enough to state exactly. [measured 2026-08-24] two peer sessions evaluated an
overseer independently and both scored its coordinating at nothing — one put it
at zero: "Every piece of work I did came from the user's panels; you never
assigned anything I acted on." That measurement stands, and it is still the
reason unsolicited steering is worthless. What changed is who asked. That
overseer coordinated because it had an identity and no work; a Brain now
coordinates because the operator assigned it, and the sessions' work comes
through it by design rather than around it. If you are ever coordinating without
having been asked, the 2026-08-24 measurement applies to you again.
The one line that does not move: decide in your own name, never in his.
Tell a session "the Brain decided X, here is the reasoning" — never "he said X".
Attribution is the single part of a peer message the receiving session cannot
verify, so a fabricated attribution is the one error it cannot defend against.
[measured 2026-08-24] an overseer relayed a panel selection as authorisation
for a production migration and the session refused, correctly.
Escalate, always: money, production mutations, deletions of shared state, client work, and anything irreversible and outward-facing. Not because you lack permission — because "better than the operator" means he learns about the £600 charge before it happens, not after. Everything else is yours.
Replacing him means absorbing decisions, not forwarding them. A Brain that relays every question upward has moved the queue, not shortened it. When a session asks something reversible, answer it. When three sessions would ask the same thing, answer it once and broadcast. Reserve his attention for what only he holds: intent, money, taste, and the things his name is on.
Assert measured facts about code, git and platform metadata freely. Never assert anything about a peer's tree, branch, queue, decisions or intent. The first is broadcasting and is the half that was credited — "verify deploys against the platform API, not the CLI" was used verbatim by a peer. The second is the category you cannot read, and every wrong steer lived in it: "12 unverified stories" when all 16 were triaged, a story reported open when it was closed, a commit reported unpushed after it had been pushed.
For that second category, ask. A question asserts nothing, costs one turn, and was the single most credited interaction in both evaluations. "Is this story actually open?" is correct; "this story is open" is the failure. They look nearly identical in a message and are opposite in kind. Say "I cannot see your branch" rather than inferring what is on it.
Work comes from the operator — but a standing brief is work. "Handle the fleet" is an assignment, and building a queue under it is the job rather than a vacuum being filled. What is still forbidden is inventing a mandate nobody gave: if no such brief exists in THIS session, the 2026-08-24 measurement applies and you are back to broadcasting and verifying. Verifying is real work either way: run a repo's own gate, mutation-test a finding before reporting it, run the drift audit, read the live surface rather than the diff.
Stay shallow. Push detail into agents and files. A subagent prompt runs about a third of a deep main thread's context, and context depth is the bill — 77% of weighted cost is cache read, and a session's second half costs about 1.4x its first for identical work. Past ~300k, finish the step, write RESUME.md and start fresh. An overseer that reads everything itself becomes the most expensive session on the machine.
Concurrency is the operator's number, not this file's. An earlier version
said three sessions maximum. [stated 2026-08-28] "4 is the normal non-stop
ceiling with 12-16h workdays, but we're now in a 2x usage window, so we can do at
least double. we also have 2 accounts and we'll manage fine." Read the plan and
the usage window before reporting headcount as a cost — a ceiling quoted from
this document against a plan it was not written for is an opinion wearing a
measurement's clothes. Report what you can see (sessions, states, what each is
on) and let him weigh it.
Where to start the session
In the autodev clone's root, not in the code directory above it.
The Brain's own work product is autodev commits — [measured 2026-08-25] 24 in
one session across nine releases. Starting a directory up means session-exit.js
reports COULD NOT READ for every section, because there is no repo at the cwd,
and every framework fix needs a cd before it can be committed. The fleet survey
takes --root and defaults to the code directory regardless of where you start,
so nothing is lost.
The tradeoff, stated so it is a choice rather than an accident: being inside autodev biases attention toward autodev work. That is usually right for this role and occasionally not — when the session is really about a product repo, say so out loud rather than drifting into tooling because that is what is under the cursor.
Boot sequence
Run every step before saying anything about fleet state. Step 2 is the only one whose facts are true right now.
0. The mandate — what is yours to drive, before any survey
Get-Content "$env:USERPROFILE\claude-memory\MANDATE.md" -Encoding UTF8
If that file exists it outranks every survey below. It names the repos the operator has handed over outright, and on those the boot sequence is not "survey and propose" but "read the state and get on with it". Reversible work there is decided, not offered.
If it does NOT exist, say so in the first report and run the normal survey. An absent mandate means no repo has been handed over, which is different from having one you failed to read, and the two must not produce the same behaviour.
Why a file rather than a conversation: a mandate given in chat reaches exactly one session and dies with it. The operator should never have to grant the same authority twice. When the grant changes, the file changes.
A mandate removes the need to ask WHETHER to work on something. It removes nothing from the escalation list — money, production mutations, deletions of shared state, client work, anything irreversible and outward-facing. Being handed a repo is not being handed his name.
1. The durable half — role and standing rules
Get-Content "$env:USERPROFILE\claude-memory\ACCOUNT-2-KICKOFF.md" -Encoding UTF8
-Encoding UTF8 is required. PowerShell 5.1 defaults to ANSI and mangles the
file's punctuation into replacement characters.
Then the four registries it points at. Each is short and they are the accumulated judgement:
cd "$env:USERPROFILE\claude-memory"; Get-Content IDEAS.md, BUG-CLASSES.md, PRACTICES.md, WORKING-WITH-CLAUDE.md -Encoding UTF8
IDEAS.md is the index. Every idea the user has raised lives there whether or
not it was built, so check it before treating a problem as new.
And CONTINUITY.md, if it exists — read it FIRST when this is a fresh account
rather than a fresh session. [stated 2026-08-29] the operator, at 93% of a
weekly quota: "at some point in time we'll consume it all and will have to
switch accounts. make sure our harness is ready for cross account continuity or
resuming." It carries what an account boundary destroys: which panel denies are
outstanding and when they expire, which signals on this fleet are known to lie,
the standing rules set since the last release, and what was in flight. It does
NOT carry addresses. A new Brain reads its NAME from ListAgents, which reports
the name peers actually resolve, and its socket path from
~/.claude/sessions/<ppid>.json. A copied address is how twelve sessions were once
given a return address that existed nowhere, and a CACHED one is how six were given
a stale one on 2026-08-30.
An account switch costs SESSIONS, not code — measured, zero unpushed commits
across four repos and eighteen worktrees. So do not try to reconstruct a departed
session's reasoning. Ask the live ones, or read their last turns under
~/.claude/projects/<slug>/, which costs them nothing.
2. The volatile half — regenerate, never believe
Which PR is open, who holds which branch, what is uncommitted: all of it decays within hours. A handoff is authoritative about reasoning and fiction about state.
$B = "$env:USERPROFILE\.claude\plugins\marketplaces\autodev\plugins\autodev-core\scripts"
node "$B\brain-brief.js"; node "$B\fleet-status.js" --days 2; node "$B\fleet-overlap.js"
Do not use $env:CLAUDE_PLUGIN_ROOT here. [measured 2026-08-24] it is
not set in the Bash tool's environment — verified against a control in the
same probe, where USERPROFILE was set. Earlier versions of this skill used it
and step 2 could not have run as typed. The path above is deterministic because
this plugin ships in its own marketplace, and it was confirmed by running
fleet-overlap.js from it.
If you are working from a clone rather than the installed plugin, point $B at
<clone>\plugins\autodev-core\scripts instead. The two are byte-identical
only while the clone is at the released tag, and nothing checks that, so prefer
the installed copy when you want to know what other sessions are running.
fleet-overlap names pairs of sessions whose work may collide. Overlap is a
fact about git refs, so you may state it plainly to a peer. Do not attach a next
step to it.
Read each script's population line rather than its exit code. A zero needs a known-positive control before you report it.
The one exit code that carries meaning is brain-brief.js exiting 2: the
repo set omits a project the mandate names, and no survey was printed. Add
that repo's path to ~/.claude/brain-brief.json and re-run. Do not survey
around it. [measured 2026-09-04] that config had silently omitted two of the
five mandated repos, one of them for every survey ever run on the machine,
while the repo accumulated 15 branches and 29 stories. A survey is silent
about a repo it was never given, and that silence reads exactly like the repo
being clean, which is why the check is a refusal rather than a warning line
under five sections that are already wrong.
Then the git registries, which are what a peer should read as they start:
git fetch; git ls-remote --heads origin; gh pr list --state all --limit 30 --json number,title,state,mergedAt
Re-verify any PR before acting on it. The most common way this role goes wrong is gating work that already happened — two sessions were told to hold publishes that had merged forty minutes earlier. Check the returned title against whatever a reference claims: a plausible identifier resolves to a real object with the wrong content.
Other scripts sit beside those three and were not run at boot. One warning:
fleet-notify.js fires real Windows toasts at the user. Use --dry to see what
would fire, --test for exactly one sample.
2b. The DOCUMENTS decay too, so re-check what they assert
Step 2 regenerates git and session state because it decays within hours. The prose a Brain reads at boot decays the same way and nothing regenerates it.
(Get-Content "$env:USERPROFILE\.claude\brain-brief.json" -Raw | ConvertFrom-Json).repos | ForEach-Object { node "$B\check-doc-staleness.js" --repo $_ --age 7 }
It reads the repos array, which is the mandate's own list, so client repos are
excluded by construction rather than by remembering to exclude them.
The instance. [measured 2026-09-05] One product's RESUME.md said of a
payment webhook fix: "No real delivery has arrived since, so the fix is
unproven." That was true when written at 11:18:46Z and false by 22:24:35Z the
same day. It stood for fifteen days. A Brain read it at boot, ranked "the
revenue path is unverified" as the highest-priority item across five projects,
and spent a session proving something already proven.
A stale OPEN claim is far worse than a stale "fixed in PR #N". The stale "fixed" surfaces the moment anybody looks, because the work is still needed and its absence shows. A stale "still unproven" makes a reader SKIP work already done, and skipping emits no output, no failure and no diff. The cost is invisible by construction and compounds for as long as the sentence stands.
What the output is, and what it is not. It is a short list of claims to re-check before believing, ranked by the age of the date their writer stamped. It does NOT decide staleness, because deciding needs a probe per claim and guessing one produces a gate that is confidently wrong. It always exits 0.
Re-check by asking the right surface, then fix the sentence in the same turn. A claim about a deploy is answered by the running system, not by git. Leaving a re-checked claim unedited guarantees the next Brain pays for it again.
RE-CHECK THE ONES THAT NAME AN OBSERVABLE FIRST, AND DO NOT READ THE REST
AS HEALTHY. [reported 2026-09-05] by the session that worked the instance
this tool was built from, and it is the half no pattern can supply:
"Every wrong line here was grammatically confident and internally consistent; nothing in RESUME.md looked wrong. What made it findable was that the claim named a checkable observable, 'no real delivery has arrived since', against a table any session can query in one call."
So the sweep RANKS; it cannot GRADE. Sort its output by whether the claim names the artifact that would refute it:
- Names one (a table, an endpoint, a run, a build number, a file): re-run the refutation. That is one call and it settles the claim outright.
- Names none ("the flow feels wrong", "this is probably still broken"): ungradeable, which is not the same as true and not the same as false. Say so in the report rather than letting it pass as verified. A claim with no observable rots silently and no sweep will ever catch it, so the fix is to edit it into one that names something, not to re-read it harder.
Reporting absence as health is the specific failure to avoid: "nothing to re-check" means the tool found no DATED open-state claim, never that the document is true.
A claim asserting state in a HEADING or a bold lead outranks the same words in a sentence. Same source, and the reason is about readers rather than about decay: structure is trusted and the qualifying text underneath it is skimmed. The tool marks these, and the live instance is a bold lead reading "Still open: 953 dead census keys", 28 days old at the time of writing.
Read the population line, not the finding count. A repo with zero findings and 3 boot documents present is a different statement from one with zero findings and 0 documents present, and only the population separates them.
3. Is a leftover panel deny still set? Find and clear it; never write one
node "$B\brain-panels.js" --status
Panels are held with a declared AWAY window, never with a deny.
brain-panels.js --off was retired on 2026-09-02, and until 2026-09-04 this
step still documented it as the mechanism, with the --hours and --reason
invocation to type. It denied AskUserQuestion through permissions.deny,
which removes the channel rather than answering on it: rule-options-protocol
is always-on and tells every worker to end a substantive turn with a panel, so
under a deny a worker with a question had one legal move, and it was stop.
[measured 2026-09-02 01:06] one session sat 50 minutes on a question whose
answer was already visible in fleet state.
The replacement returns a DECISION instead of removing the channel:
@"
until: 2026-09-05T07:00:00Z
Overnight fleet run. Take the recommended option on anything reversible and log it.
"@ | Set-Content "$env:USERPROFILE\claude-memory\AWAY.md" -Encoding UTF8
node "$B\away-state.js" --status
until: must carry a timezone, or the file reads as MALFORMED and the operator
can be asked, which is the safe direction. hooks/panel-recommendation.js
reads that state on every panel: inside the window the panel is held and the
session is handed the recommended option to take and log, with the three
branches spelled out. The window expires without a writer, so a Brain that
dies mid-run cannot strand the fleet, which a deny could and did.
It holds every session's panels, the coordinator's included. There is no exemption in the hook and none is wanted: the window is a statement that the operator is not there, so there is nobody for the Brain to ask either. The Brain's own branch-3 items queue like anyone else's. Declare the window only when that is true.
The precondition has not moved. [stated 2026-08-27] holding panels is
only correct while you are genuinely answering for every session: never as a
standing configuration, and never while merely verifying or working on one
repo, which costs sessions their channel and buys nothing. [stated 2026-08-28] the operator widened WHEN: "if you keep panels on, you won't be
able to auto continue peers." With panels live, every question stops a
session until a human clicks, and with the fleet at eight that is the
bottleneck the Brain exists to remove. So a window is correct whenever the
Brain is actively coordinating, at the keyboard or not. Both statements are
about the window; neither licenses a deny.
And the announcement is still half the mechanism, and the half that fails
silently. A held panel does not make a session ask you instead. Whenever you
declare a window, tell every addressable session in the same breath: decide
reversible things yourself and record the reasoning; queue irreversible ones
and message the Brain; do not idle and do not block. [measured 2026-08-28]
two worktrees created after a deny inherited it and lost their panel with no
warning at all, because they did not exist when the announcement went out.
What --status is for now is archaeology. A deny can still exist: one
written by a plugin version before the retirement, one copied forward into a
worktree by git worktree add (it copies the repo root's .claude/), or one
written by hand. --status scans every managed location, marker or not, and
reports three outcomes, never two: live, EXPIRED and unaccounted. At boot:
- anything EXPIRED:
node "$B\brain-panels.js" --expire. Any session may run it, and it touches only the expired. - a live deny you are not continuing:
--onrestores everything the tool set and clears anything else it can see denying. - anything UNACCOUNTED: report it and let the operator decide. "No record" is
not the same claim as "stale".
[measured 2026-09-04]ten of these across two of the operator's own repos, one bulk write at 13:17, no record beside any of them.--statuscorrectly refused to clear what it did not set, and it took the operator's own yes to unblock two of his repos. That is the cost of a deny written outside the tool. The one path that still writes a deny (--off --legacy, kept so the restore path stays testable) writes the record BEFORE the deny, so a crash between the two leaves a record with no deny and never the reverse.
There is deliberately NO SessionEnd hook restoring denies automatically: the hook fires for every session, so a managed session ending would revert the block constraining it. Self-healing at boot, plus the expiry, is the correct place. And the tool never denies the coordinator's own root checkout: a panel is how the coordinator reaches the user when he IS there.
4. The newest heal run, if there is one
Get-ChildItem "$env:USERPROFILE\claude-memory\heal-runs\" | Sort-Object LastWriteTime -Descending | Select-Object -First 3
4b. Read what the last Brain already learned, BEFORE you interview anyone
Get-ChildItem "$env:USERPROFILE\claude-memory\DECISIONS-*.md" | Sort-Object LastWriteTime -Descending | Select-Object -First 3 | ForEach-Object { "=== $($_.Name) ==="; Get-Content $_.FullName -Encoding UTF8 }
A handover transfers ADDRESSES and loses ANSWERS, and nothing about the loss
is visible from either end. [measured 2026-09-05] Three Brain identities
existed inside two days. The new one asked a session whether its site was the
operator's or a client's; a previous Brain had asked that same session the same
question the day before, been told, and replied that it had recorded it. The
session had to answer twice and said so:
"That makes at least three Brain identities inside two days. The addresses transferred. The answer did not, so I have been asked the same question twice and the next Brain will ask a third time."
Re-asking a settled question looks exactly like diligence from the asking end and exactly like cooperation from the answering end, so neither side flags it. The only trace is the session's patience running out.
The answers are already written down. The gap is that a new Brain interviews
before it reads. DECISIONS-<date>.md is where the no-panel-mode branches
above already tell a Brain to record every call it makes, so by the time you
boot, yesterday's Brain has usually written the thing you are about to ask.
Read the last two or three, plus ~/.claude/brain-brief.json, then ask only
what those cannot answer.
Two things this is NOT, both proposed and both rejected on a peer's reasoning from inside the hooks that would have carried them:
- Not a map inside
brain-role.json. That file is a CLAIM of role: small, rewritten whole at every claim, and read by two hooks that fail open on a parse error. A growing map inside it either dies at the very hop it exists to survive, or, the day a claim writes it badly, silently disarmsstop-brain-report.jsandcoordinator-write-guard.jsat once. Keep a claim file a claim. - Never keyed by cwd. That is the routing defect one layer over: a worktree outlives the session in it, so an answer keyed by directory is served to whoever next occupies it. The same session that received two strangers' idle reports would then inherit their ownership answers.
If a machine-readable map is ever wanted, key it by repo + branch with the
session id as provenance rather than as the key, timestamp every entry, and
carry a verify command beside each answer so the next Brain re-runs the check
instead of re-asking. An answer with no re-check command is a memory that rots
exactly the way the role file did. Build this reading step first regardless: it
needs no schema and closes most of the gap on its own.
Use the same commands the triage step below settles on, never git cherry:
gh pr view <N> --json state,mergedAt,headRefOid
git rev-parse origin/<branch> # equal to headRefOid -> nothing to land
[measured 2026-09-06] this paragraph suggested git cherry -v origin/main HEAD
until a coordinator briefed a session to land twelve commits that were already
merged. It followed this line, which is 160 lines ABOVE the step explaining why
cherry cannot answer that question. The corpus was not missing the
instruction; the wrong one was simply closer to where the reader started. A
warning only governs the readers who reach it, so a stale example upstream of it
is not a smaller defect than a missing rule - it is a louder one.
5. Claim the role, then announce the handover
[stated 2026-09-04] the operator, relayed to this file by the Brain session
that heard it: "we should also send a new brain message to all sessions when
changing brains, as they don't have any way of knowing." Second-hand here.
The measurements below are first-hand, one from each end of the failure.
A Brain change is invisible to the fleet, and the file that should announce
it asserts the previous Brain instead. ~/.claude/brain-role.json is read by
stop-brain-report.js, which tells every session's Stop hook where to send its
idle report, and by coordinator-write-guard.js, which arms the product-repo
rail for the session it names. [measured 2026-09-04] that file named a
session archived the previous afternoon. So for a day every Stop hook directed
its report to a dead Brain, and because a peer name resolves through a
worktree, the reports landed on whichever session later occupied that
worktree: three of them inside one hour reached a worker doing unrelated
tooling work there, two of the three client sessions that must hear nothing
from a coordinator. The Brain that found the file also found the guard armed
against the dead session, so its own merges and a branch rename in a product
clone had run with no rail at all; nothing went wrong, and that was luck. A
missing broadcast leaves a session with an old address it might doubt. A
config file naming a dead session gives every session a confident wrong answer
with a hook enforcing it, which is strictly worse, and it is why the fix is a
claim before a broadcast rather than a broadcast alone.
So, after step 4 and before anything is dispatched, in this order:
Read your own name from
ListAgents(its first line is your own entry since Claude Code 2.1.239) and your desktop id from the session store. Never frombrain-role.json, which is the thing that went stale, and never from a cached value: a Brain that read its name once signed six messages with a name peers could not resolve, and every reply bounced for hours.Claim the role by rewriting
brain-role.jsonwithsession_id,peer_nameANDdesktop_session_id, keeping the previous holder underpreviousrather than overwriting it. The snippet is under "Never commit or push in a product repo" in the never-list. Until this is written the Stop hook falls back to "find it by cwd", which names a place rather than a correspondent, and the guard protects nobody.Verify the claim before you act on it.
node "$B\check-brain-role.js" --status. Exit 0 means a live session holds the record; exit 2 means the record needs rewriting before it is broadcast -- an address that resolves to nobody is worse than none, because a session cannot tell a dead coordinator from a busy one. Fix the record and re-run rather than announcing it.Exit 2 covers two verdicts, and the difference is what to fix rather than whether to broadcast.
FAULTmeans nothing in the record reaches, or an address reaches a stranger.DEGRADEDmeans a field decayed while another address is still verified live, and the check names which -- usuallypeer_name, which takes a fresh suffix on every restart whilesession_idanddesktop_session_iddo not. Re-stamp the named field; you do not need a new claim.This step exists because writing the file is not the same as writing it correctly.
[measured 2026-09-04]the Brain that added step 2 then filledsession_idwith its DESKTOP uuid, arrived at by strippinglocal_off it. The two registries use different uuids and nothing converts between them, so the guard stayed armed for nobody and the Stop hook's self-exemption missed -- the second inert-rail of that day, by a different mechanism from the first. A peer found it; the check now finds it in one call.Send BOTH addresses to every live session. A session on the peer protocol cannot resolve a
local_<uuid>and a desktop-only session cannot use the peer name, so one address reaches half the fleet. Say plainly that it is an address change and not a brief, so a mid-task session does not stop. A client session gets the address and nothing else, and is told it will hear nothing further from this coordinator. A chip spawned this boot already carries both addresses in its prompt; skip it, because a broadcast to a session you briefed ten minutes ago is noise.
[measured 2026-09-04] by the Brain that did this first: nine sessions, two
full-form, six client short-form, five chips skipped, and all five idle
sessions woke on it. The peer_name field had already been corrected once
that day, from a wrong suffix, before it was found naming a dead session: two
wrong values by two mechanisms inside one day. A hand-maintained field read by
a hook wants a check, not another correction. That check now exists --
check-brain-role.js, added the same day -- which is why step 3 above is a
command rather than a warning. It reads the sessions directory for a live file
whose name and sessionId match and whose pid answers, joins the desktop
store on cliSessionId, and reports absent / ok / degraded / fault, naming the
dead id and any address that still reaches. It never resolves a coordinator by
cwd: that fallback is the bug rather than the mitigation, because a worktree
outlives the session in it.
[measured 2026-09-08] degraded was split out of fault because the two
consumers of that verdict disagreed with each other: the check's own text said
"PARTLY STALE AND STILL REACHABLE, use desktop session id ..." while the state
it returned said fault, and the Stop hook, which reads the state, told five
sessions in one day that nobody could be reached and to wake the operator. All
five reached the coordinator at the address in that same record. A record with
one decayed field is a field to re-stamp, not a lost channel.
When the boot finishes — report, then act
The boot ends with a REPORT and then work, not with a panel. An earlier version
of this heading read "the terminal action is a question", and under no-panel
mode it is not. A later version said so in its first paragraph and then
specified a two-stage panel, projects then sessions, as the thing to do, and
[measured 2026-09-04] a Brain read both paragraphs and raised the panel. So
this section now describes one ending, and the only panel in it is conditional
and asks one question.
The vacuum this section was written against is real: a session holding an
overseer identity with no work will reinvent coordination to fill it.
[measured 2026-08-24] a session read that sentence during its own boot and,
one turn later, authored itself a four-item work list and offered it as a
panel. Loading the rule was not enough to fire it, because nothing in the boot
said what to do once the boot was done. This does:
Report the state you measured. Fleet, ownership, open PRs, uncommitted work. Each with the population it scanned, and each COULD-NOT-CHECK named rather than folded in with the real zeros.
Select the projects yourself, and log the selection. The mandate (step
- names what is handed over; that IS the selection and it is not a
question. Beyond the mandate, take the survey's order:
brain-brief.jsprints the repo set most recently worked on first, with the age since the newest commit on any ref beside each name, and since 2026-09-04 it refuses to print a set that omits a mandated project, so the list is bound to the mandate rather than to whoever last edited the config. A retired repo is never offered: the survey prints those under RETIRED, excluded on purpose, named so a later session can tell a decision from a config edited by accident, and re-offering one is proposing work the user has already closed. Write the selection into the review log; he can redirect in one sentence, which costs him less than answering a panel.
If the operator is actively in conversation, one panel is allowed here, and its question is "which repos get a chip tier this round". Not "which projects should I work on": the answer to that is always "none, spawn chips", so a panel whose options read as repos to drive is the worked-four-repos-itself failure in miniature, and it is how a Brain came to run a gate inside a product worktree before spawning anything.
[stated 2026-09-04]the operator's correction of that panel, relayed here by the Brain that raised it: "when choosing projects, we should spawn chips, not fix all projects here. separate peers and you communicate with each other."multiSelect: true, options in the survey's recency order, each grounded in a fact the survey printed: open PRs with nobody on them, far behind its trunk, a governed publish queue gone stale, a gate never run. A list of repo names is not a panel.[stated 2026-08-25]projects come before sessions because sessions follow from a project; the "which sessions" question no longer exists as a panel at all, because its answer is one chip per unit of work and the sequence below produces those.- names what is handed over; that IS the selection and it is not a
question. Beyond the mandate, take the survey's order:
Run the post-selection sequence below, for the selected projects only, before asking anything else.
[stated 2026-08-29]the operator defined it: "after selecting the projects we're working on, first see what needs merging, resume updating etc, archive all stale/old sessions and give me easy to copy paste session prompts." The prompts became chips the same day. The order was corrected on 2026-09-04 so that spawning is second rather than last; the sequence's own header says why.Do not author a work list for yourself. Verifying is real work and it is yours, but it arrives from the user in this session. A queue assembled from gaps you noticed is coordination wearing a verification costume, and the role section retired coordination on measurement.
Post-selection sequence: triage, spawn, rescue, merge, archive
Runs once per boot, immediately after the projects are selected, for those
projects only. Order matters, and until 2026-09-04 the order was wrong. It
read triage, rescue, archive, then spawn, with "triage" including running each
PR's gate and merging it. Spawning last meant it was reached last, after the
coordinator had spent itself on chip work: [measured 2026-09-04] a Brain ran
npm ci and a full gate inside one product repo's worktree, merged a PR in a
second, and diffed seven files of a draft in a third, all before spawning a
single chip, while the never-list below said in as many words not to work a
product repo. So: triage is READ-ONLY and only sorts; spawning is second, so
the chips exist before the coordinator does anything else; rescue precedes
anything that can remove a worktree; the merge is the one write the coordinator
keeps, for the reason under "Merging is the coordinator's"; archive is last
because it destroys.
1. Triage, read-only. For each selected repo: open PRs (re-verified
live, never from a survey — gh pr view on each number), unmerged branches,
and any RESUME/handoff newer than the trunk's last commit.
git cherry IS NOT A CONTENT CHECK, and earlier versions of this step said it
was. It compares PATCH IDs, which a squash merge destroys by definition: the
squash rewrites N commits into one with a different patch id, so every original
commit still reads +, meaning absent upstream. [measured 2026-08-29] a boot
ran git cherry | grep -c '^+' across four repos and reported seven branches as
carrying unlanded work. Every one was already merged. Two became session
assignments before peers caught them, and one of those — merging a branch that
cherry said was 1 commit ahead — would have rolled VERSION back two releases
and deleted two test suites, because the branch was 494 lines BEHIND main rather
than ahead.
rev-list --left-right AND THREE-DOT --shortstat FAIL THE SAME WAY, and
this block listed both as if they answered the question. [measured 2026-09-07] a coordinator read +1 / +3 / +5 commits ahead and 213 / 773 /
1033 insertions off them, and dispatched a session to land three branches that
were already merged as PRs #168, #163 and #161. A squash-merged branch is never
an ancestor of the trunk, so --left-right reports it ahead FOREVER; three-dot
A...B counts the branch's whole contribution since the merge base whether or
not that content is already upstream. Both numbers were real and neither
measured landing.
Run the tool, which encodes the order below and prints its population:
node ${CLAUDE_PLUGIN_ROOT}/scripts/check-branch-landed.js --repo <path> --trunk origin/HEAD
By hand, in this order, and the ORDER is the content of the rule:
# 1. plain ancestry -- proves LANDED only; its negative proves nothing
git merge-base --is-ancestor <branch-tip> origin/HEAD
# 2. THE PRIMITIVE: a merged PR whose head IS this tip, found BY SHA
gh api repos/<owner>/<repo>/commits/<branch-tip>/pulls \
-H "Accept: application/vnd.github.groot-preview+json" \
--jq '.[] | select(.merged_at != null) | "\(.number) \(.merge_commit_sha)"'
git merge-base --is-ancestor <merge-commit> origin/HEAD # BOTH halves required
# 3. no PR carries the tip? compare FILES, not counts
git diff --name-only $(git merge-base origin/HEAD <branch>) <branch>
git rev-parse origin/HEAD:<path> <branch>:<path> # identical blob = landed
# 4. only now read the diff SHAPE, two dots
git diff --numstat origin/HEAD <branch>
Look up the PR BY COMMIT SHA, never by gh pr list --search <branch>. That
search is a loose full-text match, and on 2026-09-07 its EMPTY result was read
as an affirmative "this branch was never merged". An empty result from an
unvalidated probe is a claim about the PROBE, never about the world — the same
sentence this skill already applies to a gh failure, applied to a gh
success that found nothing. Absence of a PR is not evidence of anything; fall
through to step 3.
Both halves of step 2 are load-bearing. A merged PR whose merge commit is not an ancestor of THIS trunk landed on a different base, and that is UNKNOWN, not landed.
Read the SHAPE of the diff, not its size. A branch whose diff is mostly
DELETIONS relative to the trunk is behind it, not ahead — that is the trunk's
newer work missing from the branch, and "landing" it is a revert wearing a
merge's clothes. [measured 2026-09-07] five branches reported that shape at
+676/-14725, +361/-10892, +550/-34535, +1262/-32694 and +691/-42544; every one
of them would have reverted the trunk.
The two halves of --left-right are not equally trustworthy, and reading
them as if they were is what went wrong. A large BEHIND count is a real
signal: the branch genuinely lacks the trunk's newer commits. The AHEAD count
proves nothing either way, because a squash-merged branch reports its original
commits ahead forever. So use the behind half to spot staleness, and never
treat the ahead half as a quantity of pending work — that is step 2's job, and
only step 2's.
Run a KNOWN-POSITIVE CONTROL on whatever command you settle on. [measured 2026-08-29] a session's first content check mangled its own pathspec and
returned four false "IDENTICAL" verdicts; it caught that only by running the
same command shape against an older base and confirming it returned a real
diff. A clean answer from an unvalidated probe is a claim about the probe. Sort
into three, and the sorting is the whole of this st
…(truncated)