dld-kit 1.0.0-rc.2 → 1.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ name: dld-decide
3
3
  description: Record a single development decision as a markdown file with YAML frontmatter. Collects context, rationale, and code references interactively.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-decide/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-implement
3
3
  description: Implement one or more proposed decisions. Makes code changes, adds `@decision` annotations, and updates decision status.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-implement/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-init
3
3
  description: Bootstrap DLD (Decision-Linked Development) in a repository. Creates dld.config.yaml, the decisions/ directory, and INDEX.md. Run once per project.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-init/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-lookup
3
3
  description: Look up decisions by ID, tag, code path, or keyword. IMPORTANT — use this proactively whenever you encounter `@decision` annotations in code you are about to read or modify.
4
4
  compatibility: No external dependencies.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-lookup/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-plan
3
3
  description: Break down a feature into multiple decisions interactively. Creates a set of decision records grouped by a shared tag.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-plan/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -1,23 +1,23 @@
1
1
  ---
2
2
  name: dld-reindex
3
- description: Resolve decision ID collisions between a local branch and the base branch (and open PRs) before rebasing. Renames colliding local decisions with git mv, rewrites cross-references and annotations, then squashes branch commits into a single rebase-clean reindex commit.
3
+ description: Resolve decision ID collisions between a local branch and the base branch (and open PRs). Renames colliding local decisions with git mv, rewrites cross-references and annotations, and commits the renames on top of the branch. Squashes the branch only when the user asks.
4
4
  compatibility: Requires Node.js 20+ and git. Open-PR scanning additionally needs the `gh` CLI authenticated against a GitHub remote — the skill falls back gracefully when unavailable.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-reindex/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
10
10
  # /dld-reindex — Resolve Decision ID Collisions
11
11
 
12
- You are helping the developer untangle decision ID collisions before they rebase. Two or more developers can draft `DL-NNN` decisions in parallel; once one of them lands on the base branch (or appears in an open PR), the others must rename their local copies to the next free ID. This skill handles that mechanically and produces a branch state that rebases cleanly.
12
+ You are helping the developer untangle decision ID collisions. Two or more developers can draft `DL-NNN` decisions in parallel; once one of them lands on the base branch (or appears in an open PR), the others must rename their local copies to the next free ID. This skill does that mechanically.
13
13
 
14
- **This skill rewrites branch history.** That's not optional: if a colliding path (e.g. `decisions/records/DL-205.md`) was added by any commit on the branch, `git rebase` will hit an add/add conflict on that commit *before* it ever sees a later rename. The only fix is to ensure the colliding path never appears in the branch's history. The skill does this by squashing all branch commits since the merge-base into one reindex commit containing the renamed files.
14
+ **By default the skill adds a commit and does not rewrite history.** The renames go into one new commit on top of the branch (or a few, for a large reindex). A merge only compares end states, so once the branch tip has the record at its new path, `git merge <base>` and merging the PR on GitHub (merge commit or squash merge) don't conflict on the records. A normal push is enough.
15
15
 
