dflow-sdd-ddd 0.15.0 → 0.16.0

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/CONTRIBUTING.md +1 -0
  3. package/README.en.md +7 -4
  4. package/README.md +7 -4
  5. package/bin/dflow.js +75 -0
  6. package/docs/closeout-check.en.md +204 -0
  7. package/docs/closeout-check.md +204 -0
  8. package/docs/commands.en.md +4 -2
  9. package/docs/commands.md +4 -2
  10. package/docs/release-versioning-policy.md +1 -0
  11. package/lib/closeout-check.js +690 -0
  12. package/lib/doc-shapes.json +198 -0
  13. package/lib/init.js +17 -2
  14. package/package.json +2 -2
  15. package/templates/brownfield/references/finish-feature-flow.md +29 -26
  16. package/templates/brownfield/references/finish-feature-minimal-host.md +38 -17
  17. package/templates/brownfield/references/finish-feature-record-correction.md +45 -0
  18. package/templates/brownfield/references/git-integration.md +62 -28
  19. package/templates/brownfield/references/modify-existing-flow.md +55 -29
  20. package/templates/brownfield/references/modify-existing-follow-up.md +2 -1
  21. package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +121 -3
  22. package/templates/brownfield/references/new-feature-flow.md +49 -31
  23. package/templates/brownfield/references/new-phase-flow.md +2 -2
  24. package/templates/brownfield/references/pr-review-checklist.md +29 -4
  25. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +13 -5
  26. package/templates/brownfield/scaffolding/Git-principles-trunk.md +8 -4
  27. package/templates/brownfield/templates/_index.md +17 -13
  28. package/templates/common/references/flow-rationale-registry.md +20 -4
  29. package/templates/greenfield/references/finish-feature-flow.md +29 -26
  30. package/templates/greenfield/references/finish-feature-minimal-host.md +36 -16
  31. package/templates/greenfield/references/finish-feature-record-correction.md +45 -0
  32. package/templates/greenfield/references/git-integration.md +63 -29
  33. package/templates/greenfield/references/modify-existing-flow.md +42 -27
  34. package/templates/greenfield/references/modify-existing-follow-up.md +2 -1
  35. package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +121 -3
  36. package/templates/greenfield/references/new-feature-flow.md +49 -31
  37. package/templates/greenfield/references/new-phase-flow.md +2 -2
  38. package/templates/greenfield/references/pr-review-checklist.md +28 -4
  39. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +13 -5
  40. package/templates/greenfield/scaffolding/Git-principles-trunk.md +8 -4
  41. package/templates/greenfield/templates/_index.md +18 -13
@@ -162,7 +162,18 @@ skip one and the boundary is a hole rather than a division of labour.
162
162
  checkpoint 1 and closeout. A third, typically a host opened in its own
163
163
  commit before the work landed, breaks the two-commit lifecycle while
164
164
  every closeout check still passes, because those read only the hashes
165
- the record points at. **This count applies to a minimal host only**: a
165
+ the record points at. A third that comes **after** checkpoint 1 and
166
+ changes only the host's record text — no implementation path, no new
167
+ host file — may instead be a correction committed on its own once
168
+ checkpoint 1 was pushed (`references/finish-feature-record-correction.md`
169
+ sends the developer that way and asks for it to be named in the PR
170
+ description). Report which it is: the count still fails, and
171
+ accepting a correction commit is your call after reading its full
172
+ diff and the PR description. A correction that touches only a record
173
+ outside this directory — a baseline capture's document, a follow-up's
174
+ original `_index.md` — is not in this walk; the PR description is where
175
+ it is named.
176
+ **This count applies to a minimal host only**: a
166
177
  phase-bearing feature has no fixed number of host commits, so there is
167
178
  nothing to count there and this item is N/A.
168
179
  This is also where closeout's minimal-host **selector** boundary is
@@ -175,9 +186,23 @@ skip one and the boundary is a hole rather than a division of labour.
175
186
  so the developer asserts it and cites what the claim rests on —
176
187
  `hotfix-identity:` beside `hotfix-branch:` in a **T2**'s
177
188
  lightweight-spec frontmatter, or in the row Description for a **T3**.
178
- **Open that source** — PR, incident, or tracker — and confirm it names
179
- this fix and this commit. Closeout already checked that a citation is
180
- *present*; only review can check that it *matches*.
189
+ **Open that source** — PR, incident, or tracker — and confirm that it
190
+ names **this fix** and the recorded commit: one of the fix's own
191
+ commits, or the squash commit when the fix was squashed
192
+ (`references/modify-existing-post-hoc-hotfix.md` item 3). A fix merged
193
+ without a PR is identified by the source and the base branch's history
194
+ instead — the hotfix branch's name or the source's number in commit
195
+ messages.
196
+ **When the fix landed as a copy** — a rebase merge or a cherry-pick —
197
+ the recorded commit is that copy, and the source may not name it. Git
198
+ cannot prove that one commit is a copy of another: ancestry does not
199
+ follow copies, and patch comparison (`git cherry`, `git patch-id`)
200
+ cannot tell apart two changes that differ only in whitespace. A
201
+ `(cherry picked from commit …)` line or a matching subject is a clue,
202
+ not proof — settle it with the author from the source, and do not
203
+ report the identity as confirmed from command output alone.
204
+ Closeout already checked that a citation is *present*; only review can
205
+ check that it *matches*.
181
206
  - [ ] **Hosted `Commit` cell identity — confirm each hash is *that row's own*
182
207
  implementation commit** (phase-bearing host only). `finish-feature-flow.md`
