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
@@ -176,7 +176,8 @@ project's decision (GitFlow → `develop`, Trunk / GitHub Flow → `main`).
176
176
  ### Commit checkpoints
177
177
 
178
178
  At lifecycle milestones the AI offers a commit checkpoint, folded into the
179
- existing Step Gate prompt (it does not add a separate question):
179
+ prompt the flow already shows there — a Step Gate, or the flow's closing
180
+ announcement (it does not add a separate question):
180
181
 
181
182
  ```
182
183
  ✓ {milestone} complete
@@ -187,26 +188,37 @@ existing Step Gate prompt (it does not add a separate question):
187
188
 
188
189
  Tier sets how many checkpoints a change has: T1 three (spec / implementation /
189
190
  closeout), T2 two (spec+implementation merged / closeout), T3 a single commit.
190
- "T3 = a single commit" means a single **implementation** commit — its hash is
191
- only knowable afterwards, so the `_index.md` inline row and checkpoint row are
192
- swept up by the host's **next** commit (more work, or closeout). If the host has
193
- no next commit scheduled, one ledger-only tracking commit that references the T3
194
- hash is allowed. It is bookkeeping, not a lifecycle milestone: it adds no
195
- Checkpoint Log row of its own (unlike closeout, which is a checkpoint and does get
196
- a row).
191
+ **What a checkpoint carries follows from where its flow offers it.**
192
+ `references/modify-existing-flow.md` offers its checkpoint at the end of its
193
+ artifact step (Step 6), so a T2's merged commit — and a T3's single commit —
194
+ carries the change together with the documentation that step writes.
195
+ `/dflow:new-feature` and `/dflow:new-phase` offer the implementation checkpoint
196
+ at the gate into their completion step, so what that step writes rides the
197
+ host's next commit, whichever that is — for example the next phase's first
198
+ checkpoint, a hosted lightweight change's commit, or closeout.
199
+ "T3 = a single commit" means a single **implementation** commit. On a hosted T3
200
+ the entries that carry its hash ride the host's **next** commit (more work, or
201
+ closeout), as every checkpoint's do (below). If the host has no next commit
202
+ scheduled, one ledger-only tracking commit that references the T3 hash is
203
+ allowed. It is bookkeeping, not a lifecycle milestone: it adds no Checkpoint Log
204
+ row of its own (unlike closeout, which is a checkpoint and does get a row).
197
205
  **Minimal host (zero-phase) exception — there, tier does not set the count.**
198
206
  On a minimal host (`references/modify-existing-flow.md` Step 1.7 standalone, or
199
207
  its Step 1.6 follow-up minimal variant) there is no later host commit for a row
200
208
  to ride, so the count is **two for every tier** — implementation, or
201
- `spec-baseline` for a baseline capture, then closeout — and a T3's `_index.md`
202
- row is written into checkpoint 1 itself instead of sweeping into a later commit.
209
+ `spec-baseline` for a baseline capture, then closeout — and every row, a T3's
210
+ included, is written into checkpoint 1 itself; only the entries that carry
211
+ checkpoint 1's hash ride the closeout commit.
203
212
  Everything above about "T3 = a single commit" describes a **hosted** T3, the
204
213
  case where a later host commit exists.
205
214
  Whether you choose Y or N, the AI records one row in the feature `_index.md`
206
215
  Checkpoint Log — every checkpoint is accounted for (`committed` / `skipped` /
207
- `failed`), even when no commit happens. A commit hash is written only after the
208
- commit succeeds; a hook rejection or failed commit is recorded as `failed`
209
- (never a fake hash).
216
+ `failed`), even when no commit happens.
217
+ **No checkpoint commit can contain its own hash.** The entries that carry a
218
+ checkpoint's hash — its Checkpoint Log Result `committed ({hash})`, and any
219
+ Lightweight Changes `Commit` cell holding that hash — are written after the
220
+ commit succeeds and ride the host's **next** commit. A hook rejection or failed
221
+ commit is recorded as `failed` (never a fake hash).
210
222
  **One further Result value — `reconciled ({merged-hotfix-hash})`.** It records
211
223
  that this checkpoint documents a change that was **already merged** before any
212
224
  ceremony ran, and it is written only on a post-hoc hotfix host's implementation
@@ -215,8 +227,8 @@ belongs to the **merged hotfix**, not to this host's own documentation commit
215
227
  that one goes in the Lightweight Changes row's `Commit` cell, and the two have
216
228
  different provenance and must never be swapped. On every other host the three
217
229
  values above are the whole vocabulary.
218
- **Exception — the closeout row**: the closeout commit
219
- cannot contain its own hash, so the closeout row is written before the commit
230
+ **Exception — the closeout row**: no later commit of this host's lifecycle
231
+ carries it, so the closeout row is written before the commit
220
232
  as `closeout | committed` with **no hash** (see
221
233
  `references/finish-feature-flow.md` Step 4); trace that commit via
222
234
  `git log -1 -- dflow/specs/features/completed/{SPEC-ID}-{slug}` or the optional
@@ -339,12 +351,25 @@ heavily enough that git's similarity index dropped below the rename
339
351
  threshold; consider using `git mv` for the move, then making content
340
352
  edits in a follow-up commit).
341
353
 
342
- ### CI / hook automation (future)
354
+ ### CI / hook automation
343
355
 
344
- A pre-commit hook can refuse commits where `dflow/specs/features/active/` or
345
- `dflow/specs/features/completed/` show paired `D` + `A` instead of `R` for
346
- the same feature directory. Not part of Dflow today, but compatible
347
- with the rule.
356
+ Dflow ships `dflow check-closeout`, a gate for the closeout commit. For each
357
+ feature directory newly archived to `dflow/specs/features/completed/` it checks
358
+ that `_index.md` and every spec say `status: completed`, that nothing is left
359
+ under `active/`, that the Checkpoint Log has a real `closeout` row reading
360
+ `committed` or `skipped`, and — with `--staged` — that nothing under the
361
+ archived directory is left unstaged; it exits 1 when one fails. A project wires
362
+ it into a pre-commit hook (`--staged`) or a CI job (`--range <base>..<head>`)
363
+ itself — Dflow installs neither. `dflow check-closeout --help` names the page
364
+ with the hook and CI templates. When the hook refuses a closeout commit, handle
365
+ it as `references/finish-feature-flow.md` Step 4 instruction 3 says.
366
+
367
+ Passing it is not a clean closeout: the post-commit verification in
368
+ `references/finish-feature-flow.md` Step 4 still applies in full.
369
+
370
+ A hook that refuses paired `D` + `A` instead of `R` for the same feature
371
+ directory is compatible with the `git mv` rule, but it is not part of
372
+ `check-closeout`.
348
373
 
349
374
  ## Gate Checks by Branch Type
350
375
 
@@ -405,8 +430,12 @@ AI should verify:
405
430
  - [ ] All `phase-spec-*.md` in the feature directory have `status: completed`
406
431
  - [ ] `_index.md` Current BR Snapshot has been synced to BC layer
407
432
  (`rules.md` / `behavior.md`) — typically by `/dflow:finish-feature`
408
- - [ ] Whole feature directory ready to `git mv` to `dflow/specs/features/completed/`
409
- (or already moved if `/dflow:finish-feature` ran)
433
+ - [ ] **Closeout complete**: `/dflow:finish-feature` has announced closeout
434
+ complete for this host — its Local-closeout gate is satisfied (the
435
+ closeout commit landed and passed post-commit verification) and, if
436
+ the host carries `follow-up-of`, the Step 6 tracking commit has also
437
+ landed and been verified. `status: completed` or a directory already
438
+ under `completed/` does not show this on its own.
410
439
  - [ ] All new business logic is in `src/Domain/` (not delivery/entrypoint code — presentation/UI layer, controllers, handlers, jobs, message consumers, data pipelines, or stored procedures)
411
440
  - [ ] New terms added to `glossary.md`
412
441
  - [ ] `rules.md` and `models.md` updated if applicable
@@ -414,8 +443,8 @@ AI should verify:
414
443
  - [ ] Domain layer code has no delivery-framework references
415
444
 
416
445
  > **On a minimal (zero-phase) host**, split this list by what each item reads.
417
- > Items about **this host's own record** — `_index.md` status, archival
418
- > readiness, and the phase-spec check, which passes vacuously on an empty
446
+ > Items about **this host's own record** — `_index.md` status, **Closeout
447
+ > complete**, and the phase-spec check, which passes vacuously on an empty
419
448
  > table — apply **unchanged**: they are what stops a host merging with its
420
449
  > record still open. Every item that names a **Domain or bounded-context
421
450
  > artifact**, the BR-Snapshot sync included, applies only to what this change
@@ -464,10 +493,15 @@ AI should verify:
464
493
 
465
494
  ### bugfix/ branch — Before Merging (Pre-PR / Pre-Integration)
466
495
 
467
- - [ ] `_index.md` status: `completed`, and the whole feature directory ready to
468
- `git mv` to `dflow/specs/features/completed/` (or already moved if
469
- `/dflow:finish-feature` ran). A `bugfix/` host closes out exactly like a
470
- `feature/` one; an un-closed-out host must not merge.
496
+ - [ ] `_index.md` status: `completed`
497
+ - [ ] **Closeout complete**: `/dflow:finish-feature` has announced closeout
498
+ complete for this host — its Local-closeout gate is satisfied (the
499
+ closeout commit landed and passed post-commit verification) and, if
500
+ the host carries `follow-up-of`, the Step 6 tracking commit has also
501
+ landed and been verified. `status: completed` or a directory already
502
+ under `completed/` does not show this on its own.
503
+ A `bugfix/` host closes out exactly like a `feature/` one; an
504
+ un-closed-out host must not merge.
471
505
  - [ ] **The tier's artifact** has the fix documented — a **T2**'s lightweight
472
506
  spec, or for a **T3** the host `_index.md` Lightweight Changes row, which
473
507
  is its whole record. Do not ask a T3 for a spec file here either: the
@@ -68,7 +68,9 @@ the guide states them rather than continuing through the steps below.
68
68
  a follow-up (Step 1.6 minimal variant), otherwise it is standalone (Step 1.7) —
69
69
  both are tier-exempt, cut `feature/{SPEC-ID}-{slug}`, and record a `Tier = baseline`
70
70
  row rather than a change. Do not invent a spec for it, and do not read the
71
- cascade's "below workflow" as permission to skip the record.
71
+ cascade's "below workflow" as permission to skip the record. A baseline
72
+ minimal host does not stop at the capture: it goes on through Step 6 and
73
+ offers its first checkpoint (`spec-baseline`) at § 6.5 (Step 1.7 says how).
72
74
  A capture about no bounded context at all — the role reach of functions that
73
75
  belong to no context yet, say — is not a baseline capture and opens no host:
74
76
  with no active feature to attach it to, record it and offer to commit that
@@ -298,6 +300,10 @@ observation-only and carries its own capture alone.
298
300
  recorded, or a stale branch is still carrying it — and it is settled that
299
301
  way with the developer, as duplicate / existing work or branch-history
300
302
  cleanup. All identifiers are settled here, before the host exists.
303
+
304
+ For a **post-hoc hotfix** (Step 1.8), also run the branch-base check in
305
+ `references/modify-existing-post-hoc-hotfix.md` item 2 here, before step 3
306
+ creates anything: step 4 cuts from the base branch it settles.
301
307
  3. **Create the minimal `_index.md`** from `templates/_index.md` at
302
308
  `dflow/specs/features/active/{SPEC-ID}-{slug}/_index.md`, carrying all
303
309
  **seven required sections** that template names — the Metadata front
@@ -344,10 +350,13 @@ above). A **baseline capture** is observation-only: it records the capture in
344
350
  its BC-layer documents and/or `domain/analysis.md` and one `Tier = baseline`
345
351
  row in `_index.md`, and its first checkpoint is named `spec-baseline` with
346
352
  Result `committed` (never `implementation` — there is no implementation
347
- work). Otherwise the **implementation commit is checkpoint 1** — the Step 5 →
348
- Step 6 commit checkpoint records it in the Checkpoint Log. (For a **post-hoc**
349
- host, Step 5 is skipped and checkpoint 1 is the **documentation** commit
350
- instead; see Step 1.8 and Step 5's guard.)
353
+ work). Otherwise the **implementation commit is checkpoint 1**. Either way,
354
+ checkpoint 1 is offered at the end of Step 6 (§ 6.5), after Step 6 has written
355
+ its record, and recorded in the Checkpoint Log. A baseline capture skips Step 5
356
+ — there is nothing to implement — so once the capture is recorded it goes
357
+ through the Step 5 → Step 6 gate into Step 6, not straight to a commit. (For a
358
+ **post-hoc** host, Step 5 is skipped as well and checkpoint 1 is the
359
+ **documentation** commit instead; see Step 1.8 and Step 5's guard.)
351
360
 
352
361
  **Every artifact gets a Lightweight Changes row, and every row is written
353
362
  *before* checkpoint 1.** A **T3**'s row *is* its whole record; a **T2**'s row is
@@ -359,7 +368,8 @@ implementation paths (for a baseline, the documents it captured into). The
359
368
  exist until its commit does: closeout's allow-list admits that cell and no other
360
369
  change **to the row**, so a row added after checkpoint 1 **blocks**. (The
361
370
  allow-list separately admits the Checkpoint Log rows, the Resume Pointer and the
362
- Current BR Snapshot — the finalization edits this step makes next.)
371
+ Current BR Snapshot; the first two are what "Finalize + close" writes after
372
+ checkpoint 1.)
363
373
 
364
374
  **Name the implementation paths in the artifact.** Closeout's evidence checks
365
375
  assert that a commit *touches the implementation paths this change describes* —
@@ -371,16 +381,17 @@ documents it wrote instead; that is its counterpart. An artifact that
371
381
  declares none leaves that check nothing to compare, and finish-feature treats a
372
382
  missing declaration as a **block**, not a pass.
373
383
 
374
- **Finalize + close.** Before `/dflow:finish-feature`, run the Step 6
375
- completion checklist's minimal-host finalization. For **every** T2: set the
376
- lightweight-spec `status: completed`, record its commit evidence, and advance
377
- the Resume Pointer to the closeout-ready state. **Only when that T2 is
378
- BC-bearing**, additionally refresh the host's Current BR Snapshot — a no-BC T2
379
- has no snapshot to refresh, but still owes the commit evidence and the Resume
380
- Pointer. A **T3** has no spec file — record its inline row's commit evidence
381
- and advance the Resume Pointer. A **baseline capture** likewise has no spec
382
- file — record its `Tier = baseline` row's commit evidence and advance the
383
- Resume Pointer.
384
+ **Before checkpoint 1 — Step 6 finalizes the record.** Step 6's completion
385
+ checklist runs before checkpoint 1, so everything it writes rides that commit.
386
+ For **every** T2, 6.3 sets the lightweight-spec `status: completed`. **Only when
387
+ that T2 is BC-bearing**, 6.3 also refreshes the host's Current BR Snapshot — a
388
+ no-BC T2 has no snapshot to refresh. A **T3** or a **baseline capture** has no
389
+ spec file and no snapshot to refresh; Step 6 only re-verifies its row.
390
+
391
+ **Finalize + close — after checkpoint 1.** Before `/dflow:finish-feature`,
392
+ write what cannot exist until checkpoint 1 does, for every tier: each row's
393
+ commit evidence (the two surfaces below) and the Resume Pointer advanced to the
394
+ closeout-ready state.
384
395
 
385
396
  **Commit evidence goes to two surfaces, always — every row, every tier.** They
386
397
  answer different questions, so filling one is never filling the other, and
@@ -397,20 +408,22 @@ The **mode** changes only *which hash* each surface carries:
397
408
 
398
409
  - **Normal** (T2 or T3) — Checkpoint Log Result `committed ({hash})` and the
399
410
  row's `Commit` cell both name **checkpoint 1**, the single commit that
400
- carried the artifact and the implementation together. Two cells, one hash;
401
- fill both.
411
+ carried the artifact, the implementation and Step 6's record together. Two
412
+ cells, one hash; fill both.
402
413
  - **Post-hoc hotfix** (Step 1.8; T2 or T3) — the Checkpoint Log Result stays
403
- `reconciled ({merged-hotfix-hash})`, **never overwrite it**, while the row's
404
- `Commit` cell carries the **documentation** commit's hash. This is the one
405
- mode where the two values genuinely differ (Step 1.8 item 4).
414
+ `reconciled ({merged-hotfix-hash})`, **never overwrite it** — only closeout
415
+ corrects it, through `references/modify-existing-post-hoc-hotfix.md` item 6 —
416
+ while the row's `Commit` cell carries the **documentation** commit's hash.
417
+ This is the one mode where the two values genuinely differ (Step 1.8 item 4).
406
418
  - **Baseline capture** — the `spec-baseline` checkpoint's Result
407
419
  `committed ({hash})` and the `Tier = baseline` row's `Commit` cell both name
408
420
  that capture commit, the one that carried what it captured. Two cells,
409
421
  one hash; fill both.
410
422
 
411
423
  **A declined checkpoint still owes its hash.** If the developer answered **N**
412
- at the checkpoint-1 offer and made that commit themselves, the Checkpoint Log
413
- row was written `skipped` honestly — the AI did not commit — but it is now
424
+ at the checkpoint-1 offer and made that commit themselves — the change together
425
+ with Step 6's record — the Checkpoint Log row was written `skipped` honestly —
426
+ the AI did not commit — but it is now
414
427
  **incomplete, not final**: complete it here to `committed ({hash})` with the
415
428
  developer's actual hash. **That completion is for a normal checkpoint 1** (or a
416
429
  `spec-baseline` one). On a **post-hoc** host the Result stays
@@ -420,7 +433,8 @@ developer's commit lands, never by overwriting the reconciliation hash, which
420
433
  records a different commit entirely. A row left reading `skipped` blocks closeout, and
421
434
  finish-feature cannot tell "declined then committed" from "never committed" on
422
435
  your behalf. The **closeout** row is the deliberate exception — it carries no
423
- hash at all, because a commit cannot contain its own. Its **Result is still
436
+ hash at all, because no later commit of this host carries it
437
+ (`references/git-integration.md` § Commit checkpoints). Its **Result is still
424
438
  read** afterwards — finish-feature's post-commit gate rejects `failed` there. A
425
439
  declined closeout stays `skipped` because that value records the declined offer
426
440
  honestly, not because the row goes unread.
@@ -673,12 +687,18 @@ Wait for confirmation before entering Step 5.
673
687
  > **Post-hoc hotfix (Step 1.8) — skip this step.** The implementation already
674
688
  > landed on the mainline before any ceremony ran; that is the whole premise of
675
689
  > post-hoc mode, and Step 1.8 item 2 forbids redoing it. There is nothing to
676
- > implement here. Go straight to the Step 5 → Step 6 gate and commit
677
- > **documentation only** — the T2 spec or T3 row plus this host's `_index.md`,
678
- > and no source. Re-implementing the fix on this branch would satisfy the letter
690
+ > implement here. Go straight to the Step 5 → Step 6 gate and on through Step 6;
691
+ > checkpoint 1 (§ 6.5) then commits **documentation only** — the T2 spec or T3
692
+ > row, this host's `_index.md` and what Step 6 wrote, and no source.
693
+ > Re-implementing the fix on this branch would satisfy the letter
679
694
  > of the checkpoint while breaking §8's reconciliation contract, which is why
680
695
  > closeout inspects that commit's changed paths.
681
696
 
697
+ > **Baseline capture (a baseline minimal host, Step 1.7) — skip this step.**
698
+ > There is nothing to implement. Once the capture is recorded, go through the
699
+ > Step 5 → Step 6 gate into Step 6; checkpoint 1 (§ 6.5) is the `spec-baseline`
700
+ > commit of what the capture wrote.
701
+
682
702
  If extracting to Domain layer:
683
703
 
684
704
  ```csharp
