Worktree Artifact Path Mismatch — Prevention and Cleanup

Build and Verify agents run in worktrees but Claude Code's Write tool resolves absolute paths against the main tree. The agent writes `build_report.md` to the main tree. `artifact save` detects this but errors with manual recovery instructions instead of fixing it, leaving a stale copy on main. When `work complete` pulls the merged PR, git refuses because the untracked file would be overwritten. In re-build scenarios (verify FAIL, fix, new artifacts), the stale copy diverges from the PR content, and the existing content-match auto-clean (`be3374f`) can't help.

verdict PASSscore 16 / 16findings 6 (1 risk · 3 debt · 2 obs)duration 1h 15mrejection cycles 0shipped May 8, 2026surface cli

Pipeline timeline

Intent to proven code in 1h 15m across Think, Plan, Build, and Verify.

Think
9m
Plan
19m
Build
12m
Verify
5m

Assertion ledger

16 claims, each independently verified. Showing 8 — show all →

IDSaysMatcher
A001Saving an artifact that was written to the wrong tree auto-moves it to the correct locationverifiedok
A002The data companion file moves alongside its report automaticallyverifiedok
A003After auto-move, no stale copy remains on the main treeverifiedok
A004Files that are tracked by git on the main tree are never auto-movedverifiedok
A005Cross-filesystem moves fall back to copy-then-deleteverifiedok
A006After a successful worktree save, stale copies on the main tree are deletedverifiedok
A007Post-save sweep only removes untracked filesverifiedok
A008A cleanup failure during post-save sweep does not fail the saveverifiedok

Findings 6 total

debtpackages/cli/tests/commands/artifact.test.tsclosed
A005 EXDEV test doesn't exercise moveFileCrossFs — tests Node.js copyFileSync/unlinkSync directly instead of mocking renameSync to throw EXDEV
debtpackages/cli/tests/commands/artifact.test.tsclosed
A008 sweep-failure test is a no-op — tests absence of sweep (no main tree copy), not an actual cleanup failure
obspackages/cli/src/commands/artifact.tsclosed
moveFileCrossFs copy-then-delete is not atomic — if copyFileSync succeeds but unlinkSync fails, source file persists as a stale duplicate
riskpackages/cli/src/commands/work.tsclosed
Layer 3 planning artifact content-match reads file without try-catch — if file is deleted between filter and readFileSync, unhandled ENOENT crashes completeWork
debtpackages/cli/src/commands/artifact.tsclosed
Layer 2 post-save sweep calls getMainTreeRoot a second time — already computed in Layer 1 block but not threaded through
+1more findings

Integrity seal

scopesha256:f3bfd904fd1df...
contractsha256:ad3fa95025a39...
plansha256:30416c541d660...
specsha256:0061ca1bed907...
build-reportsha256:a5844792d9ec8...
build-datasha256:237bdc4adbce5...
verify-reportsha256:bdfa0868f04ed...
verify-datasha256:119ef4fcffd33...
audit cmd$ ana proof audit worktree-artifact-cleanup   → all hashes match