16
- If the branch has already been pushed, finishing the reindex will require a `--force-with-lease` push. The skill asks for explicit consent before rewriting history.
16
+ **Squash mode, only when the user asks for it.** A rebase replays every branch commit, and the commit that added the colliding path (e.g. `decisions/records/DL-205.md`) hits an add/add conflict, whatever later commits do. If the user wants to rebase (or their repo uses GitHub's "Rebase and merge"), they can ask for a squash: the branch's commits since the merge-base become one reindex commit, and a pushed branch then needs `git push --force-with-lease`. Use squash mode only when the user's request says so (e.g. `/dld-reindex squash`, "squash it", "I want to rebase"). Never pick it on your own.
17
17
 
18
18
  ## Interaction style
19
19
 
20
- Use the `AskUserQuestion` tool when prompting for consent and at the finish step. Everything else is deterministic.
20
+ Use the `AskUserQuestion` tool when asking about pushing and, in squash mode, for consent to rewrite history. Everything else is deterministic.
21
21
 
22
22
  **Do not redirect any command output to `/tmp` files.** The commands in this skill emit only what you need to act on; piping to `/tmp/*.txt`, `tee`-ing into scratch files, or stashing stderr separately is unnecessary and creates clutter outside the repo. If a command's output is too long to read in one go, narrow it (`| tail -N`, `| head -N`, or pass a more specific flag) rather than persisting it.
23
23
 
@@ -27,6 +27,8 @@ The commands below run the `dld` CLI bundled with the dld-common skill, and need
27
27
 
28
28
  This skill uses: `resolve-base`, `plan-renames`, `rename-decision`, `find-stale-mentions`, `commit-reindex`, `regenerate-index`.
29
29
 
30
+ The steps below use shell variables (`BASE`, `PLAN`, `GROUP`). If your shell doesn't keep variables between commands, set them again in each command, or write the values out (e.g. pipe the plan lines with `printf '%s\t%s\t%s\n' ...`). `find-stale-mentions` and `commit-reindex` fail on an empty plan, so a lost variable shows up as an error.
31
+
30
32
  ## Prerequisites
31
33
 
32
34
  1. Check that `dld.config.yaml` exists at the repo root. If not, tell the user to run `/dld-init` first and stop.
@@ -44,7 +46,7 @@ BASE=$(node "<skill-dir>/../dld-common/scripts/dld.mjs" resolve-base)
44
46
 
45
47
  `resolve-base` prefers the branch's upstream when it tracks a *different* branch (the typical "feature → main" setup). It falls back to `origin/main` if the upstream is unset OR if the upstream tracks the same branch name as the current branch (i.e. it's just the remote copy of this same branch, not a useful collision base).
46
48
 
47
- The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present.
49
+ The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present. The word `squash` is not a base: it selects squash mode (`/dld-reindex squash origin/develop` does both).
48
50
 
49
51
  ## Step 2: Plan the renames
50
52
 
@@ -61,17 +63,26 @@ Output is tab-separated, one rename per line:
61
63
 
62
64
  If the output is empty, exit with:
63
65
 
64
- > No ID collisions detected. Safe to rebase onto `$BASE`.
66
+ > No ID collisions with `$BASE` or open PRs.
65
67
 
66
68
  `plan-renames` may print a stderr note like `[dld-reindex] open PRs not scanned: gh CLI not installed`. **Always surface this to the user** so they know the renamed IDs were chosen against base-branch state only and may still collide with an open PR.
67
69
 
70
+ **Check that each rename is a real collision.** For each plan line, compare the local record with the base's record of the same ID: `git show "$BASE:<path>"`, or, when the base keeps it in another namespace directory, find it with `git ls-tree -r --name-only "$BASE" | grep '/DL-205\.md$'`. If they are the same decision (same title and substance, give or take later edits), the branch got it from a PR that has since merged into the base, typically a decisions PR this branch was stacked on that was squash- or rebase-merged. Don't rename it. Stop and tell the user to merge the base into the branch first (`git merge $BASE`; if INDEX.md conflicts, `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index` and `git add` it), then run `/dld-reindex` again.
71
+
72
+ A record this branch got from an open PR it is stacked on (an implementation branch cut from a decisions PR) is not a collision with that PR: it is the same decision. This needs the PR's head commit locally, which the `git fetch origin` above provides; a PR from a fork whose head isn't fetched still counts as a collision.
73
+
68
74
  The underlying helpers (`find-collisions`, `list-taken-ids`) remain available for debugging, but the SKILL flow always goes through `plan-renames`.
69
75
 
70
- ## Step 3: Get explicit consent for the history rewrite
76
+ ## Step 3: Choose how to commit
77
+
78
+ **Default mode (no squash requested).** Don't ask for consent; the renames are new commits the user can review or revert. Decide how many commits to make:
71
79
 
72
- Show the user the rename plan and the implication. Use `AskUserQuestion`:
80
+ - **One commit** for the whole plan. This is the normal case.
81
+ - **Several commits** when the reindex is large: many renames, or renames that rewrite many files (lots of annotations, amendments, supersedes or references between the renamed decisions). Each commit holds one decision, or a group of related ones (e.g. a decision and the local decisions that amend or supersede it). Say in a line which groups you chose.
73
82
 