@@ -717,9 +737,10 @@ protected void Calculate()
717
737
  Announce to developer:
718
738
  > "Implementation appears complete. Ready to update artifacts (spec, rules.md, models.md, glossary, tech-debt)? `/dflow:next` to proceed."
719
739
 
720
- > Commit checkpoint (per `references/git-integration.md` § Commit Checkpoints, Branch Gate & AI Commits): offer to commit, then record the result in the `_index.md` Checkpoint Log. Tier sets the count — T2 commits the merged spec+implementation here (closeout is the second checkpoint); T3 is a single implementation commit, and its `_index.md` rows ride along on the host's next commit (see `references/git-integration.md` § Commit checkpoints). **Minimal host exception (Step 1.7 standalone *and* the Step 1.6 follow-up variant)**: a minimal host has no later host commit for a T3 row to ride along on, so **the row rides checkpoint 1 itself** — write it into `_index.md` *before* this commit, alongside the implementation, because finish-feature Step 1 reads **checkpoint 1's committed `_index.md`** for it. Only what cannot exist until the commit does — the row's `Commit` cell and the Checkpoint Log Result — is backfilled later and rides the closeout commit. And a minimal host takes a second **closeout** checkpoint too: **every** minimal host records **two** checkpoints (implementation — or `spec-baseline` for a baseline — then closeout), for T3, T2, and baseline alike. **Post-hoc hotfix (Step 1.8) — the same two checkpoints, but the first carries no implementation**: the fix is already on the mainline, so checkpoint 1 is the **documentation** commit (spec / row + `_index.md`, no source), and its Checkpoint Log Result is `reconciled ({merged-hotfix-hash})` rather than `committed`. "Commits the merged spec+implementation" above describes a normal host and does **not** apply here.
740
+ A **baseline capture** implemented nothing, so it announces instead:
741
+ > "Baseline capture recorded. Ready to update artifacts? `/dflow:next` to proceed."
721
742
 