183
208
  Step 4 instruction 1 orders the value ("each row's own implementation
@@ -126,7 +126,11 @@ within **24 hours** after it lands. Run `/dflow:modify-existing` in
126
126
  **post-hoc mode** (`references/modify-existing-flow.md` Step 1.8): it opens a
127
127
  minimal host of its own for the fix, records the implementation checkpoint as
128
128
  `reconciled ({merged-hotfix-hash})`, and reconciles rather than re-running work
129
- that is already on the mainline.
129
+ that is already on the mainline. The hash it records is the hotfix's own
130
+ commit, **not** the tagged merge commit on `main` (step 3): the record is
131
+ never a merge commit, and step 4 merges the hotfix **branch**, so the tagged
132
+ commit does not come into `develop` with it.
133
+ `references/modify-existing-post-hoc-hotfix.md` item 3 has the rule.
130
134
  What gets written is whatever the cascade's tier calls for — a **T2** lands a
131
135
  lightweight spec (root cause + fix + a `dflow/specs/migration/tech-debt.md` entry
132
136
  if the bug reveals a systemic issue); a **T3** lands one `_index.md` Lightweight
@@ -185,8 +189,12 @@ before making key Git operations:
185
189
 
186
190
  ### Before merging a feature branch to `develop`
187
191
 
188
- - [ ] `/dflow:finish-feature` has run (or the equivalent Step 8.4
189
- manual archival is complete)
192
+ - [ ] **Closeout complete**: `/dflow:finish-feature` has announced closeout
193
+ complete for this host — its Local-closeout gate is satisfied (the
194
+ closeout commit landed and passed post-commit verification) and, if
195
+ the host carries `follow-up-of`, the Step 6 tracking commit has also
196
+ landed and been verified. `status: completed` or a directory already
197
+ under `completed/` does not show this on its own.
190
198
  - [ ] `_index.md` status = `completed`, feature directory moved to
191
199
  `dflow/specs/features/completed/` via `git mv`
192
200
  - [ ] BC layer synced: `dflow/specs/domain/{context}/rules.md` and
@@ -196,8 +204,8 @@ before making key Git operations:
196
204
  - [ ] Domain layer (`src/Domain/`) has no delivery-framework references
197
205
 
198
206
  > **Minimal (zero-phase) host.** The items about this host's own record —
199
- > `/dflow:finish-feature` having run, `_index.md` status `completed`, and the
200
- > archival move — apply **unchanged**: they are what stops a host merging with
207
+ > **Closeout complete**, `_index.md` status `completed`, and the archival
208
+ > move — apply **unchanged**: they are what stops a host merging with
201
209
  > its record still open. Every item naming a **Domain or bounded-context
202
210
  > artifact** applies only to what this change **actually touched**: a **no-BC**
203
211
  > host has no context to sync or document, a **T3** does no Domain work at all,
@@ -140,8 +140,12 @@ one is not.
140
140
 
141
141
  ### Gate Checks — Before merging a feature PR to `main`
142
142
 
143
- - [ ] `/dflow:finish-feature` has run (or the equivalent Step 8.4
144
- manual archival is complete)
143
+ - [ ] **Closeout complete**: `/dflow:finish-feature` has announced closeout
144
+ complete for this host — its Local-closeout gate is satisfied (the
145
+ closeout commit landed and passed post-commit verification) and, if
146
+ the host carries `follow-up-of`, the Step 6 tracking commit has also
147
+ landed and been verified. `status: completed` or a directory already
148
+ under `completed/` does not show this on its own.
145
149
  - [ ] `_index.md` status = `completed`, feature directory moved to
146
150
  `dflow/specs/features/completed/` via `git mv`
147
151
  - [ ] BC layer synced: `dflow/specs/domain/{context}/rules.md` and
@@ -153,8 +157,8 @@ one is not.
153
157
  - [ ] PR has at least one review approval
154
158
 
155
159
  > **Minimal (zero-phase) host.** The items about this host's own record —
156
- > `/dflow:finish-feature` having run, `_index.md` status `completed`, and the
157
- > archival move — apply **unchanged**: they are what stops a host merging with
160
+ > **Closeout complete**, `_index.md` status `completed`, and the archival
161
+ > move — apply **unchanged**: they are what stops a host merging with
158
162
  > its record still open. Every item naming a **Domain or bounded-context
159
163
  > artifact** applies only to what this change **actually touched**: a **no-BC**
160
164
  > host has no context to sync or document, a **T3** does no Domain work at all,
@@ -6,7 +6,7 @@ created: {YYYY-MM-DD}
6
6
  branch: feature/{SPEC-ID}-{slug}
7
7
  # follow-up-of: {原 SPEC-ID} # 選用:本 feature 為某個已 completed feature 的 follow-up 時填入
8
8
  ---
9
- <!-- dflow-shape: brownfield/_index.md 1 — keep this line: dflow doctor reads it -->
9
+ <!-- dflow-shape: brownfield/_index.md 3 — keep this line: dflow doctor reads it -->
10
10
 
11
11
  <!--
12
12
  Template note (for AI):
@@ -133,8 +133,8 @@ references/new-phase-flow.md。有衝突時以那些檔為準。
133
133
  > Description 另外標明這是 hotfix,並寫出識別依據(PR/incident/tracker 編號)
134
134
  > ——沒有依據的 hash 會擋下 closeout。此時路徑指的是**已合併的那個 hotfix**
135
135
  > 碰到的檔案;本列 `Commit` 欄填的是**補文件那個 commit** 的 hash,不是 hotfix
136
- > 的 hash(後者記在 Checkpoint Log 的 `reconciled (...)` 列)。兩者來源不同,
137
- > 不可互填。
136
+ > 的 hash(後者記在 Checkpoint Log 的 `reconciled (...)` 列,記哪一顆見
137
+ > references/modify-existing-post-hoc-hotfix.md item 3)。兩者來源不同,不可互填。
138
138
  >
139
139
  > Tier 判準見 AI-AGENT-GUIDE.md § Ceremony Scaling 的 ordered cascade(步驟 0–4,先命中者勝)。
140
140
 
@@ -190,8 +190,9 @@ references/new-phase-flow.md。有衝突時以那些檔為準。
190
190
  > 生命週期 checkpoint 的 commit / skip 時間線(讓三週後回溯不必手動重建)。
191
191
  > 每個 checkpoint 無論 commit 或 skip 都記一列。Tier 決定 checkpoint 數:
192
192
  > T1 三點(spec 完 / impl 完 / closeout)、T2 兩點(spec+impl 合併 / closeout)、
193
- > T3 單一實作 commit(其 inline row 與本列的 hash 由 host 的下一個 commit 一併帶進;
193
+ > T3 單一實作 commit(記它 hash 的那幾格由 host 的下一個 commit 帶進;
194
194
  > host 若沒有後續 commit,允許一個只動 ledger 的 tracking commit 收尾,該 commit 不另成列)。
195
+ > `modify-existing` 的那一顆在更新文件那一步(Step 6)做完之後才 commit,連同那一步寫的文件一起帶。
195
196
  > ⚠ **上面那些「點」在本表 `Checkpoint` 欄各有固定的字面值**:spec 里程碑寫
196
197
  > **`spec-baseline`**、實作寫 **`implementation`**、關帳寫 **`closeout`**(下表的
197
198
  > `spec-baseline` / `implementation` / `closeout` 三列就是這幾個值;表中另有一列
@@ -204,17 +205,18 @@ references/new-phase-flow.md。有衝突時以那些檔為準。
204
205
  > **Minimal host(zero-phase)例外**——見 references/modify-existing-flow.md
205
206
  > Step 1.7(standalone)、Step 1.6 的 follow-up minimal 變體,或 Step 1.8 的
206
207
  > post-hoc hotfix(其 linkage 落在前兩者之一):這種 host 之後
207
- > 沒有別的 commit 可以收攏 T3 的 row,所以**不分 tier 一律記兩個 checkpoint**
208
+ > 沒有別的 commit 可以收攏 row,所以**不分 tier 一律記兩個 checkpoint**
208
209
  > (implementation——baseline capture 記 `spec-baseline`——然後 closeout),
209
- > 而且 T3 的 inline row 要**寫進 checkpoint 1 本身**、不是等下一個 commit 帶
210
- > 進來。上一段「T3 單一實作 commit / 由下一個 commit 帶進」講的是**掛在既有
211
- > feature 底下的** T3,不適用於 minimal host。
210
+ > 而且每一列(T3 的 inline row 也一樣)都要**寫進 checkpoint 1 本身**;之後只補記
211
+ > checkpoint 1 hash 的那幾格,搭 closeout commit。上一段「T3 單一實作 commit /
212
+ > 由下一個 commit 帶進」講的是**掛在既有 feature 底下的** T3,不適用於 minimal host。
212
213
  >
213
214
  > Result 的合法值是 `committed ({hash})` / `skipped` / `failed`,外加
214
215
  > **`reconciled ({merged-hotfix-hash})`**——只給 post-hoc hotfix(Step 1.8)的
215
216
  > implementation 列用,意思是「本 checkpoint 記錄的是一個已經合併的變更」。
216
- > 括號裡是**那個 hotfix 的 hash**,不是本 host 補文件那個 commit 的 hash
217
- > (後者填在 Lightweight Changes 該列的 `Commit` 欄)。完整詞彙見
217
+ > 括號裡是**那個修正的 commit**(記哪一顆見
218
+ > references/modify-existing-post-hoc-hotfix.md item 3),不是本 host 補文件那個
219
+ > commit 的 hash(後者填在 Lightweight Changes 該列的 `Commit` 欄)。完整詞彙見
218
220
  > references/git-integration.md § Commit Checkpoints, Branch Gate & AI Commits。
219
221
  >
220
222
  > **`branch-override` 是紀錄列,不是生命週期 checkpoint。** 在 branch gate 選了
@@ -229,9 +231,11 @@ references/new-phase-flow.md。有衝突時以那些檔為準。
229
231
  > **minimal host**(Step 1.6 follow-up 變體/Step 1.7 standalone/Step 1.8
230
232
  > post-hoc)**不提供**這個選項——那些 host 正是拿 `branch:` 當權威來斷言分支相等的。
231
233
  >
232
- > commit hash 只在 commit 實際成功後填入;pre-commit hook reject 或 commit
233
- > 失敗記 `failed`、不寫假 hash。**例外:closeout 列不填 hash**——closeout
234
- > commit 無法自含自身 hash,該列於 commit 前寫入、隨歸檔目錄一起進 commit;
234
+ > **checkpoint 的 commit 不可能帶著自己的 hash**:記它 hash 的那幾格(本表那一列的
235
+ > `committed ({hash})`、Lightweight Changes 填同一個 hash 的 `Commit` 格)一律在 commit
236
+ > 實際成功後才寫,搭 host 的**下一個** commit;pre-commit hook reject 或 commit
237
+ > 失敗記 `failed`、不寫假 hash。**例外:closeout 列不填 hash**——這個 host 之後再也
238
+ > 沒有 commit 會帶上它,所以該列於 commit 前寫入、隨歸檔目錄一起進 commit;
235
239
  > 溯源用 `git log -1 -- completed/{SPEC-ID}-{slug}` 或選配的
236
240
  > `Dflow-Checkpoint` trailer(見 references/git-integration.md)。
237
241
  >
@@ -63,9 +63,9 @@ R-FF-CURSOR-02: [finish-feature-flow.md · Step 4 · "The `git mv` and this writ
63
63
 
64
64
  R-FF-GITMV-01: [finish-feature-flow.md · Step 4 · "`git mv` is mandatory — never use plain `mv` + `git add`"] This preserves git's directory rename detection so `git log --follow` / `git blame` / PR diff quality stays intact across the move.
65
65
 
66
- R-FF-SWEEPKEY-01: [gf · finish-feature-flow.md · Step 4 instruction 2 · "Key it on the flow that produced the change, never on the flow that opened the host"] Those differ, and keying on the host is how the deadlock came back: `modify-existing-flow.md` Step 1.6 opens a **phase-bearing** host for a T1 follow-up, and Step 5.4 puts a T1 new-phase or a T2 lightweight *inside* a host opened by `/dflow:new-feature` — each of those runs Step 5.3 as its own sweep. Ask "which flow opened this host" and a hosted **T2** on a phase-bearing **no-BC** host has no arm at all: half (a) is empty because Step 3 is skipped, and the `glossary.md` rename its Step 5.3 made belongs to a sweep the question refused to look at. Unstaged it fails the clean-tree item; staged it is attributable to nothing. Deriving from *this change's* flow is the fix — not widening the set to "anything dirty".
66
+ R-FF-SWEEPKEY-01: [gf · finish-feature-flow.md · Step 4 instruction 2 · "Key it on the flow that produced the change, never on the flow that opened the host"] Those differ, and keying on the host is how the deadlock came back: `modify-existing-flow.md` Step 1.6 opens a **phase-bearing** host for a T1 follow-up, and Step 5.4 puts a T1 new-phase or a T2 lightweight *inside* a host opened by `/dflow:new-feature` — each of those runs Step 5.3 as its own sweep. Ask "which flow opened this host" and a hosted **T2** on a phase-bearing **no-BC** host has no arm at all: half (a) is empty because Step 3 is skipped, and a `glossary.md` correction to its Step 5.3 sweep, made after its checkpoint 1, belongs to a sweep the question refused to look at. Unstaged it fails the clean-tree item; staged it is attributable to nothing. Deriving from *this change's* flow is the fix — not widening the set to "anything dirty".
67
67
 
68
- R-FF-SWEEPKEY-01: [bf · finish-feature-flow.md · Step 4 instruction 2 · "Key it on the flow that produced the change, never on the flow that opened the host"] Those differ, and keying on the host is how the deadlock came back: `modify-existing-flow.md` Step 1.6 opens a **phase-bearing** host for a T1 follow-up, and Step 6.4 puts a T1 new-phase or a T2 lightweight *inside* a host opened by `/dflow:new-feature` — each of those runs Step 6.3 as its own sweep. Ask "which flow opened this host" and a hosted **T2** on a phase-bearing **no-BC** host has no arm at all: half (a) is empty because Step 3 is skipped, and the `glossary.md` rename its Step 6.3 made belongs to a sweep the question refused to look at. Unstaged it fails the clean-tree item; staged it is attributable to nothing. Deriving from *this change's* flow is the fix — not widening the set to "anything dirty".
68
+ R-FF-SWEEPKEY-01: [bf · finish-feature-flow.md · Step 4 instruction 2 · "Key it on the flow that produced the change, never on the flow that opened the host"] Those differ, and keying on the host is how the deadlock came back: `modify-existing-flow.md` Step 1.6 opens a **phase-bearing** host for a T1 follow-up, and Step 6.4 puts a T1 new-phase or a T2 lightweight *inside* a host opened by `/dflow:new-feature` — each of those runs Step 6.3 as its own sweep. Ask "which flow opened this host" and a hosted **T2** on a phase-bearing **no-BC** host has no arm at all: half (a) is empty because Step 3 is skipped, and a `glossary.md` correction to its Step 6.3 sweep, made after its checkpoint 1, belongs to a sweep the question refused to look at. Unstaged it fails the clean-tree item; staged it is attributable to nothing. Deriving from *this change's* flow is the fix — not widening the set to "anything dirty".
69
69
 
70
70
  R-FF-SWEEPKEY-02: [finish-feature-flow.md · Step 4 instruction 2 · "do not go looking for a list that host never produced"] ⚠ Keying this on the minimal-host allow-list (`finish-feature-minimal-host.md`) is what deadlocked the one shape where both halves are empty: a **phase-bearing, no-BC** host that swept a global document has nothing from Step 3 *and* no Step 1 allow-list, while this instruction still requires the delta staged and the post-commit path check would then reject it as unpermitted — two mandatory requirements that could not both be met.
71
71
 
@@ -95,7 +95,7 @@ R-FF-INFLIGHT-01: [finish-feature-flow.md · Step 5 · "In-flight reminder"] Sur
95
95
 
96
96
  R-FF-COMMITCELL-01: [finish-feature-minimal-host.md · Step 1 · "Do not collapse the middle case into the first"] it is the one that reads as *filled* to every rule written against empty / non-empty, which is exactly why it survives elsewhere.
97
97
 
98
- R-FF-COMMITCELL-02: [finish-feature-minimal-host.md · Step 1 · "Deliberately not run on a phase-bearing host"] a hosted T3's row rides the host's *next* commit (`references/git-integration.md` § Commit checkpoints), and nothing requires a hosted row to declare implementation paths — so (c) has no defined input there, and demanding it would reject every hosted T3.
98
+ R-FF-COMMITCELL-02: [finish-feature-minimal-host.md · Step 1 · "Deliberately not run on a phase-bearing host"] a hosted T3's row is not asserted against any one commit — only the cells holding its hash are placed, on the host's *next* commit (`references/git-integration.md` § Commit checkpoints) — and nothing requires a hosted row to declare implementation paths — so (c) has no defined input there, and demanding it would reject every hosted T3.
99
99
 
100
100
  R-FF-BASELINECAP-01: [bf · finish-feature-minimal-host.md · Step 1 · "a `spec-baseline` checkpoint and a `Tier = baseline` row imply each other"] `modify-existing-flow.md` Step 1.7 states the contract: a baseline capture is observation-only, *there is no implementation work*, which is why its checkpoint is not named `implementation`. (1) and (2) alone are the ledger talking about itself; (3) is what makes the claim true of the commit.
101
101
 
@@ -105,19 +105,35 @@ R-FF-CKPTCOUNT-02: [finish-feature-minimal-host.md · Step 1 · "the Checkpoint
105
105
 
106
106
  R-FF-FOLLOWUP-01: [finish-feature-minimal-host.md · Step 1 · "the reverse link was opened, not only closed"] Decidable from one commit and one blob per original, so it stays inside closeout's remit.
107
107
 
108
+ R-FF-RECONCILE-01: [finish-feature-minimal-host.md · Step 1 · "Minimal host, hotfix post-hoc only", (c) "touches **at least one** of the implementation paths"] The recorded commit is one of the fix's own commits, or the commit it landed as on the base branch (`modify-existing-post-hoc-hotfix.md` item 3), and a fix made of several commits has no single commit carrying all of it: the record names the last of them that touches a declared path. Requiring every declared path would block that legitimate record. The check is a plausibility test — identity is confirmed by `pr-review-checklist.md` — and "at least one" still rejects a commit unrelated to the declaration. The normal minimal host's "checkpoint 1 is *one* commit" check keeps asking for the declared paths without that qualifier, because there the commit *is* the implementation.
109
+
110
+ R-FF-RECORDFIX-01: [finish-feature-minimal-host.md · Step 1 · "Checkpoint 1's own record, corrected after checkpoint 1"] The closeout commit cannot take the correction: its closed list admits only what a flow step writes after checkpoint 1, and this is checkpoint 1's own content. So the correction goes into checkpoint 1 itself (an amend) or, once checkpoint 1 is pushed, into a commit of its own that review reports as a departure from the two-commit lifecycle — there is no declared "maintenance" row that makes a third commit legitimate. Such a row would let any third commit through on a label, the stray host-open commit the whole-history count exists to catch included, and a reviewer would still have to judge it.
111
+
108
112
  R-FF-SUMMARY-01: [finish-feature-minimal-host.md · Step 5 · "Zero-phase minimal host — exact fields", on a no-BC host's `Related BR-IDs`] Forcing `none` here would erase a marker the approved zero-phase shape requires.
109
113
 
114
+ ## finish-feature-record-correction.md
115
+
116
+ R-FF-RECORDFIX-02: [finish-feature-record-correction.md · "Not pushed, and checkpoint 1 is `HEAD`"] `git commit --amend` rewrites whatever commit is on top; with a later commit there, the amend would miss checkpoint 1 and leave its old hash an ancestor, so hash evidence (b) would no longer catch an entry nobody refilled. The entries holding checkpoint 1's hash are set back to checkpoint 1's state before staging because a hash on the same line as the correction, or on a neighbouring row — a T3 row's text beside its `Commit` cell, a compound host's next row — leaves `git add -p` a hunk it cannot split.
117
+
110
118
  ## init-project-flow.md
111
119
 
112
120
  R-INIT-DEFERRED-01: [init-project-flow.md · Step 3.1 · "is NOT generated here", both bullets] Init creates neither a per-context `behavior.md` nor either `analysis.md`: an empty file created ahead of its content becomes a stale placeholder.
113
121
 
114
122
  ## modify-existing-flow.md
115
123
 
124
+ R-ME-CKPT-01: [gf · modify-existing-flow.md · Step 4 → Step 5 gate · "checkpoint 1 is offered at the end of Step 5"] Checkpoint 1 waits for the end of the documentation step so that one commit carries the change and the record that step writes: 5.1's verification finds a gap before anything is committed, not after; the documentation the developer confirmed in 5.2 does not sit uncommitted until closeout, which can be weeks away; and a T2 keeps the two commits its tier promises instead of a third, documentation-only one — which on a minimal host is a commit `pr-review-checklist.md`'s two-commit count flags.
125
+
126
+ R-ME-CKPT-01: [bf · modify-existing-flow.md · Step 5 → Step 6 gate · "checkpoint 1 is offered at the end of Step 6"] Checkpoint 1 waits for the end of the artifact step so that one commit carries the change and the record that step writes: 6.1's verification finds a gap before anything is committed, not after; the documentation the developer confirmed in 6.2 does not sit uncommitted until closeout, which can be weeks away; and a T2 keeps the two commits its tier promises instead of a third, documentation-only one — which on a minimal host is a commit `pr-review-checklist.md`'s two-commit count flags.
127
+
116
128
  R-ME-BASELINE-01: [bf · modify-existing-flow.md · Step 2 · "Systematic Baseline Capture"] Baseline capture looks for system-level knowledge and records it in `analysis.md`, even though it runs only when the feature being modified has no prior spec: it is how a project that adopts Dflow midway gets its existing system into `analysis.md` in bulk, rather than one entry at a time. Every other entry point only records what the change in hand happens to touch; the observation-only route in Part A records without changing anything, but only what that observation reached.
117
129
 
130
+ ## new-feature-flow.md
131
+
132
+ R-NF-PHASEDONE-01: [new-feature-flow.md · Step 8 · "8.4 Phase completion and handoff"] § 8.4 reconciles the Current BR Snapshot before handing off because `/dflow:finish-feature` Step 3 syncs the Snapshot into the bounded context as it stands: a Snapshot still holding the rule text planned at Step 4 would overwrite what § 8.3 wrote from the implementation. § 8.4 writes its own four-item list rather than pointing at `new-phase-flow.md` Step 7 because a first phase differs from a later one: its rules are in the phase-spec's Business Rules, not in a Delta; the task list is already handled by § 8.1 and § 8.2, while Step 7 keeps it; and § 8.3 has already synced the bounded context, which Step 7 leaves to closeout.
133
+
118
134
  ## new-phase-flow.md
119
135
 
120
- R-NP-ANALYSIS-01: [new-phase-flow.md · Step 2 · "System-level knowledge?"] Step 2 records into `analysis.md` on the spot rather than leaving it to closeout, because deferring needs two things this flow does not have: somewhere to hold the answer until closeout (the phase-spec template has no section for it), and a closeout step that always runs (a host with no bounded context skips `/dflow:finish-feature` Step 3 entirely). Each track's Step 7 carves these two paths out of its "remain `/dflow:finish-feature` responsibilities" sentence for the same reason. Step 6 re-checks what Step 2 recorded - what the phase was to add or change against what it built, what Step 2 uncovered in the existing system against the system as it now stands, since knowledge Step 2 uncovered was true before the phase began and does not stop being true because the phase built nothing for it - instead of leaving that to closeout as well: no closeout step is sure to reach the entry (`/dflow:new-feature` § 8.4 archives without `/dflow:finish-feature`), and by then a checkpoint has usually committed it, so no diff would show it.
136
+ R-NP-ANALYSIS-01: [new-phase-flow.md · Step 2 · "System-level knowledge?"] Step 2 records into `analysis.md` on the spot rather than leaving it to closeout, because deferring needs two things this flow does not have: somewhere to hold the answer until closeout (the phase-spec template has no section for it), and a closeout step that always runs (a host with no bounded context skips `/dflow:finish-feature` Step 3 entirely). Each track's Step 7 carves these two paths out of its "remain `/dflow:finish-feature` responsibilities" sentence for the same reason. Step 6 re-checks what Step 2 recorded - what the phase was to add or change against what it built, what Step 2 uncovered in the existing system against the system as it now stands, since knowledge Step 2 uncovered was true before the phase began and does not stop being true because the phase built nothing for it - instead of leaving that to closeout as well: no closeout step is sure to reach the entry (a host with no bounded context skips `/dflow:finish-feature` Step 3 entirely), and by then a checkpoint has usually committed it, so no diff would show it.
121
137
 
122
138
  ## templates/analysis.md
123
139
 
@@ -26,9 +26,10 @@ state, archives the feature directory, and emits a Git-strategy-neutral
26
26
  stance — the AI helps at natural checkpoints, you keep the final say.
27
27
  - The BC-layer sync in Step 3 **reuses the existing documentation-sync
28
28
  mechanism** from `new-feature-flow` (§ 8.3) and `modify-existing-flow`
29
- (Step 5.3) — it does not introduce a new sync flow. Treat it as "lift
30
- that step out of the per-phase checklist and run it once at feature
31
- closeout, with the `_index.md` Current BR Snapshot as input."
29
+ (Step 5.3) — it does not introduce a new sync flow. § 8.3 and Step 5.3
30
+ sync when the work they record completes; Step 3 runs the same mechanism
31
+ once more at feature closeout, with the `_index.md` Current BR Snapshot as
32
+ input.
32
33
 
33
34
  **Step Gates** in this flow (stop-and-confirm before proceeding):
34
35
  - Step 1 → Step 2 (validation passed → flip status)
@@ -163,8 +164,9 @@ Step 4's post-commit verification, and to Step 5's Integration Summary fields.
163
164
  none. Decide from the artifact, not from a declaration:
164
165
  "the feature is intentionally no-BR" is a claim, and the record is what
165
166
  settles it. A classic BR-delta spec carrying ADDED / MODIFIED / RENAMED
166
- entries **and** an empty Snapshot means finalization never refreshed it
167
- (`modify-existing-flow.md` Step 1.7) and **blocks**.
167
+ entries **and** an empty Snapshot means the completion checklist never
168
+ refreshed it (`modify-existing-flow.md` Step 5.3, before checkpoint 1)
169
+ and **blocks**.
168
170
 
169
171
  If any check fails:
170
172
  > "Cannot finish feature `{SPEC-ID}-{slug}` yet — {N} validation issues
@@ -388,10 +390,10 @@ Deltas are the audit trail. **Cross-reference each hosted lightweight-spec's
388
390
  recorded delta the same way** — on any host shape, for the reason above.
389
391
 
390
392
  > Note: this step does NOT read individual phase-specs to re-derive the BR
391
- > set — that work was already reconciled by `/dflow:new-phase` Step 7 each
392
- > time a phase completed. We trust `_index.md` Current BR Snapshot as the
393
- > feature-level truth here. If the developer finds drift between Snapshot
394
- > and the phase-specs, fix `_index.md` first, then re-run
393
+ > set. `/dflow:new-feature` § 8.4 and `/dflow:new-phase` Step 7 reconcile the
394
+ > Snapshot when they complete a phase, and we trust `_index.md` Current BR
395
+ > Snapshot as the feature-level truth here. If the developer finds drift
396
+ > between Snapshot and the phase-specs, fix `_index.md` first, then re-run
395
397
  > `/dflow:finish-feature`.
396
398
 
397
399
  Also update `architecture/tech-debt.md` / `models.md` / `glossary.md` /
@@ -494,13 +496,13 @@ Then, in this order:
494
496
 
495
497
  1. **Record the checkpoint row first.** Write one row in the moved
496
498
  `_index.md` Checkpoint Log — `closeout | committed` for Y, `closeout |
497
- skipped` for N. The closeout row carries **no commit hash**: the closeout
498
- commit cannot contain its own hash. Trace it later via
499
+ skipped` for N. The closeout row carries **no commit hash**: no later
500
+ commit of this host carries it. Trace it later via
499
501
  `git log -1 -- dflow/specs/features/completed/{SPEC-ID}-{slug}` (or the
500
- optional `Dflow-Checkpoint` trailer). The "hash only after success" rule
501
- still applies to spec / implementation rows — closeout is the documented
502
- exception (see `references/git-integration.md` § Commit Checkpoints,
503
- Branch Gate & AI Commits).
502
+ optional `Dflow-Checkpoint` trailer). On any other checkpoint row, a
503
+ `committed ({hash})` Result is written after its commit and carried by the
504
+ host's next commit (see `references/git-integration.md` § Commit
505
+ Checkpoints, Branch Gate & AI Commits).
504
506
  **Backfill any unfilled hosted `Commit` cell in the same edit** — a hosted row
505
507
  waits for the host's *next* commit and this is it. **Unfilled means empty *or*
506
508
  holding a placeholder** — `{hash}`, `{pending}`, `(待 commit)`, anything that
@@ -534,10 +536,11 @@ Then, in this order:
534
536
  `domain/analysis.md`, `architecture/tech-debt.md`) — Step 3 is **skipped
535
537
  entirely for a no-BC host**, so this half is empty there; **and**
536
538
  **(b)** the **documentation-sweep step of the flow that produced *this
537
- change*** — take the paths from that step, not from a list kept here. It runs
538
- *after* that flow's implementation checkpoint and *before* closeout, so **that
539
- flow** leaves its deltas uncommitted; a later phase's own checkpoints may have
540
- committed them since, and staging an already-committed path is a no-op.
539
+ change*** — take the paths from that step, not from a list kept here. A
540
+ commit may already have carried them — `modify-existing-flow.md`'s
541
+ checkpoint 1 carries its own sweep, and a later phase's checkpoint may have
542
+ carried a `new-feature-flow.md` sweep — and staging an already-committed path
543
+ is a no-op, so stage them either way.
541
544
  Those sweeps reach
542
545
  Domain-layer documents under `dflow/specs/domain/`, plus the **global**
543
546
  documents `glossary.md`, `domain/analysis.md` and
@@ -575,7 +578,8 @@ Then, in this order:
575
578
  admitted. On a **phase-bearing** host there is **no allow-list at all** —
576
579
  that check is minimal-host-only and lives in that branch file — so read the
577
580
  sweep directly and **do not go looking for a list that host never produced.**
578
- Read each delta (`git diff -- {path}`) and scope every one to *this change*;
581
+ Read each delta (`git diff HEAD -- {path}`, which includes what is already
582
+ staged) and scope every one to *this change*;
579
583
  staging only "what Step 3 wrote" leaves a no-BC host's global delta dirty and
580
584
  the post-commit clean-tree check fails.
581
585
  3. **Commit (Y) or stop (N).** For Y the AI commits. If a pre-commit hook
@@ -625,10 +629,9 @@ every item:
625
629
  only when the baseline became unreadable *during* closeout.
626
630
  Step 1 blocks unless every
627
631
  spec in the host already reads `status: completed`, so those flips are
628
- already **in** the baseline whichever step made them: a **minimal** host's
629
- come from `modify-existing-flow.md` Step 1.7's finalization, a
630
- **phase-bearing** host's from its phases completing — no phase-bearing
631
- host runs Step 1.7, whichever flow opened it. Either way the
632
+ already **in** the baseline whichever step made them: a lightweight-spec's
633
+ comes from `modify-existing-flow.md` Step 5.3, a phase-spec's from its
634
+ phase completing. Either way the
632
635
  flips are not differences this check admits; they are part of what it
633
636
  compares against. (They are differences only from the *pre-flip* record,
634
637
  which is not the baseline.)
@@ -760,8 +763,8 @@ itself, state in the conversation *how* Step 4's post-commit verification
760
763
  reached its result — not that it passed, but how it was computed:
761
764
 
762
765
  1. **The baseline** — the tree Step 1 read, and which step put each spec's
763
- `status: completed` flip into it (Step 1.7's finalization on a minimal host;
764
- the phases completing on a phase-bearing one, which never runs Step 1.7).
766
+ `status: completed` flip into it (`modify-existing-flow.md` Step 5.3 for a
767
+ lightweight-spec; the phase completing for a phase-spec).
765
768
  2. **The differences you derived, each with the step that ordered it** —
766
769
  Step 2, Step 4's terminal Resume Pointer write, or Step 4 instruction 1. This
767
770
  is the set the check *derives* instead of listing, so printing it is what
@@ -140,14 +140,16 @@ A `✗` here stops closeout there, on the same terms.
140
140
  `Commit` must still name the **same single documentation commit**.
141
141
  - [ ] **Minimal host (zero-phase) only** — **nothing that constitutes the change
142
142
  itself is still uncommitted.** This is deliberately not "the host
143
- directory is clean": **checkpoint 1 carries the change, the closeout
144
- commit carries the record the flow writes afterwards.** Checkpoint 1 sits
145
- at `modify-existing-flow.md`'s Step 4 → Step 5 gate and holds the change
146
- itself — for a **T2** its lightweight-spec plus the implementation source,
147
- for a **T3** its `_index.md` row plus the implementation source. What the
148
- flow writes *after* that gate — Step 5's completion checklist and Step
149
- 1.7's "Finalize + close" — is derived record and rides into the closeout
150
- commit.
143
+ directory is clean": **checkpoint 1 carries the change and the record
144
+ Step 5 writes; the closeout commit carries what the flow writes after
145
+ checkpoint 1.** Checkpoint 1 is offered at the end of
146
+ `modify-existing-flow.md`'s Step 5 (§ 5.5) and holds the change itself —
147
+ for a **T2** its lightweight-spec plus the implementation source, for a
148
+ **T3** its `_index.md` row plus the implementation source — together with
149
+ Step 5's completion checklist. What the flow writes *after* checkpoint 1 —
150
+ Step 1.7's "Finalize + close" — is derived record and rides into the
151
+ closeout commit, and so may a correction to Step 5's record made after
152
+ checkpoint 1.
151
153
  **A post-hoc host (Step 1.8) inverts the source half, and this is where
152
154
  the reconciliation contract is enforced.** Its change is the *already
153
155
  merged* hotfix; checkpoint 1 is the **documentation** commit. So that
@@ -187,8 +189,9 @@ A `✗` here stops closeout there, on the same terms.
187
189
  to both phase-spec and lightweight-spec"): ticking the tasks this change
188
190
  completed and labelling each unchecked one as follow-up (5.1), and — when
189
191
  the developer accepted the offer — collapsing or removing the section
190
- (5.2). On a minimal host that checklist necessarily runs *after*
191
- checkpoint 1, so these edits are uncommitted here by construction;
192
+ (5.2). That checklist runs before checkpoint 1, so these edits are
193
+ normally committed already; this admits a correction to them made after
194
+ checkpoint 1;
192
195
  (iii) the Domain-layer documents **this change's Step 5.3 sweep** updates
193
196
  — under `dflow/specs/domain/`, plus
194
197
  `dflow/specs/architecture/tech-debt.md` — scoped to *this change's*
@@ -200,11 +203,23 @@ A `✗` here stops closeout there, on the same terms.
200
203
  documents (`glossary.md`, `domain/analysis.md`,
201
204
  `architecture/tech-debt.md`) belong to no bounded context, stay in its
202
205
  sweep, and therefore still qualify under (iii).
203
- Read each delta (`git diff -- {path}`) — do not infer it from the
204
- filename. **The list is closed** — if a later change adds another
206
+ Read each delta with `git diff HEAD -- {path}` — plain `git diff` hides
207
+ what is already staged — and do not infer it from the filename.
208
+ **The list is closed** — if a later change adds another
205
209
  finalization field, add it here explicitly; "a host file is dirty" is never
206
210
  on its own a reason to pass. (Unrelated work in progress elsewhere is
207
211
  fine; say which files you judged unrelated.)
212
+ **Checkpoint 1's own record, corrected after checkpoint 1, is outside the
213
+ list** — the spec's content beyond (ii)'s edits, a row's own text, or the
214
+ reverse-link row a follow-up wrote into its original's `_index.md`. It
215
+ blocks: the closeout commit does not carry checkpoint 1's content. The
216
+ dispatch right after this item gives the repair.
217
+
218
+ **Open `references/finish-feature-record-correction.md` and follow it there.**
219
+ Do this only when the check above blocked on a correction to checkpoint 1's own
220
+ record: the repair depends on where checkpoint 1 stands now, and that file gives
221
+ it for each case. Otherwise go on to the next check.
222
+
208
223
  - [ ] **Minimal host (zero-phase), no-BC only** — **the host did not commit a
209
224
  bounded context it does not have.** This file's `nothing that constitutes
210
225
  the change itself is still uncommitted` check reads the *working
@@ -273,10 +288,11 @@ A `✗` here stops closeout there, on the same terms.
273
288
  assertion.
274
289
  The plausibility conditions, all required: the
275
290
  `reconciled ({merged-hotfix-hash})` value passes hash evidence — (a) and
276
- (b) as usual, and for **(c)** its diff touches the implementation paths
277
- the T2 lightweight-spec or the T3 row **declares** (Step 1.7 requires that
278
- declaration; an artifact naming none leaves (c) nothing to compare and
279
- **blocks** rather than passing vacuously). It must also **differ**
291
+ (b) as usual, and for **(c)** its diff touches **at least one** of the
292
+ implementation paths the T2 lightweight-spec or the T3 row **declares**
293
+ (Step 1.7 requires that declaration; an artifact naming none leaves (c)
294
+ nothing to compare and **blocks** rather than passing vacuously). It
295
+ must also **differ**
280
296
  from the Lightweight Changes row's documentation-commit hash (different
281
297
  provenance; see Step 1.8). The per-tier trace must exist too: a **T2**
282
298
  carries `hotfix-branch:` in its lightweight-spec frontmatter, a **T3** has
@@ -288,6 +304,10 @@ A `✗` here stops closeout there, on the same terms.
288
304
  for a **T3**. An uncited hash **blocks** closeout: it leaves pr-review
289
305
  nothing to confirm against, and this gate is not a substitute for that
290
306
  confirmation.
307
+ **When (b) fails for the `reconciled (...)` hash, do not report only
308
+ "not an ancestor".** Open `references/modify-existing-post-hoc-hotfix.md`
309
+ item 6: it names the causes and the repair for each; report the one that
310
+ is true.
291
311
 
292
312
  > **Zero-phase minimal host (`modify-existing-flow.md` Step 1.7 / the Step 1.6
293
313
  > minimal variant / Step 1.8's post-hoc hotfix, whose linkage resolves to one of
@@ -0,0 +1,45 @@
1
+ # Correcting Checkpoint 1's Own Record — Greenfield Clean Architecture
2
+
3
+ Branch file of `references/finish-feature-minimal-host.md`. It **adds** the
4
+ repair for one case that file's working-tree check blocks; it does not restate
5
+ or override anything in that file.
6
+
7
+ **You are here because** that file's `nothing that constitutes the change
8
+ itself is still uncommitted` check blocked on a correction to checkpoint 1's
9
+ own record — it owns that judgement — or because
10
+ `references/modify-existing-post-hoc-hotfix.md` item 6 found that a post-hoc
11
+ record names a different fix.
12
+
13
+ The correction belongs in checkpoint 1 itself, and whether it can go there
14
+ depends on where checkpoint 1 stands now. Report the case that is true and give
15
+ the developer its repair:
16
+
17
+ - **Not pushed, and checkpoint 1 is `HEAD`** (`git rev-parse HEAD` prints
18
+ checkpoint 1's hash) — first set every entry that holds checkpoint 1's hash
19
+ back to what checkpoint 1 has (`git show HEAD:{path}`); the refill below
20
+ writes them again. Then stage the correction alone (in `_index.md`, `git add
21
+ -p` only its hunk: the Resume Pointer rides the closeout commit), check that
22
+ `git diff --cached` shows nothing else, and `git commit --amend`. Then write
23
+ the new hash into every entry that names checkpoint 1 — the Checkpoint Log
24
+ `committed (...)` and each row's `Commit` cell. An entry left on the old hash
25
+ fails hash evidence (b).
26
+ - **Not pushed, but a later commit sits on top of checkpoint 1** — stop:
27
+ rewriting an earlier commit is the developer's to do.
28
+ - **Already pushed** — stop and leave it to the developer. Rewriting a pushed
29
+ commit is the team's call (refill the entries as above if they do). The other
30
+ way is to commit the correction on its own: add no Checkpoint Log row for it
31
+ (a second row blocks), and ask for it to be named in the PR description — it
32
+ departs from the two-commit lifecycle, and
33
+ `references/pr-review-checklist.md`'s whole-history item sees it only when it
34
+ touches this host's directory.
35
+
36
+ **A post-hoc host's `reconciled (...)`** names the hotfix, not checkpoint 1, so
37
+ refilling after an amend leaves it as it is — **unless the correction changes
38
+ which fix the record names** (`references/modify-existing-post-hoc-hotfix.md`
39
+ item 6, "The record names a different fix"). Then `reconciled (...)` names the
40
+ wrong fix too. Item 3 wrote it before checkpoint 1, so checkpoint 1 carries it:
41
+ correct it to the right fix's commit — the one item 3 says to record — as part
42
+ of the same correction, whichever way that lands (the amend, the developer's
43
+ rewrite, or the correction's own commit). Put that commit through item 6's test
44
+ before closeout continues; that item gives the repair if (b) fails for it as
45
+ well.