74
- > Resolving these collisions requires rewriting branch history. I will squash the N commits since `<merge-base>` into a single reindex commit. The original commit subjects will be preserved in the new commit body. If the branch has already been pushed, finishing will require `git push --force-with-lease`. How should I proceed?
83
+ **Squash mode (the user asked for it).** Show the rename plan and the implication, then use `AskUserQuestion`:
84
+
85
+ > Squashing rewrites branch history. I will squash the N commits since `<merge-base>` into a single reindex commit. The original commit subjects will be preserved in the new commit body. If the branch has already been pushed, finishing will require `git push --force-with-lease`. How should I proceed?
75
86
 
76
87
  Options:
77
88
  - **Rewrite and force-push** — agent applies renames, squashes, commits, and runs `git push --force-with-lease`.
@@ -82,7 +93,14 @@ If the user cancels, exit without touching anything.
82
93
 
83
94
  ## Step 4: Apply renames
84
95
 
85
- For each line in the plan, call:
96
+ Run steps 4 to 6 once for the whole plan, or, in default mode, once per group when you split the work. Put the lines you are working on in `GROUP`, in the plan's format. For a single commit or a squash, that's the whole plan; for a group, pick its lines by old ID:
97
+
98
+ ```bash
99
+ GROUP="$PLAN" # whole plan
100
+ GROUP=$(printf '%s\n' "$PLAN" | grep -E $'\t(DL-205|DL-207)\t') # one group
101
+ ```
102
+
103
+ For each line in the plan (or group), call:
86
104
 
87
105
  ```bash
88
106
  node "<skill-dir>/../dld-common/scripts/dld.mjs" rename-decision --old DL-OLD --new DL-NEW --path <relative-path> --base "$BASE"
@@ -102,10 +120,10 @@ The substitution is digit-aware: renaming `DL-100` will not accidentally rewrite
102
120
 
103
121
  ## Step 5: Review plain-text DL-OLD mentions
104
122
 
105
- After all renames are applied (but before the squash), find any remaining bare `DL-OLD` references in non-decision changed files:
123
+ After the renames are applied (and before committing), find any remaining bare `DL-OLD` references in non-decision changed files:
106
124
 
107
125
  ```bash
108
- echo "$PLAN" | node "<skill-dir>/../dld-common/scripts/dld.mjs" find-stale-mentions --base "$BASE"
126
+ echo "$GROUP" | node "<skill-dir>/../dld-common/scripts/dld.mjs" find-stale-mentions --base "$BASE"
109
127
  ```
110
128
 
111
129
  Output is tab-separated, one match per line: `<path>\t<line>\t<DL-OLD>\t<DL-NEW>\t<line-content>`. Empty output means nothing to review.
@@ -119,12 +137,25 @@ For each match:
119
137
 
120
138
  Surface the list of matches to the user with your verdicts before you finish, so they can sanity-check the judgment calls.
121
139
 
122
- ## Step 6: Squash and commit
140
+ ## Step 6: Commit
141
+
142
+ **Do not use `git add -A` or `git commit -a` anywhere in this flow.** Use only `commit-reindex` to commit.
143
+
144
+ **Default mode.** Regenerate INDEX.md so it lists the new IDs, then commit:
145
+
146
+ ```bash
147
+ node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index
148
+ echo "$GROUP" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE"
149
+ ```
150
+
151
+ `commit-reindex` checks that every renamed file exists, stages the plan's old and new paths plus every tracked file changed since HEAD (the renames, the rewritten references and annotations, your step 5 edits, INDEX.md), and commits on top of HEAD. Untracked files are not staged. The message lists the group's renames. If the commit fails (e.g. a hook rejects it), the index is restored and HEAD has not moved.
123
152
 
124
- Pipe the rename plan into `commit-reindex`:
153
+ If you split the work, go back to step 4 for the next group.
154
+
155
+ **Squash mode.** Pipe the whole rename plan into `commit-reindex --squash`:
125
156
 
126
157
  ```bash
