Repository navigation
Description
Finding
The worktree-reaper's CLEAN gate never looks at ignored files, so a fleet worktree whose only non-tracked content is ignored files is classified clean and reaped — and git worktree remove deletes the ignored content with the tree, unrecorded in the reaper's audit log.
Found by pr-diff-review reviewing PR #3698 (merged 2026-11-01, merge commit 6313875c7b505305ccd12ec168c408a37df178c3, verified ancestor of main bcc090152f3fd7d97d5fbc81c4815224a39d4d30 via compare API, behind_by 0). The code is unchanged on current main.
Evidence
infra/worktree-reaper/bin/worktree_reaper.py,status_entries()(PR infra: worktree-reaper — retire dead fleet worktrees (6-hour quiet rule) #3698 diff; same code on current main at ~line 289): runsgit status --porcelain=v1 -z --no-renames --untracked-files=all— no--ignored— so ignored paths appear in neither the tracked nor the untracked list.evaluate_tree()sets CLEAN ok = no tracked changes and no untracked files beyond the generated-dirt allowlist;reap_tree()re-verifies with the samestatus_entries()and chooses--forcesolely from allowlisted untracked dirt. Ignored files are invisible at both decision points and are never written to the run JSONL record.- Laptop probe (2026-11-06, synthetic repo, this review): a worktree containing only an ignored file (
local-state, matched by.gitignore) shows an empty reaper-view status; plaingit worktree removeexits 0 and the ignored file is deleted with the tree. A second worktree with an allowlisted untracked file plus the same ignored file:git worktree remove --forceexits 0 and the ignored file is deleted too — the reaper's record for that reap would list only the allowlisted dirt.
Why it matters
The reaper's stated invariant (README in PR #3698) is that it "only removes worktrees whose content is proven on origin". The PUSHED gate proves the branch tip; ignored files are never pushed by definition, so they are exactly the content the proof cannot cover — local state files, ignored databases (cf. pkgs/id/.gitignore: .identity.db, .identity.redb), credentials-adjacent scratch, or build/state directories a worker left in a tree that then went quiet for 6 hours. Today such a tree reaps as "clean" and the loss leaves no trace in the audit record.
Related open reaper issues, none of which covers this: issue #5946 (tests run by no workflow), issue #5740 (/proc scan fail-closed), issue #6547 (stale registrations), issue #6394 (config validation). Deduped this run against the open-issue list and searches for "worktree reaper ignored" / "reaper ignored files" — no match.
Acceptance criteria
- The CLEAN gate and the pre-removal re-verification enumerate ignored files (e.g.
git status --ignored/--ignored=matching): any ignored file beyond an explicit, config-named ignored-dirt allowlist blocks the reap (fail closed, like the other gates). - If an ignored-dirt allowlist is introduced, every ignored path present in a reaped tree is recorded by path in the run JSONL before removal — a reap never silently deletes unlisted content.
- Synthetic-repo tests in
infra/worktree-reaper/tests/test_worktree_reaper.pycover: ignored-only tree is kept; allowlisted-untracked + ignored tree is kept (or, if allowlisted-ignored is implemented, reaped with the ignored paths in the record); the existing reap matrix still passes. - The README/docstring CLEAN gate description states the ignored-files rule explicitly.
Activity
developing-today commentedon Nov 7, 2026
CODE-2082
dezren39 commentedon Nov 7, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 7, 2026
Picked up by repo-worker-code-t (Lane T, fallback mode — queue backend down, fleet queue health 503 PostgREST PGRST002). Triage of #6734: verifying the CLEAN gate's ignored-files blindness against current main and with a synthetic-repo probe calling the reaper's own functions.
🤖 Agent note · picked up
22 remaining items
dezren39 commentedon Nov 7, 2026
Progress from
repo-worker-code-x: verified + designed: probe on the laptop confirms git status --ignored=matching enumerates ignored files individually and collapses ignored directories to one trailing-slash entry (target/, sub/target/) even with --untracked-files=all, so the run record stays compact. Plan: status_entries() gains a third list via --ignored=matching (!! entries); new ignored_allowlisted() split (config ignored_dirt_names matched on any path component — covers target/ at any depth — plus ignored_dirt_prefixes / ignored_dirt_files mirroring generated_dirt_*; seeds: target, node_modules, pycache, .direnv); CLEAN gate and reap_tree re-verification both fail closed on unlisted ignored paths; reap records the allowlisted ignored paths on the record before removal and --force covers allowlisted ignored dirt like allowlisted untracked dirt. Next: patch + tests.dezren39 commentedon Nov 7, 2026
Progress from
repo-worker-code-x: pr_opened: PR #6749 (branch queue/fb-6734-reaper-ignored-gate, commit ff164e8; 4 files +172/-14) — status_entries enumerates ignored via --ignored=matching, ignored_allowlisted with component/prefix/basename rules (seeds target/node_modules/pycache/.direnv in DEFAULT_CONFIG + shipped config.json), both gates fail closed on unlisted ignored paths, reap records ignored_removed before removal; tests 15/15 pass on the laptop. Next: merge under the standing criteria, then terminal bookkeeping.Merge pull request #6748 from developing-today/fallback/6734-reaper-i…
dezren39 commentedon Nov 7, 2026
Released by repo-worker-code-t (Lane T, fallback mode) — DONE. Fix landed and merged: PR #6748 (#6748); this issue auto-closed via
Fixes #6734. Verification: python3 tests/test_worktree_reaper.py on the laptop worktree — 14 tests OK including the new ignored-gate cases and the full pre-existing reap matrix. Codex review on PR #6748 completed with no findings. Outcome: fixed_merged; ci: PR checks queued at merge (standing merge authority — CI runs on main), Codex clean.dezren39 commentedon Nov 7, 2026
🤖 Agent note · done (landed by lane code-t's PR #6748 — duplicate race, this lane's PR #6749 closed as superseded)
repo-worker-code-x(issue worktree-reaper: CLEAN gate is blind to ignored files — an ignored-only tree reaps as clean and git worktree remove deletes the ignored content unrecorded #6734 is the task in fallback mode)Outcome: FIXED & MERGED — by PR #6748; this lane's duplicate PR #6749 closed after a salvage diff
953bf96705on main) and auto-closed this issue.ff164e8acb, tests 15/15 on the laptop) was diffed against the merged implementation per the duplicate-PR protocol: same--ignored=matchingenumeration, same fail-closed gates, same pre-removal recording; the only delta was two extra allowlist seed names (__pycache__,.direnv), judged not worth a follow-up (they fail closed under the merged config). Salvage note posted on PR fix(worktree-reaper): gate on ignored files, record ignored paths before removal (#6734) #6748 (comment 6701834307): nothing unique to salvage. PR fix(worktree-reaper): enumerate ignored files in the CLEAN gate (#6734) #6749 closed and its branch deleted.ignored_dirt_prefixes:target/,node_modules/); ignored paths in a reaped tree are recorded by path (ignored_paths) before removal; synthetic tests cover ignored-only kept / untracked+ignored kept / allowlisted-ignored reaped-with-record, existing matrix green (14 tests); README/docstring state the rule.dezren39 commentedon Nov 7, 2026
Released by
repo-worker-code-x(outcome: done). The #6734 fix landed via lane code-t's PR #6748 (merge commit 953bf96), which auto-closed this issue; this lane's parallel duplicate PR #6749 was salvage-diffed against the merged implementation (nothing unique — see the salvage note on PR #6748) and closed with its branch deleted. Acceptance is satisfied by the merged implementation; no follow-up owed.