722
- Wait for confirmation before entering Step 6. This step gate is where the completion checklist is triggered — do not skip.
743
+ Wait for confirmation before entering Step 6. This step gate is where the completion checklist is triggered — do not skip. It offers **no** commit: checkpoint 1 is offered at the end of Step 6 (§ 6.5).
723
744
 
724
745
  ## Step 6: Update Artifacts
725
746
 
@@ -756,6 +777,7 @@ Ask these one-by-one.
756
777
  > **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).
757
778
 
758
779
  - [ ] Update or create the feature / bug spec; set `status: completed` — **T3 and baseline: N/A** (no spec file exists; the `_index.md` inline row — a T3 row, or a `Tier = baseline` row — is the record, and the host's own status is not touched)
780
+ - [ ] **BC-bearing T2, on every host** — refresh the host `_index.md` Current BR Snapshot from this spec's final delta (`templates/_index.md` regenerates it when a T2 lightweight-spec is finalized; on a minimal host this is the refresh Step 1.7 places before checkpoint 1). A spec with no BR delta leaves the Snapshot as it is. N/A for a no-BC T2, a T3 and a baseline capture
759
781
  - [ ] The items below are the Domain sweep — **N/A for a T3**. For a **no-BC change** (one whose host Goals & Scope says it touches no bounded context) the **BC-scoped** items are N/A — everything under `dflow/specs/domain/{context}/`: there is no `{context}` to sweep, and inventing one plants the fiction Step 2's no-BC guard refuses. The **global** documents are *not* covered by that: `glossary.md`, `domain/analysis.md` and `migration/tech-debt.md` belong to no bounded context, and a no-BC operational T2 can genuinely rename a term, change which roles reach a function, or discover migration debt — judge those three from the actual change, as always. For a no-BR family T2 only the *BR-derived* items are N/A; run each remaining item where this change actually touches that document
760
782
  - [ ] `dflow/specs/domain/{context}/rules.md` — business rules updated
761
783
  - [ ] `dflow/specs/domain/{context}/models.md` — domain model updated
@@ -800,3 +822,7 @@ directory — the closeout `git mv` happens at `/dflow:finish-feature` time
800
822
 
801
823
  Only announce "change complete" after the appropriate archival step
802
824
  above (or the Step 6.3 docs sweep) is done.
825
+
826
+ ### 6.5 Commit checkpoint (checkpoint 1)
827
+
828
+ > Commit checkpoint (per `references/git-integration.md` § Commit Checkpoints, Branch Gate & AI Commits): fold the offer into the "change complete" announcement, then record the result in the `_index.md` Checkpoint Log. This commit is **checkpoint 1**: it carries the change — the spec or row plus the implementation — **and** everything Steps 2–6 wrote. Tier sets the count — T2 commits the merged spec+implementation here (closeout is the second checkpoint); T3 is a single implementation commit. The entries that carry this commit's hash — the Checkpoint Log Result and each Lightweight Changes `Commit` cell naming it — are written after the commit and ride the host's next commit; on a phase-bearing host, `/dflow:finish-feature` Step 4 backfills any `Commit` cell still unfilled at closeout. **Minimal host exception (Step 1.7 standalone *and* the Step 1.6 follow-up variant)**: a minimal host takes no host commit between checkpoint 1 and closeout, so **every row rides checkpoint 1 itself** — a T3 row included, written into `_index.md` *before* this commit, because finish-feature Step 1 reads **checkpoint 1's committed `_index.md`** for it. Only the entries that carry the hash come later: Step 1.7's "Finalize + close" writes them, and they ride the closeout commit. And a minimal host takes a second **closeout** checkpoint too: **every** minimal host records **two** checkpoints (implementation — or `spec-baseline` for a baseline — then closeout), for T3, T2, and baseline alike. **Post-hoc hotfix (Step 1.8) — the same two checkpoints, but the first carries no implementation**: the fix is already on the mainline, so checkpoint 1 is the **documentation** commit (spec / row, `_index.md` and what Step 6 wrote, no source), and its Checkpoint Log Result is `reconciled ({merged-hotfix-hash})` rather than `committed`. "Commits the merged spec+implementation" above describes a normal host and does **not** apply here.
@@ -103,7 +103,8 @@ hardcoded `feature/` branch). Step 1.7's **step 1 does not apply**: it is the
103
103
  standalone-classification gate, and this caller has already been classified as