127
- echo "$PLAN" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE"
158
+ echo "$PLAN" | node "<skill-dir>/../dld-common/scripts/dld.mjs" commit-reindex --base "$BASE" --squash
128
159
  ```
129
160
 
130
161
  This:
@@ -135,11 +166,19 @@ This:
135
166
  4. Stages **only** an explicit path list derived from the original branch diff and the rename plan — the old paths (for deletions), the new paths (for additions), every other file the branch touched. Untracked unrelated paths (e.g. `.claude/worktrees`, scratch files, in-progress edits to unrelated files) are deliberately NOT swept in.
136
167
  5. Commits with a templated message that lists the renames in the subject and preserves the original branch commits' subjects in the body.
137
168
 
138
- **Do not use `git add -A` or `git commit -a` anywhere in this flow.** Use only `commit-reindex` to commit. Targeting paths explicitly is the whole point of this step.
169
+ ## Step 7: Push
139
170
 
140
- ## Step 7: Push (if the user chose force-push)
171
+ **Default mode.** Use `AskUserQuestion` to ask whether to push now (options: **Push** / **Don't push**). If yes:
141
172
 
142
- If step 3's answer was "Rewrite and force-push":
173
+ ```bash
174
+ if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
175
+ git push
176
+ else
177
+ git push -u origin HEAD
178
+ fi
179
+ ```
180
+
181
+ **Squash mode.** If step 3's answer was "Rewrite and force-push":
143
182
 
144
183
  ```bash
145
184
  if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
@@ -157,8 +196,16 @@ Print:
157
196
 
158
197
  - The renames table.
159
198
  - The stderr note from step 2 if `gh` was skipped.
160
- - The number of commits squashed.
161
- - The next steps:
199
+ - The commits created (default mode), or the number of commits squashed (squash mode).
200
+ - The next steps.
201
+
202
+ Default mode:
203
+
204
+ > 1. Bring in the base when you need it: `git merge $BASE`, or merge the PR as usual.
205
+ > 2. If INDEX.md conflicts (both sides added rows), run `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index`, then `git add` INDEX.md and finish the merge.
206
+ > 3. Don't rebase this branch onto `$BASE`: its earlier commits still add the old paths and will conflict. To rebase anyway, before you merge the base, drop the reindex commits (`git reset --keep HEAD~N`, N = the number of reindex commits) and run `/dld-reindex squash`.
207
+
208
+ Squash mode:
162
209
 
163
210
  > 1. `git rebase $BASE`
164
211
  > 2. `node "<skill-dir>/../dld-common/scripts/dld.mjs" regenerate-index` (to repopulate INDEX.md with the renamed locals — the reindex commit intentionally leaves INDEX.md alone to keep the rebase conflict-free; INDEX.md is missing the renamed rows until you regenerate)
@@ -169,5 +216,5 @@ The skill never rebases or merges — that is always the user's call.
169
216
  ## Out of scope
170
217
 
171
218
  - **Already-conflicted rebases.** If the user is mid-rebase with conflicts, tell them to `git rebase --abort` first and re-run this skill.
172
- - **Preserving per-commit granularity.** The squash trades original commit boundaries for a deterministic rewrite. A future `--preserve-history` flag could perform a cherry-pick walk that rewrites each commit individually, but the edge cases (commits modifying an already-renamed file, merge commits, partial reruns) make it materially more complex than the squash.
219
+ - **Rewriting each commit in place.** Neither mode rewrites the branch commit by commit (a cherry-pick walk); the edge cases (commits modifying an already-renamed file, merge commits, partial reruns) make it much more complex than adding a commit or squashing.
173
220
  - **Cross-namespace ID reconciliation** in namespaced projects. IDs are assumed globally unique across namespaces, matching `next-id`.
@@ -3,7 +3,7 @@ name: dld-retrofit
3
3
  description: Bootstrap DLD decisions from an existing codebase. Analyzes code to infer rationale, generates decision records, and adds `@decision` annotations.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-retrofit/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-snapshot
3
3
  description: Generate SNAPSHOT.md (detailed decision reference) and OVERVIEW.md (narrative synthesis with diagrams) from the decision log.
