dflow-sdd-ddd 0.13.0 → 0.15.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 (96) hide show
  1. package/CHANGELOG.md +824 -1
  2. package/CONTRIBUTING.md +16 -10
  3. package/README.en.md +156 -200
  4. package/README.md +89 -144
  5. package/TEMPLATE-COVERAGE.md +15 -8
  6. package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
  7. package/bin/dflow.js +36 -4
  8. package/docs/commands.en.md +110 -0
  9. package/docs/commands.md +101 -0
  10. package/docs/doctor-uncertainty.en.md +212 -0
  11. package/docs/doctor-uncertainty.md +212 -0
  12. package/docs/evaluating-dflow.en.md +29 -11
  13. package/docs/evaluating-dflow.md +8 -6
  14. package/docs/npm-publish-checklist.md +3 -1
  15. package/docs/release-versioning-policy.md +8 -2
  16. package/docs/upgrading.en.md +196 -0
  17. package/docs/upgrading.md +197 -0
  18. package/docs/using-with-claude-code.en.md +25 -10
  19. package/docs/using-with-claude-code.md +20 -7
  20. package/docs/using-with-codex.en.md +18 -6
  21. package/docs/using-with-codex.md +16 -5
  22. package/docs/using-with-github-copilot.en.md +25 -10
  23. package/docs/using-with-github-copilot.md +21 -8
  24. package/lib/doc-shapes.json +997 -0
  25. package/lib/doctor-checks.js +2654 -0
  26. package/lib/init.js +3583 -107
  27. package/lib/render-diagrams.js +1474 -0
  28. package/lib/render.js +865 -49
  29. package/package.json +2 -2
  30. package/templates/brownfield/references/drift-verification.md +4 -0
  31. package/templates/brownfield/references/finish-feature-flow.md +635 -88
  32. package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
  33. package/templates/brownfield/references/finish-feature-minimal-host.md +406 -0
  34. package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  35. package/templates/brownfield/references/git-integration.md +160 -15
  36. package/templates/brownfield/references/init-project-flow.md +26 -4
  37. package/templates/brownfield/references/modify-existing-flow.md +412 -87
  38. package/templates/brownfield/references/modify-existing-follow-up.md +121 -0
  39. package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  40. package/templates/brownfield/references/new-feature-flow.md +61 -6
  41. package/templates/brownfield/references/new-phase-flow.md +57 -7
  42. package/templates/brownfield/references/pr-review-checklist.md +303 -10
  43. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +158 -34
  44. package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
  45. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +75 -6
  46. package/templates/brownfield/scaffolding/Git-principles-trunk.md +82 -8
  47. package/templates/brownfield/scaffolding/_conventions.md +50 -28
  48. package/templates/brownfield/scaffolding/_overview.md +1 -0
  49. package/templates/brownfield/templates/_index.md +151 -7
  50. package/templates/brownfield/templates/analysis.md +79 -0
  51. package/templates/brownfield/templates/behavior.md +1 -0
  52. package/templates/brownfield/templates/context-definition.md +1 -0
  53. package/templates/brownfield/templates/context-map.md +2 -1
  54. package/templates/brownfield/templates/glossary.md +1 -0
  55. package/templates/brownfield/templates/lightweight-spec.md +154 -11
  56. package/templates/brownfield/templates/models.md +1 -0
  57. package/templates/brownfield/templates/phase-spec.md +9 -1
  58. package/templates/brownfield/templates/rules.md +1 -0
  59. package/templates/brownfield/templates/tech-debt.md +1 -0
  60. package/templates/common/references/ddd-modeling-guide.md +33 -16
  61. package/templates/{greenfield → common}/references/dflow-feedback-flow.md +2 -1
  62. package/templates/common/references/flow-rationale-registry.md +130 -0
  63. package/templates/common/skill/SKILL.md +13 -11
  64. package/templates/greenfield/references/drift-verification.md +4 -0
  65. package/templates/greenfield/references/finish-feature-flow.md +625 -89
  66. package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
  67. package/templates/greenfield/references/finish-feature-minimal-host.md +363 -0
  68. package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  69. package/templates/greenfield/references/git-integration.md +148 -15
  70. package/templates/greenfield/references/init-project-flow.md +28 -8
  71. package/templates/greenfield/references/modify-existing-flow.md +378 -85
  72. package/templates/greenfield/references/modify-existing-follow-up.md +103 -0
  73. package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  74. package/templates/greenfield/references/new-feature-flow.md +67 -4
  75. package/templates/greenfield/references/new-phase-flow.md +56 -7
  76. package/templates/greenfield/references/pr-review-checklist.md +287 -8
  77. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +153 -32
  78. package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
  79. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +74 -6
  80. package/templates/greenfield/scaffolding/Git-principles-trunk.md +88 -12
  81. package/templates/greenfield/scaffolding/_conventions.md +50 -28
  82. package/templates/greenfield/scaffolding/_overview.md +6 -2
  83. package/templates/greenfield/templates/_index.md +137 -7
  84. package/templates/greenfield/templates/aggregate-design.md +1 -0
  85. package/templates/greenfield/templates/analysis.md +79 -0
  86. package/templates/greenfield/templates/behavior.md +1 -0
  87. package/templates/greenfield/templates/context-definition.md +1 -0
  88. package/templates/greenfield/templates/context-map.md +2 -1
  89. package/templates/greenfield/templates/events.md +4 -1
  90. package/templates/greenfield/templates/glossary.md +1 -0
  91. package/templates/greenfield/templates/lightweight-spec.md +154 -11
  92. package/templates/greenfield/templates/models.md +1 -0
  93. package/templates/greenfield/templates/phase-spec.md +9 -1
  94. package/templates/greenfield/templates/rules.md +1 -0
  95. package/templates/greenfield/templates/tech-debt.md +1 -0
  96. package/templates/brownfield/references/dflow-feedback-flow.md +0 -251
@@ -15,25 +15,35 @@ state, archives the feature directory, and emits a Git-strategy-neutral
15
15
  own. Merge strategy follows the team's selected Git policy (`gitflow` /
16
16
  `trunk`, recorded in `dflow/specs/shared/_conventions.md` § Git Policy).
17
17
  - Closeout is split into two gates so it works offline: a **Local-closeout
18
- gate** (Steps 1–4: validation, status flip, BC sync, archive + an optional
19
- commit checkpoint — all doable with no network) and an **Integration / PR
18
+ gate** (Steps 1–4: validation, status flip, BC sync, archive + the closeout
19
+ commit checkpoint — all doable with no network; what is optional is **who**
20
+ runs that commit, not whether it happens, see Step 4) and an **Integration / PR
20
21
  gate** (Step 5: push / merge / PR — needs network; the AI only runs
21
22
  `git push` / `gh pr create` when you explicitly ask).
22
23
  - At the archive checkpoint the AI may offer to commit using your Git identity;
23
24
  you can always decline. The commit marker mode is read from `_conventions.md`
24
25
  § AI Commit Policy. This replaces Dflow's earlier "the AI never commits"
25
26
  stance — the AI helps at natural checkpoints, you keep the final say.