104
104
  a follow-up. Everything Step 1.7 states **after** step 4 *does* apply
105
105
  unchanged — every artifact's row written before checkpoint 1, the
106
- implementation-path declaration, tier-aware "Finalize + close", and the two
106
+ implementation-path declaration, the finalization before checkpoint 1 and
107
+ "Finalize + close" after it, and the two
107
108
  commit-evidence surfaces — because finish-feature reads for all of it on a
108
109
  follow-up host exactly as on a standalone one. Then record the change's
109
110
  artifacts — a T2 lightweight-spec, one
@@ -32,6 +32,10 @@ If you arrived here with a T1, go back to Part A's routing.
32
32
  includes *a related feature that is still in flight* — see above for why
33
33
  it cannot be the host.
34
34
 
35
+ Make the choice here, and **act on it only after item 2's branch-base
36
+ check**: on either route, nothing is opened, linked or cut before that
37
+ check.
38
+
35
39
  Do not skip this choice. If a feature that was in flight turns out to have
36
40
  touched the same code, that is a **merge** question, settled when the
37
41
  branches actually meet — see `finish-feature-flow.md` Step 5.
@@ -42,11 +46,69 @@ If you arrived here with a T1, go back to Part A's routing.
42
46
  1.6's delegation to it) like any other minimal host — that branch *is* the