4
4
  compatibility: Requires Node.js 20+ and git.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-snapshot/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -3,7 +3,7 @@ name: dld-status
3
3
  description: Quick overview of the decision log state — counts by status, recent decisions, and run tracking info.
4
4
  compatibility: No external dependencies.
5
5
  metadata:
6
- dld-kit-version: "1.0.0-rc.2"
6
+ dld-kit-version: "1.0.0-rc.4"
7
7
  ---
8
8
  <!-- Generated by dld-kit from templates/skills/dld-status/SKILL.md. Do not edit: updating the skills overwrites this file. -->
9
9
 
@@ -19,4 +19,4 @@ This project uses Decision-Linked Development. Decision records (DL-*.md) live i
19
19
  - dld-snapshot: regenerate SNAPSHOT.md and OVERVIEW.md from the decision log
20
20
  - dld-status: a quick overview of the decision log state
21
21
  - dld-retrofit: generate decisions from an existing codebase
22
- - dld-reindex: resolve decision-ID collisions with the base branch (and open PRs) before rebasing
22
+ - dld-reindex: resolve decision-ID collisions with the base branch and open PRs
@@ -1,20 +1,20 @@
1
1
  ---
2
2
  name: dld-reindex
3
- description: Resolve decision ID collisions between a local branch and the base branch (and open PRs) before rebasing. Renames colliding local decisions with git mv, rewrites cross-references and annotations, then squashes branch commits into a single rebase-clean reindex commit.
3
+ description: Resolve decision ID collisions between a local branch and the base branch (and open PRs). Renames colliding local decisions with git mv, rewrites cross-references and annotations, and commits the renames on top of the branch. Squashes the branch only when the user asks.
4
4
  compatibility: Requires Node.js 20+ and git. Open-PR scanning additionally needs the `gh` CLI authenticated against a GitHub remote — the skill falls back gracefully when unavailable.
5
5
  ---
6
6
 
7
7
  # /dld-reindex — Resolve Decision ID Collisions
8
8
 
9
- You are helping the developer untangle decision ID collisions before they rebase. Two or more developers can draft `DL-NNN` decisions in parallel; once one of them lands on the base branch (or appears in an open PR), the others must rename their local copies to the next free ID. This skill handles that mechanically and produces a branch state that rebases cleanly.
9
+ You are helping the developer untangle decision ID collisions. Two or more developers can draft `DL-NNN` decisions in parallel; once one of them lands on the base branch (or appears in an open PR), the others must rename their local copies to the next free ID. This skill does that mechanically.
10
10
 
11
- **This skill rewrites branch history.** That's not optional: if a colliding path (e.g. `decisions/records/DL-205.md`) was added by any commit on the branch, `git rebase` will hit an add/add conflict on that commit *before* it ever sees a later rename. The only fix is to ensure the colliding path never appears in the branch's history. The skill does this by squashing all branch commits since the merge-base into one reindex commit containing the renamed files.
11
+ **By default the skill adds a commit and does not rewrite history.** The renames go into one new commit on top of the branch (or a few, for a large reindex). A merge only compares end states, so once the branch tip has the record at its new path, `git merge <base>` and merging the PR on GitHub (merge commit or squash merge) don't conflict on the records. A normal push is enough.
12
12
 