26
- - The BC-layer sync in Step 3 **reuses the existing Step 8.3 mechanism**
27
- from `new-feature-flow` — it does not introduce a new sync flow. Treat
28
- it as "lift Step 8.3 out of the per-phase checklist and run it once at
29
- feature closeout, with the `_index.md` Current BR Snapshot as input."
27
+ - The BC-layer sync in Step 3 **reuses the existing documentation-sync
28
+ mechanism** from `new-feature-flow` (§ 8.3) and `modify-existing-flow`
29
+ (Step 6.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."
30
32
 
31
33
  **Step Gates** in this flow (stop-and-confirm before proceeding):
32
34
  - Step 1 → Step 2 (validation passed → flip status)
33
35
  - Step 3 → Step 4 (BC sync done → archive)
34
- - Step 5 → Step 6 (Integration Summary emitted → optional follow-up reverse-link)
35
36
 
36
- All other step transitions are **step-internal**: announce "Step N complete,
37
+ **Step 5 → Step 6 is deliberately not a step gate, and when the host carries a
38
+ `follow-up-of` field it also stops and waits.** It is this flow's
39
+ **post-Local-closeout confirmation**: by the time it is reached the archive has
40
+ landed and this feature's cursor reads `none`, so the step-gate protocol —
41
+ `/dflow:next` and `/dflow:cancel`, and the cursor update a step gate carries —
42
+ does not apply to it. **Step 5 states what it accepts.** Do not treat it as a
43
+ step gate because it waits, and do not treat it as step-internal because it is
44
+ not a gate.
45
+
46
+ **Every other step transition is step-internal**: announce "Step N complete,
37
47
  entering Step N+1" and proceed without waiting. See AI-AGENT-GUIDE.md § Workflow
38
48
  Transparency for the full transparency protocol and confirmation signals.
39
49
 
@@ -48,12 +58,55 @@ AI runs mechanical checks first. Report `✓` / `✗` for every item; if any
48
58
  `✗` appears, **stop here** and ask the developer to address them before
49
59
  proceeding (do not flip status, do not archive, do not emit summary).
50
60
 
61
+ **What these checks do and do not prove.** They catch the mistakes that actually
62
+ happen — work left uncommitted, a placeholder hash never replaced, a row copied
63
+ from another feature — not a record deliberately built to look right.
64
+ Whole-history assertions ("no stray host-open commit exists anywhere on this
65
+ branch") are **review's** job, not closeout's: they belong to
66
+ `references/pr-review-checklist.md`.
67
+
68
+ > **Reconcile a mainline hotfix BEFORE you run these checks.** If a T2 / T3
69
+ > post-hoc hotfix (`modify-existing-flow.md` Step 1.8) landed on the mainline
70
+ > while this feature was in flight and the two touched the same code, settle the
71
+ > overlap **now, before the checklist below** — anything *recorded into this
72
+ > host* after the checks have passed has bypassed them.
73
+ >
74
+ > **Open `references/finish-feature-post-hoc-hotfix.md` and follow it there.**
75
+ > What you classify, where the merge-resolution delta may be recorded, and what
76
+ > must not be routed mid-closeout are decided there.
77
+
78
+ **What "Minimal host (zero-phase) only" selects, and what that does not prove.**
79
+ A minimal host takes extra checks and extra field rules. Decide it from the
80
+ **persisted shape**: an **empty Phase Specs table and no `phase-spec-*` file**
81
+ in the host directory. There is no other selector — closeout runs in a fresh
82
+ session and cannot know which flow step created the host. So a host that
83
+ *carries* a phase is certified as **phase-bearing**, whatever it was intended to
84
+ be, and takes the ordinary checks rather than these. This gate does **not**
85
+ prove "this host was opened as minimal and stayed that way"; that is a claim
86
+ about history, and it belongs with the whole-history assertion already assigned
87
+ to `references/pr-review-checklist.md`.
88
+
89
+ **Open `references/finish-feature-minimal-host.md` and follow it there.** Do
90
+ this only when this host is minimal; a **phase-bearing** host does not open it
91
+ at all. Its rules live in that file and are not repeated here, and it stays open
92
+ for the whole closeout — it adds to this checklist, to Step 3's sync input, to
93
+ Step 4's post-commit verification, and to Step 5's Integration Summary fields.
94
+
51
95
  - [ ] Locate the feature directory at `dflow/specs/features/active/{SPEC-ID}-{slug}/`
52
- - [ ] `_index.md` exists and parses (YAML front matter intact, seven required
53
- sections present, including the Checkpoint Log)
96
+ - [ ] `_index.md` exists and parses (YAML front matter intact, and all seven
97
+ required sections present — the Metadata front matter, Goals & Scope,
98
+ Phase Specs, Current BR Snapshot, Lightweight Changes, Checkpoint Log,
99
+ Resume Pointer; `Follow-up Tracking` is `templates/_index.md`'s optional
100
+ eighth section and appears only when this feature has follow-ups)
54
101
  - [ ] Every row in `_index.md` Phase Specs table has Status = `completed`
55
102
  - [ ] Every phase-spec file referenced in the Phase Specs table exists at
56
103
  the path the table claims
104
+ - [ ] **And the other direction, for every host shape: every spec file in the
105
+ host directory is named by a row** — each `phase-spec-*` by a Phase Specs
106
+ row, each `lightweight-*.md` / `BUG-*.md` by a `Tier = T2` Lightweight
107
+ Changes row. An orphan file **blocks** — either it belongs to a phase or
108
+ change whose row is missing, or it belongs to nothing and does not belong
109
+ in the host.
57
110
  - [ ] Every phase-spec file's frontmatter has `status: completed`
58
111
  - [ ] Every Tier = T2 row in `_index.md` Lightweight Changes references an
59
112
  existing `lightweight-*.md` / `BUG-*.md` file in the feature directory
@@ -61,8 +114,54 @@ proceeding (do not flip status, do not archive, do not emit summary).
61
114
  `status: completed`
62
115
  - [ ] `_index.md` has no obvious open items in Resume Pointer (e.g. "phase-N
63
116
  drafting" / "implementation pending" / "TODO" markers)
64
- - [ ] Current BR Snapshot table is non-empty (or feature is intentionally
65
- a no-BR feature — confirm with developer if uncertain)
117
+ - [ ] **You are on this host's branch — or on a branch this host recorded a
118
+ sanctioned override for.** `git rev-parse --abbrev-ref HEAD`
119
+ equals `_index.md`'s `branch:` value — that field is authoritative for the
120
+ whole host — and for a **T2** the lightweight-spec's frontmatter `branch:`
121
+ equals it too. Steps 1–4 run *before* the merge / PR gate, so a mismatch
122
+ means you are closing out somewhere other than where the work was
123
+ finished, and it **blocks**. A hash check does not cover this: any sibling
124
+ branch descended from checkpoint 1 carries the same commits, so every hash
125
+ this host's record names still resolves and is still an ancestor. For a
126
+ **post-hoc** host this compares against the **documentation** branch,
127
+ never `hotfix-branch:`, which names the already-merged hotfix.
128
+ **Before blocking on a mismatch, read the Checkpoint Log for a
129
+ `branch-override` row.** `references/git-integration.md` § Branch gate
130
+ offers "override and stay" as a sanctioned third option and defines that
131
+ row's shape. If **any** such row's Result names the branch `HEAD` is on,
132
+ the mismatch is one this host recorded deliberately and this item
133
+ **passes**. `branch:` itself is still never rewritten — the override is a
134
+ record beside it, not a correction of it.
135
+ ⚠ **The row must name *this* branch** — that is the whole test.
136
+ ⚠ **What this cannot decide, stated rather than patched:** whether a
137
+ recorded override is still the current intent. **Nothing expires a row at
138
+ all** — not when the developer moves back to the host's own branch, not
139
+ when a later override supersedes it, and not with distance from closeout.
140
+ So *any* branch this host ever recorded an override for stays acceptable
141
+ here: closing out on the base branch after the feature branch was merged
142
+ passes, and so does closing out on an abandoned spike branch that an
143
+ override named two phases ago. Closeout cannot separate either from a
144
+ legitimate override. **Which branch the closeout commit actually landed on
145
+ is visible in the PR**, and it is judged by
146
+ `references/pr-review-checklist.md`'s **"A recorded branch override still
147
+ matches where the closeout landed"** item.
148
+ **When it passes this way, say so in the conversation** — name the row and
149
+ the branch, e.g. *"HEAD differs from `branch:`; found a recorded
150
+ `branch-override` for `{branch}` in the Checkpoint Log — verified and
151
+ passing."*
152
+ - [ ] Current BR Snapshot table is non-empty — **or this host's own record
153
+ carries no BR delta**, which is what makes an empty one legitimate.
154
+ That condition is the check; the shapes below are illustrations of it,
155
+ not the list of them: a **T3** host; a **no-BR family T2** whose Behavior
156
+ Delta is a `BR:` / `BR Delta:` none line; a **baseline capture**
157
+ (observation-only, so it has no BR delta to record); a **phase-bearing**
158
+ host — **including a T1** — whose phase-specs establish no BR delta,
159
+ which the cascade explicitly allows; or a shape added later that likewise
160
+ carries none. Decide from the artifact, not from a
161
+ declaration: "the feature is intentionally no-BR" is a claim, and the
162
+ record is what settles it. A classic BR-delta spec carrying ADDED /
163
+ MODIFIED / RENAMED entries **and** an empty Snapshot means finalization
164
+ never refreshed it (`modify-existing-flow.md` Step 1.7) and **blocks**.
66
165
 
67
166
  If any check fails:
68
167
  > "Cannot finish feature `{SPEC-ID}-{slug}` yet — {N} validation issues
@@ -74,6 +173,50 @@ If any check fails:
74
173
  > Address these (run `/dflow:new-phase` to add missing work, or fix the
75
174
  > stale status manually), then re-run `/dflow:finish-feature`."
76
175
 
176
+ **Once every item above — and every item this host's branch file added — reads
177
+ `✓`, record the baseline the post-commit check compares against.** Capture
178
+ **exactly the set the closeout commit will carry**:
179
+
180
+ ```bash
181
+ git ls-files --cached --others --exclude-standard -- dflow/specs/features/active/{SPEC-ID}-{slug}
182
+ ```
183
+
184
+ ⚠ **Both halves of that command are load-bearing, because it has to agree with
185
+ `git add`.** It **excludes** ignored files, which `git add` also skips and the
186
+ span can never carry. It **includes** untracked files that are not ignored,
187
+ which `git add` **does** stage — a host file created during this feature and
188
+ never committed still rides into the closeout commit, so a baseline without it
189
+ reports that file as added after Step 1 and blocks a correct closeout.
190
+
191
+ Run `git hash-object -w {path}` for each and build the `path → blob` list, one
192
+ entry per line. **Strip the host-directory prefix**: that command prints paths
193
+ from the repository root, and Step 4 matches on the path *relative to the host
194
+ directory*.
195
+ ⚠ **The `-w` is mandatory.** It writes each blob into the object database, so
196
+ Step 4 can read the baseline's **content** back; without it the list is
197
+ fingerprints only, and Step 4's comparison cannot run.
198
+
199
+ **Then anchor the list itself where Step 4 can recompute its name** — pipe the
200
+ list into `git hash-object -w --stdin`, then:
201
+
202
+ ```bash
203
+ git update-ref refs/dflow/closeout-baseline/{SPEC-ID}-{slug} {list blob}
204
+ ```
205
+
206
+ **That ref is the baseline.** Step 4 resolves it by recomputing the same name
207
+ from this host's own `{SPEC-ID}-{slug}`; if `git check-ref-format` rejects that
208
+ name, say so and stop.
209
+ ⚠ **Trim both fields of every entry, on write and on read.** A shell that
210
+ writes CRLF leaves a trailing `\r` on each line, and `git cat-file -p` on a blob
211
+ id carrying one fails as an invalid object name — which would report a healthy
212
+ baseline as unreadable.
213
+ ⚠ **`refs/dflow/**` is local by construction.** A default `git push`, `git fetch`
214
+ and a plain `git clone` all leave it behind; an explicit refspec or
215
+ `clone --mirror` does carry it. Do not add it to any refspec.
216
+ ⚠ **`HEAD^` is not a substitute** — at this point the working tree legitimately
217
+ carries uncommitted finalization edits, so comparing against the parent commit
218
+ reports every one of them as a difference no step ordered.
219
+
77
220
  **→ Step Gate: Step 1 → Step 2**
78
221
 
79
222
  If all checks pass:
@@ -93,43 +236,121 @@ spec-id: SPEC-{YYYYMMDD}-{NNN}
93
236
  slug: {slug}
94
237
  status: completed # ← flipped from in-progress
95
238
  created: {YYYY-MM-DD}
96
- branch: feature/{SPEC-ID}-{slug}
239
+ branch: {unchanged — keep the host's existing value} # feature/{SPEC-ID}-{slug} or bugfix/BUG-{NUMBER}-{slug}; never rewrite
97
240
  ---
98
241
  ```
99
242
 
100
- Also update the **Resume Pointer** to reflect closeout — this writes the
101
- cursor's terminal state (after closeout no workflow is active on this
102
- feature; do not edit the cursor again after the Step 4 closeout commit):
243
+ **Flip `status` only — never rewrite `branch:`.** The `branch:` field is
244
+ authoritative and stays exactly as opened: a bugfix host keeps its
245
+ `bugfix/BUG-{NUMBER}-{slug}` value, a **non-bug** standalone / follow-up host —
246
+ and every baseline capture, which is never a functional bug — keeps its
247
+ `feature/{SPEC-ID}-{slug}` value. The branch follows the **change class**, not
248
+ how the host was opened — a functional-bug standalone or follow-up host **is** a
249
+ bugfix host and keeps `bugfix/BUG-*` (the branch-by-class rule at
250
+ `modify-existing-flow.md` Step 1.7 step 4). Overwriting it to `feature/...` at
251
+ closeout would break branch equality for a bugfix host.
252
+
253
+ Also update the **Resume Pointer** — to the state closeout is actually in, which
254
+ is **not** the terminal state. **Step 4 writes the terminal value**, immediately
255
+ after the `git mv`:
103
256
 
104
257
  ```
105
- **Current Progress**: feature completed ({date}); all phase-specs status = completed.
106
- **Next Action**: integration — push / merge / PR per the selected Git policy.
107
- **Active Workflow**: none
108
- **Current Step**: n/a
109
- **Gates Passed**: n/a
110
- **Awaiting**: none
258
+ **Current Progress**: status flipped to `completed` ({date}); closeout in progress.
259
+ **Next Action**: continue closeout — sync the BR Snapshot to the BC layer (Step 3).
260
+ **Active Workflow**: finish-feature
261
+ **Current Step**: Step 3 — sync BR Snapshot to BC layer
262
+ **Gates Passed**: 1→2
263
+ **Awaiting**: none (mid-step)
111
264
  ```
112
265
 
266
+ ⚠ **`Awaiting` is `none (mid-step)`, never `gate 3→4`.** Step 3 has not run at
267
+ this point, and a cursor that says otherwise sends the next session to
268
+ `/dflow:next`, **skipping the BC sync**. `none (mid-step)` is the existing
269
+ convention for this position.
270
+
113
271
  **→ Transition (step-internal)**: Step 2 complete. Announce "Step 2 complete (status flipped). Entering Step 3: Sync BR Snapshot to BC layer." and continue.
114
272
 
115
273
  ## Step 3: Sync `_index.md` Current BR Snapshot to BC Layer
116
274
 
117
- This step **reuses the existing sync mechanism** from `new-feature-flow`
275
+ **First, branch on what this host actually carries** — "zero-phase" (no
276
+ phase-spec) is independent of whether a bounded context exists, so a minimal
277
+ host may still touch a real BC, or none at all:
278
+
279
+ - **(i) BC-bearing** — the host touched a real bounded context, **whether or
280
+ not it carries a BR delta** (BC presence and BR presence are independent). Run
281
+ the sync below **for the documents this host actually changed**: a BR delta
282
+ updates `rules.md` / `behavior.md`; a no-BR family that touched a documented
283
+ behaviour still updates `behavior.md`. Fill the Integration Summary's BC field
284
+ with the context, and its BR-IDs with whatever applies — a real set, the
285
+ per-family no-BR marker, or empty. Do **not** skip, and do **not** manufacture
286
+ a BR delta a no-BR host does not have. (A host whose **entire** payload is a
287
+ baseline capture is **(iii)**, not this case.)
288
+ - **(ii) no-BC** — the host touched **no** bounded context at all (a display
289
+ T3, an appearance sweep). **Skip this sync entirely** — do **not** create
290
+ `rules.md` / `behavior.md`, and do not invent a BC to sync into. Its
291
+ Integration Summary sets the field that **reports a sync** — `BC` — to
292
+ `none`. **`Related BR-IDs` is not one of those**: it reports what this
293
+ change's own record carries, so it stays empty or keeps the per-family no-BR
294
+ marker. Step 5's "exact fields" block is the authority on the shape; do not
295
+ flatten it to "all BR fields are none" here.
296
+ - **(iii) baseline pre-captured** — this host's **entire** payload is the
297
+ baseline capture: a **zero-phase minimal host** whose only Lightweight Changes
298
+ row is the `Tier = baseline` one. That capture already wrote its
299
+ documents when it ran (`modify-existing-flow.md` Step 1.7), so there is
300
+ **nothing more to sync**; the Integration Summary's BC field names the
301
+ captured context (not `none`). A **phase-bearing** host carrying a baseline row
302
+ alongside other work takes **(i)** instead and syncs everything except the
303
+ baseline. A *minimal* host cannot be in that position at all —
304
+ `references/finish-feature-minimal-host.md`'s `baseline only` check blocks a
305
+ `spec-baseline` host that carries anything beyond its capture.
306
+
307
+ For a **BC-bearing host (case i only)**, continue with the sync. This step **reuses the
308
+ existing sync mechanism** from `new-feature-flow`
118
309
  Step 8.3 (`dflow/specs/domain/{context}/rules.md` + `behavior.md` updates). The
119
310
  input is the feature's `_index.md` Current BR Snapshot table; the output
120
311
  is the BC's `rules.md` and `behavior.md` updated to reflect the
121
312
  feature's net effect.
122
313
 
123
- Before syncing, ensure required BC files exist. If missing, create from templates:
314
+ **Minimal host — sync input.** A minimal host reads "phase-spec" differently
315
+ here, and a phase-bearing host must not apply that reading. The rule is
316
+ `references/finish-feature-minimal-host.md` § Step 3.
317
+
318
+ **Every host — lightweight-spec deltas are a sync input too.** A host of **any**
319
+ shape may carry hosted Lightweight Changes rows: the two `Tier = T2` checks in
320
+ Step 1 carry no host-shape restriction, so they run on a phase-bearing host as
321
+ well. So a
322
+ phase-bearing host can hold a hosted T2 whose delta belongs in this sync, and
323
+ **the steps below name phase-specs only**. Read them as *this feature's
324
+ phase-specs **and** its hosted lightweight-specs' recorded deltas*.
325
+ ⚠ **Where this bites hardest is a no-BR family**, because the BR-driven sections
326
+ below cannot reach it at all: with no BR-ID there is no Current BR Snapshot row
327
+ to iterate, so a hosted T2 that changed only a documented behaviour is visible
328
+ **solely** in its own recorded delta. Miss it and `behavior.md` silently loses a
329
+ change that a phase-bearing closeout had no other instruction to look for.
330
+ ⚠ This does not reach a **baseline** row: case (iii) above already wrote the
331
+ capture when it ran, so there is nothing left for this sync to take from
332
+ it.
333
+
334
+ Before syncing, ensure the BC files **this sync actually writes** exist; create a
335
+ missing one from its template **only when this host's delta writes to it**. Case
336
+ (i) above syncs "the documents this host actually changed", so a document this
337
+ host does not touch is **not** created — a BC-bearing **T3**, or a no-BR family
338
+ that touched only one of them, leaves the others absent, exactly as case (ii)
339
+ does for a no-BC host. Creating one anyway plants the same fiction the no-BC
340
+ guard refuses:
124
341
  - `dflow/specs/domain/{context}/rules.md` → `templates/rules.md`
125
342
  - `dflow/specs/domain/{context}/behavior.md` → `templates/behavior.md`
126
343
 
127
- For each row in Current BR Snapshot where Status = `active`:
344
+ > **Table-cell formatting**: keep table cells concise — separate multiple short items with `<br>` (never chain them into one line with ;/; separators), and move long narrative detail out of the cell into a document section (full convention: the formatting comment at each spec doc's head).
128
345
 
129
- - If the BR-ID is **not yet in `rules.md`** → add it (new ADDED rule
130
- introduced by this feature)
131
- - If the BR-ID is **already in `rules.md`** but the rule text differs →
132
- update it (MODIFIED rule, reflect the new text)
346
+ For each row in Current BR Snapshot — **every** row, not only the `active` ones.
347
+ The branch below is selected by the row's own Status, and a `removed` row is
348
+ exactly what the REMOVED branch needs:
349
+
350
+ - Status `active`, and the BR-ID is **not yet in `rules.md`** → add it (new
351
+ ADDED rule introduced by this feature)
352
+ - Status `active`, and the BR-ID is **already in `rules.md`** but the rule text
353
+ differs → update it (MODIFIED rule, reflect the new text)
133
354
  - If the BR-ID was previously in `rules.md` and is now in Current BR
134
355
  Snapshot with Status = `removed` → remove the corresponding section in
135
356
  `rules.md` (REMOVED rule)
@@ -151,7 +372,8 @@ rules.md ↔ behavior.md drift check (see `references/drift-verification.md`).
151
372
 
152
373
  Cross-reference each phase-spec's Delta-from-prior-phases section to
153
374
  double-check the net result; the Snapshot is the SSOT but the per-phase
154
- Deltas are the audit trail.
375
+ Deltas are the audit trail. **Cross-reference each hosted lightweight-spec's
376
+ recorded delta the same way** — on any host shape, for the reason above.
155
377
 
156
378
  > Note: this step does NOT read individual phase-specs to re-derive the BR
157
379
  > set — that work was already reconciled by `/dflow:new-phase` Step 7 each
@@ -160,10 +382,12 @@ Deltas are the audit trail.
160
382
  > and the phase-specs, fix `_index.md` first, then re-run
161
383
  > `/dflow:finish-feature`.
162
384
 
163
- Also update `migration/tech-debt.md` / `models.md` / `glossary.md` as
164
- discovered during the feature (the same items listed in
165
- `new-feature-flow.md` Step 8.3) — these may have been touched per phase
166
- already; this is the closeout sweep.
385
+ Also update `migration/tech-debt.md` / `models.md` / `glossary.md` /
386
+ `analysis.md` as discovered during the feature (the same items listed in
387
+ `new-feature-flow.md` Step 8.3; `analysis.md` is two files — the owning
388
+ context's and the domain-root one — each created from `templates/analysis.md`
389
+ the first time there is something to record) — these may have been touched per
390
+ phase already; this is the closeout sweep.
167
391
 
168
392
  **→ Step Gate: Step 3 → Step 4**
169
393
 
@@ -172,25 +396,83 @@ already; this is the closeout sweep.
172
396
  > updated, `last-updated` set to {date}. Ready to archive the feature
173
397
  > directory? `/dflow:next` to proceed."
174
398
 
399
+ For a **no-BC host** (case ii) the sync was skipped — do **not** announce a
400
+ sync that did not happen. Say instead: "No BC-scoped sync was performed (no-BC
401
+ host). Ready to archive the feature directory? `/dflow:next` to proceed." Do
402
+ **not** say "nothing was written to the Domain layer" — a no-BC host may still
403
+ have updated a **global** document (`glossary.md`, `domain/analysis.md`,
404
+ `migration/tech-debt.md`); those belong to no bounded context and Step 4 must
405
+ still stage them.
406
+
407
+ For a **baseline host** (case iii) the BC was already captured at baseline —
408
+ nothing further to sync. Say instead: "BC `{context}` was pre-captured at
409
+ baseline; nothing further to sync. Ready to archive the feature directory?
410
+ `/dflow:next` to proceed."
411
+
175
412
  Wait for confirmation before entering Step 4.
176
413
 
177
414
  ## Step 4: Archive — `git mv` the Feature Directory
178
415
 
179
- AI runs:
416
+ **First, confirm the baseline is still readable — before anything irreversible:**
417
+
418
+ ```bash
419
+ git cat-file -p refs/dflow/closeout-baseline/{SPEC-ID}-{slug}
420
+ ```
421
+
422
+ If it does not resolve, or the list it prints names a blob that does not read
423
+ back, **stop here** and say the baseline is unavailable: closeout restarts from
424
+ Step 1, which re-captures it. **Nothing is committed yet and the host is still in
425
+ `active/`** — that is what this position buys.
426
+ ⚠ **This does not prove the baseline is the *right* one** — only that it is
427
+ readable. Reading back and *judging* is the post-commit item's job.
428
+
429
+ Then AI runs:
180
430
 
181
431
  ```bash
182
432
  git mv dflow/specs/features/active/{SPEC-ID}-{slug} \
183
433
  dflow/specs/features/completed/{SPEC-ID}-{slug}
184
- git status # confirm rename detection AND check for `RM` — an `M` next to
185
- # a rename means unstaged edits you must re-add before committing
186
434
  ```
187
435
 
188
- `git mv` is mandatory — never use plain `mv` + `git add`. This preserves
189
- git's directory rename detection so `git log --follow` / `git blame` /
190
- PR diff quality stays intact across the move. See
436
+ `git mv` is mandatory — never use plain `mv` + `git add`. See
191
437
  `references/git-integration.md` § "Directory Moves Must Use git mv" for
192
438
  the full rule set.
193
439
 
440
+ **Immediately after the `git mv`, write the Resume Pointer's terminal value into
441
+ the moved `_index.md`.** Write both prose lines and all four cursor fields —
442
+ all six lines of the Resume Pointer — because this write sets each line's final
443
+ value:
444
+
445
+ ```
446
+ **Current Progress**: feature completed ({date}); all phase-specs status = completed.
447
+ **Next Action**: integration — push / merge / PR per the selected Git policy.
448
+ **Active Workflow**: none
449
+ **Current Step**: n/a
450
+ **Gates Passed**: n/a
451
+ **Awaiting**: none
452
+ ```
453
+
454
+ ⚠⚠ **The `git mv` and this write are one uninterruptible pair.** Nothing goes
455
+ between them — not the Y / N prompt below, not a question to the developer, not
456
+ a tool call that can wait on input, and not the `git status` check below.
457
+
458
+ ⚠ **From here the cursor is terminal and closeout does not edit it again** — not
459
+ when the developer declines the commit (N), not when the commit fails, not when
460
+ the post-commit verification below reports `✗`. **Do not restore it to an
461
+ in-progress value on any of those paths.** What surfaces a closeout that failed
462
+ after this point is `git status` — the staged rename plus the uncommitted
463
+ `_index.md` edits — not the cursor, which no global scan reads once the host has
464
+ left `active/`.
465
+
466
+ **Now check the rename landed**, with the terminal cursor already written:
467
+
468
+ ```bash
469
+ git status --short # confirm rename detection AND check for `RM` — an `M`
470
+ # next to a rename means unstaged edits you must re-add
471
+ # before committing. --short is required: the default long
472
+ # format lists the rename and the modification separately
473
+ # and never prints a two-column `RM`.
474
+ ```
475
+
194
476
  **Closeout commit checkpoint** (completes the offline Local-closeout gate):
195
477
 
196
478
  ```
@@ -211,6 +493,14 @@ Then, in this order:
211
493
  still applies to spec / implementation rows — closeout is the documented
212
494
  exception (see `references/git-integration.md` § Commit Checkpoints,
213
495
  Branch Gate & AI Commits).
496
+ **Backfill any unfilled hosted `Commit` cell in the same edit** — a hosted row
497
+ waits for the host's *next* commit and this is it. **Unfilled means empty *or*
498
+ holding a placeholder** — `{hash}`, `{pending}`, `(待 commit)`, anything that
499
+ `git cat-file -t` cannot resolve; both are backfilled the same way, and the
500
+ placeholder case matters more because the cell is **non-empty** and therefore
501
+ invisible to every rule written against empty / non-empty. Write each row's own
502
+ implementation hash, never the closeout hash. After closeout there is no next
503
+ commit, which is why `references/pr-review-checklist.md` asserts it there.
214
504
  2. **Stage the whole archived feature directory:**
215
505
 
216
506
  ```bash
@@ -218,13 +508,72 @@ Then, in this order:
218
508
  ```
219
509
 
220
510
  This is required, not optional: `git mv` stages the rename with the
221
- **last-committed** content, so working-tree edits made earlier in this
222
- flow to the moved files — the Step 2 status flip and Resume Pointer
223
- update, plus the checkpoint row you just wrote — stay **unstaged** until
224
- this `git add`. In `git status`, the moved `_index.md` showing `RM`
225
- instead of plain `R` is exactly this signal. Then also `git add` the
226
- files updated in Step 3 (the updated `rules.md`, `behavior.md`,
227
- `glossary.md`, `tech-debt.md`, etc.) into the same stage.
511
+ **last-committed** content, so **every** working-tree edit to the moved files
512
+ stays **unstaged** until this `git add` — Step 2's status flip and its
513
+ in-progress cursor, whatever gate 3 → 4 wrote to the cursor, the terminal
514
+ cursor value written just after the `git mv`, and **everything instruction 1
515
+ ordered**: the checkpoint row *and* every hosted `Commit` cell it backfilled.
516
+ Take that last part from instruction 1 itself, not from this sentence — it is
517
+ the step that orders those edits, and a copy kept here would go stale the
518
+ next time it changes. In `git status --short`, the moved `_index.md` showing
519
+ `RM` instead of plain `R` is exactly this signal — the same `--short`
520
+ requirement as the rename check above. Then also `git add`
521
+ **every external document this closeout carries.** That set is defined
522
+ here, once, and it covers **every** host shape — take it from this
523
+ instruction, not from a list kept somewhere else:
524
+ **(a)** whatever Step 3 wrote (`rules.md`, `behavior.md`, `models.md`,
525
+ `analysis.md`, `glossary.md`, `domain/analysis.md`,
526
+ `migration/tech-debt.md`) — Step 3 is **skipped entirely for a no-BC host**
527
+ (and for a baseline host), so this half is empty there; **and**
528
+ **(b)** the **documentation-sweep step of the flow that produced *this
529
+ change*** — take the paths from that step, not from a list kept here. It runs
530
+ *after* that flow's implementation checkpoint and *before* closeout, so **that
531
+ flow** leaves its deltas uncommitted; a later phase's own checkpoints may have
532
+ committed them since, and staging an already-committed path is a no-op.
533
+ Those sweeps reach
534
+ Domain-layer documents under `dflow/specs/domain/`, plus the **global**
535
+ documents `glossary.md`, `domain/analysis.md` and `migration/tech-debt.md`,
536
+ which belong to no bounded context and stay legitimate for a **no-BC** host.
537
+ The two that exist
538
+ today —
539
+ `modify-existing-flow.md` **Step 6.3** and `new-feature-flow.md`
540
+ **§ 8.3 Documentation updates** — are **illustrations, not the definition**:
541
+ a later flow with a sweep of its own is covered without editing this line.
542
+ ⚠ **Key it on the flow that produced the change, never on the flow that
543
+ opened the host.**
544
+ ⚠ **How to tell which flow produced a change: it is determined by the
545
+ artifact, and it is recorded nowhere.** The ledger has no producer column, so
546
+ read it off the shape.
547
+ A **Lightweight Changes row** was produced by `modify-existing-flow.md` **by
548
+ construction** — that is the only flow that writes such a row
549
+ (`/dflow:bug-fix` routes into the same file, and `new-feature-flow.md` creates
550
+ that table empty and never adds to it), so its sweep is **Step 6.3**.
551
+ A **phase-spec** has three possible producers — `/dflow:new-feature`,
552
+ `/dflow:new-phase`, or `modify-existing-flow.md` Step 1.6 / 6.4 routing into
553
+ the new-feature machinery — and **you do not have to tell them apart**:
554
+ § 8.3 and Step 6.3 enumerate the same external paths, and `/dflow:new-phase`
555
+ has no sweep at all, so every branch yields that same set or a part of it,
556
+ save one file the next paragraph names.
557
+ ⚠ **`/dflow:new-phase` has no sweep and is deliberately absent here.**
558
+ `new-phase-flow.md` Step 7 updates the phase-spec and this host's own tables,
559
+ and says so in as many words — "System-level domain docs, migration debt
560
+ sync, and the feature directory move to `completed/` remain
561
+ `/dflow:finish-feature` responsibilities; Step 2 records into
562
+ `analysis.md` directly, at whichever of its two paths the entry belongs
563
+ to. **Do not sync any other system-level current state**". The external
564
+ writes its own steps call for are those two paths and, on an
565
+ Aggregate-emergence escalation, the `models.md` row its Step 2 marks; the
566
+ set above already carries all three. The one file outside it is
567
+ `context-map.md`: `ddd-modeling-guide.md`, which its Step 2 sends it to,
568
+ records there a choice to model deeper — stage it too when a phase did.
569
+ On a **minimal host** that set is exactly what allow-list member (iii) of
570
+ `references/finish-feature-minimal-host.md`'s uncommitted-source check
571
+ admitted. On a **phase-bearing** host there is **no allow-list at all** —
572
+ that check is minimal-host-only and lives in that branch file — so read the
573
+ sweep directly and **do not go looking for a list that host never produced.**
574
+ Read each delta (`git diff -- {path}`) and scope every one to *this change*;
575
+ staging only "what Step 3 wrote" leaves a no-BC host's global delta dirty and
576
+ the post-commit clean-tree check fails.
228
577
  3. **Commit (Y) or stop (N).** For Y the AI commits. If a pre-commit hook
229
578
  rejects it or the commit fails, flip the checkpoint row to `failed` (the
230
579
  row is not committed yet — edit it directly), surface the error, and
@@ -234,19 +583,159 @@ Then, in this order:
234
583
  declaring the Local-closeout gate satisfied, AI runs and reports `✓` / `✗` for
235
584
  every item:
236
585
 
237
- - [ ] `git show HEAD:dflow/specs/features/completed/{SPEC-ID}-{slug}/_index.md`
238
- — one blob read verifying **two** things: frontmatter `status: completed`
239
- **and** the Checkpoint Log contains the closeout row. This reads the
240
- **committed** content, not the working tree — the former catches "rename
241
- carried stale content", the latter catches "row never made it into the
242
- commit".
586
+ - [ ] `git ls-tree -r --name-only HEAD
587
+ dflow/specs/features/completed/{SPEC-ID}-{slug}/` — **every file this
588
+ commit carries from inside the archived host directory** — then
589
+ `git show HEAD:<path>` for each.
590
+ **The span, fixed once and used by both halves of this check:** exactly
591
+ that set — nothing wider, nothing narrower. What may differ, and what
592
+ blocks, are judged over exactly that span.
593
+ ⚠ **The span is derived from what was staged, not from what the tables
594
+ name.**
595
+ **The baseline is the `path → blob` list Step 1 anchored** — read it with
596
+ `git cat-file -p refs/dflow/closeout-baseline/{SPEC-ID}-{slug}`, recomputing
597
+ that name from this host's own `{SPEC-ID}-{slug}`. The tree Step 1 read is
598
+ captured rather than remembered, **and so is the way back into it** — this
599
+ resolves in a session that never saw Step 1. **Both sides are already
600
+ host-relative** — Step 1 stripped its prefix, this span is read under
601
+ `completed/{SPEC-ID}-{slug}/`, and the `git mv` preserved the relative
602
+ tree — so the two sets line up with no prefix left to reconcile.
603
+ **Compare the two path sets first, before any blob comparison.** A
604
+ **baseline path missing from the span** is a file removed after Step 1; a
605
+ **span path with no baseline entry** is a file added after Step 1. Each is
606
+ a difference and is judged like any other: it **blocks** unless a step of
607
+ this closeout ordered it.
608
+ Then, for each path the two sets share, take the committed blob with
609
+ `git rev-parse HEAD:{completed path}`. **Different from that path's
610
+ baseline blob → read the delta itself with `git diff {baseline blob}
611
+ HEAD:{completed path}`, and judge that delta against the derived
612
+ condition below. Equal → that path carries no difference at all, which
613
+ satisfies the condition only where no step of this closeout ordered an
614
+ edit to it; where one did, the ordered edit never landed and this
615
+ **blocks**.**
616
+ ⚠ **Blob ids alone answer only *same* / *different*, which cannot decide
617
+ the derived condition.**
618
+ ⚠ **If the ref does not resolve, or the content behind it does not read
619
+ back, report this check as degraded and say so — do not substitute
620
+ `HEAD^`.** The probe at the top of this step exists so that this is reached
621
+ only when the baseline became unreadable *during* closeout.
622
+ Step 1 blocks unless every
623
+ spec in the host already reads `status: completed`, so those flips are
624
+ already **in** the baseline whichever step made them: a **minimal** host's
625
+ come from `modify-existing-flow.md` Step 1.7's finalization, a
626
+ **phase-bearing** host's from its phases completing — no phase-bearing
627
+ host runs Step 1.7, whichever flow opened it. Either way the
628
+ flips are not differences this check admits; they are part of what it
629
+ compares against. (They are differences only from the *pre-flip* record,
630
+ which is not the baseline.)
631
+ **The condition is derived, not listed:** within that span, the committed
632
+ blob differs from that baseline in **exactly the edits Step 2, Step 4's
633
+ terminal Resume Pointer write and Step 4 instruction 1 ordered, carrying
634
+ the values those steps state, and in nothing else**. Read those three
635
+ steps and compute the set — do not keep a second copy of it here. Any
636
+ difference **no step of this closeout ordered** is edit fallout and
637
+ **blocks**.
638
+ ⚠ **This compares final net state, not the sequence of edits.** Step 2
639
+ writes an in-progress cursor and gate 3 → 4 updates it again; Step 4's
640
+ terminal write then overwrites every field either of them touched, so
641
+ neither appears in the committed blob. **Their absence is not a difference
642
+ and must not block**, and nothing here proves they happened. What this
643
+ comparison settles is that the *terminal* value is the one that landed.
644
+ **What this check cannot decide — stated, not asserted:** whether a
645
+ backfilled hosted `Commit` cell holds *that row's own* implementation hash
646
+ rather than some other commit's. Instruction 1 orders that value and is
647
+ the single place the constraint lives; closeout cannot verify it, because
648
+ the hash-evidence test is minimal-host-only and lives in
649
+ `references/finish-feature-minimal-host.md`, so on a phase-bearing host
650
+ **no check reads the value at all**.
651
+ **Identity is confirmed by `references/pr-review-checklist.md`'s
652
+ "Hosted `Commit` cell identity" item**, which sits in that file's
653
+ *Delegated to review by `finish-feature-flow.md`* block — the same
654
+ assignment this file already makes for the whole-history assertion.
655
+ ⚠ **Not** its "Every Lightweight Changes row now carries a `Commit` hash"
656
+ item: that one asserts **presence, not correctness**, and pointing a
657
+ boundary at a presence check would make it a hole rather than a division
658
+ of labour.
659
+ ⚠ **An empty hosted `Commit` cell does not fail this item — and this item
660
+ does not clear it either.** `references/pr-review-checklist.md` asserts that
661
+ cell and says so in its own words; **this check does not**. Judge the item on
662
+ the rest of its evidence and leave the cell to that checklist.
663
+ **Scope, stated so it is not read as more than it is:** this gate re-reads
664
+ the archived **host directory** only — what the archived record *says*.
665
+ What the closeout **commit** *contains* is the separate item below; neither
666
+ implies the other, and "closeout is clean" needs both.
667
+ What has actually gone missing this way: `branch:` rewritten away from the
668
+ value the host opened with (Step 2 flips `status` only, so a bugfix host
669
+ still reads `bugfix/BUG-{NUMBER}-{slug}`); `follow-up-of` dropped by the
670
+ Step 2 metadata edit — Step 5 skips Step 6 when it is absent, so losing it
671
+ strands the original feature's reverse link at `in-progress` while
672
+ closeout reports success; a Lightweight Changes row, or its `Commit` cell,
673
+ deleted — archiving an effectively empty host.
674
+ The closeout row's Result must also **not** be `failed`: you are reading a
675
+ commit that landed, so `failed` contradicts reality (`committed` on the Y
676
+ path, `skipped` when the developer declined and committed it themselves).
677
+ **Minimal host (zero-phase) additionally**: one further condition on the
678
+ first checkpoint row, stated in
679
+ `references/finish-feature-minimal-host.md` § Step 4.
680
+ - [ ] **The closeout commit contains only what closeout is allowed to write.**
681
+ Read its changed paths (`git show --stat HEAD`) and admit **only** the
682
+ archived host directory — the `git mv` rename plus the finalization
683
+ Step 1 validated — **plus exactly the external paths Step 4 instruction 2
684
+ ordered staged**. That is the same derivation the item above uses, applied
685
+ to the other half of "closeout is clean": the permitted set comes from the
686
+ instruction that ordered the staging, never from a copy kept here.
687
+ **Anything else blocks**: implementation source, or a
688
+ `domain/{context}/…` document under a host that declared itself no-BC.
689
+ ⚠ **That no-BC half applies to every host shape, and on a phase-bearing
690
+ host this is the only place closeout tests it.** The
691
+ `Minimal host (zero-phase), no-BC only` check in
692
+ `references/finish-feature-minimal-host.md` reads checkpoint 1, and a
693
+ phase-bearing host has no checkpoint 1 to read — so do not treat "the
694
+ minimal-host branch file already covers no-BC" as true here. It is true
695
+ only for a minimal host.
696
+ **Scope: this is a path-level spill check and nothing more.** It proves no
697
+ unpermitted *file* entered the commit — not that the permitted ones carry
698
+ only this host's delta. Judging a hunk inside `rules.md` as "this host's
699
+ change rather than an unrelated BR edit" needs the intended delta, which
700
+ lives in the spec and the row, not in a path list. The item that reads one
701
+ against the other is `references/pr-review-checklist.md`'s
702
+ **"The closeout commit carries only this host's delta"**.
703
+ Say that plainly rather than implying the stronger claim.
704
+ ⚠ **And its span is this one commit.** Between them, the minimal-host
705
+ branch file's check (a minimal host's checkpoint 1) and this one (any
706
+ host's closeout commit)
707
+ still leave every *other* commit on the branch unread — where a no-BC host
708
+ is concerned, that gap is closed by
709
+ `references/pr-review-checklist.md`'s **"A no-BC host committed no
710
+ BC-scoped Domain material"** item, which takes the branch range for
711
+ **every** no-BC host.
243
712
  - [ ] `dflow/specs/features/active/{SPEC-ID}-{slug}/` no longer exists (the
244
713
  directory was moved, not copied)
245
714
  - [ ] `git status --short` shows no leftovers related to this feature
246
715
  (working tree clean; identify any unrelated dirty files explicitly)
247
716
 
248
- If any item fails, do **not** declare closeout complete — fix it (re-add and
249
- amend, or a follow-up commit; the developer chooses) and re-verify.
717
+ **Once every item above reads `✓`, release the baseline anchor:**
718
+
719
+ ```bash
720
+ git update-ref -d refs/dflow/closeout-baseline/{SPEC-ID}-{slug}
721
+ ```
722
+
723
+ ⚠ **Only after they all pass** — a failed item is fixed and re-verified against
724
+ this same evidence, so the anchor has to survive it.
725
+ ⚠ **Deleting the ref is not a worktree edit**, so it is not a difference the
726
+ item above could have seen.
727
+
728
+ If any item fails, do **not** declare closeout complete — fix it and re-verify.
729
+ **How you may fix it depends on the host.** A **phase-bearing** feature has no
730
+ fixed commit count, so re-add and amend *or* a follow-up commit both work; the
731
+ developer chooses. A **minimal (zero-phase) host** does not: its lifecycle is
732
+ exactly checkpoint 1 plus closeout, and every check its branch file adds rests
733
+ on that, so the
734
+ repair must go **into the closeout commit itself** — re-add and amend. Nothing
735
+ has been pushed yet (integration is Step 5), so amending is safe here. A
736
+ follow-up commit would give the host a third host-mutating commit while its
737
+ ledger still claims two; the Step 6 flip is the *only* sanctioned
738
+ post-closeout commit, and it touches the original feature, not this host.
250
739
 
251
740
  The Local-closeout gate is satisfied **only when the closeout is committed and
252
741
  the verification above passes**. If you declined the commit (chose N) or it
@@ -262,7 +751,32 @@ offline; integration happens in Step 5 when you have network.
262
751
 
263
752
  ## Step 5: Emit Integration Summary (Git-strategy-neutral)
264
753
 
265
- Produce a plain-text summary of what this feature did. The summary is
754
+ **First, print the closeout verification's derivation.** Before the summary
755
+ itself, state in the conversation *how* Step 4's post-commit verification
756
+ reached its result — not that it passed, but how it was computed:
757
+
758
+ 1. **The baseline** — the tree Step 1 read, and which step put each spec's
759
+ `status: completed` flip into it (Step 1.7's finalization on a minimal host;
760
+ the phases completing on a phase-bearing one, which never runs Step 1.7).
761
+ 2. **The differences you derived, each with the step that ordered it** —
762
+ Step 2, Step 4's terminal Resume Pointer write, or Step 4 instruction 1. This
763
+ is the set the check *derives* instead of listing, so printing it is what
764
+ lets a reader check the derivation rather than trust it. Say as well that the
765
+ comparison reads final net state, so the transitional cursor values Step 2
766
+ and gate 3 → 4 wrote are outside what it can show.
767
+ 3. **The external paths admitted, and which half of Step 4 instruction 2 each
768
+ came from** — what Step 3 wrote, or this change's own sweep.
769
+ 4. **What the check could not decide, and who holds it** — for a backfilled
770
+ hosted `Commit` cell, that closeout cannot decide whether the value is that
771
+ row's own implementation hash, and that
772
+ `references/pr-review-checklist.md`'s **"Hosted `Commit` cell identity"**
773
+ item is where it gets confirmed (not its presence check). Say this **even
774
+ when there was nothing to backfill**, so its absence is visible rather than
775
+ silent.
776
+ 5. **Any check that passed on a recorded exception** — e.g. a branch mismatch
777
+ honoured by a `branch-override` row, naming the row and the branch.
778
+
779
+ Then produce a plain-text summary of what this feature did. The summary is
266
780
  **not** a commit message template — it is reference material the
267
781
  developer adapts to whichever merge strategy their project uses
268
782
  (merge commit, squash, rebase, fast-forward — Dflow stays neutral).
@@ -282,7 +796,7 @@ Feature Goal: {1-2 sentences from _index.md Goals & Scope}
282
796
  Change Scope:
283
797
  - BC: {context-name}
284
798
  - Phase Count: {N} (phase-spec-{date1}-{slug1} ... phase-spec-{dateN}-{slugN})
285
- - Lightweight Changes: {n_t2} T2 lightweight specs + {n_t3} T3 inline rows
799
+ - Lightweight Changes: {n_t2} T2 lightweight specs + {n_t3} T3 inline rows + {n_baseline} baseline rows
286
800
 
287
801
  Related BR-IDs (post-closeout state):
288
802
  - ADDED: BR-NN, BR-NN, ...
@@ -301,17 +815,70 @@ Next Steps (developer) — Integration / PR gate (needs network):
301
815
  you, but only when you explicitly ask; it never pushes on its own
302
816
  ```
303
817
 
818
+ **Zero-phase minimal host — exact fields.** A zero-phase host does not fill the
819
+ format above the way a phase-bearing one does. Its whole field set, and the
820
+ authority on that shape, is `references/finish-feature-minimal-host.md`
821
+ § Step 5.
822
+
304
823
  Print the summary to the conversation; do not write it to a file (it is
305
824
  ephemeral closeout output).
306
825
 
307
- **→ Step Gate: Step 5 → Step 6**
308
-
309
- If the feature has `follow-up-of: {原 SPEC-ID}` in its Metadata, prompt
310
- the developer:
311
- > "This feature is a follow-up of `{原 SPEC-ID}`. Ready to update the
312
- > original feature's `_index.md` Follow-up Tracking row to mark this
313
- > follow-up as `completed`? `/dflow:next` to proceed (or skip if you
314
- > prefer to do it manually)."
826
+ **A mainline hotfix that overlapped this feature.** Step 4 has already archived
827
+ and committed this host — **it is frozen**, so anything a merge with the
828
+ mainline leaves over is routed elsewhere and never recorded back into it. What
829
+ is left over, how to classify it, and where each class goes are decided in
830
+ `references/finish-feature-post-hoc-hotfix.md` § After closeout — the same
831
+ branch file Step 1's hotfix callout dispatches to.
832
+
833
+ **→ Post-Local-closeout confirmation: Step 5 → Step 6**
834
+
835
+ **This stops and waits, and it is not a step gate.** By now the host is in
836
+ `completed/` and its cursor reads `none`. **Confirming here updates no cursor**,
837
+ and nothing below re-opens the workflow.
838
+
839
+ If the feature has `follow-up-of` in its Metadata, prompt the developer —
840
+ naming **every** original it lists (the field may be a YAML array):
841
+ > "This feature is a follow-up of `{原 SPEC-ID}` (and `{原 SPEC-ID-2}`, …).
842
+ > Ready to update **each** original feature's `_index.md` Follow-up Tracking row
843
+ > to mark this follow-up as `completed`? (Or tell me you'll do it manually —
844
+ > either way this tracking commit is **required** before closeout is complete;
845
+ > see Step 6.)"
846
+
847
+ **What this confirmation accepts** — this governs the follow-up prompt above; a
848
+ host with no `follow-up-of` never reaches it and takes the branch below instead:
849
+
850
+ - **Any affirmative reply → enter Step 6.** The verbal signals the guide lists
851
+ (`AI-AGENT-GUIDE.md` § Confirmation Signals) are illustrations, **not the
852
+ list**: a reply that plainly means "go ahead" counts however it is worded.
853
+ **Implicit confirmation counts too** — a developer who supplies what Step 6
854
+ needs, by naming the originals or by saying they will make the commit
855
+ themselves, has confirmed, not declined.
856
+ - **`/dflow:next` and `/dflow:cancel` do not apply here.** No workflow is
857
+ active, and the guide requires both to be refused in that state. Refuse as the
858
+ guide says, **then ask this question again in plain language**. ⚠ **Do not
859
+ read either as a decline** — every earlier gate in this flow asked for
860
+ `/dflow:next`, so typing it here is a trained reflex, not an answer.
861
+ - **A request to change something first** — revise what was asked about, then
862
+ ask again. Nothing already committed is undone, and the Local-closeout gate
863
+ stays satisfied.
864
+ - **A plain decline, or "not now" — stop, and do not ask again.** There is
865
+ nothing to revise, so re-asking is the same question and reads as pressure.
866
+ Report that closeout is **not complete**, name the Step 6 flip as what is
867
+ still owed, and leave restarting to the developer.
868
+
869
+ ⚠ **Declining does not waive the tracking commit** — Step 6's flip is required
870
+ either way. `references/finish-feature-follow-up.md` holds that rule and states
871
+ what it takes to satisfy it.
872
+
873
+ **The reverse link must have been opened, not only closed — and that is not
874
+ minimal-host-only.** This confirmation point puts no host shape on
875
+ `follow-up-of`, so a phase-bearing host can carry it too, but it has no single
876
+ commit required to carry the row. The requirement still holds for that host; nothing in closeout
877
+ tests it. **The opening half is confirmed for a phase-bearing follow-up host by
878
+ `references/pr-review-checklist.md`'s "A follow-up's reverse link was opened,
879
+ not only closed" item**, in that file's *Delegated to review by
880
+ `finish-feature-flow.md`* block — it reads the branch history, which is exactly
881
+ what closeout cannot take.
315
882
 
316
883
  If no `follow-up-of` field, skip Step 6 and announce closeout complete:
317
884
  > "`/dflow:finish-feature` complete for `{SPEC-ID}-{slug}`. Feature
@@ -323,29 +890,9 @@ If no `follow-up-of` field, skip Step 6 and announce closeout complete:
323
890
  **In-flight reminder** — after the closeout announcement (with or without
324
891
  Step 6), run the in-flight overview scan (see `AI-AGENT-GUIDE.md` § Status /
325
892
  Control Commands) and list any other unfinished features in `active/` and any
326
- in-flight feature / bugfix branches. Surfacing them at closeout is deliberate:
327
- attention is about to move elsewhere, and this is exactly where half-done work
328
- sinks.
893
+ in-flight feature / bugfix branches.
329
894
 
330
895
  ## Step 6: Reverse-Update Follow-up Tracking (only if follow-up)
331
896
 
332
- For features that were created as follow-ups of an earlier completed
333
- feature, update the original feature's
334
- Follow-up Tracking table.
335
-
336
- 1. Locate `dflow/specs/features/completed/{原 SPEC-ID}-{原 slug}/_index.md`
337
- 2. Find the Follow-up Tracking section's row for this feature's SPEC-ID
338
- 3. Flip Status → `completed`
339
-
340
- ```bash
341
- # The AI makes the edit and may offer to commit it (Y / N), per the AI commit policy
342
- ```
343
-
344
- After the update:
345
- > "Follow-up Tracking row in `{原 SPEC-ID}-{原 slug}/_index.md` updated
346
- > to Status = `completed`. Closeout complete."
347
-
348
- The connection is bidirectional and weakly redundant: the new feature's
349
- `follow-up-of` field is the authoritative source; the old feature's
350
- Follow-up Tracking row is a derived index. If they ever disagree, trust
351
- `follow-up-of`.
897
+ **Open `references/finish-feature-follow-up.md` and follow it there.** This
898
+ step's rules live in that file and are not repeated here.