Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,29 @@ went red, and no workflow reads that label back. So the Console finds both by as
at spawn, because no hook will invent a goal, by design: a machine that invents one writes a
record that looks declared and says nothing.

6. **A brief can be wrong by the time you read it, and nothing will tell you.** Verify it against
the tree before you act on it: read the diff of **every PR it names**, at hunk granularity, and
re-locate every line number by symbol. **Where the brief and the tree disagree, the tree wins.**
***Do not scope this check to how recently the brief was written.*** Two windows give the same
symptom and **the wider one dominates**: a brief goes stale AFTER dispatch, in minutes, and it is
written stale because the ITEM it was cut from is stale, over weeks. A Manager seat reported six
of eleven briefed items already answered at spawn on 2026-09-04 -- by an ADR accepted before the
brief, by work shipped under a different number, by a PR the item itself says not to rebuild.
**Attributed, not verified here.** The structural cause is that an item records its own research
and nothing records the work that ANSWERS it, so a settled row still reads as current.
**Line numbers are navigation aids and never evidence** -- the same seat measured four anchors
adrift by 50, 86, 581 and 593 lines in one day, one item with both of its anchors dead.
Measured here 2026-09-04, the after-dispatch window: a chip named three drift sites, and minutes
later the spawner took item 3 itself and pushed it as `c2f549f42` on PR 837. The receiver read
that diff before touching anything, saw both hunks already rewritten, and skipped it. Trusting
the brief would have put two PRs on the same two comment blocks, to meet at merge with the Lander
resolving prose by hand. **Two of the same brief's other three items also failed to survive a
read of their sources**, so one confirmed drift is a reason to re-check the rest, not to correct
that line and carry on. ***"The brief is disposable" above says it may be thrown away; it does
not say it was true when written.*** **Finding an item already answered is a GOOD outcome** --
record it with evidence and stop, rather than building it again. BACKLOG #1448, same family
as #1391.

### The Console plans, spawns, and holds the owner's attention

- **Plan first, then spawn.** For anything past a trivial change the Console produces a plan and
Expand All @@ -346,6 +369,13 @@ went red, and no workflow reads that label back. So the Console finds both by as
- One brief per Builder. After about two failed attempts at the same problem, spawn a fresh Builder
with a better brief rather than reuse a poisoned context. A Builder cannot do this. When you are
stuck after two attempts, push what is green and say in the PR body that the brief needs re-cutting.
- **If you take back part of a brief you already dispatched, mail the receiver, because you cannot
update the chip.** `dismiss_task` withdraws only a chip the user has **not** acted on, so a
started one stays live and frozen around your stale text, and no channel carries the correction.
Say which item is already done and where it landed. **This binds whichever seat dispatched, not
only the Console:** any seat can raise a chip, and in the 2026-09-04 case above the spawner was
the session that then pushed the fix. It corrected its own BACKLOG item in the same change and
still could not reach the chip, which is the whole shape of the defect -- BACKLOG #1448.
- Give each session its own git worktree (`scripts/worktree/new.ps1 -Name <x>`, cleanup with
`remove.ps1`). Each gets an isolated checkout, branch and `.venv` on the same remote and the same
PR flow. See [`docs/WORKTREES.md`](docs/WORKTREES.md). The AI project memory is shared across
Expand Down
138 changes: 138 additions & 0 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22560,6 +22560,7 @@ So `test_the_script_prefers_its_own_repo_over_an_earlier_path_entry` supplies th

**Adjacent and NOT fixed here, named rather than numbered.** `docs/WEBCONSOLE-PACKAGE.md`'s seam-refresh procedure is stale in three steps left behind by #1220: it says to bump `ENGINE_UI_SEAM` by hand (`1` to `2`) when the value is a derived digest, it says to update curated lists in this script that #1220 retired, and its step 5 prescribes `python scripts/webconsole_seam_snapshot.py > tests/golden/...`, the shell redirect this script's own docstring forbids because PowerShell's `>` writes UTF-16LE with a BOM into a file the test reads as UTF-8. That is doc drift with its own cause and it wants its own item; folding a documentation rewrite into a `sys.path` fix would make both harder to review.


## 1434. the tooling-partition gate names the file it rejects and not the remedy, so the Builder who trips it has already exited