13
- If the branch has already been pushed, finishing the reindex will require a `--force-with-lease` push. The skill asks for explicit consent before rewriting history.
13
+ **Squash mode, only when the user asks for it.** A rebase replays every branch commit, and the commit that added the colliding path (e.g. `decisions/records/DL-205.md`) hits an add/add conflict, whatever later commits do. If the user wants to rebase (or their repo uses GitHub's "Rebase and merge"), they can ask for a squash: the branch's commits since the merge-base become one reindex commit, and a pushed branch then needs `git push --force-with-lease`. Use squash mode only when the user's request says so (e.g. `/dld-reindex squash`, "squash it", "I want to rebase"). Never pick it on your own.
14
14
 
15
15
  ## Interaction style
16
16
 
17
- Use the `AskUserQuestion` tool when prompting for consent and at the finish step. Everything else is deterministic.
17
+ Use the `AskUserQuestion` tool when asking about pushing and, in squash mode, for consent to rewrite history. Everything else is deterministic.
18
18
 
19
19
  **Do not redirect any command output to `/tmp` files.** The commands in this skill emit only what you need to act on; piping to `/tmp/*.txt`, `tee`-ing into scratch files, or stashing stderr separately is unnecessary and creates clutter outside the repo. If a command's output is too long to read in one go, narrow it (`| tail -N`, `| head -N`, or pass a more specific flag) rather than persisting it.
20
20
 
@@ -24,6 +24,8 @@ Use the `AskUserQuestion` tool when prompting for consent and at the finish step
24
24
 
25
25
  This skill uses: `resolve-base`, `plan-renames`, `rename-decision`, `find-stale-mentions`, `commit-reindex`, `regenerate-index`.
26
26
 
27
+ The steps below use shell variables (`BASE`, `PLAN`, `GROUP`). If your shell doesn't keep variables between commands, set them again in each command, or write the values out (e.g. pipe the plan lines with `printf '%s\t%s\t%s\n' ...`). `find-stale-mentions` and `commit-reindex` fail on an empty plan, so a lost variable shows up as an error.
28
+
27
29
  ## Prerequisites
28
30
 
29
31
  1. Check that `dld.config.yaml` exists at the repo root. If not, tell the user to run `/dld-init` first and stop.
@@ -41,7 +43,7 @@ BASE=$({{dld}} resolve-base)
41
43
 
42
44
  `resolve-base` prefers the branch's upstream when it tracks a *different* branch (the typical "feature → main" setup). It falls back to `origin/main` if the upstream is unset OR if the upstream tracks the same branch name as the current branch (i.e. it's just the remote copy of this same branch, not a useful collision base).
43
45
 
44
- The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present.
46
+ The user may pass an explicit base when invoking the skill (e.g. `/dld-reindex origin/develop`) — honor it if present. The word `squash` is not a base: it selects squash mode (`/dld-reindex squash origin/develop` does both).
45
47
 
46
48
  ## Step 2: Plan the renames
47
49
 
@@ -58,17 +60,26 @@ Output is tab-separated, one rename per line:
58
60
 
59
61
  If the output is empty, exit with:
60
62
 
61
- > No ID collisions detected. Safe to rebase onto `$BASE`.
63
+ > No ID collisions with `$BASE` or open PRs.
62
64
 
63
65
  `plan-renames` may print a stderr note like `[dld-reindex] open PRs not scanned: gh CLI not installed`. **Always surface this to the user** so they know the renamed IDs were chosen against base-branch state only and may still collide with an open PR.
64
66
 
67
+ **Check that each rename is a real collision.** For each plan line, compare the local record with the base's record of the same ID: `git show "$BASE:<path>"`, or, when the base keeps it in another namespace directory, find it with `git ls-tree -r --name-only "$BASE" | grep '/DL-205\.md$'`. If they are the same decision (same title and substance, give or take later edits), the branch got it from a PR that has since merged into the base, typically a decisions PR this branch was stacked on that was squash- or rebase-merged. Don't rename it. Stop and tell the user to merge the base into the branch first (`git merge $BASE`; if INDEX.md conflicts, `{{dld}} regenerate-index` and `git add` it), then run `/dld-reindex` again.
68
+
69
+ A record this branch got from an open PR it is stacked on (an implementation branch cut from a decisions PR) is not a collision with that PR: it is the same decision. This needs the PR's head commit locally, which the `git fetch origin` above provides; a PR from a fork whose head isn't fetched still counts as a collision.
70
+
65
71
  The underlying helpers (`find-collisions`, `list-taken-ids`) remain available for debugging, but the SKILL flow always goes through `plan-renames`.
66
72
 
67
- ## Step 3: Get explicit consent for the history rewrite
73
+ ## Step 3: Choose how to commit
74
+
75
+ **Default mode (no squash requested).** Don't ask for consent; the renames are new commits the user can review or revert. Decide how many commits to make:
68
76
 
