dflow-sdd-ddd 0.11.0 → 0.13.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 (55) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.en.md +83 -17
  3. package/README.md +39 -9
  4. package/TEMPLATE-COVERAGE.md +1 -0
  5. package/bin/dflow.js +58 -2
  6. package/docs/evaluating-dflow.en.md +21 -2
  7. package/docs/evaluating-dflow.md +17 -3
  8. package/docs/using-with-claude-code.en.md +23 -16
  9. package/docs/using-with-claude-code.md +20 -14
  10. package/docs/using-with-codex.en.md +15 -8
  11. package/docs/using-with-codex.md +10 -7
  12. package/docs/using-with-github-copilot.en.md +8 -3
  13. package/docs/using-with-github-copilot.md +6 -3
  14. package/lib/init.js +93 -8
  15. package/lib/render.js +1263 -0
  16. package/package.json +5 -2
  17. package/templates/brownfield/references/finish-feature-flow.md +85 -29
  18. package/templates/brownfield/references/git-integration.md +29 -9
  19. package/templates/brownfield/references/init-project-flow.md +43 -1
  20. package/templates/brownfield/references/modify-existing-flow.md +23 -0
  21. package/templates/brownfield/references/new-feature-flow.md +34 -1
  22. package/templates/brownfield/references/new-phase-flow.md +12 -1
  23. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +45 -5
  24. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +1 -1
  25. package/templates/brownfield/scaffolding/Git-principles-trunk.md +2 -2
  26. package/templates/brownfield/templates/_index.md +25 -4
  27. package/templates/brownfield/templates/context-definition.md +2 -0
  28. package/templates/brownfield/templates/context-map.md +1 -0
  29. package/templates/brownfield/templates/glossary.md +1 -0
  30. package/templates/brownfield/templates/lightweight-spec.md +3 -3
  31. package/templates/brownfield/templates/models.md +1 -0
  32. package/templates/brownfield/templates/phase-spec.md +5 -3
  33. package/templates/brownfield/templates/rules.md +1 -0
  34. package/templates/brownfield/templates/tech-debt.md +1 -0
  35. package/templates/common/references/ddd-modeling-guide.md +197 -3
  36. package/templates/greenfield/references/finish-feature-flow.md +86 -29
  37. package/templates/greenfield/references/git-integration.md +29 -9
  38. package/templates/greenfield/references/init-project-flow.md +43 -1
  39. package/templates/greenfield/references/modify-existing-flow.md +23 -0
  40. package/templates/greenfield/references/new-feature-flow.md +35 -1
  41. package/templates/greenfield/references/new-phase-flow.md +11 -0
  42. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +45 -5
  43. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +1 -1
  44. package/templates/greenfield/scaffolding/Git-principles-trunk.md +4 -2
  45. package/templates/greenfield/templates/_index.md +25 -4
  46. package/templates/greenfield/templates/aggregate-design.md +4 -1
  47. package/templates/greenfield/templates/context-definition.md +2 -0
  48. package/templates/greenfield/templates/context-map.md +1 -0
  49. package/templates/greenfield/templates/events.md +3 -1
  50. package/templates/greenfield/templates/glossary.md +1 -0
  51. package/templates/greenfield/templates/lightweight-spec.md +3 -3
  52. package/templates/greenfield/templates/models.md +1 -0
  53. package/templates/greenfield/templates/phase-spec.md +5 -3
  54. package/templates/greenfield/templates/rules.md +1 -0
  55. package/templates/greenfield/templates/tech-debt.md +1 -0
@@ -1,10 +1,10 @@
1
1
  ---
2
- id: BUG-{NUMBER}
2
+ id: BUG-{NUMBER} # bug-type T2 only; a non-bug T2 (lightweight-{date}-{slug}.md) carries no id — the filename identifies it
3
3
  title: {簡述問題}
4
- status: in-progress
4
+ status: in-progress # in-progress | completed
5
5
  bounded-context: {ContextName}
6
6
  created: {YYYY-MM-DD}
7
- branch: bugfix/BUG-{NUMBER}-{short-description}
7
+ branch: bugfix/BUG-{NUMBER}-{slug}
8
8
  ---
9
9
 
10
10
  <!--
@@ -1,4 +1,5 @@
1
1
  <!-- Seeded by Dflow. -->
2
+ <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
2
3
 
3
4
  # Domain Models
4
5
 
@@ -1,13 +1,15 @@
1
1
  ---
2
- id: {CONTEXT}-{NUMBER}
2
+ spec-id: SPEC-{YYYYMMDD}-{NNN} # the owning feature's SPEC-ID (matches the feature directory name)
3
3
  title: Feature title
4
- status: draft | in-progress | completed
4
+ status: in-progress # in-progress | completed
5
5
  bounded-context: {ContextName}
6
6
  created: {YYYY-MM-DD}
7
7
  author: {developer-name}