43
47
  **post-hoc branch** the rest of this step names, and the value `_index.md`
44
48
  `branch:` records.
49
+
50
+ **Branch-base check — run it before Step 1.7 step 3 creates anything**
51
+ (on the follow-up route: before Step 1.6 opens the host and writes the
52
+ reverse link). It settles which base branch this host's branch is cut
53
+ from. Closeout requires the commit item 3 records to be an ancestor of
54
+ this branch, so a branch cut from a base that does not contain that commit
55
+ yet passes every step until closeout and is blocked there. Settle that
56
+ commit with the developer first (item 3), then:
57
+ 1. Settle the base branch and fetch it. The developer decides which
58
+ branch is the base — Dflow does not know it, though the project may
59
+ record it under `_conventions.md` § Git Policy — and it is the branch
60
+ Step 1.7 step 4 cuts from anyway. `{base-ref}` is that branch's latest
61
+ ref (for example `origin/develop`). Fetch the remote it belongs to
62
+ (`git fetch origin` for `origin/develop`; `git fetch --all` when
63
+ unsure): a bare `git fetch` updates only the default remote, so a base
64
+ on another remote stays stale and the steps below misjudge it. The fix
65
+ is merged and pushed by now, and it often reached the base on the
66
+ server — a PR merge or a squash — that this clone has not fetched yet.
67
+ 2. Run `git cat-file -t {hash}`. It must print `commit`. If it prints
68
+ anything else or fails, and step 1's fetch failed, the commit may only
69
+ be missing from this clone: stop, say the check needs the fetch, and
70
+ run it again once the remote is reachable. Otherwise the hash is
71
+ mistyped, names an object that is not a commit, or names a commit that
72
+ remote does not have — stop and ask the developer. Do not guess.
73
+ 3. Run `git show --stat {hash}`. At least one path it lists must be an
74
+ implementation path this record will declare. If none is, stop and
75
+ settle the commit with the developer — closeout's (c) would block it.
76
+ A last commit that touches only tests does this. Then run
77
+ `git rev-list --parents -n 1 {hash}`: more than two hashes means a
78
+ merge commit — for example the one a PR page shows as its merged
79
+ commit. Item 3 never records one, and a merge usually passes the path
80
+ check above, so stop here and settle the commit with the developer.
81
+ 4. Run `git merge-base --is-ancestor {hash} {base-ref}`:
82
+ - **Exit `0`** — cut this host's branch from `{base-ref}` at Step 1.7
83
+ step 4. Do **not** let the new branch track the base: run
84
+ `git checkout -b {branch} --no-track {base-ref}`, or update the local
85
+ base branch the way the project normally does and cut from that. A
86
+ branch that tracks `origin/develop` can, under some push settings,
87
+ push its documentation commits straight to `develop`.
88
+ - **Exit `1`** — the fix is not in that base yet. Tell the developer
89
+ both causes. **The fix has not been merged back yet**: merge it back
90
+ first (under GitFlow, into `develop`). **The recorded commit is not
91
+ the one item 3 names**: under a squash or rebase merge, or a
92
+ cherry-pick into the base, the fix's own commits never reach it —
93
+ record the commit the fix landed as (item 3).
94
+ ⚠ Do **not** cut from `main` instead to make the check pass: the
95
+ documentation branch would carry `main`'s merge commits into
96
+ `develop` when it merges, and its documents would start from
97
+ `main`'s older Domain documents and conflict there.
98
+ - **Any other exit code** — a mistyped ref or a failed command. Stop
99
+ and show the developer the output.
100
+ - **If step 1's fetch failed** (offline): exit `0` is still
101
+ trustworthy — the ref you cut from does contain the commit. Exit `1`
102
+ may only mean the local ref is behind; treat it as provisional and
103
+ say so.
45
104
  3. **Implementation checkpoint Result = `reconciled ({merged-hotfix-hash})`.**