> 🚧 **Filed 2026-09-03. The failure message is fixed on this branch. Five open pull requests still carry unregistered test files, and only their own branches can fix them -- they are named below.** `tests/test_tooling_partition.py::test_every_non_engine_test_is_classified` fails when a file matching `tests/test_*.py` sits in neither `tests/tooling_manifest.txt` nor `_STAYS_WITHOUT_IMPORTING`. It is not deselected by `-m 'not tooling'`, so it reds **all three required `test` legs at once**. PR 774 hit exactly that, and its Builder's process had exited before any leg reported.
Expand Down Expand Up @@ -22627,3 +22628,140 @@ All 137 listed entries were swept for a repo-rooted read of `messagefoundry/**`.

1. **The five pull requests above.** A Builder must not push to another seat's branch, so each is fixed by whoever next touches it: append `tests/<name>.py` to `tests/tooling_manifest.txt`, keeping the list alphabetical. **The gate is passable and two pull requests that night passed it correctly -- nothing here asks for it to be weakened.**
2. **Three copies of the manifest parser, pinned against nothing.** This file, `_tooling_basenames` in `tests/conftest.py` (the copy that actually applies the marker), and `_manifest_paths` in `tests/test_ci_tooling_gate.py` all implement the same rule. Extracting one `tests/_tooling_manifest.py` is the real fix. Related: `test_every_manifest_entry_trips_its_own_gate` in `tests/test_ci_tooling_gate.py` feeds each manifest entry back as its own changed path, so on the manifest arm every entry matches itself unconditionally.

---

## 1448. a dispatched brief is frozen at spawn: the chip cannot be corrected and nothing tells the receiver it has drifted

> 🔢 **FILED 2026-09-04 by the session that read the stale brief.** Not started. A **fleet-process** defect with no engine, PHI or deployment axis (sec. 0) -- nothing here reaches a running instance, because there are none. What it would cost is duplicated Builder turns and a prose merge conflict a human then resolves by hand.

**The defect in one sentence: a chip is a snapshot of the tree at spawn, and once the receiver has started it there is no path to correct it and no signal that it needs correcting.**

`CLAUDE.md` sec. 5 already says *"The brief is disposable. The BACKLOG item is the record."* That covers a brief being **thrown away**. It does not cover a brief being **wrong** by the time it is read, and those are different failures with opposite remedies: a disposable brief you may ignore, a stale one you must first detect.

### The measured incident, 2026-09-04

**#1443 and #1445 are allocated and open in PRs 837 and 859; neither is on `main` yet**, so `dangling_citation_check.py` reports both unresolved until they land. #1439 is on main. Cited anyway, per the rule in `CLAUDE.md` sec. 5 that an allocated-but-unlanded number is cited and said so.

A session working **#1443** raised a task chip naming three drift sites. Item 3 was a docstring in `scripts/webconsole_seam_snapshot.py`.

Minutes later the author of that line -- working **#1439**, and declining to open a competing branch over two comment blocks -- handed the same fix to the spawning session directly. It took it and pushed `c2f549f42` on **PR 837**. **That was the correct call**: the session already in the file is the cheapest place for the change.

**The chip was already dispatched, and the spawner then had nowhere to put that fact.** `dismiss_task` withdraws only a chip the user has **not** yet acted on; this one had been started, so it stayed live, frozen, and wrong.

***THE SHARPEST PART OF THIS ROW IS THAT THE RECORD WAS UPDATED AND ONLY THE CHIP WAS NOT.*** #1443's own body carries the handover in the same change, under the heading *"ONE MORE SIBLING WAS TAKEN, ON A HANDOVER"*. So the spawner did exactly what sec. 5 prescribes -- the BACKLOG item is the record, and it corrected the record -- **and the receiver was reading the other artifact, the one with no update path.**

**The receiver -- PR 859, working #1445 -- avoided the duplicate for a reason that does not generalise.** It read PR 837's diff at hunk granularity before touching anything and saw both comment blocks already rewritten. Had it trusted the brief, two PRs would have rewritten the same two hunks and met at merge, with the Lander resolving prose by hand.

### Two more of the same brief's items failed to survive a read of their sources, and they are three different faults

**A brief that drifts once has usually drifted more than once**, because the tree moves and the brief does not, so the count of bad items tracks elapsed time rather than the author's care. But the three faults below have three different causes, and a fix aimed at one will not touch the others.

