Repository navigation
fix(agents): give agent output a well-known home and stop lock markers leaking into new worktrees - #423
Conversation
…s leaking into new worktrees Agents saved evidence outside the worktree with no location named, so logs, PR-body drafts and validation folders piled up loose in the repository's parent directory, where no cleanup looks. Evidence now goes in <main checkout>-evidence/<lock-name>/, and Remove-MergedWorktrees.ps1 deletes entries whose newest file is older than -EvidenceRetentionDays (default 7). The issue-pr-loop skill names one home for throwaway checkouts and tool downloads too. `git worktree add` copies the source checkout's config.worktree, so a stale agent.lockName in the main checkout, or in any worktree used as a source, reached every new worktree and implicit verbs resolved the wrong Redis lock. The marker now records the worktree path it belongs to in agent.lockWorktree; a copied marker names another path and is ignored. Set-WorktreeLockMarker also refuses the main checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing: 6
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ReviewOverall this is a solid, well-scoped change. The Non-blocking suggestions:
Nice work on the tests for the dry-run and |
|
Pushed directly to main as 7cd1277. |
[Medium impact] The changes affect the agent work-item workflow and its local cleanup sweep. Fix the Linux path comparison before merging so copied markers cannot select another worktree's lock. Findings
SummaryThe PR gives agent evidence a shared home and adds age-based cleanup. It also ties lock markers to the worktree that wrote them.
Reviews (1) · Last reviewed commit: "fix(agents): give agent output a well-kn..." · Reviewed by Greptile |
| # worktree, so a marker counts only in the worktree it was written for. | ||
| $owner = (git config --worktree --get agent.lockWorktree 2>$null) | ||
| $top = (git rev-parse --show-toplevel 2>$null) | ||
| if (-not $owner -or -not $top -or (Get-MarkerPath $owner) -ne (Get-MarkerPath $top)) { return $null } |
There was a problem hiding this comment.
Different worktrees share a marker
Get-WorktreeLockMarker compares paths with -ne, which ignores case even on Linux. If /work/task inherits a marker from /work/Task, this check accepts it despite those being separate directories. Implicit status, renew, or release then targets the first worktree's lock. Compare paths case-sensitively outside Windows, as Test-SameNativePath already does.
| } | ||
| $items = @($entry) | ||
| if ($entry.PSIsContainer) { | ||
| $items += @(Get-ChildItem -LiteralPath $entry.FullName -Recurse -Force -ErrorAction SilentlyContinue) |
There was a problem hiding this comment.
Failed scans allow evidence deletion
The age scan suppresses errors and treats the files it could read as the complete entry. If a nested directory cannot be listed, its recent files are omitted. The sweep can then delete readable files from an entry that should be kept. Skip the entry and log a warning whenever its age scan is incomplete.
| New-Evidence (Join-Path $root 'issue-1/build.log') 10 | ||
| New-Evidence (Join-Path $root 'issue-2/old.log') 10 | ||
| New-Evidence (Join-Path $root 'issue-2/nested/fresh.log') 1 | ||
| New-Evidence (Join-Path $root 'pr-3-body.md') 10 | ||
| New-Evidence (Join-Path $root 'pr-4-body.md') 1 |
There was a problem hiding this comment.
The new cleanup test never creates a link, so it cannot catch changes that break the sweep's link safeguards. Add a top-level linked entry and a stale folder containing a junction or symlink to an outside directory. Assert that the linked entry is skipped and that cleanup leaves the outside files unchanged.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Problem
git worktree addcopies the source checkout'sconfig.worktreeinto the new worktree. A staleagent.lockNamein the main checkout, or in any worktree used as a source, therefore reached every new worktree, and implicitstatus/renew/releaseresolved the wrong Redis lock.Changes
scripts/WorktreeCleanup.ps1:Get-AgentEvidenceRoot(<main checkout>-evidence) andRemove-StaleAgentEvidence, which removes each top-level entry whose newest write is older than the retention window. Links are skipped; directory junctions inside an entry are detached before deletion.scripts/Remove-MergedWorktrees.ps1: new-EvidenceRetentionDays(default 7, range 1 to 3650); the sweep runs the evidence cleanup (honouring-WhatIf) and reports the count in the summary line.scripts/AgentLocks.ps1:Set-WorktreeLockMarkerrefuses the main checkout (git-dir equals git-common-dir) and writesagent.lockWorktreewith the worktree's full path next toagent.lockName.Get-WorktreeLockMarkertrusts a marker only whenagent.lockWorktreematches the current worktree. Markers withoutagent.lockWorktreeare ignored; no compatibility path..claude/skills/issue-pr-loop/SKILL.md: one rule for where agent output goes (evidence root, worktrees for throwaway checkouts, shared toolchain directory, never the parent directory or the main checkout) and the 7-day retention.scripts/Test-AgentEvidenceCleanup.ps1(no Docker);scripts/Test-AgentLocks.ps1gains an inherited-marker case and a main-checkout case.Validation
pwsh scripts/Test-AgentEvidenceCleanup.ps1: passed.pwsh scripts/Test-WorktreeCleanup.ps1: passed.pwsh scripts/Remove-MergedWorktrees.ps1 -WhatIf: summary line includes the evidence count.pwsh scripts/Test-AgentLocks.ps1: not run to completion locally. It needs Docker for Redis and Docker Desktop failed to start on the test machine (Error response from daemon: Docker Desktop is unable to start). The marker functions were checked separately without Redis against a temp repository: the main checkout got no marker, a worktree's own marker resolved, and a worktree created from it inherited the copiedagent.lockNamebut did not resolve it.