feat(B-0506): mechanize stale-worktree audit — audit-stale-worktrees.ts#3224
feat(B-0506): mechanize stale-worktree audit — audit-stale-worktrees.ts#3224AceHack wants to merge 2 commits into
Conversation
…+ branch-contamination untangle Tick output: - PR #3221 (chore(b-0502)): new .gemini/launchd/com.zeta.backlog-ready-notifier.plist + AUTONOMOUS-LOOP.md + tools/bg/README.md + B-0441/B-0502 row updates. Closes B-0441 AC #2 ("Runs under existing launchd / cron infrastructure"). Auto-merge armed. - This shard. Branch-state contamination caught + untangled mid-tick: - Local main had orphan 1856Z shard (9fa74ac) — work already on main via #3187. Reset. - During B-0502 work, my commit landed on a different Otto's branch (the exact B-0519 pattern). Untangled via reset + cherry-pick to the correct otto/b0502-... branch. - ZETA_EXPECTED_BRANCH hook didn't catch the wrong-branch commit (env-var didn't persist across separate Bash-tool calls). Worth a follow-up row. Substrate-or-it-didn't-happen check: - PR #3221 = B-0502 substrate (will be on main when CI clears) - This shard = the tick record (substrate-honest visibility of the contamination untangle so future-Otto sees the field-test result) Cron sentinel 12fb713e armed at session start; one entry, recurring. Co-Authored-By: Claude <noreply@anthropic.com>
Implements B-0506 Phase 2: a TS audit tool that catches the recurring "branch already used by worktree at <missing-path>" lockout pattern. What it does: - Enumerates 'git worktree list --porcelain' - For each prunable entry, tests whether its working-dir path exists - Reports stale entries (markdown summary) - With --prune, runs 'git worktree prune --expire=now -v' Parallel in shape to PR #3202 (audit-rule-cross-refs) and PR #3208 (audit-user-scope-memory-index) — three hygiene tools now share the same pattern. Live first-run output: 163 total worktrees, 0 stale (post the 23-entry prune I did earlier this session at 1817Z). The tool correctly reports the healthy state. Tests: 8 pass / 21 expect calls (parseWorktreePorcelain + renderReport). Composes with B-0506 (the row this implements), B-0519 (multi-Otto branch-state contamination RCA), the encoding-rules-without-mechanizing rule, and the factory-hygiene-audit-cadence.yml workflow (which could add a 3rd job for this tool in a future slice). Co-Authored-By: Claude <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Branch-state contamination caught (B-0519 pattern): this PR's branch incorrectly includes Aaron's cab98bf commit (B-0502 launchd plist) which belongs on PR #3221. Closing + redoing on a clean branch with only my audit-stale-worktrees commit. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Pull request overview
Adds a Bun/TypeScript hygiene tool to audit stale Git worktree metadata and optional pruning, plus unit tests and a tick-history shard.
Changes:
- Adds
tools/hygiene/audit-stale-worktrees.tsforgit worktree list --porcelainauditing and report generation. - Adds parser/report renderer tests for the new audit tool.
- Adds a tick-history shard documenting branch-state contamination recovery.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
tools/hygiene/audit-stale-worktrees.ts |
New CLI audit/prune tool for stale Git worktree entries. |
tools/hygiene/audit-stale-worktrees.test.ts |
Unit tests for porcelain parsing and markdown report rendering. |
docs/hygiene-history/ticks/2026/05/14/2010Z.md |
New historical tick shard for a prior branch-state contamination episode. |
Comments suppressed due to low confidence (4)
tools/hygiene/audit-stale-worktrees.ts:201
- P1:
git worktree pruneis global, so running it whenever any missing-path entry exists will also prune entries instalePathExists. That contradicts this tool's own “path still exists — investigate before prune” classification and can remove metadata for entries the report says require manual triage.
if (parsed.args.prune && r.stalePathMissing.length > 0) {
const p = runPrune();
tools/hygiene/audit-stale-worktrees.ts:203
- P1: A failed prune only logs an error and
mainstill returns 0, even though the header documents exit 0 as “prune ran successfully.” This will make cron/manual callers treat an unsuccessful cleanup as successful.
if (!p.ok) console.error("git worktree prune exited non-zero (continuing — some entries may have pruned)");
tools/hygiene/audit-stale-worktrees.ts:138
- P1: This new PATH-resolved
gitspawn also needs the repository-standardsonarjs/no-os-command-from-pathsuppression with a rationale; sibling worktree/git helpers already carry that documented suppression.
const r = spawnSync("git", ["worktree", "prune", "--expire=now", "-v"], { encoding: "utf8" });
docs/hygiene-history/ticks/2026/05/14/2010Z.md:57
- P1: markdownlint MD032 also requires a blank line before ordered lists. Add a blank line before this numbered untangle sequence so the markdown gate passes.
Untangle (per B-0519 RCA procedure):
1. `git reset --hard 5bd5697` on the contaminated branch (restored to pre-contamination
| if (entry.prunable && !existsSync(entry.path)) { | ||
| stalePathMissing.push(entry); | ||
| } else if (entry.prunable) { |
| } | ||
|
|
||
| function audit(): AuditResult | { error: string; code: AuditExitCode } { | ||
| const list = spawnSync("git", ["worktree", "list", "--porcelain"], { encoding: "utf8" }); |
| Substantive landing: | ||
| - New `.gemini/launchd/com.zeta.backlog-ready-notifier.plist` (copy-adapted from |
| @@ -0,0 +1,90 @@ | |||
| # Tick 2026-05-14T20:10Z — B-0502 launchd plist for backlog-ready-notifier; branch-state contamination untangle | |||
Implements B-0506 Phase 2 / B-0519 cheap mechanization. Three hygiene tools now share the same pattern (PR #3202 rule cross-refs, PR #3208 MEMORY.md bloat, this PR for stale worktrees).
Tests: 8 pass / 21 expect calls. Live first-run: 163 worktrees, 0 stale (post-prune).
(B-0519 branch-state contamination pattern recurred; applied own workaround.)
🤖 Generated with Claude Code