69
- Show the user the rename plan and the implication. Use `AskUserQuestion`:
77
+ - **One commit** for the whole plan. This is the normal case.
78
+ - **Several commits** when the reindex is large: many renames, or renames that rewrite many files (lots of annotations, amendments, supersedes or references between the renamed decisions). Each commit holds one decision, or a group of related ones (e.g. a decision and the local decisions that amend or supersede it). Say in a line which groups you chose.
70
79
 
71
- > Resolving these collisions requires rewriting branch history. I will squash the N commits since `<merge-base>` into a single reindex commit. The original commit subjects will be preserved in the new commit body. If the branch has already been pushed, finishing will require `git push --force-with-lease`. How should I proceed?
80
+ **Squash mode (the user asked for it).** Show the rename plan and the implication, then use `AskUserQuestion`:
81
+
82
+ > Squashing rewrites branch history. I will squash the N commits since `<merge-base>` into a single reindex commit. The original commit subjects will be preserved in the new commit body. If the branch has already been pushed, finishing will require `git push --force-with-lease`. How should I proceed?
72
83
 
73
84
  Options:
74
85
  - **Rewrite and force-push** — agent applies renames, squashes, commits, and runs `git push --force-with-lease`.
@@ -79,7 +90,14 @@ If the user cancels, exit without touching anything.
79
90
 
80
91
  ## Step 4: Apply renames
81
92
 
82
- For each line in the plan, call:
93
+ Run steps 4 to 6 once for the whole plan, or, in default mode, once per group when you split the work. Put the lines you are working on in `GROUP`, in the plan's format. For a single commit or a squash, that's the whole plan; for a group, pick its lines by old ID:
94
+
95
+ ```bash
96
+ GROUP="$PLAN" # whole plan
97
+ GROUP=$(printf '%s\n' "$PLAN" | grep -E $'\t(DL-205|DL-207)\t') # one group
98
+ ```
99
+
100
+ For each line in the plan (or group), call:
83
101
 
84
102
  ```bash
85
103
  {{dld}} rename-decision --old DL-OLD --new DL-NEW --path <relative-path> --base "$BASE"
@@ -99,10 +117,10 @@ The substitution is digit-aware: renaming `DL-100` will not accidentally rewrite
99
117
 
100
118
  ## Step 5: Review plain-text DL-OLD mentions
101
119
 
102
- After all renames are applied (but before the squash), find any remaining bare `DL-OLD` references in non-decision changed files:
120
+ After the renames are applied (and before committing), find any remaining bare `DL-OLD` references in non-decision changed files:
103
121
 
104
122
  ```bash
105
- echo "$PLAN" | {{dld}} find-stale-mentions --base "$BASE"
123
+ echo "$GROUP" | {{dld}} find-stale-mentions --base "$BASE"
106
124
  ```
107
125
 
108
126
  Output is tab-separated, one match per line: `<path>\t<line>\t<DL-OLD>\t<DL-NEW>\t<line-content>`. Empty output means nothing to review.
@@ -116,12 +134,25 @@ For each match:
116
134
 
117
135
  Surface the list of matches to the user with your verdicts before you finish, so they can sanity-check the judgment calls.
118
136
 
119
- ## Step 6: Squash and commit
137
+ ## Step 6: Commit
138
+
139
+ **Do not use `git add -A` or `git commit -a` anywhere in this flow.** Use only `commit-reindex` to commit.
140
+
141
+ **Default mode.** Regenerate INDEX.md so it lists the new IDs, then commit:
142
+
143
+ ```bash
144
+ {{dld}} regenerate-index
145
+ echo "$GROUP" | {{dld}} commit-reindex --base "$BASE"
146
+ ```
147
+
148
+ `commit-reindex` checks that every renamed file exists, stages the plan's old and new paths plus every tracked file changed since HEAD (the renames, the rewritten references and annotations, your step 5 edits, INDEX.md), and commits on top of HEAD. Untracked files are not staged. The message lists the group's renames. If the commit fails (e.g. a hook rejects it), the index is restored and HEAD has not moved.
120
149
 
121
- Pipe the rename plan into `commit-reindex`:
150
+ If you split the work, go back to step 4 for the next group.
151
+
152
+ **Squash mode.** Pipe the whole rename plan into `commit-reindex --squash`:
122
153
 
123
154
  ```bash
