Preserve branch-committed skills on worktree teardown (#59)#61
Merged
Conversation
…itted skills cleanupMergedSkills previously os.RemoveAll'd every worktree .claude/skills entry whose name matched a factory skill, with no record of whether the lifecycle merge-copied it or the branch committed it. Branch-committed skills whose directory name collided with a factory skill were destroyed on teardown (af down/reset/GC/formula completion), surfacing as uncommitted deletions and making the skills unloadable for the session. Fixes #59. Gate deletion on git-tracked status: a skill directory tracked in the worktree's index is branch content (preserve); an untracked, factory-name- matching directory is a merge copy (remove). When git tracking cannot be determined, delete nothing (ADR-017: when in doubt, don't delete). The fix is localized to cleanupMergedSkills, so all teardown paths inherit it, and it also resolves the cleanup-before-non-force-remove ordering hazard. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lock in ADR-017 Rule 3: a non-git worktree dir makes provenance undeterminable, so cleanupMergedSkills must delete nothing. Add a clarifying comment that git ls-files always emits forward-slash paths so the literal prefix/split is intentional. No change to fix logic. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…iew follow-ups) - Make the fail-safe test deterministic with a broken .git boundary so it no longer depends on TMPDIR being outside an enclosing git repo. - Add an end-to-end TestRemove_PreservesBranchCommittedCollidingSkill driving the public non-force Remove path, reproducing the "contains modified or untracked files" teardown-brick symptom against the pre-fix code. - Tighten the cleanupMergedSkills/trackedSkillDirs docstrings: the fail-safe triggers on git invocation failure, not on any ambiguous result. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
agentfactory seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #59. Implements the git-provenance solution selected in the #60 analysis (intended for the
ultra-implementpass). PR #60 carries analysis only; this PR is the code fix.Summary
cleanupMergedSkills(internal/worktree/worktree.go) decided what to delete by inferring provenance from a name collision with the live factory.claude/skills/— itos.RemoveAll'd every worktree skill dir whose name matched a factory skill, with no record of whether the lifecycle merge-copied it or the branch committed it. So a branch-committed skill whose directory name collided with a factory skill was destroyed on teardown (reachable viaaf down,af down --reset,af sling --reset, formula completion, andGC()), surfacing as uncommittedDdeletions and making the skill unloadable for the session. This was the 4th recurrence of the anti-pattern ADR-017 forbids.Fix
Gate deletion on git provenance:
The change is localized to
cleanupMergedSkills(plus a newtrackedSkillDirshelper usinggit ls-files -z), so all teardown paths inherit it. It also resolves the latent ordering hazard: deleting a tracked dir before the non-forcegit worktree removedirtied the tree and bricked teardown; preserving tracked dirs lets the remove succeed. No new imports.Tests
TestCleanupMergedSkills_PreservesBranchCommittedCollidingSkill— tracked colliding skill survives; untracked merge copy removed.TestCleanupMergedSkills_FailSafeWhenProvenanceUnknown— git failure ⇒ nothing deleted (deterministic via broken.gitboundary).TestRemove_PreservesBranchCommittedCollidingSkill— end-to-end through the public non-forceRemovepath, reproducing the teardown-brick symptom against pre-fix code.TestCleanupMergedSkills_RemovesFactoryCopiedEntriesto a real git repo with untracked merge copies.All four new/changed tests confirmed to fail against pre-fix code and pass after. Full
make testgreen; changed funcs at 93.8% / 100% coverage.ultra-implement process
🤖 Generated with Claude Code