8
- branch: feature/{CONTEXT}-{NUMBER}-{short-description}
8
+ branch: feature/{SPEC-ID}-{slug}
9
9
  ---
10
10
 
11
+ <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
12
+
11
13
  # {Feature Title}
12
14
 
13
15
  <!--
@@ -1,4 +1,5 @@
1
1
  <!-- Seeded by Dflow. -->
2
+ <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
2
3
 
3
4
  # Business Rules
4
5
 
@@ -1,4 +1,5 @@
1
1
  <!-- Seeded by Dflow. -->
2
+ <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
2
3
 
3
4
  # Migration Tech Debt
4
5
 
@@ -101,6 +101,9 @@ Is it identified by an ID that persists over time?
101
101
  └─ No → Re-examine — it's probably one of the above
102
102
  ```
103
103
 
104
+ A stateful multi-step flow is not a Domain Service — see "Long-Running
105
+ Processes".
106
+
104
107
  ## Aggregate Design
105
108
 
106
109
  Aggregates are the most important and most commonly misunderstood DDD concept.
@@ -186,6 +189,9 @@ When designing a new Aggregate:
186
189
  6. **Will any child collection grow without bound over time?** (history,
187
190
  comments, audit entries) Unbounded growth is a split signal — move it to
188
191
  its own Aggregate or a read model and reference by ID.
192
+ 7. **What would make this boundary wrong?** Record the answer in the
193
+ worksheet's Design Decisions as a re-evaluation condition ("revisit
194
+ when …") — it is what "Revising an Established Model" re-reads later.
189
195
 
190
196
  ### Invariant Classification
191
197
 
@@ -255,6 +261,79 @@ serialization tactics — distributed locks, per-key actors, aggregate-per-key
255
261
  sharding — exist but are advanced; reach for a store-level constraint or version
256
262
  check first.)
257
263
 
264
+ ## Revising an Established Model
265
+
266
+ The sections above are about getting a boundary right the first time. This
267
+ one is about the other half of a model's life: an established model whose
268
+ original decision was right — until the conditions changed. **A recorded
269
+ design decision is not settled law.** It is a decision *plus the conditions
270
+ under which it was right*; when those conditions expire, the decision is
271
+ due for review, not deference.
272
+
273
+ ### The re-read rule
274
+
275
+ **When extending an existing Aggregate, re-read its recorded Design
276
+ Decisions before adding to it** (the `aggregate-design.md` worksheet;
277
+ Brownfield — see the Edition note). You are not reading for format — you
278
+ are checking two things:
279
+
280
+ 1. Does any recorded **re-evaluation condition** ("revisit when …") match
281
+ the change in front of you?
282
+ 2. Did the original rationale assume something that is no longer true?
283
+
284
+ ### Signals that the model is resisting
285
+
286
+ Any of these appearing in your change is a signal — go to the ladder below:
287
+
288
+ - **Bending a field to fit** — a field that was previously required by the
289
+ entity's lifecycle is made nullable to fit a new case.
290
+ - **Discriminator creep** — adding a `Purpose` / `Type` discriminator so
291
+ one entity carries materially different lifecycles, required fields, or
292
+ rules.
293
+ - **Stacking another branch** — a third or later *distinct business branch*
294
+ on the same decision axis (validation / error variants don't count; the
295
+ count is an anchor, not an automatic refactor rule).
296
+ - **Qualifying a term to use it** — you keep saying "the Order here means
297
+ the cart-order"; one glossary term now covers two lifecycles.
298
+ - **A recorded re-evaluation condition matches** the current change.
299
+ - **Cross-instance transaction pressure** — a new invariant or operation
300
+ needs same-transaction writes across Aggregate instances because the
301
+ current boundary cannot own the rule (see Aggregate Design Rules #2; a
302
+ flow over *time* is a different topic — see "Long-Running Processes").
303
+ - **A child collection grows without bound** — the split signal from
304
+ Aggregate Design Rules #4 and Common Mistakes #7 applies to established
305
+ models too.
306
+
307
+ ### What to do when a signal fires — take the lowest rung that fits
308
+
309
+ 1. **Name it in the spec** (mandatory when a signal fires; zero design
310
+ cost). One short passage in the spec's design decisions / open
311
+ questions: which signal fired, the options, and the decision —
312
+ **proceed as-is, split, or rename — with the reason**. Deciding *not*
313
+ to split, recorded, is a perfectly good outcome when the rationale
314
+ still holds. What is not acceptable is extending the model as if the
315
+ question did not exist.
316
+ 2. **Treat the revision as its own change** when the answer is "split" or
317
+ "rename": record it as tech debt or a follow-up feature — or, if the
318
+ feature cannot proceed sanely on the old boundary, make the split /
319
+ rename the feature's first phase (T1 ceremony; the glossary moves
320
+ first, RENAMED deltas, code follows).
321
+
322
+ Two guards, so this section cannot become its own kind of
323
+ over-engineering:
324
+
325
+ > Signals are the trigger, not a schedule. Do not re-litigate the model on
326
+ > every touch: no signal → no ceremony; one signal → name it; several
327
+ > signals, or a matched re-evaluation condition → evaluate seriously.
328
+
329
+ > A signal triggers **review, not redesign**. Do not split or rename just
330
+ > because a signal fired. A short recorded decision to keep the current
331
+ > model is a valid outcome when the rationale still holds.
332
+
333
+ This applies where a model exists to revise — core / supporting contexts.
334
+ A generic context's thin wrapper (see "Subdomain-Aware Modeling Depth")
335
+ has no worksheet and needs none of this ceremony.
336
+
258
337
  ## Value Objects
259
338
 
260
339
  ### When to Use Value Objects
@@ -286,11 +365,16 @@ public record Money(decimal Amount, Currency Currency)
286
365
  }
287
366
 
288
367
  // DateRange
289
- public record DateRange(DateOnly Start, DateOnly End)
368
+ public record DateRange
290
369
  {
291
- public DateRange
370
+ public DateOnly Start { get; }
371
+ public DateOnly End { get; }
372
+
373
+ public DateRange(DateOnly start, DateOnly end)
292
374
  {
293
- if (Start > End) throw new DomainException("Start must be before End.");
375
+ if (start > end) throw new DomainException("Start must be before End.");
376
+ Start = start;
377
+ End = end;
294
378
  }
295
379
 
296
380
  public bool Contains(DateOnly date) => date >= Start && date <= End;
@@ -433,6 +517,95 @@ the values that changed — not a snapshot of the whole Aggregate.
433
517
  delivery. If no dispatcher is wired yet, record it as deferred tech debt — but
434
518
  still clear on a successful save so events cannot accumulate unbounded.
435
519
 
520
+ ## Long-Running Processes
521
+
522
+ The Aggregate design rules above already cover the simple case: one Aggregate
523
+ per transaction, with cross-Aggregate work flowing through Domain Events and
524
+ eventual consistency. This section is for the flows where that is not the
525
+ whole story — where the multi-step flow is itself a business thing with
526
+ state, and that state needs a home.
527
+
528
+ ### Process or event chain?
529
+
530
+ Use an event chain when each reaction can succeed or fail independently — a
531
+ later failure does not change the earlier domain commitment. Treat the flow
532
+ as a **process** when the business must track and decide the multi-step
533
+ **outcome**: compensate or reverse an earlier commitment, answer current
534
+ progress, enforce a deadline, or enforce an ordered cross-Aggregate workflow
535
+ whose intermediate state matters.
536
+
537
+ The quickest entry test: **if a later step fails, must an earlier step be
538
+ undone?** "Payment failed → release the reserved stock" is a process, not an
539
+ event chain.
540
+
541
+ Further signals that the flow is a process:
542
+
543
+ - The business asks "where is order #123 in the flow?" and no single
544
+ Aggregate can answer.
545
+ - A deadline is part of the rules ("if payment is not confirmed within 30
546
+ minutes, release the seats").
547
+ - Steps across Aggregates must run in a prescribed order **and** the
548
+ intermediate state matters to the business — being ordered by itself is
549
+ not enough.
550
+
551
+ **Not a process:** fire-and-forget notifications, read-model updates,
552
+ logging, and other best-effort side effects — even when ordered, even when
553
+ they cross Aggregates. Those remain plain event chains; their failure
554
+ handling is already covered by the failure-path guideline under Event
555
+ Handling Guidelines.
556
+
557
+ ### Where does the process state live? Take the lowest rung that fits
558
+
559
+ 1. **A status field on the Aggregate that owns the flow** — the first choice
560
+ when the process is naturally part of one Aggregate's lifecycle:
561
+ `Order.Status = PendingPayment → Paid → Shipped`. Event handlers advance
562
+ the status; each compensation is an explicit state-transition method
563
+ (`order.Cancel(reason)`) with its own BR. Most mid-size flows stop here.
564
+ The boundary: a status field is enough only when the process is naturally
565
+ part of that Aggregate's lifecycle — do not store every downstream
566
+ system's bookkeeping on the owner just to avoid a process Aggregate.
567
+
568
+ 2. **A dedicated process Aggregate** (the DDD shape of a *process manager*) —
569
+ when the coordination belongs to no existing Aggregate, or the flow needs
570
+ its own bookkeeping (steps completed, retries, deadline): a small
571
+ Aggregate (e.g. `OrderFulfillment`) whose state *is* the flow's progress.
572
+ It reacts to events, issues commands, and its invariants are process
573
+ rules ("cannot ship before payment is confirmed"). It is an ordinary
574
+ Aggregate — same aggregate-design worksheet, same Invariants table,
575
+ events cataloged in `events.md` like any other (Brownfield — see the
576
+ Edition note). Two boundaries: a process Aggregate **coordinates
577
+ progress** — it does not pull the participating Aggregates into one
578
+ transaction or take over their invariants. And do not create one for a
579
+ two-step event reaction with no compensation, no deadline, and no
580
+ business-visible progress to answer — keep that as an event chain, or as
581
+ the owner Aggregate's normal state if it already has one.
582
+
583
+ 3. **An orchestration framework / workflow engine** — the Phase 2+ upgrade,
584
+ worth it only at operational scale (versioning long-lived in-flight
585
+ flows, visibility dashboards). Like event sourcing, this is a separate,
586
+ heavyweight architecture decision: record it as an ADR if genuinely
587
+ needed; never introduce it as a side effect of modeling.
588
+
589
+ ### Compensation is business behavior, not plumbing
590
+
591
+ A compensating action is not a rollback — the mail was sent, the money
592
+ moved; they cannot un-happen. Compensation is a **new domain fact**
593
+ (`RefundIssued`, `ReservationReleased`) with its own BR and Given/When/Then
594
+ in `behavior.md`. The failure-path guideline under Event Handling Guidelines
595
+ says *which* final failures must become BRs; this section says *where the
596
+ logic that answers them lives*.
597
+
598
+ ### Deadlines
599
+
600
+ A deadline is part of the process state. Detecting expiry is infrastructure
601
+ (a scheduled check), but the decision — "expired → release the seats" — is a
602
+ domain rule, expressed as a domain event (`BookingExpired`) and handled like
603
+ any other.
604
+
605
+ Two search terms, so you can find the literature: *choreography* is a plain
606
+ event chain; *orchestration* means an explicit process owner. These are
607
+ search terms, not a framework choice.
608
+
436
609
  ## Specifications
437
610
 
438
611
  For complex query logic that belongs to the domain:
@@ -504,6 +677,10 @@ Use Domain Services for operations that:
504
677
  - Require external information (through interfaces) to make domain decisions
505
678
  - Don't naturally belong to any single Entity
506
679
 
680
+ A Domain Service can make a **stateless** cross-Aggregate decision. It is
681
+ not a home for process progress, retries, deadlines, or compensation state.
682
+ When the flow has state, use the "Long-Running Processes" ladder.
683
+
507
684
  ```csharp