46
105
  In the host's Checkpoint Log the implementation row's Result is
47
106
  `reconciled (...)` — the value meaning "this checkpoint documents an
48
- already-merged change" — carrying the merged hotfix commit's hash. It sits
49
- alongside the usual `committed` / `skipped` / `failed` Results (see
107
+ already-merged change" — carrying the hash of **one of the fix's own
108
+ commits**, or of **the commit the fix landed as** when it reached the base
109
+ branch by a squash, a rebase or a cherry-pick — a commit on the base
110
+ branch this host's branch is cut from (item 2).
111
+ It sits alongside the usual `committed` / `skipped` / `failed` Results (see
50
112
  `references/git-integration.md` § Commit Checkpoints, Branch Gate & AI
51
113
  Commits). That hash belongs to the **hotfix**, so it is **not** evidence
52
114
  that this host's own first commit landed — the documentation work still owes
@@ -54,12 +116,27 @@ If you arrived here with a T1, go back to Part A's routing.
54
116
  Committing the documentation together with closeout collapses the host to
55
117
  one commit and fails finish-feature Step 1.
56
118
 
119
+ **Which commit that is depends on how the fix reached the base branch:**
120
+
121
+ | The fix reached the base branch by | Record |
122
+ |---|---|
123
+ | a merge (under GitFlow, merging the hotfix branch back into `develop`) or a fast-forward | the last of **the fix's own commits** that touches a declared implementation path — never a merge commit, and under GitFlow never the tagged merge commit on `main`, which the back-merge does not bring into `develop` |
124
+ | a squash merge | the squash commit on the base branch |
125
+ | a rebase merge or a cherry-pick | the last of the fix's commits **as they landed on the base branch** that touches a declared implementation path |
126
+
127
+ When the fix's own commits are on the base branch, record one of them even if
128
+ a copy of the fix got there first. Closeout accepts a commit that touches only
129
+ some of the declared paths: it asks for at least one. The hotfix branch's own
130
+ name still goes in `hotfix-branch:` (item 4).
131
+
57
132
  **You are asserting this hash, not deriving it.** Nothing in the repository
58
133
  records which commit was the hotfix, so no check in this flow can confirm
59
134
  it — finish-feature's reconciliation gate is explicit that it tests
60
135
  plausibility only. So state the identity
61
136
  and **cite what it rests on**: the PR, incident, or tracker reference that
62
- identifies this fix. Record the citation alongside the per-tier trace
137
+ identifies this fix. **Cite the source, not the hash** — a corrected hash
138
+ (item 6) then never has to touch the citation. Record the citation
139
+ alongside the per-tier trace
63
140
  (item 4). An **uncited** hash blocks closeout —
64
141
  `references/pr-review-checklist.md` is what confirms the identity, and it
65
142
  needs something to confirm against.
@@ -80,3 +157,44 @@ If you arrived here with a T1, go back to Part A's routing.
80
157
  5. **Branch equality** for the host is asserted against the **post-hoc
81
158
  documentation branch** (the `_index.md` `branch:` value), never the deleted
82
159
  hotfix branch.
160
+ 6. **Closeout: (b) fails on the `reconciled (...)` hash.**
161
+ `references/finish-feature-minimal-host.md`'s `Minimal host, hotfix
162
+ post-hoc only` check sends you here. Report which cause it is — never only
163
+ "not an ancestor" — and give the developer the repair for that cause. First
164
+ fetch the remote `{base-ref}` belongs to (item 2 step 1) and find the
165
+ commit item 3 says to record, then test where it is. If the fetch fails, a
166
+ result that depends on `{base-ref}`, or on a commit this clone does not
167
+ have, is provisional — say so, as item 2 does:
168
+ - **It is already on this branch** (`git merge-base --is-ancestor
169
+ {its-hash} HEAD` exits `0`) — the record names the right fix but the
170
+ wrong commit — for example the tagged merge commit on `main`. Correct
171
+ only the Checkpoint Log hash. The citation (item 3 cites the source, not
172
+ the hash), `hotfix-branch:` and the declared paths stay as they are, so
173
+ the correction rides the closeout commit — closeout's closed list admits
174
+ Checkpoint Log rows. Checkpoint 1 is not touched.
175
+ - **It is on the base branch but not on this branch** (`git merge-base
176
+ --is-ancestor {its-hash} {base-ref}` exits `0`) — this branch was cut
177
+ before the fix reached the base, so correcting the hash is not enough.
178
+ Merge `{base-ref}` into this branch — or, if nothing is pushed yet,
179
+ rebase this branch onto it and refill every Lightweight Changes row's
180
+ `Commit` cell — then record that commit. Set closeout's uncommitted
181
+ finalization edits aside first with `git stash push --include-untracked`
182
+ (a plain `git stash` leaves newly created files behind), check that
183
+ `git status --porcelain` prints nothing, and restore them afterwards
184
+ with `git stash pop`: `git rebase` refuses to run over uncommitted
185
+ edits, and `git merge` refuses when the base changed the same files. If
186
+ `git stash pop` reports a conflict — it can when the base changed the
187
+ same part of a file those edits touch — resolve it before closeout
188
+ continues.
189
+ - **It is not on the base branch either** — the fix was never merged back.
190
+ Merge it back first (under GitFlow, into `develop`), then take the case
191
+ above.
192
+ - ⚠ **Never merge `main` or the tag into this branch to make (b) pass.** It
193
+ lets the wrongly recorded commit pass unchanged, and it carries `main`'s
194
+ commits into `develop` when this branch merges.
195
+ - **The record names a different fix** (the citation, the hotfix branch and
196
+ the declared paths are all wrong) — checkpoint 1's own record is wrong,
197
+ and closeout cannot repair that. Follow
198
+ `references/finish-feature-record-correction.md`: it says when
199
+ checkpoint 1 can be amended and when to stop for the developer, and it
200
+ covers `reconciled (...)`, which names the wrong fix too.
@@ -416,7 +416,7 @@ During implementation, continuously check:
416
416
  **→ Step Gate: Step 7 → Step 8**
417
417
 
418
418
  Announce to developer:
419
- > "Implementation appears complete. Ready to run the completion checklist (verify against spec, update domain docs, archive the spec)? `/dflow:next` to proceed."
419
+ > "Implementation appears complete. Ready to run the completion checklist (verify against spec, update domain docs, complete the phase)? `/dflow:next` to proceed."
420
420
 
421
421
  > Commit checkpoint (T1 milestone 2 of 3): offer to commit the implementation, then record the result in the `_index.md` Checkpoint Log. Milestone 3 (closeout) is the `/dflow:finish-feature` checkpoint.
422
422
 
@@ -468,33 +468,51 @@ Ask these one-by-one; do not dump all five at once.
468
468
  - [ ] `analysis.md`, at either of its two paths — re-check every entry this feature recorded before building: an entry for what the feature was to add or change against what was built (corrected where the build differs, removed where nothing was built for it), and an entry for something already in the system against the system as it now stands (corrected where this feature changed it, never removed because this feature built nothing for it). Read the entries in the file, not in a diff: a checkpoint may already have committed them.
469
469
  - [ ] `dflow/specs/migration/tech-debt.md` — tech debt discovered during implementation recorded
470
470
 
471
- ### 8.4 Archival
472
-
473
- For a single-phase feature, this is the closeout point. If the feature has
474
- later phases still ahead, this phase is complete but the feature is not —
475
- don't archive yet; run `/dflow:new-phase` to start the next phase, not
476
- `/dflow:finish-feature` yet. For a multi-phase feature, the developer
477
- typically reaches this point at the end of the final phase — at which time
478
- `/dflow:finish-feature` is the recommended trigger (it bundles steps 8.1 /
479
- 8.2 verification, BC sync, and archival into one explicit ceremony). Either
480
- path is acceptable; pick the one that matches the developer's habit.
481
-
482
- - [ ] `_index.md` `status` field changed to `completed`
483
- - [ ] All `phase-spec-*.md` files in the feature directory have `status:
484
- completed` in their frontmatter
485
- - [ ] **Whole feature directory** moved from `dflow/specs/features/active/`
486
- to `dflow/specs/features/completed/` using `git mv` (preserves rename
487
- tracking — see `references/git-integration.md` § "Directory Moves
488
- Must Use git mv"):
489
- ```
490
- git mv dflow/specs/features/active/{SPEC-ID}-{slug} \
491
- dflow/specs/features/completed/{SPEC-ID}-{slug}
492
- ```
493
-
494
- > **Recommended path for multi-phase features**: instead of doing
495
- > 8.1–8.4 manually at the end of every phase, run `/dflow:finish-feature`
496
- > once the feature's last phase is complete. It executes the same checks
497
- > + BC sync + `git mv` + emits an Integration Summary. See
498
- > `references/finish-feature-flow.md`.
499
-
500
- Only announce "feature complete" after 8.4 is done.
471
+ ### 8.4 Phase completion and handoff
472
+
473
+ This step completes the phase and hands the feature on. It does not close the
474
+ feature out: `/dflow:finish-feature` is the only closeout path — it flips the
475
+ `_index.md` `status`, moves the feature directory to `completed/`, and takes
476
+ the closeout commit checkpoint.
477
+
478
+ **Complete this phase** — all four items, whether or not another phase
479
+ follows:
480
+
481
+ - [ ] This phase-spec's frontmatter `status` changed from `in-progress` to
482
+ `completed`
483
+ - [ ] This phase's row in the `_index.md` Phase Specs table changed from
484
+ `in-progress` to `completed`
485
+ - [ ] `_index.md` Current BR Snapshot reconciled against this phase-spec's
486
+ **Business Rules** section — this is the feature's first phase, so it
487
+ has no Delta from prior phases. If implementation changed a rule, update
488
+ the phase-spec first, then reconcile. Touch only this phase's rows: a
489
+ row that another change in this host wrote — a lightweight change
490
+ hosted under this feature, for example — stays as it is. Where this
491
+ phase and such a change both changed the same rule, keep the later
492
+ change's text; if the host's record does not show which change came
493
+ later, ask the developer which text is current instead of choosing.
494
+ - [ ] `_index.md` Resume Pointer: rewrite **both** narrative lines — Current
495
+ Progress says this phase is completed, and Next Action names the
496
+ command the handoff below picks. Step 4 wrote "phase-1 in progress:
497
+ drafting phase-spec." and "finish phase-spec, then implement …" into
498
+ these two lines; either one left standing reads as open work.
499
+
500
+ `/dflow:finish-feature` relies on these four whenever the feature reaches it,
501
+ next or after later phases. Its Step 1 checks the phase-spec `status`, the
502
+ Phase Specs row and the Resume Pointer, and stops closeout on a stale one.
503
+ Its Step 3 syncs the Current BR Snapshot into the bounded context as it
504
+ stands, without re-deriving it from the phase-specs.
505
+
506
+ `Implementation Tasks` is not on this list: 8.1 verified it, and 8.2 may have
507
+ collapsed or removed it by team convention. BC document sync is not on it
508
+ either: 8.3 did it.
509
+
510
+ **Hand off:**
511
+
512
+ - Another phase is still ahead → run `/dflow:new-phase` next.
513
+ - No further phase — a single-phase feature, or this was the last phase →
514
+ run `/dflow:finish-feature` next. It is the only closeout path.
515
+
516
+ Announce that this phase is completed and name the next command. Do not
517
+ announce the feature as complete: `/dflow:finish-feature` announces that once
518
+ its closeout is complete.
@@ -338,5 +338,5 @@ feature from `/dflow:new-phase`.
338
338
 
339
339
  After completion, summarize for the developer:
340
340
  > "Phase {N+1} is implemented and marked completed. `_index.md` is refreshed.
341
- > If another slice is needed, run `/dflow:new-phase`; if the feature is done,
342
- > run `/dflow:finish-feature`."
341
+ > If another slice is needed, run `/dflow:new-phase`; if there is no further
342
+ > phase, run `/dflow:finish-feature` to close the feature out."