Add /dld-reindex skill for resolving decision ID collisions#21
Merged
Conversation
Renames locally-added decisions whose IDs clash with the base branch (and, when gh is available, open PRs) before rebasing. Uses git mv to preserve history, patches frontmatter, updates cross-references in other local decisions, and rewrites @Decision annotations in local code changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
regenerate-index.sh gains an --include-base <ref> flag that merges in decision rows from a base ref for any DL-*.md the local branch doesn't have. The reindex SKILL now passes --include-base so the pre-rebase INDEX.md contains both renamed-local rows and base-branch rows that landed while the branch was diverged — making the subsequent rebase auto-merge instead of conflicting on INDEX.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… files Adds plan-renames.sh which folds collision detection, taken-ID scan, and free-ID assignment into one deterministic output. The SKILL now calls that single script instead of having the agent stitch together three helpers and recompute the max — which is what was driving the /tmp/taken-ids.txt and /tmp/stderr.txt detours. Also adds an explicit "do not redirect to /tmp" guard in the SKILL prelude. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r base
The previous flow renamed colliding decisions in a final commit on top of the
branch, but `git rebase` replays commits one at a time and chokes on the
original add commit's colliding path (e.g. decisions/records/DL-205.md)
before it ever sees the rename. The only fix is to ensure the colliding path
never appears in the branch's history.
This change replaces step 6 of the SKILL with a squash flow: the renames are
applied to the working tree, then commit-reindex.sh mixed-resets HEAD to the
merge-base and creates one new reindex commit. The original branch commits'
subjects are preserved in the new commit body. An explicit AskUserQuestion
gate makes the history rewrite consensual.
INDEX.md is intentionally excluded from the reindex commit. Including it
caused content conflicts during rebase whenever the base also modified
INDEX.md (git's 3-way merge can't align both sides' top-of-file inserts even
when row content overlaps). commit-reindex.sh restores INDEX.md to merge-base
state in the working tree so the user has no uncommitted changes after the
flow; the post-rebase step is a one-line regenerate.
Also:
- commit-reindex.sh stages an EXPLICIT path list (the rename plan's old/new
paths plus every file touched by the original branch). `git add -A` is no
longer used anywhere in the flow, so untracked unrelated paths (e.g.
.claude/worktrees, scratch files) can never be swept in.
- resolve-base.sh resolves the base ref via @{upstream}, but falls back to
origin/main when the upstream tracks a branch with the same name as the
current branch (i.e. it's just the remote copy of this same branch, not a
useful collision base).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a Skills table row, a "Working in a team" subsection describing when to reach for the skill, and drops the stale roadmap line that referenced a differently-scoped /dld-reindex (sync decision references after refactors, issue #8) — the name now belongs to collision resolution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… step Two fixes from real-world testing: 1. rename-decision.sh computed CHANGED_FILES from "$BASE"...HEAD (committed state only). On a multi-rename run, the second invocation saw the OLD path from the first rename (now gone from disk) instead of the NEW path with its uncommitted state, so cross-references in the first-renamed file's body to the second-renamed ID were never updated. Compute against $BASE alone so working-tree state participates, and add R to the diff filter + --find-renames so post-mv detection is robust. 2. Plain-text DL-OLD mentions in code comments / log strings / prose were silently left alone. Blanket substitution risks false positives, so add a new find-stale-mentions.sh that surfaces those mentions with file, line, and content; the SKILL gains a Step 5 where the agent reviews each one in context and decides whether to update it via Edit (not a blanket replace). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- list-taken-ids.sh: scope the open-PR scrape to paths under decisions/records/ so unrelated PRs touching e.g. notes/DL-007-meeting.md don't poison the taken-IDs set. - rename-decision.sh / find-stale-mentions.sh: diff against the merge-base instead of $BASE's tip so we don't conflate main's post-branch-point changes with feature's local work. Working-tree state is still included. - Add a namespaced end-to-end test. find-collisions.sh already worked for namespaces (git's pathspec recurses); the test pins that behavior down alongside the rename + rebase flow with auth/billing namespaces. - commit-reindex.sh: trap EXIT to roll HEAD back if anything between the mixed reset and the commit fails (previously the branch was left at merge-base with renames floating in the working tree, no recovery hint). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Adds a
/dld-reindexskill that resolves decision ID collisions between a local branch and the base branch (and open PRs) before rebasing — handling renames, cross-references, annotations, and the history rewrite that a clean rebase requires.Why a history rewrite
When two developers independently draft
DL-NNNdecisions under the same ID and one of them merges first, the simple "rename + commit on top" approach doesn't work:git rebasereplays commits one at a time and hits an add/add conflict on the original add commit before it ever sees a later rename. The colliding paths must never appear in the branch's history. The skill addresses this by squashing all branch commits since the merge-base into a single reindex commit containing the renamed files. The original commit subjects are preserved in the new commit's body.AskUserQuestiongates the rewrite — the user explicitly chooses Rewrite and force-push / Rewrite only / Cancel.What the skill does
plan-renames.sh— single planning step that combines collision detection, thegh-aware "taken IDs" scan (scoped todecisions/records/so unrelated PRs can't poison it), and free-ID assignment. Emits a deterministic plan:<path>\t<DL-OLD>\t<DL-NEW>.rename-decision.sh—git mvpreserves rename history; patches the file'sid:frontmatter, body self-references, cross-refs in other local decisions (supersedes/amends/referencesand body), and@decision(DL-OLD)annotations in locally-changed code. Digit-aware substitution (renamingDL-100won't touchDL-1000). Diffs against the merge-base + working tree so multi-rename runs see uncommitted state from prior calls.find-stale-mentions.sh— surfaces bareDL-OLDmentions in code comments / log strings / prose thatrename-decision.shdeliberately doesn't auto-rewrite (false-positive risk). The SKILL has the agent review each match in context and decide whether to update it viaEdit.commit-reindex.sh— mixed-resets to the merge-base, stages an explicit path list (nogit add -A, so untracked unrelated paths like.claude/worktreescan never be swept in), and commits. INDEX.md is intentionally excluded from the reindex commit and restored to merge-base state in the working tree — including it caused content conflicts during rebase because git's 3-way merge can't align both sides' top-of-file inserts. AnEXITtrap rolls HEAD back if anything fails between the reset and the commit.resolve-base.sh— prefers the branch's upstream, falls back toorigin/mainwhen the upstream tracks the same branch name (i.e., it's just the remote copy of the local branch, not a useful collision base).regenerate-index.shonce aftergit rebase— a small, conflict-free repopulation of INDEX.md with the renamed rows.ghintegrationOpportunistic. If
ghisn't installed, the origin isn't a GitHub remote, orgh auth statusfails, the PR scan is skipped and a stderr note explains why — surfaced through to the user so they know renamed IDs were chosen against the base branch only.Files
skills/dld-reindex/+.claude/skills/dld-reindex/— SKILL.md + 7 scripts (resolve-base.sh,plan-renames.sh,find-collisions.sh,list-taken-ids.sh,rename-decision.sh,find-stale-mentions.sh,commit-reindex.sh).tile.json— registers the skill, bumps version to 0.7.0.rules/dld-workflow.md— adds a one-line bullet pointing at/dld-reindex.README.md— Skills table row + "Working in a team" subsection.tests/test_reindex.bats— covers collision detection, multi-rename cross-refs, namespaced projects, the recovery trap, the digit-aware edge case, and an end-to-end rebase + post-rebase regenerate.Test plan
tests/bats/bin/bats tests/— 165/165 pass.tessl tile lint— clean.@decisionannotations across multiple Java files, and aDL-2050-style digit-collision case.🤖 Generated with Claude Code