508
685
  // Domain Service — in Domain layer
509
686
  public class ExpenseApprovalService
@@ -641,3 +818,20 @@ The less-obvious three — when to reach for them:
641
818
  Aggregates just to display a list / report causes object-graph bloat and N+1
642
819
  → Use a read model (denormalized projection / DTO) for the display path; see
643
820
  "Read Models (Query Side)"
821
+
822
+ 11. **Compensation logic scattered across event handlers** — Each handler
823
+ patches state on its own; no one owns the process, and nobody can answer
824
+ "where is order #123 in the flow"
825
+ → Give the process a home (a status field on the owning Aggregate or a
826
+ dedicated process Aggregate); write each compensating action as a BR; see
827
+ "Long-Running Processes"
828
+
829
+ 12. **Treating recorded design decisions as settled law** — Extending an
830
+ Aggregate while bending it to fit (a lifecycle-required field turned
831
+ nullable for a new case, a `Purpose` / `Type` discriminator making one
832
+ entity carry materially different lifecycles or rules), never re-reading
833
+ the Design Decisions whose re-evaluation condition the change just
834
+ triggered
835
+ → Re-read recorded decisions when extending an existing Aggregate; when a
836
+ resistance signal fires, name the revision question in the spec (proceed /
837
+ split / rename, with reason); see "Revising an Established Model"
@@ -56,6 +56,10 @@ proceeding (do not flip status, do not archive, do not emit summary).
56
56
  - [ ] Every phase-spec file referenced in the Phase Specs table exists at
