Skip to content

fix(guard): the roadmap guard counted agy lane scratch as a second copy of the tree (1107 phantom claims) - #3272

Closed
noahgift wants to merge 1 commit into
mainfrom
PMAT-3226-lane-scratch-universe
Closed

noahgift wants to merge 1 commit into
mainfrom
PMAT-3226-lane-scratch-universe

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

What

make gate went red on a clean branch with 1107 "NEW unprovable claims", every one pointing at an innocent roadmap row. The claims were real rows — read twice.

.claude/worktrees/ is agy's throwaway lane workspace: a full checkout, roadmap.yaml included, created per review lane and left behind when a lane dies. It is in .git/info/exclude, so nothing under it can ever reach the tree CI gates. But this guard's universe deliberately includes untracked files — that is the hole it was written to close (#2512: an untracked file is exactly how a new uncited claim arrives) — so it walked straight into the lane checkouts and counted every entry twice.

The arithmetic says it plainly:

before after
roadmap files in the universe 46 23
completed claims 2266 1133
NEW (unbaselined, uncited) 1107 0

Exactly half. The lane worktree was duplicating the entire universe.

Why it is a one-line class of fix that was already known

scripts/check_complexity_ratchet.sh:149 already prunes */.claude/worktrees/*. That lesson was paid for once and never propagated; this guard is the second place it was needed. Of the scripts that mention lane scratch at all, only one actually excluded it — the rest name it in prose.

The prune is applied in is_candidate() rather than only in the os.walk, so the git ls-files half of the universe is covered by the same rule; the walk additionally skips descending into what was measured here as 66 registered lane worktrees.

This widens no hole

A path under .claude/worktrees/ is in .git/info/exclude and cannot be committed, so it can never be part of the tree this guard gates. Excluding scratch from a guard over the tree is not a weakening — scanning it was the defect, because a guard that reds on files CI cannot see is a guard nobody can act on.

RED first

A fixture roadmap planted at .claude/worktrees/lane-deadbeef/docs/roadmaps/roadmap.yaml with a completed-and-uncited entry must contribute 0 records. Without the prune the guard's own case table reads:

  FAIL  want 0          got 1          lane scratch contributes no records

The row ships with the fix, in the guard's existing case table: the universe block.

Found by

Verifying #3271 (APR-RELEASE-001 §5 P0·Instrument). A make gate run there failed on four checks; three were pre-existing or time-dependent and one was mine. A second gate run after fixing mine surfaced this one, which the first run had not reached — the review quorum I had dispatched in between left the lane checkout that the guard then walked into.

Separately measured and not acted on here: 66 registered worktrees under .claude/worktrees, 1 prunable, 0 with a missing directory. They are live directories that may belong to other sessions, so pruning them is not this PR's business.

no-close: this is a guard-universe defect found while verifying #3271; it has no issue of its own and arrival already outruns closure 2:1, so it files nothing new.

ont-delta: none — the §11.1 delta for the guard surface is a code contract over the universe predicate, and it waits on ONT-1 (pv census), epic #3269.

🤖 Generated with Claude Code

…py of the tree

`make gate` went red on a clean branch with 1107 "NEW unprovable claims",
every one of them pointing at an innocent roadmap row. The claims were real
rows — read twice.

`.claude/worktrees/` is agy's throwaway lane workspace: a full checkout,
roadmap.yaml included, created per review lane and left behind when a lane
dies. It is in `.git/info/exclude`, so nothing under it can ever reach the
tree CI gates. But this guard's universe deliberately includes UNTRACKED
files — that is the hole it was written to close (#2512, an untracked file
is exactly how a new uncited claim arrives) — so it walked straight into the
lane checkouts and counted every entry twice.

The arithmetic says it plainly: completed claims 2266 before, 1133 after.
Exactly half. Universe 46 roadmap files before, 23 after. NEW 1107 -> 0.

check_complexity_ratchet.sh:149 already prunes `*/.claude/worktrees/*`. That
lesson was paid for once and never propagated; this guard is the second
place it was needed. The prune is applied in `is_candidate` rather than only
in the walk, so the `git ls-files` half of the universe is covered by the
same rule, and the walk skips descending into what was measured as 66
registered lane worktrees.

This does not widen any hole: a path under `.claude/worktrees/` is in
`.git/info/exclude` and cannot be committed, so it can never be part of the
tree this guard gates.

Case-table row ships with the fix: a fixture roadmap planted at
`.claude/worktrees/lane-deadbeef/docs/roadmaps/roadmap.yaml` with a
completed-and-uncited entry must contribute 0 records. It reads
`want 0 got 1` without the prune.

Pmat-Ticket: PMAT-3226
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift noahgift added this to the 0.69.0 milestone Sep 14, 2026
@noahgift
noahgift enabled auto-merge September 14, 2026 14:11
@github-actions

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3272 head=9cab1fc0fa293258062e61cad4b393c6b3cfbcfc verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

@noahgift
noahgift added this pull request to the merge queue Sep 14, 2026
@noahgift
noahgift removed this pull request from the merge queue due to a manual request Sep 14, 2026
@noahgift
noahgift added this pull request to the merge queue Sep 14, 2026
@noahgift
noahgift removed this pull request from the merge queue due to a manual request Sep 14, 2026
@noahgift
noahgift added this pull request to the merge queue Sep 14, 2026
@noahgift
noahgift removed this pull request from the merge queue due to a manual request Sep 14, 2026
@noahgift

Copy link
Copy Markdown
Contributor Author

Superseded by #3295, which carries this change unmodified along with eight other
collision-free CI/guard PRs as a single merge-queue entry.

Not abandoned and not rejected: the commits are merged into
PMAT-3231-batch-1-ci-guards verbatim (clean=9 conflicted=0), and this branch is
retained — reopening is one click if #3295 does not land.

Why: a successful merge_group CI run is median 78 min with max_entries_to_merge = 1,
so nine entries is ~12 h of queue for 623 insertions. As one entry it is 78 minutes.
Root cause and the full five-whys: #3294.

@noahgift noahgift closed this Sep 15, 2026
noahgift added a commit that referenced this pull request Sep 15, 2026
… semantically

Nine branches each inserted their roadmap entry at the correct slot RELATIVE TO
MAIN AT THE TIME. main then gained PMAT-3228 and PMAT-3229, so merging #3272
placed PMAT-3226 after them by textual context: 3222, 3228, 3229, 3226.

check_roadmap_sorted.sh is RED on the merged tree and GREEN on all nine branches
individually. This is the class of defect a batch surfaces and per-PR CI cannot:
each input is sorted, the union is not.

Pmat-Ticket: PMAT-3231
@noahgift noahgift mentioned this pull request Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant