Steps to Reproduce
- On
main, commit plan/work/WORK-001-alpha.md. - Branch off, and add
plan/work/WORK-002-from-branch.md there. - Back on
main, commit plan/work/WORK-002-from-main.md. - On the branch, run
refrakt plan validate --against main.
Expected
A collision on WORK-002, naming both files, and a non-zero exit.
Actual
No ID collisions with main (2 ids compared)., exit 0.
Environment
@refrakt-md/plan at the current main.refrakt plan migrate ids --against shares readRefIds with the validator through baseClaimants, but it only acts on duplicates already present in the working tree, so it is not affected.
Approach
Tell the two cases apart with the merge-base, which git already knows. A base-side file that is absent from the working tree:
- existed at
git merge-base HEAD <ref>: the branch deleted or renamed it. One claimant survives the merge, so not a collision. This is the case the check was written for. - did not exist there: the base added it after the branch point. Both claimants survive the merge, so it is a collision.
When no merge-base exists (unrelated histories), fail loudly rather than guess, matching how an unresolvable ref is handled.
Acceptance Criteria
- A collision with an ID the base added after the branch point is reported, naming both files
- A base claimant the branch deleted or renamed is still not reported
- A ref with no merge-base with
HEAD fails with an error instead of reporting a clean run against.test.ts has a test that advances the base after the branch point, and the existing tests still pass
References
- SPEC-135 D8 and WORK-582: the check and why it runs pre-merge.
Resolution
Completed: 2026-10-15
Branch: claude/site-changelog-readability-k4p902 PR: refrakt-md/refrakt#704
What was done
plugins/plan/src/commands/against.ts: branchPointPaths lists plan files at git merge-base HEAD <ref>; collisionsFrom forgives a base claimant missing from the tree only if it existed there, plus a mirror rule for base-side renames/deletions. No merge-base is an error naming the fix (git fetch --unshallow).cli-plugin.ts wires it into plan validate --against.against.test.ts: five tests that advance the base after the branch point, plus unrelated history. Verified the main regression tests fail against the old condition..github/workflows/validate.yml: fetch-depth: 0; base fetched without --depth=1; base ref via env.- CLAUDE.md notes the history requirement; patch changeset.
Notes
migrate ids --against is unaffected: it does not call the new function.
Completed: 2026-10-16
Branch: claude/site-changelog-readability-k4p902 PR: refrakt-md/refrakt#704
What was done
plugins/plan/src/commands/against.ts: branchPointPaths lists plan files at git merge-base HEAD <ref>; collisionsFrom forgives a base claimant missing from the tree only if it existed there, plus a mirror rule for base-side renames/deletions. No merge-base is an error naming the fix (git fetch --unshallow).cli-plugin.ts wires it into plan validate --against.against.test.ts: five tests that advance the base after the branch point, plus unrelated history. Verified the main regression tests fail against the old condition..github/workflows/validate.yml: the --against step runs last and unshallows the checkout itself (git fetch --unshallow) before fetching the base; base ref via env.- CLAUDE.md notes the history requirement; patch changeset.
Notes
migrate ids --against is unaffected: it does not call the new function.- The first version used
fetch-depth: 0 on the checkout. That made two tests that build real plan content time out (~170s on 3,473 commits against ~17s shallow), because the plan pipeline reads git history for timestamps and plan-history. The checkout stays shallow; only the last step pays for full history (~6s).