feat(coord): durable per-seat episode records + a reader that refuses to lie - #399
Merged
Merged
Conversation
…at refuses to lie Cold-start recovery across an account switch. When a pool is exhausted the owner opens ONE session under another account; it must reconstruct what every seat was doing from disk alone, with no inherited context, memory or realtime channel. seat.ps1 writes one episode record per (worktree, session) under mefor-coord/seats/. Involuntary: -Record derives branch, tip, merge-base, touched paths, unpushed count, dirty state and claim/allocation attribution from git and the hook payload. A seat registry was rejected twice on this project, and one ground -- voluntary declaration decays -- is correct and measured, so the hook-written half needs no discipline and -Declare only adds intent on top. fleet.ps1 projects. Nothing is stored as a verdict, because a verdict is read after the world moved; every judgement is recomputed at read time. The receipt states what was EXAMINED, not what was found, and STOP conditions suppress the roster rather than render a confident empty answer. Three defects found by running it, each fixed here: - A worktree path outlives its occupant. The first record written inherited 18 backlog allocations from a session eight days earlier in the same directory. Claims and allocations now carry this-episode vs worktree-inherited, compared against the episode start, and the briefing tells a replacement not to rehome the inherited ones. - ConvertFrom-Json parses an ISO string into a DateTime whose string cast drops the Z, so parsing it back applied the local offset and ages rendered five hours in the future. Re-reading the pattern finds nothing; the type was wrong. - git stash create captures TRACKED edits only, so the highest-value thing to rescue -- a file that exists nowhere but that working directory -- was silently uncovered. Measured against these two scripts, which it would have lost. The record now separates tracked from untracked and the briefing names untracked files as held by no git object anywhere. Design, 29 adversarial attacks and residual risk: mefor-coord/handoffs/2026-08-14-fleet-continuity-cold-start-DESIGN.md
…ting shim Stop only. Stop already pays a pwsh spawn for the mail drain, and the installer measured roughly 366ms per spawn against about 19 tool calls per turn -- a second UserPromptSubmit hook would tax every prompt for a path exercised at account-switch frequency. Stop still bounds the record to the last COMPLETED turn. NOT Shim=std, and that is the point. The std shim is Test-Path-then-run: with the script absent it emits zero bytes and exits 0, byte-identical to a healthy hook with nothing to do. Demonstrated here at 0 bytes / exit 0. The installer already records that class as how a wired-but-resolving-nothing hook survived for weeks. It matters more for this hook than for any existing one. The shim resolves against the PRIMARY checkout, which tracks main and lags it. Until this lands on main every seat resolves nothing -- and under the std shim the fleet would look quiet and healthy while recording nothing, in every session, with every settings.json green. So the shim speaks when it cannot resolve, gated on the checkout actually being this project so unrelated repos on the same machine stay quiet. Fifth marker, mefor-seat. Pairwise non-containment against the four existing markers was re-checked rather than assumed, and the check was validated by a positive control on mefor-mail / mefor-mail-urgent, the exact pair the installer names as the mistake to avoid. Separate marker so the recorder can be removed without disarming the mail drain, which shares the Stop event. Verified: hook exits 0, advances the record, picks up session_id, kind and entrypoint from the payload, and emits zero bytes on the success path -- Stop output is shown to the model, and a line every turn trains the reader to skip the channel including the turn it matters. Not installed. Wiring a user-global hook is the owner's call and comes last.
wshallwshall
enabled auto-merge (squash)
August 14, 2026 20:39
wshallwshall
added a commit
that referenced
this pull request
Aug 15, 2026
… backfill (BACKLOG #1010) The gate this branch adds went RED on this branch's own update-branch merge, and it was right to. scripts/coord/fleet.ps1, scripts/coord/seat.ps1 and scripts/hooks/seat-record.ps1 arrived with #399 at 23:41Z tonight carrying no SPDX header: 3 violations across 1273 files checked, with ruff check and ruff format clean in the same run. NEITHER PR COULD HAVE CAUGHT THIS ALONE, which is the finding rather than an excuse. #399's author had no gate to satisfy, because this branch had not landed. This branch's backfill could not cover files that did not exist when it was written. The incompatibility exists only once BOTH are on main -- which is what the update-branch produced, and why it surfaced on a PR head instead of on main with the cause three commits back. THE WINDOW BETWEEN A BACKFILL AND ITS GATE IS WHERE VIOLATIONS ENTER. A standing note in this project already says a NEW .py needs the header because an earlier sweep covered only EXISTING files; this shows the same hole for .ps1 and shows it is not language-specific. ANY file added between a backfill and its gate landing evades both. The remedy is that the two land close together, and the gap between them is the exposure. Verified in a worktree on this branch: the gate exits 0 over 1273 files, measured UNPIPED because $? after a pipe reports the pager's status and not the gate's -- the trap CLAUDE.md section 11 names by example, which I walked into on the first attempt. Negative control run in BOTH directions: stripping the header back off one file returns the gate to exit 1, restoring it returns 0, so the green means the headers are present rather than that the gate stopped looking. The claim on #1010 was MOVED from worktree b3-quickwins to this one rather than the citation being dropped from the subject. The commit-msg gate keys ownership on the COMMITTING worktree, so it refused the first attempt correctly; dropping the BACKLOG #1010 citation would have cleared the check by making it look at nothing, which is the dishonest spelling BUILDER.md forbids by name.
wshallwshall
added a commit
that referenced
this pull request
Aug 15, 2026
…fill (BACKLOG #1010) (#398) * feat(quality): licence-header gate, with its negative control and the backfill (BACKLOG #1010) AGPL-3.0-or-later is asserted twice at project level, in LICENSE and in pyproject.toml, and per-file provenance was then left to habit. Habit held above 93 percent and decayed silently across a whole package: messagefoundry/tray/ landed (ADR 0113) with no header on any of its seventeen files, every one of them wheel content, and nothing reported it. THE GATE ASSERTS THE VALUE, NOT THE PRESENCE OF THE STRING. A presence-only check passes a file that affirmatively declares the WRONG licence, and five test modules declared Apache-2.0 in an AGPL project. Those are corrected here and are reported as their own violation class, never folded into "missing": an affirmative misstatement is a worse defect than an omission. SCOPE IS POSITIVE AND WAS MEASURED, NOT ASSUMED. Every tracked file carrying one of six extensions is in scope, with no tree exemptions. The trees a reader would expect to need one do not: tee/ (vendored), harness/, samples/, packaging/, docker/, messagefoundry_webconsole/ and the archived docs/benchmarks/results/ are already fully compliant, and a scan for generated content finds none tracked. An exemption list would enumerate things that do not exist, which is worse than no list because it reads as policy and quietly widens. tests/ is in scope on the same evidence: it sits near 94 percent on its own, which is not what a deliberately-exempt tree looks like, and nothing anywhere excludes it. THE GATE SHIPS WITH ITS NEGATIVE CONTROL rather than acquiring one later (BACKLOG #1000). Four planted classes were each observed failing before it was wired anywhere: a missing header; a wrong identifier, including the near-miss AGPL-3.0-only that a prefix comparison would accept; a correct tag inside a STRING LITERAL, which is real here because corepoint_import.py generates headers and a substring check reads a header-emitting file as a headered one; and a correct tag BURIED past the head window, which a whole-file grep accepts. The last two are the ways a header can be present and still not be a header. Measured over the tracked tree: 1266 files in scope, 1049 already compliant, 212 given a header, 5 wrong identifiers corrected. The backfill splices at a single offset, after any shebang, so every other byte is untouched and line endings elsewhere are not normalised. Not yet wired into pre-commit or CI; that is the next commit. The pytest case that runs the gate over the real tree is included, so the invariant is enforced by the ordinary test job regardless. * ci(quality): wire the licence-header gate into pre-commit and CI (BACKLOG #1010) A local pre-commit hook beside ledger-gate and forbidden-content, plus a CI step beside the ruff ones, so a local commit and a PR enforce the same rule. SCOPE IS SINGLE-SOURCED IN THE SCRIPT rather than restated per caller. ruff and bandit each had their scope written twice, drifted, and now need tests/test_lint_scope_parity.py to hold them together; that whole class is avoided here because the extension map lives in COMMENT_PREFIXES and nothing else states it. The hook's files: pattern only narrows what pre-commit bothers to forward, and CI passes no paths at all. THE CI STEP IS DELIBERATELY NOT PATHS-FILTERED, which is a departure from the ruff steps directly above it and is the one judgement call here. The changes.code filter keys on alwayscode='\.(py|ps1|sh|ts|js|yml|yaml|toml|lock|cfg|ini)$' which omits .go. A Go-only PR therefore sets code=false, and an inherited filter would skip this gate on exactly the PR that introduced the headerless file. No .go source is tracked today, so this is a latent gap rather than a live one -- but BACKLOG #1011 rules on whether a Go tree stays, and a gate that silently stops covering a language the moment one is added is the failure this item exists to remove. The scan reads about 1,300 files in under a second, so filtering it would buy nothing and cost a blind spot. THE WIRING WAS WATCHED WORK, not merely configured: the hook was run against a planted headerless file and observed Failed with the file named and the fix quoted, then against a compliant file and observed Passed. A hook that is present in the config and never fires renders identically to one that fires and finds nothing, and only the second is evidence. * test(ide): pin the extension's licence metadata; #1010 step 6's premise was false BACKLOG #1010 step 6 says ide/package.json declares "SEE LICENSE IN LICENSE" while no ide/LICENSE exists, and asks for the file to be added or the manifest field corrected. RE-MEASURED BEFORE ACTING, THE PREMISE DOES NOT HOLD, and neither proposed fix is right: vscode:prepublish runs copyFileSync('../LICENSE','LICENSE') and vsce package runs vscode:prepublish, so the published .vsix carries the licence by construction; ide/.gitignore line 7 lists LICENSE explicitly, so the file's absence from the tree is a RECORDED DECISION, not an oversight. Committing a tracked ide/LICENSE would therefore fight that ignore rule and put a second copy of a GENERATED file under version control -- two records of one fact with no drift signal between them. That is the defect this project keeps paying for, not a fix for one. The item's step 6 is closed as research: there is no dangling pointer. WHAT WAS GENUINELY MISSING IS THE CONTROL, and the master test plan already had it right in the words "marketplace metadata is unverified" (12-vs-code-ide-extension.md, P1). Nothing asserted any of it. Rename the root LICENSE, edit the one-liner, or add LICENSE to .vscodeignore, and the extension publishes with no licence -- or dies at publish time, the latest and most expensive moment to learn it. Four assertions now run in the ordinary pytest job: the prepublish copy's SOURCE resolves, and is this project's LICENSE rather than some other file; the manifest's SEE-LICENSE-IN name matches the file prepublish actually writes; .vscodeignore does not exclude the licence and undo the copy; ide/.gitignore still records that ide/LICENSE is generated. The last one is the assertion that would have saved this investigation: the absence looks exactly like an oversight, so it pins WHY the file is missing where the next reader will meet it. MUTATION-PROVED rather than merely green: LICENSE was appended to ide/.vscodeignore, the suite went to 1 failed, the file was reverted and confirmed byte-identical by hash, and the suite returned to 4 passed. The parsed values were also printed to confirm the assertions are not vacuous -- source '../LICENSE' resolving to the root LICENSE, destination 'LICENSE' matching the declared name. * backlog: record #1010's gate landing, and the owner action CI cannot see THE PAIRED LEDGER COMMIT FOR THIS PR, under the sanctioned interim (c): the required check 'a PR that implements BACKLOG #N must update BACKLOG.md' reads the PR's own diff, and ledger authoring is not a builder's lane. Third such pairing tonight, after #1249/#1026 and #1241. WORTH NAMING: THE GATE FIRED BECAUSE OF A TOKEN I WROTE. It greps the PR title and body for 'BACKLOG #N', and this PR's title carries it because the Lander put it there. The claim is ACCURATE -- the PR does implement the item -- so the honest satisfaction is to update the ledger, not to reword the title until the gate stops looking. A gate you can silence by changing how you describe the work is not a gate. THE AMENDMENT CARRIES THE OWNER ACTION, because the ledger is where it survives: the backfill changed three INSTALLED hook scripts, so machine copies no longer match source and three tests go red locally on EVERY worktree. Those are local-machine tests; CI has no user settings and SKIPS them, so A GREEN CI IS NOT EVIDENCE THE DRIFT IS ABSENT. Remedy is owner-run from a CURRENT checkout, in order: land, pull, then install -- a stale source DOWNGRADES the gate. CLOSURE NOT TAKEN. Step 6's premise was separately found false and the item carries more than the gate itself. Same line held on every pairing tonight. Verified: 528 items each declaring exactly one status; #1010 still OPEN; U+26A0 unchanged at 121. * fix(quality): licence headers on the three files #399 added after the backfill (BACKLOG #1010) The gate this branch adds went RED on this branch's own update-branch merge, and it was right to. scripts/coord/fleet.ps1, scripts/coord/seat.ps1 and scripts/hooks/seat-record.ps1 arrived with #399 at 23:41Z tonight carrying no SPDX header: 3 violations across 1273 files checked, with ruff check and ruff format clean in the same run. NEITHER PR COULD HAVE CAUGHT THIS ALONE, which is the finding rather than an excuse. #399's author had no gate to satisfy, because this branch had not landed. This branch's backfill could not cover files that did not exist when it was written. The incompatibility exists only once BOTH are on main -- which is what the update-branch produced, and why it surfaced on a PR head instead of on main with the cause three commits back. THE WINDOW BETWEEN A BACKFILL AND ITS GATE IS WHERE VIOLATIONS ENTER. A standing note in this project already says a NEW .py needs the header because an earlier sweep covered only EXISTING files; this shows the same hole for .ps1 and shows it is not language-specific. ANY file added between a backfill and its gate landing evades both. The remedy is that the two land close together, and the gap between them is the exposure. Verified in a worktree on this branch: the gate exits 0 over 1273 files, measured UNPIPED because $? after a pipe reports the pager's status and not the gate's -- the trap CLAUDE.md section 11 names by example, which I walked into on the first attempt. Negative control run in BOTH directions: stripping the header back off one file returns the gate to exit 1, restoring it returns 0, so the green means the headers are present rather than that the gate stopped looking. The claim on #1010 was MOVED from worktree b3-quickwins to this one rather than the citation being dropped from the subject. The commit-msg gate keys ownership on the COMMITTING worktree, so it refused the first attempt correctly; dropping the BACKLOG #1010 citation would have cleared the check by making it look at nothing, which is the dishonest spelling BUILDER.md forbids by name.
wshallwshall
added a commit
that referenced
this pull request
Aug 15, 2026
…tection left Builder 3 released #1010's claim after verifying the landing on the ref with a control, which was the correct call: PR #398 merged 03:49:37Z, adding commit 8e551f4, the gate ships on main with its negative control and the backfill. The consequence is what this amendment records. Until now #1010 was protected by a claim, which is machine-local and unversioned and therefore protected exactly one checkout. That claim is now gone on that checkout too, so the item is landed, unclaimed, and still reading as an unstarted P2 quick win -- and a quick-win label is an active invitation rather than a neutral one. Also recorded: a banner protects nobody until it is on main. This text is written on a dispatcher branch and builders read origin/main, so the write and the protection are two separate events with a gap between them, during which the item is exactly as dangerous as before anyone noticed. That is the general property of every ledger correction, not a flaw in this one, and it is the argument for a standing check over a diligent reader. Carried forward for whoever writes the closure: the window between a backfill and its gate landing is where violations enter, and it is not language-specific. #399 added three .ps1 files with no header after the backfill was written and before the gate landed; each pull request was green in isolation and the incompatibility existed only once both were on main, so no single-PR check could have caught it. That makes the hole structural rather than anecdotal, and it will recur in the next language the gate learns. Verified: backlog_status_check.py passes; #1010 still open with exactly one banner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Durable per-seat episode records plus a reader that refuses to lie, so a session under a different
account can cold-start and rebuild the fleet.
4 files, 1072 insertions, ZERO deletions. Nothing existing is touched.
(A two-dot diff against this branch reports a fifth file with 58 deletions. That is
mainmovingforward under a branch one commit behind --
#393touched that test -- not an edit here. Three-dot,and
git log -- <path>on the branch, both confirm it was never touched.)The design choice worth reading, because it is a refusal to inherit a silent failure
The Stop-hook shim resolves against the primary checkout first and falls back to the current worktree,
so until this is on
main, every other seat's recorder resolves nothing.The author deliberately did not use the standard shim for that case.
Shim='std'emits zerobytes and exit 0 when it cannot resolve -- byte-identical to a healthy hook with nothing to do.
The fleet would have looked quiet and green while recording nothing, with every
settings.jsonpassing. They demonstrated that failure at 0 bytes / exit 0 before choosing. Theirs speaks instead.
That is the same class this repo has been hit by all day: a null that a broken instrument and a
healthy one print identically.
Not installed, deliberately
Wiring a user-global hook across five config roots is the owner's call and the last step, not the
first. This PR lands the scripts; it installs nothing.
The leak gate caught a slug and it was fixed, not allowlisted
The first attempt was blocked on an internal branch slug hardcoded in a comment. Removed the slug
rather than adding an exemption -- clean on re-run, and re-verified here over all 1072 added lines
before pushing to a public repo.
A finding that belongs in the record, not just the design doc
git stash createcaptures TRACKED edits only -- there is no-u. So a file that exists nowherebut a working directory -- the single highest-value thing any rescue could capture -- is exactly
what a stash does not cover. The author found this because it silently failed to capture the two
scripts they were writing at the time.
The record now separates tracked from untracked, and names untracked files as held by no git object
anywhere. Unpushed work is the thing that dies when a seat goes quiet; untracked work is a
strictly worse case of it, and it is invisible to the mechanism most likely to be trusted for rescue.