| item | what the brief said | what the source says | fault |
|---|---|---|---|
| 3 | fix the docstring in `scripts/webconsole_seam_snapshot.py` | already fixed and pushed as `c2f549f42` | **stale** -- correct at spawn, overtaken |
| 1 | delete one line, `messagefoundry_webconsole/__init__.py:29` | the deletion makes the file **worse**. Verified by reading: `:29` is a truncated fragment ending on the word "the"; removing it alone puts the pre-#279 range framing on `:27-28` (*"A pair outside this set"*, *"PEP 508 compat range"*) directly against its contradiction on `:30-31` (*"EXACTLY the engine seam it was built against -- deliberately a single value, not a range"*). The correct fix is a three-line rewrite. | **wrong when written** -- never true, not stale |
| adjacency | `docs/adr/0143-...md` is "adjacent, probably leave alone" | #1443's carve-out **does** list it, among *"Three more, all in the #1220 class and all deliberately out of this diff"* -- **and the same sentence argues against fixing it**, that *"an accepted ADR records what was true when written and is better left dated than edited"* | **a compression of a source that says two things.** The brief is defensible here; what it is not is a substitute for reading the carve-out |

***THE THIRD ROW IS RECORDED AGAINST THIS ITEM'S OWN INTEREST, AND DELIBERATELY.*** It was reported to this session as a plain staleness -- the brief deferring a site the carve-out wanted fixed -- and the source does not support that reading. A row asserting the brief was wrong there would be a stale citation of exactly the kind this item exists to describe. **It still supports the rule**, because the receiver's recovery is the same in all three cases: go read the source, not the brief's one-line summary of it.

### THE DEFECT IS THE MECHANISM, NOT THE AUTHOR

Handing item 3 to the session already in that file was right, and re-cutting the chip is not an option anyone declined -- **it does not exist**. Read this row as an argument about the channel: **a dispatch artifact with no update path and no staleness signal will eventually be acted on stale, and the only thing standing between that and a hand-resolved merge conflict is a receiver who happens to verify.**

### Family, and why this is NOT folded into #1391

**#1391 is the same family and is cited rather than extended** -- both are a dispatch artifact that is wrong by the time it is used. The mechanisms and the fixes are disjoint:

| | #1391 | this row |
|---|---|---|
| when it goes wrong | **at generation.** `respawn.ps1:1339`/`:1426` emit a relative command rooted at the predecessor's checkout, so the brief is wrong the instant it is written | **after dispatch.** The brief was correct at spawn and the tree moved underneath it |
| candidate fixes | an absolute path in the brief, or `seat.ps1` keying on the session rather than the invoking tree | a chip that cites the record instead of restating it, or a head-SHA stamp the receiver can diff from |

Neither fix does anything for the other row. **#1391's own body warns that "a fix aimed at the wrong half will look like it worked on the seats that were never affected"** -- folding these together would build that trap on purpose.

### AMENDED SAME DAY: THE WINDOW THIS ROW FIRST NAMED IS THE SMALLER HALF

**This item was filed scoped to spawn-to-read, and two Manager seats answered within the hour that
the dominant window is filed-to-dispatch.** Both reports are attributed, not verified here; the
correction is recorded because **the original scoping was the defect this row exists to describe.**
A rule reading *"a brief can go stale between spawn and read"* invites a reader to check how fresh
the brief is and skip -- which is the SDS-3.4 shape the incident's own `c2f549f42` commit was fixing,
a warning whose stated reason licenses the failure it exists to prevent. `CLAUDE.md` sec. 5 item 6
was widened accordingly.

**The reported population, from a seat that dispatched fourteen Builders that day: six of eleven
briefed items were already answered at spawn.** Each by a different mechanism, and none of them a
race:

| item | how it was already answered |
|---|---|
| #1117 | founding premise died when ADR 0172 shipped the change **under a different item number** |
| #1317 | its open half decided by ADR 0180, **accepted five days before the brief**; the item never recorded it |
| #1139 | one of three named targets already built under #1248 |
| #1085 | marked fixed 2026-08-26, banner deliberately left open; the item said so, the ranking table did not |
| #1107 | shipped in PR 488, and the item's own body says **"DO NOT REBUILD IT"** |
| #1172 | both brief items shipped, in PR 603 and a 2026-09-03 gate change |

***THE STRUCTURAL CAUSE, AND IT IS BIGGER THAN THE CHIP.*** **An item records its own research, and
nothing records the work that ANSWERS it.** A ruling or a PR that settles a question never touches
the row that asked it, so the row keeps pointing at a settled question and every dispatch screen
reads it as current. The frozen chip is one instance; a row that outlives its own answer is the
general case, and it is measured in weeks rather than minutes.

**Anchor drift is the second half and is reported worse than the first.** Four anchors adrift by
**50, 86, 581 and 593 lines** in one day, one item with both of its anchors dead. Hence the flat
rule now in sec. 5: **line numbers are navigation aids and never evidence.**

### THE CORRECTION PATH EXISTS FOR ONE WORKER TYPE AND NOT THE OTHER

**This row's title says "cannot be corrected", and that is true of a chip and of a `claude -p`
session. It is NOT true of a subagent.** `MANAGER.md:30` carries the table: a Console's workers are
separate `claude -p` sessions, a Manager's are **subagents in the Manager's own process**. A Manager
reported sending mid-flight corrections to four running subagents on 2026-09-04; each landed at the
worker's next tool round and all four acted on it.

**So "what to do about a stale brief" has two answers by seat, and a single rule that ignores that is
wrong for one of them.** A fix aimed only at the frozen-chip case would leave the Manager path
untouched, and a fix assuming corrections are deliverable would not reach a Console Builder at all.

***AND THE OBVIOUS FIX PUSHES IN A DIRECTION ANOTHER RULE ALREADY WARNS ABOUT.*** "Send more
corrections" collides with `MANAGER.md` sec. 5, which says to hand down **readings, not
conclusions**, because a worker told your conclusion will apply it and its own correct evidence will
lose. **A correction must therefore carry the new MEASUREMENT and let the worker re-decide** -- never
the sender's new conclusion.

### The mitigation that has evidence behind it, and it is two lines

The same seat put two lines at the top of every brief, and reports **six Builders hitting a stale
item with none wasting its turn**; two produced better findings than the original item asked for:

1. *Check it is not already answered. If it is, record that with evidence and STOP -- that is a good
outcome, not a failure.*
2. *Re-measure every line number this item cites.*

**Line 1 is the load-bearing half**, and it is a permission rather than an instruction: a Builder
that believes finding-it-done is a failed turn will build it again rather than report it.

### Where the authoritative text lives is itself unresolved, and this row does not settle it

`CLAUDE.md` sec. 5 is no longer the only place seat rules live. The vault's `roles/` folder carries
`COMMON.md`, `MANAGER.md`, `BUILDER.md` and others, and `MANAGER.md`'s standing rules say a conflict
between a playbook and `COMMON` goes to the **owner** rather than being resolved by any seat.

**Checked before amending sec. 5, and reported as a null result rather than skipped:** `BUILDER.md`
carries no rule about verifying a brief against the tree, and `MANAGER.md` carries none the new
bullets contradict. **So there is no conflict to escalate today** -- but sec. 5's roster does not
list a Manager seat at all, while `MANAGER.md` exists and is being followed, and **that gap is a
question for the owner, not for this row.**

### What a fix looks like, smallest first

1. **Prose, and it is already written.** `CLAUDE.md` sec. 5 now tells the receiver to verify a brief against the tree before acting, and tells whichever seat dispatched to mail the receiver when it takes an item back. **That is a workaround, not a mechanism.** It costs the receiver a diff read per named PR, and it fails silently the first time somebody skips it.
2. **A chip that cites the record rather than restating it.** Sec. 5 already names the BACKLOG item as the record, and the incident above shows the record staying accurate while the chip did not. A chip reduced to a citation would let the spawner correct the item and have the receiver read the correction. **Unmeasured:** nobody has checked whether a citation alone carries enough for a cold receiver, which is the whole reason briefs restate.
3. **A staleness signal.** Stamp the chip with the head SHA of every branch it names, so the receiver can diff from a known point rather than guessing what moved.

**Do not start at 3.** The receiver already recovers by reading diffs, so a signal telling it to do what it already does buys the least of the three. **Measure first how often a chip outlives its own accuracy** -- this row is a population of one, and one incident cannot tell a rare race from a routine one.

**Cluster:** Fleet coordination. **Priority:** unscored -- filed after the 2026-09-03 scoring pass, so it carries no row in the ranking table above. **Verdict:** build (small), pending that measurement. **Severity:** no engine effect, no PHI axis, no deployment axis (sec. 0). It costs duplicated Builder turns and prose conflicts resolved by hand.

Loading