124
- echo "$PLAN" | {{dld}} commit-reindex --base "$BASE"
155
+ echo "$PLAN" | {{dld}} commit-reindex --base "$BASE" --squash
125
156
  ```
126
157
 
127
158
  This:
@@ -132,11 +163,19 @@ This:
132
163
  4. Stages **only** an explicit path list derived from the original branch diff and the rename plan — the old paths (for deletions), the new paths (for additions), every other file the branch touched. Untracked unrelated paths (e.g. `.claude/worktrees`, scratch files, in-progress edits to unrelated files) are deliberately NOT swept in.
133
164
  5. Commits with a templated message that lists the renames in the subject and preserves the original branch commits' subjects in the body.
134
165
 
135
- **Do not use `git add -A` or `git commit -a` anywhere in this flow.** Use only `commit-reindex` to commit. Targeting paths explicitly is the whole point of this step.
166
+ ## Step 7: Push
136
167
 
137
- ## Step 7: Push (if the user chose force-push)
168
+ **Default mode.** Use `AskUserQuestion` to ask whether to push now (options: **Push** / **Don't push**). If yes:
138
169
 
139
- If step 3's answer was "Rewrite and force-push":
170
+ ```bash
171
+ if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
172
+ git push
173
+ else
174
+ git push -u origin HEAD
175
+ fi
176
+ ```
177
+
178
+ **Squash mode.** If step 3's answer was "Rewrite and force-push":
140
179
 
141
180
  ```bash
142
181
  if git rev-parse --verify --quiet "@{upstream}" >/dev/null; then
@@ -154,8 +193,16 @@ Print:
154
193
 
155
194
  - The renames table.
156
195
  - The stderr note from step 2 if `gh` was skipped.
157
- - The number of commits squashed.
158
- - The next steps:
196
+ - The commits created (default mode), or the number of commits squashed (squash mode).
197
+ - The next steps.
198
+
199
+ Default mode:
200
+
201
+ > 1. Bring in the base when you need it: `git merge $BASE`, or merge the PR as usual.
202
+ > 2. If INDEX.md conflicts (both sides added rows), run `{{dld}} regenerate-index`, then `git add` INDEX.md and finish the merge.
203
+ > 3. Don't rebase this branch onto `$BASE`: its earlier commits still add the old paths and will conflict. To rebase anyway, before you merge the base, drop the reindex commits (`git reset --keep HEAD~N`, N = the number of reindex commits) and run `/dld-reindex squash`.
204
+
205
+ Squash mode:
159
206
 
160
207
  > 1. `git rebase $BASE`
161
208
  > 2. `{{dld}} regenerate-index` (to repopulate INDEX.md with the renamed locals — the reindex commit intentionally leaves INDEX.md alone to keep the rebase conflict-free; INDEX.md is missing the renamed rows until you regenerate)
@@ -166,5 +213,5 @@ The skill never rebases or merges — that is always the user's call.
166
213
  ## Out of scope
167
214
 
168
215
  - **Already-conflicted rebases.** If the user is mid-rebase with conflicts, tell them to `git rebase --abort` first and re-run this skill.
169
- - **Preserving per-commit granularity.** The squash trades original commit boundaries for a deterministic rewrite. A future `--preserve-history` flag could perform a cherry-pick walk that rewrites each commit individually, but the edge cases (commits modifying an already-renamed file, merge commits, partial reruns) make it materially more complex than the squash.
216
+ - **Rewriting each commit in place.** Neither mode rewrites the branch commit by commit (a cherry-pick walk); the edge cases (commits modifying an already-renamed file, merge commits, partial reruns) make it much more complex than adding a commit or squashing.
170
217
  - **Cross-namespace ID reconciliation** in namespaced projects. IDs are assumed globally unique across namespaces, matching `next-id`.