57
57
  the path the table claims
58
58
  - [ ] Every phase-spec file's frontmatter has `status: completed`
59
+ - [ ] Every Tier = T2 row in `_index.md` Lightweight Changes references an
60
+ existing `lightweight-*.md` / `BUG-*.md` file in the feature directory
61
+ - [ ] Every such lightweight / BUG spec file's frontmatter has
62
+ `status: completed`
59
63
  - [ ] `_index.md` has no obvious open items in Resume Pointer (e.g. "phase-N
60
64
  drafting" / "implementation pending" / "TODO" markers)
61
65
  - [ ] Current BR Snapshot table is non-empty (or feature is intentionally
@@ -66,6 +70,7 @@ If any check fails:
66
70
  > found:
67
71
  > ✗ phase-spec-2026-04-15-foo.md status is still `in-progress`
68
72
  > ✗ Phase Specs table row 3 references missing file phase-spec-...
73
+ > ✗ lightweight-2026-06-20-rounding.md frontmatter status is still `in-progress`
69
74
  >
70
75
  > Address these (run `/dflow:new-phase` to add missing work, or fix the
71
76
  > stale status manually), then re-run `/dflow:finish-feature`."
@@ -93,11 +98,17 @@ branch: feature/{SPEC-ID}-{slug}
93
98
  ---
94
99
  ```
95
100
 
96
- Also update the **Resume Pointer** to reflect closeout:
101
+ Also update the **Resume Pointer** to reflect closeout — this writes the
102
+ cursor's terminal state (after closeout no workflow is active on this
103
+ feature; do not edit the cursor again after the Step 4 closeout commit):
97
104
 
98
105
  ```
99
106
  **Current Progress**: feature completed ({date}); all phase-specs status = completed.
100
107
  **Next Action**: integration — push / merge / PR per the selected Git policy.
108
+ **Active Workflow**: none
109
+ **Current Step**: n/a
110
+ **Gates Passed**: n/a
111
+ **Awaiting**: none
101
112
  ```
102
113
 
103
114
  **→ Transition (step-internal)**: Step 2 complete. Announce "Step 2 complete (status flipped). Entering Step 3: Sync BR Snapshot to BC layer." and continue.
@@ -184,7 +195,8 @@ AI runs:
184
195
  ```bash
185
196
  git mv dflow/specs/features/active/{SPEC-ID}-{slug} \
186
197
  dflow/specs/features/completed/{SPEC-ID}-{slug}
187
- git status # confirm rename detection
198
+ git status # confirm rename detection AND check for `RM` — an `M` next to
199
+ # a rename means unstaged edits you must re-add before committing
188
200
  ```
189
201
 
190
202
  `git mv` is mandatory — never use plain `mv` + `git add`. This preserves
@@ -193,37 +205,75 @@ PR diff quality stays intact across the move. See
193
205
  `references/git-integration.md` § "Directory Moves Must Use git mv" for
194
206
  the full rule set.
195
207
 
196
- After the move, also `git add` any modified files from Step 3 (the
197
- updated `rules.md`, `behavior.md`, `events.md`, `context-map.md`,
198
- `glossary.md`, `architecture/tech-debt.md`, etc.) into the same stage.
199
-
200
208
  **Closeout commit checkpoint** (completes the offline Local-closeout gate):
201
209
 
202
210
  ```
203
- ✓ Feature archived to completed/ and closeout files staged
211
+ ✓ Feature archived to completed/ and closeout ready to stage
204
212
  Commit this closeout now?
205
213
  [Y] Yes — the AI commits with your Git identity (marker per _conventions.md § AI Commit Policy)
206
214
  [N] No — skip; you commit yourself
207
215
  ```
208
216
 
209
- Whether you choose Y or N, record one row in the feature `_index.md`
210
- Checkpoint Log (`closeout | committed ({hash})` or `closeout | skipped`). Only
211
- write a hash after the commit actually succeeds; if a pre-commit hook rejects it
212
- or the commit fails, record `failed` and surface the error — never write a fake
213
- hash.
214
-
215
- The Local-closeout gate is satisfied **only when the closeout is committed**:
216
- closeout complete, Checkpoint Log updated, and the working tree clean (no
217
- uncommitted changes). If you declined the commit (chose N) or it failed,
218
- Local-closeout is **not** satisfied yet — commit the staged closeout yourself
219
- before continuing; do not enter the Integration / PR gate with uncommitted
220
- changes. Once committed, the gate stands on its own offline; integration happens
221
- in Step 5 when you have network.
222
-
223
- **→ Transition (step-internal)**: Step 4 complete. Branch on whether the closeout commit landed:
224
-
225
- - **Closeout commit landed (working tree clean)** → announce "Step 4 complete (feature archived; Local-closeout gate satisfied). Entering Step 5: Integration / PR gate." and continue.
226
- - **Closeout commit was declined (N) or failed** → **stop here.** Announce "Step 4 complete (feature archived), but the Local-closeout gate is not satisfied yet — the closeout is staged but uncommitted. Commit those changes (or address the failure), then resume to Step 5." Do **not** enter Step 5 with uncommitted closeout changes.
217
+ Then, in this order:
218
+
219
+ 1. **Record the checkpoint row first.** Write one row in the moved
220
+ `_index.md` Checkpoint Log — `closeout | committed` for Y, `closeout |
221
+ skipped` for N. The closeout row carries **no commit hash**: the closeout
222
+ commit cannot contain its own hash. Trace it later via
223
+ `git log -1 -- dflow/specs/features/completed/{SPEC-ID}-{slug}` (or the
224
+ optional `Dflow-Checkpoint` trailer). The "hash only after success" rule
225
+ still applies to spec / implementation rows — closeout is the documented
226
+ exception (see `references/git-integration.md` § Commit Checkpoints,
227
+ Branch Gate & AI Commits).
228
+ 2. **Stage the whole archived feature directory:**
229
+
230
+ ```bash
231
+ git add dflow/specs/features/completed/{SPEC-ID}-{slug}
232
+ ```
233
+
234
+ This is required, not optional: `git mv` stages the rename with the
235
+ **last-committed** content, so working-tree edits made earlier in this
236
+ flow to the moved files — the Step 2 status flip and Resume Pointer
237
+ update, plus the checkpoint row you just wrote — stay **unstaged** until
238
+ this `git add`. In `git status`, the moved `_index.md` showing `RM`
239
+ instead of plain `R` is exactly this signal. Then also `git add` the
240
+ files updated in Step 3 (the updated `rules.md`, `behavior.md`,
241
+ `events.md`, `context-map.md`, `glossary.md`,
242
+ `architecture/tech-debt.md`, etc.) into the same stage.
243
+ 3. **Commit (Y) or stop (N).** For Y the AI commits. If a pre-commit hook
244
+ rejects it or the commit fails, flip the checkpoint row to `failed` (the
245
+ row is not committed yet — edit it directly), surface the error, and
246
+ treat the gate as unsatisfied.
247
+
248
+ **Post-commit closeout verification** — after a successful commit, and before
249
+ declaring the Local-closeout gate satisfied, AI runs and reports `✓` / `✗` for
250
+ every item:
251
+
252
+ - [ ] `git show HEAD:dflow/specs/features/completed/{SPEC-ID}-{slug}/_index.md`
253
+ — one blob read verifying **two** things: frontmatter `status: completed`
254
+ **and** the Checkpoint Log contains the closeout row. This reads the
255
+ **committed** content, not the working tree — the former catches "rename
256
+ carried stale content", the latter catches "row never made it into the
257
+ commit".
258
+ - [ ] `dflow/specs/features/active/{SPEC-ID}-{slug}/` no longer exists (the
259
+ directory was moved, not copied)
260
+ - [ ] `git status --short` shows no leftovers related to this feature
261
+ (working tree clean; identify any unrelated dirty files explicitly)
262
+
263
+ If any item fails, do **not** declare closeout complete — fix it (re-add and
264
+ amend, or a follow-up commit; the developer chooses) and re-verify.
265
+
266
+ The Local-closeout gate is satisfied **only when the closeout is committed and
267
+ the verification above passes**. If you declined the commit (chose N) or it
268
+ failed, Local-closeout is **not** satisfied yet — commit the staged closeout
269
+ yourself before continuing; do not enter the Integration / PR gate with
270
+ uncommitted changes. Once committed and verified, the gate stands on its own
271
+ offline; integration happens in Step 5 when you have network.
272
+
273
+ **→ Transition (step-internal)**: Step 4 complete. Branch on the verification result:
274
+
275
+ - **Closeout commit landed and post-commit verification passed** → announce "Step 4 complete (feature archived; Local-closeout gate satisfied). Entering Step 5: Integration / PR gate." and continue.
276
+ - **Closeout commit was declined (N), failed, or verification reported `✗`** → **stop here.** Announce "Step 4 complete (feature archived), but the Local-closeout gate is not satisfied yet — the closeout is staged but uncommitted, or the committed content failed verification. Commit the staged changes (or fix the failure), then resume to Step 5." Do **not** enter Step 5 with uncommitted or unverified closeout changes.
227
277
 
228
278
  ## Step 5: Emit Integration Summary (Git-strategy-neutral)
229
279
 
@@ -286,10 +336,17 @@ the developer:
286
336
 
287
337
  If no `follow-up-of` field, skip Step 6 and announce closeout complete:
288
338
  > "`/dflow:finish-feature` complete for `{SPEC-ID}-{slug}`. Feature
289
- > directory is now at `dflow/specs/features/completed/{SPEC-ID}-{slug}/`.
290
- > If you skipped the closeout commit, commit the staged changes first to
291
- > finish the Local-closeout gate. Then integration — merge / push / PR —
292
- > follows the selected Git policy, at your discretion."
339
+ > directory is now at `dflow/specs/features/completed/{SPEC-ID}-{slug}/`,
340
+ > with the Local-closeout gate satisfied (closeout committed and verified).
341
+ > Integration — merge / push / PR — follows the selected Git policy, at
342
+ > your discretion."
343
+
344
+ **In-flight reminder** — after the closeout announcement (with or without
345
+ Step 6), run the in-flight overview scan (see `AI-AGENT-GUIDE.md` § Status /
346
+ Control Commands) and list any other unfinished features in `active/` and any
347
+ in-flight feature / bugfix branches. Surfacing them at closeout is deliberate:
348
+ attention is about to move elsewhere, and this is exactly where half-done work
349
+ sinks.
293
350
 
294
351
  ## Step 6: Reverse-Update Follow-up Tracking (only if follow-up)
295
352
 
@@ -43,8 +43,8 @@ bugfix/{BUG-ID}-{slug}
43
43
  Examples:
44
44
 
45
45
  ```
46
- feature/EXP-001-expense-submission-aggregate
47
- feature/HR-003-leave-approval-workflow
46
+ feature/SPEC-20260424-002-submit-expense-report
47
+ feature/SPEC-20260430-001-leave-approval-workflow
48
48
  bugfix/BUG-042-money-rounding
49
49
  ```
50
50
 
@@ -149,10 +149,30 @@ existing Step Gate prompt (it does not add a separate question):
149
149
  Tier sets how many checkpoints a change has: T1 three (spec / implementation /
150
150
  closeout), T2 two (spec+implementation merged / closeout), T3 a single commit.
151
151
  Whether you choose Y or N, the AI records one row in the feature `_index.md`
152
- Checkpoint Log. A commit hash is written only after the commit succeeds; a hook
153
- rejection or failed commit is recorded as `failed` (never a fake hash). After
154
- several consecutive skips in a project the AI mentions you can turn checkpoints
155
- off in config — it does not turn them off for you.
152
+ Checkpoint Log — every checkpoint is accounted for (`committed` / `skipped` /
153
+ `failed`), even when no commit happens. A commit hash is written only after the
154
+ commit succeeds; a hook rejection or failed commit is recorded as `failed`
155
+ (never a fake hash). **Exception — the closeout row**: the closeout commit
156
+ cannot contain its own hash, so the closeout row is written before the commit
157
+ as `closeout | committed` with **no hash** (see
158
+ `references/finish-feature-flow.md` Step 4); trace that commit via
159
+ `git log -1 -- dflow/specs/features/completed/{SPEC-ID}-{slug}` or the optional
160
+ `Dflow-Checkpoint` trailer below. After several consecutive skips in a project
161
+ the AI mentions you can turn checkpoints off in config — it does not turn them
162
+ off for you.
163
+
164
+ **Optional machine-greppable trailer.** Teams that want cross-flow checkpoint
165
+ accounting can append a commit trailer at checkpoint commits:
166
+
167
+ ```
168
+ Dflow-Checkpoint: {SPEC-ID} {spec|impl|closeout}
169
+ ```
170
+
171
+ The `_index.md` Checkpoint Log **remains the source of truth**; the trailer is
172
+ a cheap derived mirror (`git log --grep 'Dflow-Checkpoint: {SPEC-ID}'`). Use
173
+ role names, not (k/N) counts — the checkpoint total can change mid-feature
174
+ (tier escalation, follow-ups), and a role gap ("impl exists but no closeout for
175
+ this SPEC-ID") is detectable without predicting N, even across flows.
156
176
 
157
177
  ### AI commits
158
178
 
@@ -312,9 +332,9 @@ with the rule.
312
332
  ```
313
333
  [SPEC-ID] Short description
314
334
 
315
- [EXP-001] Define ExpenseReport Aggregate with submission invariants
316
- [EXP-001] Add CreateExpenseReport command and handler
317
- [EXP-001] Implement persistence configuration for ExpenseReport
335
+ [SPEC-20260424-002] Define ExpenseReport Aggregate with submission invariants
336
+ [SPEC-20260424-002] Add CreateExpenseReport command and handler
337
+ [SPEC-20260424-002] Implement persistence configuration for ExpenseReport
318
338
  [BUG-042] Fix rounding in Money value object
319
339
  ```
320
340
 
@@ -178,6 +178,32 @@ Wait for answers.
178
178
  > and refreshes it in place on re-run. Merge snippets under
179
179
  > `dflow/specs/shared/` are used only if Dflow markers conflict."
180
180
 
181
+ Wait for answers.
182
+
183
+ ### Q9. Project-level skill (agent-gated, default yes)
184
+
185
+ Asked only when Q8 selected at least one agent — with no agents there is no
186
+ projection target and this question is skipped entirely.
187
+
188
+ > "Install the project-level Dflow skill for natural-language auto-trigger?
189
+ > (Y/n)
190
+ >
191
+ > The skill is what makes requests like 'I want to add a feature' surface the
192
+ > matching workflow automatically; without it, triggering relies on the
193
+ > instruction files alone and degrades in long sessions. Skill files are
194
+ > Dflow-generated derivatives — the recommended default is to gitignore them
195
+ > and re-project after cloning."
196
+
197
+ Wait for the answer. **Blank defaults to yes.** On `n`, tell the developer:
198
+
199
+ > "Skipped the project-level skill; add it later with
200
+ > `dflow configure-agents --skills`."
201
+
202
+ CLI note: the CLI asks this question only on an interactive terminal. A
203
+ non-interactive (piped) `dflow init` never reads an extra stdin answer for it
204
+ — existing scripted answer sequences keep their structure and keep working —
205
+ and installs the skill for the selected agents by default.
206
+
181
207
  **→ Transition (step-internal)**: Step 2 complete. Announce
182
208
  > "Step 2 complete (project information captured). Entering Step 3:
183
209
  > File-list preview."
@@ -293,7 +319,7 @@ skip, and wait for developer confirmation:
293
319
  **→ Step Gate: Step 3 → Step 4**
294
320
 
295
321
  Wait for explicit confirmation. If the developer asks to change the
296
- selection, go back to the relevant Step 2 question (Q5–Q8) and re-run Step 3.
322
+ selection, go back to the relevant Step 2 question (Q5–Q9) and re-run Step 3.
297
323
 
298
324
  ---
299
325
 
@@ -367,6 +393,22 @@ For each selected tool-specific file (`AGENTS.md`, `CLAUDE.md`,
367
393
  in the preview, and refresh that same block on re-run. If the developer later
368
394
  deletes the block, a later `init` / `configure-agents` run appends it again
369
395
 
396
+ If the developer chose to install the project-level skill (Q9), the CLI also
397
+ creates the skill file for each selected tool at its native project-level
398
+ path:
399
+
400
+ - `.claude/skills/dflow/SKILL.md` — Claude Code
401
+ - `.agents/skills/dflow/SKILL.md` — Codex
402
+ - `.github/skills/dflow/SKILL.md` — GitHub Copilot
403
+
404
+ All three are the same edition-neutral thin skill projected from the single
405
+ canonical source in the npm package. An existing file at one of those paths
406
+ that is **not** Dflow-generated (missing the
407
+ `<!-- dflow-generated: skill-adapter -->` marker) is left unchanged with a
408
+ warning. Manual AI fallback (no npm available): do **not** hand-write SKILL.md
409
+ content — report that the skill install is deferred and the developer should
410
+ run `dflow configure-agents --skills` once npm is available.
411
+
370
412
  ### 4.4 Directory-only entries
371
413
 
372
414
  For directories that Git otherwise wouldn't track (empty `active/` /
@@ -9,6 +9,8 @@ Triggered by `/dflow:modify-existing` or `/dflow:bug-fix` (or natural language i
9
9
  - Step 3 → Step 4 (DDD impact decision → implement)
10
10
  - Step 4 → Step 5 (implementation done → update documentation)
11
11
 
12
+ Crossing any step gate above also updates the host feature's `_index.md` Resume Pointer cursor (Active Workflow / Current Step / Gates Passed / Awaiting) once the host feature directory exists — fold it into that gate's existing `_index.md` / Resume Pointer edit, no separate ceremony (see the `_index.md` template's Resume Pointer notes).
13
+
12
14
  All other step transitions are **step-internal**: announce "Step N complete, entering Step N+1" and proceed without waiting. See AI-AGENT-GUIDE.md § Workflow Transparency for the full transparency protocol and confirmation signals.
13
15
 
14
16
  **Note on step count**: Greenfield edition has 5 steps (Brownfield has
@@ -61,6 +63,16 @@ Walk through these in order:
61
63
  this is a new concern. For T1, use `/dflow:new-feature`. For T2 / T3
62
64
  on a standalone bug, see Step 1.5 — `/dflow:bug-fix` will create a
63
65
  minimal feature directory to host the lightweight-spec.
66
+ 4. **In-flight overlap scan (cross-branch)**: this branch's `active/` is not
67
+ everything in flight. Run the in-flight scan (classification and dedup
68
+ rules in `AI-AGENT-GUIDE.md` § Status / Control Commands) — `git fetch`
69
+ when the network allows, then
70
+ `git branch --all --list '*feature/*' --list '*bugfix/*'` — and also list
71
+ other unfinished features in this branch's `active/` (one cursor line
72
+ each). If a scanned branch classified as in flight elsewhere, closed out
73
+ awaiting integration, or unknown — or an unfinished feature — semantically
74
+ overlaps this change, surface it and wait for the developer to decide
75
+ before creating anything new (stale branches are non-blocking).
64
76
 
65
77
  > **Why scan completed too?** Completed features are frozen history
66
78
  > and **cannot accept** any T2 / T3 directly
@@ -237,6 +249,17 @@ Changes that require Aggregate redesign:
237
249
  boundary still make sense, or do we need to split/merge?"
238
250
  ```
239
251
 
252
+ **Established-model re-read.** When the change extends an existing
253
+ Aggregate, re-read that Aggregate's recorded Design Decisions — its
254
+ `aggregate-design.md` worksheet in the feature directory that introduced it
255
+ (usually under `features/completed/`). If this change matches a recorded
256
+ re-evaluation condition ("revisit when …") or trips a model-resistance
257
+ signal, follow `references/ddd-modeling-guide.md` § "Revising an
258
+ Established Model": record one short passage in the spec's Design
259
+ Decisions / Open Questions — proceed as-is, split, or rename, with the
260
+ reason. Deciding to keep the current model, recorded, is a valid outcome;
261
+ extending silently is not.
262
+
240
263
  ### Do we need new Domain Events?
241
264
 
242
265
  If the behavior change means other parts of the system need to react differently: