A security suite that gates git push reported 49 failures locally and
zero in CI. Every failure was this repo's own source, found a second time inside a live worktree.
Fixing it produced a worse bug for a few minutes: a scan of zero files that printed
"3 passed".
test-jq-injection.sh -not -path "$REPO_ROOT/.worktrees/*" excluded
.claude/worktrees/ NOT excluded
test-json-island-breakout.sh dirs if d not in {".git","node_modules",".worktrees"}
← prunes by NAME; .claude/worktrees' leaf is
"worktrees", so it is walked into
.claude/worktrees/ is CC's
native layout and the default since #3315 retired ork's own provisioning. CI's clean clone has no
worktrees, so it stayed green while local dev went red — and a security gate that teaches its
operator to ignore it is worse than one that is absent.
paths[0] is not "this checkout"git worktree list puts the main worktree first, which is a different tree
whenever the test runs from a worktree. Skipping paths[1:] made the scanned tree its own
prune root.
<repo>/.worktrees/<task> means the main checkout is an ancestor of
this one. Prefix-matching against it pruned everything:
repo_targets | wc -l → 0 suite output → "3 passed, 0 failed"
A scan of nothing finds nothing and reports clean. Only
test-json-island-breakout.sh caught it, because it already asserts its own scan is
non-vacuous — "scanner matched NO JSON islands — the scan below is vacuous, not clean". That
one assertion is the entire reason this did not merge green.
exclusions DERIVED from `git worktree list`, not literals skip the tree being scanned skip any checkout that CONTAINS it ← the ancestor rule keep both literals as a backstop for UNregistered debris git will not list + vacuity guard in test-jq-injection.sh: fail if the target list collapses below 100 scripts
remove the ancestor check: ✗ target list collapsed to 0 shell scripts — the scan below would be vacuous, not clean (check worktree_prunes) 3 passed, 1 failed restore: ✓ target list is live (359 shell scripts in scope) 4 passed, 0 failed
| where | jq-injection | island-breakout |
|---|---|---|
| primary checkout | 4/4 · 359 scripts | 5/5 · 30 islands |
| git worktree | 4/4 · 359 scripts | 5/5 · 24 islands |
Passing in one environment is what created this bug in the first place, so a single green run would not have been evidence.
Third instance of one root cause today. #3391 was context-gate switching limit
regimes between a checkout and a worktree. The branch guard needs an explicit path because it cannot
see the shell's cwd (#3411). Now two security scanners walking into the wrong layout. The repo has two
worktree layouts and its tooling keeps knowing about one — which is why the exclusion here is
derived rather than a second hardcoded literal. Adding .claude/worktrees beside
.worktrees would have fixed today and set up a fourth instance.