Filed by the objectui domain:ui execution seat (PM session session_01CRJge11jso9TpXRWFt1Z49) after making this exact mistake today and taking a wrong action on it. Unassigned and unlabelled — grading, domain:* and the repo it should live in are triage's.
The gap
Both repos' agent instructions carry the rule "核验 main 用 origin/main" — read file contents with git show origin/main:<path> or git grep <pat> origin/main, never from the shared working tree, whose HEAD other agents switch under you.
⛔ The rule covers reading contents. It does not cover enumerating which files exist. A sweep that obeys the first half and violates the second produces a zero that looks like a full-tree scan:
for f in .github/workflows/*.yml; do # ← file list from the WORKING TREE
git show "origin/main:$f" | grep -q PATTERN # ← contents from origin/main
done
Every file it examines is read correctly. Files that exist on origin/main but not at the working tree's current HEAD are never iterated at all, so the loop cannot report them and cannot know it missed them.
Measured, today, on objectui
| reading |
value |
| shared checkout HEAD |
claude/pm-dispatch-ui-838ssh @ 14ef9f5f2 |
ls .github/workflows/*.yml | wc -l (working tree) |
30 |
git ls-tree --name-only origin/main .github/workflows/ | grep -c '\.yml$' |
31 |
| the missing one |
.github/workflows/governed-surface-guard.yml |
I ran that loop to decide whether flipping a batch of draft PRs to ready would trigger any CI. It reported no workflow subscribes ready_for_review. The correct answer is that governed-surface-guard.yml:36 declares types: [opened, synchronize, reopened, ready_for_review], and that file carries a comment explaining precisely why:
ready_for_review is the addition and it is the point: flipping a governed draft to ready is the first move of the exact sequence this guard exists to interrupt, and it is not in the default set
Acting on the wrong reading, I flipped 10 PRs to ready. Each fired a fresh Governed Surface Queue Guard run and went from mergeable_state: clean to unstable — during an ongoing runner-capacity outage (objectstack#13281), where a queued check is not cheap.
⚠️ Why the zero-hit control did not save it — this is the transferable part
The existing discipline says a zero is only a reading if a control term that must hit is run in the same query and does. I ran one: pull_request matched 5 workflow files, so the method demonstrably worked.
⛔ The control validated the matcher and said nothing about the enumeration. Those 5 files existed at the working tree's HEAD, so a control drawn from the same faulty file list can never expose the file list as faulty. A control has to be able to fail for the reason you are worried about, and a same-source control structurally cannot.
⇒ the generalisation worth having: when a sweep's population and its per-item read come from different sources, the control must be drawn from the population source, not the read source — e.g. compare the iterated count against git ls-tree origin/main before trusting any zero.
Suggested fix shape — mechanical, ⛔ not more prose
The instruction files already carry the read-side rule; adding a paragraph next to it is the weakest available fix and the one most likely to be skimmed. Better candidates, in rough order of strength:
- A guard/lint rule over agent-authored shell that flags a working-tree glob (
for f in <path>/*, ls <path>/*) in the same command as a git show origin/main: or git grep ... origin/main read — the two together are the signature, and either alone is fine.
- A one-line canonical idiom in the instruction text to be copied rather than re-derived:
git ls-tree --name-only origin/main <dir>/ as the way to enumerate, mirroring how git show origin/main:<path> is already the canonical way to read.
- Extend the zero-hit rule to name the population/read split explicitly, since the existing wording is satisfied by a control that cannot detect this class.
Adjacent, checked, distinct
objectstack#11809 — both worktree-first guards substring-match /worktrees/ in the git-dir path, so a primary checkout under ~/worktrees/ is unguarded from subdirectories. Same family (worktree-discipline holes), different mechanism: that one is about where you edit, this one is about what you enumerate when reading. Not a duplicate.
Generated by Claude Code
Filed by the
objectuidomain:uiexecution seat (PM sessionsession_01CRJge11jso9TpXRWFt1Z49) after making this exact mistake today and taking a wrong action on it. Unassigned and unlabelled — grading,domain:*and the repo it should live in are triage's.The gap
Both repos' agent instructions carry the rule "核验 main 用
origin/main" — read file contents withgit show origin/main:<path>orgit grep <pat> origin/main, never from the shared working tree, whose HEAD other agents switch under you.⛔ The rule covers reading contents. It does not cover enumerating which files exist. A sweep that obeys the first half and violates the second produces a zero that looks like a full-tree scan:
Every file it examines is read correctly. Files that exist on
origin/mainbut not at the working tree's current HEAD are never iterated at all, so the loop cannot report them and cannot know it missed them.Measured, today, on
objectuiclaude/pm-dispatch-ui-838ssh@14ef9f5f2ls .github/workflows/*.yml | wc -l(working tree)git ls-tree --name-only origin/main .github/workflows/ | grep -c '\.yml$'.github/workflows/governed-surface-guard.ymlI ran that loop to decide whether flipping a batch of draft PRs to ready would trigger any CI. It reported no workflow subscribes
ready_for_review. The correct answer is thatgoverned-surface-guard.yml:36declarestypes: [opened, synchronize, reopened, ready_for_review], and that file carries a comment explaining precisely why:Acting on the wrong reading, I flipped 10 PRs to ready. Each fired a fresh
Governed Surface Queue Guardrun and went frommergeable_state: cleantounstable— during an ongoing runner-capacity outage (objectstack#13281), where a queued check is not cheap.The existing discipline says a zero is only a reading if a control term that must hit is run in the same query and does. I ran one:
pull_requestmatched 5 workflow files, so the method demonstrably worked.⛔ The control validated the matcher and said nothing about the enumeration. Those 5 files existed at the working tree's HEAD, so a control drawn from the same faulty file list can never expose the file list as faulty. A control has to be able to fail for the reason you are worried about, and a same-source control structurally cannot.
⇒ the generalisation worth having: when a sweep's population and its per-item read come from different sources, the control must be drawn from the population source, not the read source — e.g. compare the iterated count against
git ls-tree origin/mainbefore trusting any zero.Suggested fix shape — mechanical, ⛔ not more prose
The instruction files already carry the read-side rule; adding a paragraph next to it is the weakest available fix and the one most likely to be skimmed. Better candidates, in rough order of strength:
for f in <path>/*,ls <path>/*) in the same command as agit show origin/main:orgit grep ... origin/mainread — the two together are the signature, and either alone is fine.git ls-tree --name-only origin/main <dir>/as the way to enumerate, mirroring howgit show origin/main:<path>is already the canonical way to read.Adjacent, checked, distinct
objectstack#11809 — both worktree-first guards substring-match
/worktrees/in the git-dir path, so a primary checkout under~/worktrees/is unguarded from subdirectories. Same family (worktree-discipline holes), different mechanism: that one is about where you edit, this one is about what you enumerate when reading. Not a duplicate.Generated by Claude Code