dflow-sdd-ddd 0.11.0 → 0.12.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 (28) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/bin/dflow.js +0 -0
  3. package/package.json +1 -1
  4. package/templates/brownfield/references/finish-feature-flow.md +85 -29
  5. package/templates/brownfield/references/git-integration.md +29 -9
  6. package/templates/brownfield/references/modify-existing-flow.md +23 -0
  7. package/templates/brownfield/references/new-feature-flow.md +34 -1
  8. package/templates/brownfield/references/new-phase-flow.md +12 -1
  9. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +40 -5
  10. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +1 -1
  11. package/templates/brownfield/scaffolding/Git-principles-trunk.md +2 -2
  12. package/templates/brownfield/templates/_index.md +23 -4
  13. package/templates/brownfield/templates/lightweight-spec.md +3 -3
  14. package/templates/brownfield/templates/phase-spec.md +3 -3
  15. package/templates/common/references/ddd-modeling-guide.md +197 -3
  16. package/templates/greenfield/references/finish-feature-flow.md +86 -29
  17. package/templates/greenfield/references/git-integration.md +29 -9
  18. package/templates/greenfield/references/modify-existing-flow.md +23 -0
  19. package/templates/greenfield/references/new-feature-flow.md +35 -1
  20. package/templates/greenfield/references/new-phase-flow.md +11 -0
  21. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +40 -5
  22. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +1 -1
  23. package/templates/greenfield/scaffolding/Git-principles-trunk.md +4 -2
  24. package/templates/greenfield/templates/_index.md +23 -4
  25. package/templates/greenfield/templates/aggregate-design.md +2 -1
  26. package/templates/greenfield/templates/events.md +2 -1
  27. package/templates/greenfield/templates/lightweight-spec.md +3 -3
  28. package/templates/greenfield/templates/phase-spec.md +3 -3
@@ -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
 
@@ -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:
@@ -10,6 +10,8 @@ Triggered by `/dflow:new-feature` (or natural language implying a new-feature ta
10
10
  - Step 6 → Step 7 (branch ready → start implementation)
11
11
  - Step 7 → Step 8 (implementation done → completion)
12
12
 
13
+ 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 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).
14
+
13
15
  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.
14
16
 
15
17
  **Ceremony**: this flow always defaults to **T1 Heavy** — the first phase of a brand-new feature is by definition a full SDD cycle. Tier judgement (T1 / T2 / T3) only applies to `/dflow:modify-existing` (see `references/modify-existing-flow.md` and AI-AGENT-GUIDE.md § Ceremony Scaling).
@@ -31,6 +33,27 @@ Check existing assets:
31
33
  - Search `dflow/specs/features/` for related features
32
34
  - Check `dflow/specs/domain/glossary.md` and `context-map.md`
33
35
 
36
+ **In-flight overlap scan (cross-branch + other unfinished features)** — this
37
+ branch's `dflow/specs/` does not show everything in flight. Run the in-flight
38
+ scan (classification and dedup rules in `AI-AGENT-GUIDE.md` § Status / Control
39
+ Commands):
40
+
41
+ ```bash
42
+ git fetch # when the network allows; skip gracefully offline
43
+ git branch --all --list '*feature/*' --list '*bugfix/*'
44
+ ```
45
+
46
+ - List other unfinished features already in this branch's `active/` (one
47
+ cursor line each, from their `_index.md` Resume Pointer).
48
+ - Classify every listed branch by the guide's rules — in flight elsewhere /
49
+ closed out awaiting integration / stale (completed here) / unknown — do
50
+ not shortcut the classification. If a branch classified as **in flight
51
+ elsewhere, closed out awaiting integration, or unknown** has an ID / slug
52
+ that semantically overlaps this request, surface it and wait for the
53
+ developer to decide — continue there / integrate it first / treat as
54
+ related / unrelated — **before creating any new directory, spec, or
55
+ branch**. Only stale (completed here) branches are non-blocking.
56
+
34
57
  **→ Transition (step-internal)**: Step 1 complete. Announce "Step 1 complete (intake). Entering Step 2: Identify the Bounded Context." and continue.
35
58
 
36
59
  ## Step 2: Identify the Bounded Context
@@ -96,6 +119,17 @@ Those things form an Aggregate. Everything else is eventually consistent."
96
119
  - What entities belong inside this Aggregate?
97
120
  - What Value Objects can we extract?
98
121
 
122
+ **Established-model re-read (when the feature reuses an existing
123
+ Aggregate).** Re-read that Aggregate's recorded Design Decisions — its
124
+ `aggregate-design.md` worksheet in the feature directory that introduced it
125
+ (usually under `features/completed/`) — before extending it. If this change
126
+ matches a recorded re-evaluation condition ("revisit when …") or trips a
127
+ model-resistance signal, follow `references/ddd-modeling-guide.md`
128
+ § "Revising an Established Model": record one short passage in the
129
+ phase-spec's Design Decisions / Open Questions — proceed as-is, split, or
130
+ rename, with the reason. Deciding to keep the current model, recorded, is a
131
+ valid outcome; extending silently is not.
132
+
99
133
  ### Domain Events
100
134
  ```
101
135
  "After this happens, what else in the system needs to know?"
@@ -192,7 +226,7 @@ dflow/specs/features/active/{SPEC-ID}-{slug}/
192
226
  - Current BR Snapshot: initialise from the first phase's planned BRs
193
227
  (will be refreshed when the phase-spec finalises)
194
228
  - Lightweight Changes: empty table at start
195
- - Resume Pointer: "phase-1 in progress: drafting phase-spec." / "Next Action: finish phase-spec, then implement Domain layer."
229
+ - Resume Pointer: "phase-1 in progress: drafting phase-spec." / "Next Action: finish phase-spec, then implement Domain layer." / cursor fields: Active Workflow `new-feature`, Current Step `Step 4 — write the spec`, Gates Passed `3→3.5`, Awaiting `none (mid-step)`
196
230
  3. **Create the first phase-spec** at `phase-spec-{YYYY-MM-DD}-{slug}.md`
197
231
  using `templates/phase-spec.md`. The "Delta from prior phases" section
198
232
  is filled with "首 phase,無前置 Delta" (first phase has nothing to
@@ -19,6 +19,8 @@ adds a new phase to an in-progress feature only.
19
19
  - Step 5 → Step 6 (`_index.md` refreshed → start implementation)
20
20
  - Step 6 → Step 7 (implementation done → complete the phase)
21
21
 
22
+ Crossing any step gate above also updates the feature's `_index.md` Resume Pointer cursor (Active Workflow / Current Step / Gates Passed / Awaiting) — fold it into that gate's existing `_index.md` / Resume Pointer edit, no separate ceremony (see the `_index.md` template's Resume Pointer notes).
23
+
22
24
  All other step transitions are **step-internal**: announce "Step N complete,
23
25
  entering Step N+1" and proceed without waiting. See AI-AGENT-GUIDE.md § Workflow
24
26
  Transparency for the full transparency protocol and confirmation signals.
@@ -66,6 +68,11 @@ AI must locate the target feature and load its current state:
66
68
  - Cross-reference the bounded context's `dflow/specs/domain/{context}/rules.md`
67
69
  and `behavior.md` if the new phase is likely to touch system-level
68
70
  state (BC-level current state lives there, not in `_index.md`)
71
+ - Run the in-flight overlap scan (classification and dedup rules in
72
+ `AI-AGENT-GUIDE.md` § Status / Control Commands): list other unfinished
73
+ features in `active/` and any feature / bugfix branches whose work is
74
+ not visible on this branch — if the incoming phase scope overlaps one
75
+ of them, surface it before writing the phase-spec.
69
76
 
70
77
  4. **Branch gate — ensure you are on this feature's branch (before any commit)**
71
78
 
@@ -99,6 +106,10 @@ Walk the developer through what the new phase covers:
99
106
  at the depth set by the BC's Subdomain Type (see
100
107
  `references/ddd-modeling-guide.md` § Subdomain-Aware Modeling Depth) — don't
101
108
  bypass the classification just because this is a phase, not a new feature.
109
+ If the phase **extends an existing Aggregate**, apply the established-model
110
+ re-read from `references/ddd-modeling-guide.md` § "Revising an Established
111
+ Model" (match recorded re-evaluation conditions; record proceed / split /
112
+ rename in the phase-spec).
102
113
  4. **Cross-context impact?** Does this phase introduce / change Domain
103
114
  Events that other contexts consume? (If yes, plan for `context-map.md`
104
115
  updates at finish-feature time.)