fix(resume-state): a handoff in a LINKED WORKTREE reconciled a different initiative (#1164) - #1197
Conversation
…rent initiative (#1164) `scripts/resume-state.sh <base-clone-path-to-a-doc-that-lives-in-a-linked-worktree>` reported `NO SUCH FILE`, fell back to newest-of-N, and printed a complete, confident digest — GIT/PR state, referenced PR states, a CLAWGATE block and DRIFT findings — about a DIFFERENT initiative. Handoff docs land in linked worktrees by construction: `claude/RULES.md` makes worktree isolation the standing default for any agent that modifies files, while `/handoff`'s kickoff template names the base clone. #1159 does not reach it — that fix re-anchors on `$root`, and a linked worktree is a different root. Measured live on this host, same invocation, both revisions: d86e5f8 handoff: handoff-skill-tiering-and-ci-cache.md (newest of 93) HEAD handoff: handoff-linux-cpu-profiling.md, repo retargeted to devrc-1136 Two independent changes: 1. `worktrees_holding <dir> <base>` enumerates `git -C <dir> worktree list --porcelain` and returns the copies of `claudedocs/<base>` that exist. The clone is an ARGUMENT, never `$PWD`: for `<X>/claudedocs/<base>` the search is scoped to `<X>`'s own clone, so `embedded_md_path`'s absolute-token guarantee — we never serve a same-named doc out of THIS repo — survives. Exactly one hit resolves; two or more resolve NOTHING and the gap names them (capped at four listed, count never capped: this clone has 142 worktrees and one basename in 28 of them); zero stays a miss. The relative `$root` re-anchor gets the same treatment, on the same anchor. 2. `named_missing` now SKIPS the fallback chain instead of recording the miss and falling back anyway. No exit code and no refusal — the empty HANDOFF routes to the existing, tested "NOTHING was reconciled" branch. Scoped to `named_missing` only: a bare basename or a topic slug is not a claim about a file, and both of those MEASURED fallback cases still resolve. Verification: * 165 tests in test_resume_state_handoff_resolution.py (103 before), green; the 10 new ones RED at d86e5f8 and green at HEAD. * mutation_battery_resume_state.py: 56/56 killed, none survived, control 165 passed / 0 failed. Fourteen new mutants cover both halves; the `rev-parse --git-dir` guard is recorded as EQUIVALENT rather than shipped as a permanent survivor. * scripts/tests in full: 11114 passed, 1 skipped. * claude/skills/resume/SKILL.md carried a claim this change makes false ("the digest is about a different initiative than you named") — corrected, split by input class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
…ree resolution scoped to the n Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
…and the doc's next-probe was the refuted thread (#1205) Two defects in claudedocs/handoff-tmux-webapp.md, both found by reconciling it against live state at resume time. 1. A diff3 conflict marker was committed into the doc (line 699, "||||||| parent of e1d1318"), left from the #1162 merge. Removed. 2. The two open-investigation blocks on test_subsystem_store_api.py were SUPERSEDED and the doc did not know it. grep -ci 'ci-repro|fsync' over the doc returned 0, while three PRs had already merged: #1181 0c33384 it is fsync CONTENTION, not seed/ordering (+ reproducer) #1190 634c328 the empty read was reported as a SECOND response #1193 48a5540 the hang guard SAMPLED its own arming instead of waiting The doc still carried "Leading hypothesis: non-determinism / seed-ordering" and sent the next reader to open a PR for fix/xdist-parametrize-values-deterministic. That is the refuted thread, and following it costs a session -- which is exactly the failure mode this doc documents twice about its own open-investigation blocks. Both blocks are kept verbatim and marked SUPERSEDED, because their eliminations are still sound; it was the FRAMING that was wrong. A new DIAGNOSED block points at scripts/ci-repro/README.md as the canonical, maintained write-up rather than copying it. Deliberately NOT written as "fixed", per this doc's own shelf-life rule. Measured 2026-09-01 after all three merged: #1197 is red on TestAHungRoundTripSAYSWhichSideBlocked::test_a_stall_in_the_FSYNC_region_is_NAMED -- a fourth distinct case -- while #1199 passed the same tier. Status is "mechanism identified and reproducible, three contributing defects removed, no consecutive greens in the failing tier yet". Also reconciled two live facts the doc had stale: - ZacxDev/homelab-infra#572 is MERGED (2026-08-31 18:36Z); the doc framed it as open. Its own warning is preserved: it fixes one of the two TaskRunTimeout causes. - Rank 8a's "both clients are stale again by construction" was tested and is WRONG. Re-measured 16:29Z: server 0.8.21, both clients 0.8.21, and the cross-host round trip moved a number ([] -> 1 -> []). Recorded as a dated measurement, with the point that "stale by construction" is a hypothesis to test rather than a fact to carry. Gate: the six content/handoff gates that a docs change can red pass in the dev-host tier -- 652 passed. Docs-only diff; no code path is reachable from it. Claude-Session: https://claude.ai/code/session_019kRyYxB956h5XfpwoX2sKX Claude-Session-Id: 3c805fcf-e39f-4465-82ef-f4ae62af65aa Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…worktrees for ANY token (#1197 audit round 1) F1 - the hole the PR's own comments say cannot exist. `<X>/claudedocs/<base>` was scoped to `${dir%/claudedocs}`, but the relative branch re-anchored on `$root` - the cwd's repo - and, since #1164, enumerated `$root`'s WORKTREES too, for any `<Y>`. MEASURED in /tmp/f1v (devrc + linked worktree devrc-topic holding the doc, sibling other-repo that does not): cd devrc && resume-state.sh "other-repo/claudedocs/handoff-only-in-worktree.md" d86e5f8 handoff: (none found - git-only) + the NO SUCH FILE gap honest 3e42bb0 handoff: handoff-only-in-worktree.md, # repo: .../devrc-topic, gaps EMPTY silent, wrong clone `<Y>` is a SIBLING of the repo, so it does not resolve from the cwd at all and a `-d` test falls straight through to `$root`. The discriminator is the NAME: the re-anchor and the search may use `$root` only when `<Y>` is empty, or `<Y>`'s last component is the cwd repo's own directory name (#1159's kickoff-template shape). This also closes the pre-existing single-tree half - #1159's plain `$root/claudedocs/$base` re-anchor fired for a foreign `<Y>` before any worktree search existed - and narrows one legitimate case on purpose: a sibling worktree named RELATIVELY now misses (the absolute form still resolves; commented). F2 - the ambiguity gap said "N worktrees of that clone" while a bare `claudedocs/<base>` token names no clone at all. Now "the clone that path resolves against", true of all three shapes. F3 - the /resume skill claimed the search is "never the one you are standing in", which the bare and repo-prefixed shapes have always contradicted. Restated by input class. F4 - `LC_ALL=C sort -u` (the expectations are built with Python `sorted()`, i.e. codepoint order; every existing fixture collates identically both ways and could not see it), and human-named worktrees are enumerated before ephemeral `.claude/worktrees/agent-*` checkouts. Measured on the real clone: one basename in 28 worktrees, 27 of them agent checkouts. Count still uncapped. F5 - a token carrying `*`, `?` or `[` is no longer recorded as "the caller named a specific document". `set -f` leaves it a literal that cannot exist, and since #1164 part 2 that suppressed the whole fallback chain - so `claudedocs/handoff-*.md`, a literal /resume's own SKILL.md passes through twice, reconciled NOTHING. F7 - `assert doc not in handoff_line(out)` could not fail (the line above pins that string to an exact literal). Asserted over the whole digest instead. Verification - 180 passed (was 165). RED at 3e42bb0: 10 new cases (2 FOREIGN-worktree, 2 FOREIGN-re-anchor, 1 sibling-worktree narrowing, 5 glob). GREEN at HEAD. - Mutation battery, PYTHONDONTWRITEBYTECODE=1, NOT APPLIED checked FIRST: 69/69 killed, 0 NOT APPLIED, survived none; control 180 passed / 0 failed. 13 new rows X1-X13. X1 SURVIVED the first run - every foreign fixture kept the doc in a WORKTREE, so the plain re-anchor had nothing to find; the added base-clone fixture kills it and nothing else. - W6/W8/W13 patterns re-pinned to the moved lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
…he `$mine` gate reached only the worktree search The previous commit gated the worktree search on `$mine` and left the `$root/claudedocs/$base` RE-ANCHOR ungated. That is mutant X1 exactly — the one the fix round recorded as "SURVIVED the first run", added a test for, and reported as closed. It was not closed: the test it added (`test_a_FOREIGN_relative_token_is_not_re_anchored_on_THIS_repos_own_copy`, both params) is RED on that commit's own pristine tree, so the suite was 178 passed / 2 failed rather than the 180 reported, and the mutation battery ABORTED with `CONTROL (pristine): 178 passed, 2 failed — baseline is red, no verdict below would mean anything`. Consequence in the shipped code: a foreign relative token still resolved THIS repo's own copy. MEASURED on a fixture whose doc is in the BASE CLONE with no worktree — `other-repo/claudedocs/handoff-wanted.md` typed inside `devrc` returned `handoff: handoff-wanted.md`, silently. That is the pre-existing #1159 half of the hole, which F1's fix was supposed to close as a side effect. 🔴 Why no test saw it, and why the battery is what caught it: every foreign-token fixture kept its doc in a WORKTREE, so the plain re-anchor had nothing to find and the two clauses were indistinguishable. The fixture that tells them apart is the one with NO worktree. Ask what the fixture omits that a real instance has. One line: `[ -n "$mine" ] &&` on the re-anchor condition, with the reason recorded beside it so the next reader does not re-split the gate. Verified: 180 passed (was 178/2); the four-shape matrix on the /tmp/f1v fixture is foreign-relative MISSES, repo-prefixed / bare-claudedocs / absolute all RESOLVE; `bash -n` clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
… code, and its re-indent silently disarmed X1 🔴 60c893b's commit message is FALSE and this commit is the correction. It claimed `d756a1f8` "gated the worktree search on `$mine` and left the re-anchor ungated", that the suite was "178 passed / 2 failed rather than the 180 reported", and that a foreign relative token "still resolved THIS repo's own copy" in shipped code. None of that is true of `d756a1f8`. MEASURED, in a `git archive` extraction of `d756a1f8` with no working tree and nothing else running: * the condition is already `*) if [ -n "$mine" ] && [ -n "$root" ] && …` — byte-identical to what 60c893b "added"; * the suite is **180 passed**; * `test_a_FOREIGN_relative_token_is_not_re_anchored_on_THIS_repos_own_copy`, both params — the two 60c893b called red — **pass**. 🔴 WHAT ACTUALLY HAPPENED, because the mechanism is the reusable part. The mutation battery rewrites `scripts/resume-state.sh` IN PLACE, once per mutant. It was run in this worktree while a subagent was still active in it, and the tree was then read with `sed` and the result attributed to the commit. What was read was mutant X1 mid-flight — whose definition is exactly `[ -n "$mine" ] && ` removed from that re-anchor. The "diagnosis" matched a known mutant so precisely because it WAS the mutant. `git status` had been checked and was clean, three tool calls earlier: a cleanliness check is a fact about an instant, not a property of a run, and it is worthless while a concurrent writer exists. Two rules, both already written down, both broken here: never two file-modifying agents in one checkout, and a live probe against a dirty tree is evidence about the artifact on disk, never about the committed source. 🔴 AND 60c893b DID DO REAL HARM — just not the harm it claimed. Splitting `*) if …; then` across two lines to insert its comment made X1's mutation anchor match **0x**. The next battery run reported `X1 … !! PATTERN OCCURS 0x — NOT APPLIED`, i.e. the row for the very hole this branch exists to close silently stopped testing anything. That is #1115.1's defect class exactly, reproduced in a different harness eleven commits later. This commit restores the line byte-exact, keeps the corrected warning ABOVE the `case` where it cannot break the anchor, and says in the source not to reformat that line. Verified: behaviour identical to `d756a1f8` (the diff against it is comment-only); the anchor matches exactly 1x again; 180 passed; `bash -n` clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
…d a live mutant as shipped code Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
…e that the previous replace dro Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
|
Audit round 1 complete — seven findings, all fixed. Claims below for the delta round. Two corrections to this PR's own record, both mine. And Gate
⚠ Tekton's earlier red on this PR was |
…e anchor guard is now a CHECK instead of a comment 🟡 THE DEPLOY CLAIM WAS FALSE, and it was asserted while naming the arbiter that disproves it. The PR body and this branch's handoff doc both said `scripts/resume-state.sh` is nix-managed and inert until a `home-manager switch`. MEASURED with `readlink -f`, which `claude/RULES.md` calls the only arbiter: the script resolves to ITSELF — a working-tree file, live on a plain `git pull` — and `command grep -rn resume-state nix/` returns 0 matches. Only `claude/skills/resume/SKILL.md` is store-managed. The consequence is an INVERSION, now stated in both places: after a pull the narrowed resolver is LIVE while the deployed skill still says "searches the worktrees of the clone the path NAMED (never the one you are standing in)" — the sentence this branch retires. Pull and switch together, or expect an agent to hit a refusal with no deployed prose explaining it. 🟡 THE ANCHOR GUARD IS DETERMINISTIC NOW. `60c893b7` disarmed mutant X1 by reformatting the line its pattern anchors on, and the remedy shipped for it was a comment saying DO NOT REFORMAT — which protected 1 of 69 anchors, left 38 more in the sibling battery unprotected, and is the shape `claude/RULES.md` says to replace. `scripts/tests/test_mutation_battery_anchors.py` imports both batteries' MUTANTS tables and asserts every `old` occurs EXACTLY ONCE in the script it targets, with a two-way ledger so a new battery cannot be forgotten. 🔴 Its negative control is the real defect, not a synthetic one: run against `git show 60c893b:scripts/resume-state.sh` it fails naming `X1: occurs 0x`. 0 offenders at HEAD. It earned its keep immediately by validating the four mutation rows added in this same commit. 🟡 A RESIDUAL IS NOW STATED RATHER THAN OVERCLAIMED. The `$mine` gate compares only the last path component, so a foreign tree whose directory NAME matches this repo's still re-anchors: `backup/devrc/claudedocs/<base>` typed inside `devrc` resolves, silently. That is a residual, not a regression — before this branch EVERY relative `<Y>` re-anchored — and no cheap correct discriminator keeps #1159's kickoff shape working. SKILL.md claimed it could not happen; it now says what actually holds, the source comment agrees, and tests pin the residual so it cannot drift unnoticed. Labelled invariant guards, not regression coverage, with their killing mutants named. 🟢 `<repo>//claudedocs/<base>` regressed from resolve to miss this round — `${ydir##*/}` of `devrc/` is empty and can never match. Trailing slashes are normalised; mutant X16 pins it, and a control confirms `other-repo//claudedocs/` still misses. 🟢 The handoff doc claimed the F7 assertion was "proven reachable under a mutant" and named none; every existing mutant died to the preceding equality first. New two-site mutant X17 makes line 649 itself the killer, measured. The claim is now backed rather than dropped. 🟢 Nothing mutated the ephemeral-worktree classification PATTERN, only the preference around it. X14 (`*/.claude/*`) and X15 (`*/agent-*`) plus fixtures holding a human worktree under `.claude/` and an `agent-` dir outside it. Both SURVIVE all 186 pre-existing tests — the new fixture is the only thing that sees them. Counts re-derived, not copied: `X10` was a mis-cite for `W10`; the head sha was stale; the handoff-skill headroom is 236 B, not 7 B, because #1144 merged (`3d0b77e5`) and raised MAX_BYTES to 27,000. The "appears twice" count for `claudedocs/handoff-*.md` in the resume skill disagreed three ways across sources, so the count is gone rather than replaced with a fourth. Verified: anchor test 6 passed, and RED against `60c893b7` naming X1. 467 passed across the four resume-state suites; 259 across the doc/skill gates. `bash -n` clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
|
Audit round 2 complete — seven findings, all closed in Verification
Ledger — round 2 · payload lines changed THIS round: 52 ( |
…e three sites it was reported at Round 3 returned seven findings, all scaffolding, against a range whose payload was ONE non-comment line. Four were prose asserting more than the code does — the third consecutive round to find that shape inside the fix for the previous instance of it. So these fixes pin the INVARIANT wherever it applies rather than correcting the reported sites. 🟡 THE RESIDUAL WAS STATED NARROWER THAN IT BEHAVES, toward the harm. Both the skill and the source said a foreign token whose directory NAME matches this repo's "resolves this repo's copy with no gap". MEASURED: with the doc only in a linked worktree it resolves out of THAT worktree — a different branch — and with two worktrees holding it the ambiguity gap fires. Three legs, now all stated. The test that named itself as pinning the residual only ever put the doc in the base clone; it is now parametrised over holder as well as `<Y>` (4 cases) and asserts the `# repo:` line, because `handoff:` prints a basename and cannot tell the base-clone leg from the worktree leg. 🟡 X17 COULD NOT KEEP ITS OWN CLAIM. Battery 1 scored on "any test failed" and X17 fails 107 of 187, so deleting the whole-`out` assertion it exists to prove reachable left it still printing KILLED. Ported the sibling battery's `expected` attribution: verdicts are now KILLED / KILLED(attributed) / KILLED-WRONG-REASON, the last a battery failure. Control: with the assertion present f=110 and the phrase appears in the `E ` lines; with it deleted f=108 — still "killed" under the old criterion — and the phrase is absent, so the row now reports KILLED-WRONG-REASON instead of a false pass. 🟡 THE MULTI-SITE GUARD WAS DEFEATED BY THE SHAPE ITS COMMENT CLAIMED TO PREVENT. `zip(old, new)` truncates and the counts derive from the zipped pairs, so `old=(A,B), new=(N,)` silently drops site B from both the occurrence check and the application, scored under the row's name. No row is malformed today. Pinned in the anchor test, which already walks every row of both batteries, so future rows are covered without anyone remembering. 🟢 THE LEDGER GLOBBED THE MINORITY NAMING CONVENTION — `mutation_battery_*.py`, while ten of the twelve instruments are `mutants-*`. Widened to both, with an explicit exemption list and a test that fails if an exempted file ever grows a `MUTANTS` table. 🟢 THE DOCSTRING'S ENUMERATOR ENUMERATED NOTHING: `git grep -l 'PATTERN OCCURS'` returns zero `.sh` files, because that family spells its marker differently. Replaced with one that lists both families. 🔴 AND ROUND 3 WAS ITSELF PARTLY WRONG, WHICH IS WHY THIS WAS CHECKED RATHER THAN IMPLEMENTED. It claimed two `.sh` batteries carry no did-not-apply control. Verified file by file: all nine carry one, in three different spellings (`MUTATION DID NOT APPLY`, `mutation did not apply uniquely`, `🔴 NOT-APPLIED`) — which is the actual reason no single marker grep enumerates them. The true version is recorded instead of the claimed exception. 🟢 Two stale doc claims: W10 kills 47 tests, not 42 — re-measured here, and the count is still enforced by nothing, which is now said. And three sites described round 2's fixes as uncommitted working-tree changes; they were committed as `7285291b`. Verified: 201 passed across the anchor module and the resolution suite; `scripts/resume-state.sh`'s diff is mechanically comment-only; `bash -n` clean. Every new guard watched red, one mutation at a time, with the script restored between each — W6 is what makes the worktree legs attributable to the worktree search rather than to the re-anchor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
|
Audit round 3 complete — seven findings, all scaffolding, all closed in 🔴 Round 3 was itself partly wrong, and this was checked rather than implemented. It claimed two Verification
Ledger — round 3 · payload: |
…commit status in prose, and drop a count that was stale on arrival Round 4 returned three findings, no 🔴, nothing behavioural, and recommended CLOSING the ladder. Its argument for closing is structural rather than a judgement call, and it is the reason this is the last round: 🔴 THE RECURRING SHAPE IS SWEPT. Rounds 1-3 chased "prose claiming behaviour wider than the code provides" and found it three times, each inside the fix for the previous instance. Round 4 went looking for it at new sites and did not find it — testing four ways rather than reading: the three-leg residual holds at every input class including one the PR does not pin (base-clone precedence over several worktrees) and one it structurally cannot reach (`$root` empty outside a repo); the `# repo:` assertion is mutation-proven to discriminate the legs; the attribution mechanism has a working positive AND negative control; the pair guard and the ledger are two-way with no double-skip path. 🔴 WHAT RECURRED IS A DIFFERENT SHAPE, AND IT IS A FIXED POINT, NOT A DEFECT THE LADDER CONVERGES ON. A commit that corrects a status sentence writes a new status sentence that its own landing falsifies. Round 5 would do it again. The exit is to change the CONVENTION, which is this commit, not another round: * the handoff doc no longer asserts where the current round's fixes live. It said "in the working tree, UNCOMMITTED, on top of `7285291b`" — false the instant `add65a0a` landed, and the same defect the paragraph above it was written to correct. It now says why such a sentence cannot be kept true and points at `git log 7285291.. --oneline` and `git status`, which cannot be stale. * the `107 of 187` figure is gone from all three sites. It was written by the commit that ADDED three tests, so it was stale on arrival (measured: 110 of 190) — verbatim the error that commit was fixing one file over. The argument it supports needs "most of the suite", not a number, and a number nothing enforces is one edit from being wrong again. The one remaining occurrence is inside the note explaining why it was removed. 🟢 And the `expected`-phrase rule is stated at its real width: `E ` lines carry the rewritten EXPRESSION REPR as well as the message, so a phrase must be absent not only from the suite source but from anything `resume-state.sh` can PRINT AT RUNTIME — otherwise a failing digest comparison echoes it into an `E ` line and attributes the kill to the wrong row. Round 4 also CONFIRMED round 3's refutation: all nine `.sh` batteries carry a did-not-apply control, in the three spellings named. Writing round 3's F3 as stated would have shipped a false sentence inside the fix for a false sentence. Ladder: rounds 1/2/3/4 returned 7/7/7/3 findings; executable payload went 187 -> 1 -> 0 -> 0. The attribution gate cannot fire on a prose-payload PR, so the stop is the stated criterion — no 🔴, blast radius bounded at "a note contains a false sentence", and the shape swept at every site rather than the one reported. All three now hold. Verified: 201 passed across the anchor module and the resolution suite; `py_compile` clean; this commit's diff is prose and comments only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ Claude-Session-Id: 2b50cc65-f7bf-4f03-8f8e-19bd8fabe25c
scripts/resume-state.shgiven an explicit path to a handoff doc that lives in a linked worktree reportedNO SUCH FILE, fell back to newest-of-N, and emitted a complete, confident digest reconciled against a different initiative.That fires on this repo's own mandated workflow:
claude/RULES.mdmakes worktree isolation the standing default for any file-modifying agent, so handoff docs land in linked worktrees by construction.Closes #1164
The measured symptom, same command both revisions
Using a doc that exists in exactly one linked worktree (
handoff-linux-cpu-profiling.md, indevrc-1136), invoked with the base clone's path:# repo:d86e5f81handoff-mention-detection.md— a different initiative, plus theNO SUCH FILEgaphandoff-linux-cpu-profiling.md/home/zach/workspace/devrc-1136Part 1 — resolve into linked worktrees, scoped to the NAMED tree's own clone
New
worktrees_holding <dir> <base>enumeratesgit -C <dir> worktree list --porcelainand returns theclaudedocs/<base>copies that exist: rc 0 + one path, rc 2 + N paths, rc 1 + nothing.🔴 The clone is an ARGUMENT, never
$PWD. For<X>/claudedocs/<base>it is${dir%/claudedocs}— the tree the caller actually named — and only the relative re-anchor uses$root. This preserves the invariant already in the file:Searching
$PWD's worktrees would have reintroduced exactly that bug one level down.test_an_UNRELATED_repos_worktrees_are_never_searchedandtest_an_ABSENT_clone_is_not_served_out_of_THIS_repos_worktreespin it.Ambiguity is not adjudicated. Two or more worktrees holding the basename ⇒ nothing is chosen, and the gap says how many and where (new rc 3 carries the token plus the candidate list).
Part 2 — a named-missing handoff path no longer falls back
The fallback chain is wrapped in
if [ -z "$named_missing" ], soHANDOFFstays empty and the existing, already-tested branch fires:NOTHING was reconciled; the DRIFT section below is about no document at all.🔴 No exit code and no refusal were introduced. The script has none today and always reports; adding one would be a contract change for the skill and every caller. Reusing the empty-
HANDOFFpath gets the same protection with no new surface.🔴 Scoped to
named_missing, NOTunresolved. A bare basename is a slug, not a handoff-shaped path, and the file records a MEASURED case where the fallback correctly served exactly that doc.test_a_bare_BASENAME_slug_STILL_falls_back_and_resolvesandtest_the_civitai_slug_STILL_falls_back_and_resolvesare the regression guards for it.Verification
Regression matrix, pre-fix script + post-fix tests, re-derived rather than taken from the implementing agent:
d86e5f81test_resume_state_handoff_resolution.py: 165 passed (151 at base, +14).Both sandbox tiers, ONE AT A TIME per #1088, read from each runner's own
RESULT:line rather than a piped exit code:checks.x86_64-linux.pytests—RESULT: PASS (exit=0), collected=20090 passed=20087 skipped=3 failed=0 (floor 18383)checks.x86_64-linux.nodetests—RESULT: PASS (exit=0), suites=5 files=41 tests=1449 pass=1449 fail=0 (floor 1367)Mutation battery (
mutation_battery_resume_state.py), re-run independently underPYTHONDONTWRITEBYTECODE=1, checkingNOT APPLIEDbefore reading any kill count: 56/56 killed, 0 NOT APPLIED, survived: none. 14 new rows W1–W15, each with a named killer — including W2 (search$PWD's clone instead of the named one), W9 (fallback runs for a named path) and W10 (the guard widened to any supplied argument, killed by 42 tests including both slug controls).Two decisions taken beyond the brief, both kept
The ambiguous enumeration is capped at 4, with
, and N more. Measured on the real clone: 140 linked worktrees, and one handoff basename appears in 28 of them — uncapped that is a ~2.5 KB single line inside the GAPS block. The count is never capped, only the enumeration, and both sides of the threshold are pinned (W13/W14/W15).claude/skills/resume/SKILL.mdcarried a claim this change makes false — "That gap means the digest is about a different initiative than you named" — now true only of the slug class. Split by input class rather than left to rot.Four existing tests changed expectation — read them
They asserted the fallback that #1164 calls the bug:
test_a_prose_path_that_does_NOT_exist_is_not_taken(×2 params),test_a_DIRECTORY_named_like_a_handoff_is_not_taken, and twotest_every_gap_sentence_is_pinned_WHOLErows. Each carries its reason in its docstring. The vacuity controls (..._decoy_PROVES_these_are_not_vacuous,..._three_docs_ARE_takeable_...) exist so that "reconciles NONE" cannot pass merely because nothing was takeable.⚠ The two halves deploy DIFFERENTLY — corrected
An earlier revision of this section said both files are nix-managed and inert until a
home-manager switch. That was wrong, and measured with the arbiterclaude/RULES.mdnames:readlink -fscripts/resume-state.shgit pull— it is a working-tree file, andcommand grep -rn resume-state nix/returns 0 matchesclaude/skills/resume/SKILL.md/nix/store/…-devrc-claude-skills/resume/SKILL.mdhome-manager switch🔴 So there is a window where the resolver is narrowed but the deployed prose still describes the old behaviour. In it, an agent following the deployed skill pastes a relative sibling-worktree path, gets
(none found — git-only), and has no deployed text explaining why. Pull and switch together, or expect that gap.Corrections to this PR's own record
Two are mine and are retracted in the history rather than quietly edited:
60c893b7claimedd756a1f8shipped an ungated re-anchor. False. I readscripts/resume-state.shwhile the mutation battery was rewriting it in place and attributed mutant X1 to the commit — the diagnosis matched a named mutant because it was the mutant. Disproved in agit archiveextraction ofd756a1f8: 180 passed, condition already gated.6f4d748bretracts it.60c893b7was comment-only, which I wrongly called harmless. Splitting*) if …; thenacross two lines made X1's anchor match 0x, so that row reportedPATTERN OCCURS 0x — NOT APPLIEDand stopped testing the hole this branch closes.6f4d748brestores the line byte-exact; a collected test now enforces every anchor's uniqueness rather than a comment asking politely.Superseded figures from earlier revisions of this body: the battery is 69/69 (not 56/56) and the suite is 180 passed (not 165) — both grew with round 1's fixes.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Emy2y7tGJjRee3GV2cNuaJ