dflow-sdd-ddd 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +759 -0
  2. package/CONTRIBUTING.md +10 -1
  3. package/README.en.md +155 -210
  4. package/README.md +88 -145
  5. package/TEMPLATE-COVERAGE.md +13 -6
  6. package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
  7. package/bin/dflow.js +15 -4
  8. package/docs/commands.en.md +110 -0
  9. package/docs/commands.md +101 -0
  10. package/docs/doctor-uncertainty.en.md +212 -0
  11. package/docs/doctor-uncertainty.md +212 -0
  12. package/docs/evaluating-dflow.en.md +29 -11
  13. package/docs/evaluating-dflow.md +8 -6
  14. package/docs/release-versioning-policy.md +5 -0
  15. package/docs/upgrading.en.md +196 -0
  16. package/docs/upgrading.md +197 -0
  17. package/docs/using-with-claude-code.en.md +13 -4
  18. package/docs/using-with-claude-code.md +13 -4
  19. package/docs/using-with-codex.en.md +13 -4
  20. package/docs/using-with-codex.md +13 -4
  21. package/docs/using-with-github-copilot.en.md +13 -4
  22. package/docs/using-with-github-copilot.md +13 -4
  23. package/lib/doc-shapes.json +997 -0
  24. package/lib/doctor-checks.js +2500 -24
  25. package/lib/init.js +2846 -143
  26. package/lib/render-diagrams.js +1474 -0
  27. package/lib/render.js +865 -49
  28. package/package.json +2 -2
  29. package/templates/brownfield/references/drift-verification.md +4 -0
  30. package/templates/brownfield/references/finish-feature-flow.md +635 -88
  31. package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
  32. package/templates/brownfield/references/finish-feature-minimal-host.md +406 -0
  33. package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  34. package/templates/brownfield/references/git-integration.md +160 -15
  35. package/templates/brownfield/references/init-project-flow.md +23 -3
  36. package/templates/brownfield/references/modify-existing-flow.md +412 -87
  37. package/templates/brownfield/references/modify-existing-follow-up.md +121 -0
  38. package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  39. package/templates/brownfield/references/new-feature-flow.md +61 -6
  40. package/templates/brownfield/references/new-phase-flow.md +57 -7
  41. package/templates/brownfield/references/pr-review-checklist.md +303 -10
  42. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +151 -34
  43. package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
  44. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +75 -6
  45. package/templates/brownfield/scaffolding/Git-principles-trunk.md +82 -8
  46. package/templates/brownfield/scaffolding/_conventions.md +50 -28
  47. package/templates/brownfield/scaffolding/_overview.md +1 -0
  48. package/templates/brownfield/templates/_index.md +151 -7
  49. package/templates/brownfield/templates/analysis.md +79 -0
  50. package/templates/brownfield/templates/behavior.md +1 -0
  51. package/templates/brownfield/templates/context-definition.md +1 -0
  52. package/templates/brownfield/templates/context-map.md +2 -1
  53. package/templates/brownfield/templates/glossary.md +1 -0
  54. package/templates/brownfield/templates/lightweight-spec.md +154 -11
  55. package/templates/brownfield/templates/models.md +1 -0
  56. package/templates/brownfield/templates/phase-spec.md +9 -1
  57. package/templates/brownfield/templates/rules.md +1 -0
  58. package/templates/brownfield/templates/tech-debt.md +1 -0
  59. package/templates/common/references/ddd-modeling-guide.md +33 -16
  60. package/templates/{greenfield → common}/references/dflow-feedback-flow.md +2 -1
  61. package/templates/common/references/flow-rationale-registry.md +130 -0
  62. package/templates/common/skill/SKILL.md +13 -11
  63. package/templates/greenfield/references/drift-verification.md +4 -0
  64. package/templates/greenfield/references/finish-feature-flow.md +625 -89
  65. package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
  66. package/templates/greenfield/references/finish-feature-minimal-host.md +363 -0
  67. package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  68. package/templates/greenfield/references/git-integration.md +148 -15
  69. package/templates/greenfield/references/init-project-flow.md +23 -3
  70. package/templates/greenfield/references/modify-existing-flow.md +378 -85
  71. package/templates/greenfield/references/modify-existing-follow-up.md +103 -0
  72. package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  73. package/templates/greenfield/references/new-feature-flow.md +67 -4
  74. package/templates/greenfield/references/new-phase-flow.md +56 -7
  75. package/templates/greenfield/references/pr-review-checklist.md +287 -8
  76. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +146 -32
  77. package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
  78. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +74 -6
  79. package/templates/greenfield/scaffolding/Git-principles-trunk.md +88 -12
  80. package/templates/greenfield/scaffolding/_conventions.md +50 -28
  81. package/templates/greenfield/scaffolding/_overview.md +6 -2
  82. package/templates/greenfield/templates/_index.md +137 -7
  83. package/templates/greenfield/templates/aggregate-design.md +1 -0
  84. package/templates/greenfield/templates/analysis.md +79 -0
  85. package/templates/greenfield/templates/behavior.md +1 -0
  86. package/templates/greenfield/templates/context-definition.md +1 -0
  87. package/templates/greenfield/templates/context-map.md +2 -1
  88. package/templates/greenfield/templates/events.md +4 -1
  89. package/templates/greenfield/templates/glossary.md +1 -0
  90. package/templates/greenfield/templates/lightweight-spec.md +154 -11
  91. package/templates/greenfield/templates/models.md +1 -0
  92. package/templates/greenfield/templates/phase-spec.md +9 -1
  93. package/templates/greenfield/templates/rules.md +1 -0
  94. package/templates/greenfield/templates/tech-debt.md +1 -0
  95. package/templates/brownfield/references/dflow-feedback-flow.md +0 -251
@@ -114,7 +114,7 @@ AI 的完整決策樹、Workflow Transparency、Ceremony Scaling 三層判準
114
114
  - `/dflow:new-feature` — 新功能開發
115
115
  - `/dflow:new-phase` — 在 active feature 內新增階段
116
116
  - `/dflow:modify-existing` — 修改既有功能
117
- - `/dflow:bug-fix` — 輕量修復
117
+ - `/dflow:bug-fix` — Bug 修復
118
118
  - `/dflow:finish-feature` — feature 收尾
119
119
  - `/dflow:pr-review` — PR 審查
120
120
  - `/dflow:verify` — rules.md ↔ behavior.md 漂移檢查
@@ -17,6 +17,8 @@ trunk-based, GitHub Flow), use `Git-principles-trunk.md` instead.
17
17
 
18
18
  ---
19
19
 
20
+ <!-- dflow-generated: git-principles-canonical START -->
21
+
20
22
  ## 1. Branch Structure
21
23
 
22
24
  | Branch | Naming | Cut from | Merges to |
@@ -24,6 +26,7 @@ trunk-based, GitHub Flow), use `Git-principles-trunk.md` instead.
24
26
  | `main` / `master` | `main` | — | release / hotfix only |
25
27
  | `develop` | `develop` | — | integration branch for features |
26
28
  | feature | `feature/{SPEC-ID}-{slug}` | `develop` | `develop` |
29
+ | bugfix | `bugfix/{BUG-ID}-{slug}` | `develop` | `develop` |
27
30
  | release | `release/{version}` | `develop` | `main` + `develop` |
28
31
  | hotfix | `hotfix/{version}-hotfix{n}` | `main` | `main` + `develop` |
29
32
 
@@ -31,6 +34,17 @@ The `feature/{SPEC-ID}-{slug}` pattern is a **Dflow requirement** (not
31
34
  a Git Flow requirement). It ties each feature branch to its
32
35
  corresponding `dflow/specs/features/active/{SPEC-ID}-{slug}/` directory.
33
36
 
37
+ A **`bugfix/{BUG-ID}-{slug}`** branch is the same shape as a feature branch and
38
+ follows the same topology — cut from the same base, pushed the same way,
39
+ integrated the same way, and **closed out the same way**. It is used when a
40
+ defect owns its own host rather than being picked up by a feature already in
41
+ flight. Its host is a **minimal (zero-phase) host**, so it carries no
42
+ phase-spec: the SPEC-ID lives in the directory name while the branch name
43
+ carries the BUG-NUMBER, and the host `_index.md` `branch:` field is
44
+ authoritative for both. Closeout is not optional because the branch prefix
45
+ differs — `/dflow:finish-feature` runs and the directory is archived to
46
+ `completed/` before the branch merges, exactly as for `feature/`.
47
+
34
48
  ### Feature Branch Workflow
35
49
 
36
50
  ```bash
@@ -107,10 +121,18 @@ git branch -d hotfix/{version}-hotfix{n}
107
121
  ```
108
122
 
109
123
  **Hotfix spec requirement (team convention)**: Hotfixes often skip the
110
- upfront SDD cycle for speed. This project commits to writing a
111
- lightweight spec within **24 hours** after the hotfix lands, documenting
112
- root cause + fix + (if applicable) a tech-debt entry in
113
- `dflow/specs/migration/tech-debt.md` if the bug reveals a systemic issue.
124
+ upfront SDD cycle for speed. This project commits to documenting the fix
125
+ within **24 hours** after it lands. Run `/dflow:modify-existing` in
126
+ **post-hoc mode** (`references/modify-existing-flow.md` Step 1.8): it opens a
127
+ minimal host of its own for the fix, records the implementation checkpoint as
128
+ `reconciled ({merged-hotfix-hash})`, and reconciles rather than re-running work
129
+ that is already on the mainline.
130
+ What gets written is whatever the cascade's tier calls for — a **T2** lands a
131
+ lightweight spec (root cause + fix + a `dflow/specs/migration/tech-debt.md` entry
132
+ if the bug reveals a systemic issue); a **T3** lands one `_index.md` Lightweight
133
+ Changes row and no spec file. Step 1.8 admits **T2 / T3 only**: a **T1**
134
+ post-hoc keeps the normal phase-bearing route and documents the merged work
135
+ there.
114
136
  This is a **human-to-human commitment** — Dflow / AI cannot track the
115
137
  24-hour clock; the team enforces it in retros.
116
138
 
@@ -156,7 +178,10 @@ before making key Git operations:
156
178
  justification in the spec's notes section)
157
179
  - [ ] `_index.md` status reflects the current work (Phase Specs row
158
180
  updated, `Resume Pointer` refreshed if the commit reaches a meaningful
159
- checkpoint)
181
+ checkpoint). A **minimal (zero-phase) host** has no Phase Specs row to
182
+ update — its record is the Lightweight Changes row, and that row must
183
+ already be written **before** this commit, not after it. Refresh the
184
+ Resume Pointer as usual.
160
185
 
161
186
  ### Before merging a feature branch to `develop`
162
187
 
@@ -170,6 +195,23 @@ before making key Git operations:
170
195
  - [ ] `dflow/specs/migration/tech-debt.md` updated with any debt discovered
171
196
  - [ ] Domain layer (`src/Domain/`) has no delivery-framework references
172
197
 
198
+ > **Minimal (zero-phase) host.** The items about this host's own record —
199
+ > `/dflow:finish-feature` having run, `_index.md` status `completed`, and the
200
+ > archival move — apply **unchanged**: they are what stops a host merging with
201
+ > its record still open. Every item naming a **Domain or bounded-context
202
+ > artifact** applies only to what this change **actually touched**: a **no-BC**
203
+ > host has no context to sync or document, a **T3** does no Domain work at all,
204
+ > and a **baseline capture** wrote Domain documents directly rather than syncing
205
+ > to them — so for those they read N/A. **Bounded-context-scoped only** —
206
+ > `glossary.md` and `migration/tech-debt.md` belong to no bounded context, stay
207
+ > in a no-BC host's sweep, and are a baseline capture's own capture
208
+ > destinations, so they are **not** N/A for either. **Code invariants are not
209
+ > artifacts** — the items here that state a rule about the **source** rather
210
+ > than name a document to update are **never N/A**: they hold for any change
211
+ > that touches code at all, and a host wrongly claiming to be no-BC is exactly
212
+ > what they catch. Record the N/A; do not create a context, a Domain document,
213
+ > or a BR row to tick a box.
214
+
173
215
  ---
174
216
 
175
217
  ## 4. Integration Commit Message Conventions
@@ -200,6 +242,31 @@ Related BR-IDs:
200
242
  Related SPEC-IDs: {SPEC-ID}{, follow-up SPEC-IDs if any}
201
243
  ```
202
244
 
245
+ **Zero-phase (minimal host) form.** A minimal host has no phases and may have
246
+ no bounded context, so its Change Scope block carries the values that are
247
+ actually true rather than padded ones:
248
+
249
+ ```
250
+ Change Scope:
251
+ - BC: none # or the real context, when the change has one
252
+ - Phase Count: 0
253
+ - Lightweight Changes: 1 T2 + 0 T3 # at least one row, always
254
+
255
+ Related BR-IDs: {empty, or the per-family no-BR marker this change carries}
256
+ ```
257
+
258
+ `none` and `0` are the honest values here, not placeholders waiting to be
259
+ filled. **`Related BR-IDs` is the exception — never force it to `none`.**
260
+ It reports what this change's own record carries, not what was synced, so it
261
+ takes the same values a BC-bearing host would: empty, or the per-family no-BR
262
+ marker when the T2 carries one. Writing `none` there erases the marker the
263
+ zero-phase shape requires (`references/finish-feature-flow.md` Step 5 states
264
+ the rule, and a baseline host leaves the field empty). A minimal host that genuinely touches a bounded context reports that
265
+ context and its real BR delta exactly as a phase-bearing feature would — the
266
+ zero is the **phase count**, not the significance. A **baseline capture** is
267
+ the one variant that always reports a real `BC:` — capturing it is the whole
268
+ point of the host.
269
+
203
270
  ### Concrete example
204
271
 
205
272
  ```bash
@@ -252,7 +319,7 @@ release.
252
319
  Example entry:
253
320
 
254
321
  ```markdown
255
- ## [1.2.3] — {YYYY-MM-DD}
322
+ ## [1.2.3] — {2026-04-21}
256
323
 
257
324
  ### Added
258
325
  - {SPEC-20260421-001}: JPY currency support in Money value object
@@ -266,6 +333,8 @@ Example entry:
266
333
 
267
334
  ---
268
335
 
336
+ <!-- dflow-generated: git-principles-canonical END -->
337
+
269
338
  ## 6. AI Collaboration Rules (Project Policy)
270
339
 
271
340
  Three categories:
@@ -22,6 +22,8 @@ and long-lived release branches.
22
22
 
23
23
  ---
24
24
 
25
+ <!-- dflow-generated: git-principles-canonical START -->
26
+
25
27
  ## 1. Branch Structure
26
28
 
27
29
  | Branch | Naming | Cut from | Merges to |
@@ -35,6 +37,17 @@ happens on short-lived feature / bugfix branches cut from `main` and
35
37
  merged back into `main` quickly (ideally within a day, always
36
38
  within a week).
37
39
 
40
+ A **`bugfix/{BUG-ID}-{slug}`** branch is the same shape as a feature branch and
41
+ follows the same topology — cut from the same base, pushed the same way,
42
+ integrated the same way, and **closed out the same way**. It is used when a
43
+ defect owns its own host rather than being picked up by a feature already in
44
+ flight. Its host is a **minimal (zero-phase) host**, so it carries no
45
+ phase-spec: the SPEC-ID lives in the directory name while the branch name
46
+ carries the BUG-NUMBER, and the host `_index.md` `branch:` field is
47
+ authoritative for both. Closeout is not optional because the branch prefix
48
+ differs — `/dflow:finish-feature` runs and the directory is archived to
49
+ `completed/` before the branch merges, exactly as for `feature/`.
50
+
38
51
  The `feature/{SPEC-ID}-{slug}` pattern is a **Dflow requirement** (not
39
52
  a trunk-based requirement). It ties each feature branch to its
40
53
  corresponding `dflow/specs/features/active/{SPEC-ID}-{slug}/` directory.
@@ -76,8 +89,8 @@ Commits must tie back to a SPEC-ID:
76
89
 
77
90
  ### Conventional Commits style (recommended, optional)
78
91
 
79
- This project {does / does not} require Conventional Commits. When
80
- adopted, the format is:
92
+ Conventional Commits is optional. **Record whether this project requires it
93
+ in § 6, under "Commit and merge choices".** When adopted, the format is:
81
94
 
82
95
  ```
83
96
  {type}({scope}): {short description}
@@ -103,9 +116,9 @@ Example: `feat(expense): add JPY currency support` with
103
116
  ## 3. Merge Strategy (Project Chooses)
104
117
 
105
118
  Trunk-based strategies typically pick **one** merge style and stick to
106
- it. This project uses: **{squash | rebase | fast-forward}**. Delete
107
- the two unused options once decided, or keep all three in the table
108
- and circle the chosen one.
119
+ it. The table below gives the trade-offs; **record this project's choice
120
+ in § 6, under "Commit and merge choices"** — that section is yours, this
121
+ one is not.
109
122
 
110
123
  | Strategy | How the feature lands on `main` | When to prefer |
111
124
  |----------|--------------------------------|----------------|
@@ -120,7 +133,10 @@ and circle the chosen one.
120
133
  justification in the spec's notes section)
121
134
  - [ ] `_index.md` status reflects the current work (Phase Specs row
122
135
  updated, `Resume Pointer` refreshed if the commit reaches a meaningful
123
- checkpoint)
136
+ checkpoint). A **minimal (zero-phase) host** has no Phase Specs row to
137
+ update — its record is the Lightweight Changes row, and that row must
138
+ already be written **before** this commit, not after it. Refresh the
139
+ Resume Pointer as usual.
124
140
 
125
141
  ### Gate Checks — Before merging a feature PR to `main`
126
142
 
@@ -136,6 +152,23 @@ and circle the chosen one.
136
152
  - [ ] CI green
137
153
  - [ ] PR has at least one review approval
138
154
 
155
+ > **Minimal (zero-phase) host.** The items about this host's own record —
156
+ > `/dflow:finish-feature` having run, `_index.md` status `completed`, and the
157
+ > archival move — apply **unchanged**: they are what stops a host merging with
158
+ > its record still open. Every item naming a **Domain or bounded-context
159
+ > artifact** applies only to what this change **actually touched**: a **no-BC**
160
+ > host has no context to sync or document, a **T3** does no Domain work at all,
161
+ > and a **baseline capture** wrote Domain documents directly rather than syncing
162
+ > to them — so for those they read N/A. **Bounded-context-scoped only** —
163
+ > `glossary.md` and `migration/tech-debt.md` belong to no bounded context, stay
164
+ > in a no-BC host's sweep, and are a baseline capture's own capture
165
+ > destinations, so they are **not** N/A for either. **Code invariants are not
166
+ > artifacts** — the items here that state a rule about the **source** rather
167
+ > than name a document to update are **never N/A**: they hold for any change
168
+ > that touches code at all, and a host wrongly claiming to be no-BC is exactly
169
+ > what they catch. Record the N/A; do not create a context, a Domain document,
170
+ > or a BR row to tick a box.
171
+
139
172
  ---
140
173
 
141
174
  ## 4. Integration Commit Message Conventions
@@ -170,6 +203,31 @@ Related BR-IDs:
170
203
  Related SPEC-IDs: {SPEC-ID}{, follow-up SPEC-IDs if any}
171
204
  ```
172
205
 
206
+ **Zero-phase (minimal host) form.** A minimal host has no phases and may have
207
+ no bounded context, so its Change Scope block carries the values that are
208
+ actually true rather than padded ones:
209
+
210
+ ```
211
+ Change Scope:
212
+ - BC: none # or the real context, when the change has one
213
+ - Phase Count: 0
214
+ - Lightweight Changes: 1 T2 + 0 T3 # at least one row, always
215
+
216
+ Related BR-IDs: {empty, or the per-family no-BR marker this change carries}
217
+ ```
218
+
219
+ `none` and `0` are the honest values here, not placeholders waiting to be
220
+ filled. **`Related BR-IDs` is the exception — never force it to `none`.**
221
+ It reports what this change's own record carries, not what was synced, so it
222
+ takes the same values a BC-bearing host would: empty, or the per-family no-BR
223
+ marker when the T2 carries one. Writing `none` there erases the marker the
224
+ zero-phase shape requires (`references/finish-feature-flow.md` Step 5 states
225
+ the rule, and a baseline host leaves the field empty). A minimal host that genuinely touches a bounded context reports that
226
+ context and its real BR delta exactly as a phase-bearing feature would — the
227
+ zero is the **phase count**, not the significance. A **baseline capture** is
228
+ the one variant that always reports a real `BC:` — capturing it is the whole
229
+ point of the host.
230
+
173
231
  Example:
174
232
 
175
233
  ```
@@ -208,11 +266,17 @@ losing the feature-level summary.
208
266
 
209
267
  ### 4.3 Fast-forward (feature has exactly 1 commit)
210
268
 
211
- If the entire feature was a single commit (common for T2 / T3-only
212
- features or minimal features), fast-forward is fine. Use the commit
269
+ If the entire feature was a single commit (a hosted T2 / T3 whose work and
270
+ record landed together), fast-forward is fine. Use the commit
213
271
  message format from § 4.1 (squash) for that single commit — it already
214
272
  reads as the feature's single narrative.
215
273
 
274
+ **A minimal (zero-phase) host is never this case.** It records **exactly two**
275
+ commits — the implementation (or `spec-baseline`) checkpoint, then the closeout
276
+ commit that carries the archival `git mv` — so it does not arrive here with
277
+ one. A minimal host showing a single commit has not closed out: run
278
+ `/dflow:finish-feature` first rather than fast-forwarding it as it stands.
279
+
216
280
  ```bash
217
281
  git checkout main
218
282
  git pull --ff-only origin main
@@ -244,6 +308,8 @@ style.
244
308
 
245
309
  ---
246
310
 
311
+ <!-- dflow-generated: git-principles-canonical END -->
312
+
247
313
  ## 6. AI Collaboration Rules (Project Policy)
248
314
 
249
315
  Three categories:
@@ -292,6 +358,14 @@ can always decline. If your team also wants vendor attribution, appending the
292
358
  assistant's documented line (e.g. `Co-Authored-By: Claude
293
359
  <noreply@anthropic.com>`) is an independent, optional convention on top.
294
360
 
361
+ ### Commit and merge choices
362
+
363
+ Dflow does not choose these for you; §§ 2-3 give the trade-offs. Record what
364
+ this project settled on:
365
+
366
+ - **Conventional Commits**: {required / not required — fill in}
367
+ - **Merge strategy**: {squash / rebase / fast-forward — fill in}
368
+
295
369
  ---
296
370
 
297
371
  ## 7. CI / CD
@@ -30,25 +30,6 @@ dflow/specs/features/active/{SPEC-ID}-{slug}/
30
30
  T3 Trivial changes do **not** produce a separate file — they are
31
31
  recorded as one row in `_index.md` Lightweight Changes.
32
32
 
33
- ## Prose Language
34
-
35
- Project prose language: `{prose-language}`
36
-
37
- Dflow templates keep canonical English structural language: headings,
38
- table headers, fixed labels, placeholders, IDs, anchors, and code-facing
39
- terms remain English.
40
-
41
- Free prose written inside those sections should follow the project prose
42
- language:
43
-
44
- - `en`: write free prose in English.
45
- - `zh-TW`: write free prose in Traditional Chinese.
46
- - `{xx-XX}`: write free prose in that explicit BCP-47 language.
47
-
48
- Do not translate code identifiers, DDD pattern names, BR IDs, SPEC IDs,
49
- file paths, branch names, anchors, or inline code only to satisfy the
50
- prose-language setting.
51
-
52
33
  ### SPEC-ID Format
53
34
 
54
35
  - Pattern: `SPEC-YYYYMMDD-NNN` (e.g. `SPEC-20260421-001`)
@@ -56,6 +37,15 @@ prose-language setting.
56
37
  - Once assigned, the SPEC-ID is immutable — it appears in the feature
57
38
  directory name, the first phase-spec filename, and the git branch
58
39
  name (see `Git-principles-*.md`)
40
+ - **Minimal (zero-phase) host exception.** A host that records a small
41
+ standalone or follow-up change — or a baseline capture — carries **no
42
+ phase-spec**, so there is no phase-spec filename for the SPEC-ID to appear
43
+ in; the directory name and the branch carry it. A **functional bug** host
44
+ goes one step further: its branch is `bugfix/BUG-{NUMBER}-{slug}`, so it
45
+ carries the BUG-NUMBER and not the SPEC-ID at all. In every case the host
46
+ `_index.md` `branch:` field is authoritative, and the SPEC-ID itself stays
47
+ immutable. Do not create a phase-spec, or rename a branch, to make the
48
+ three-name rule above hold.
59
49
 
60
50
  ### Slug Conventions (Project-Specific Fill-In)
61
51
 
@@ -69,6 +59,25 @@ prose-language setting.
69
59
  - **Length target**: 2–4 English words or 2–6 Chinese characters
70
60
  (Dflow skill guidance)
71
61
 
62
+ ## Prose Language
63
+
64
+ Project prose language: `{prose-language}`
65
+
66
+ Dflow templates keep canonical English structural language: headings,
67
+ table headers, fixed labels, placeholders, IDs, anchors, and code-facing
68
+ terms remain English.
69
+
70
+ Free prose written inside those sections should follow the project prose
71
+ language:
72
+
73
+ - `en`: write free prose in English.
74
+ - `zh-TW`: write free prose in Traditional Chinese.
75
+ - `{xx-XX}`: write free prose in that explicit BCP-47 language.
76
+
77
+ Do not translate code identifiers, DDD pattern names, BR IDs, SPEC IDs,
78
+ file paths, branch names, anchors, or inline code only to satisfy the
79
+ prose-language setting.
80
+
72
81
  ---
73
82
 
74
83
  ## Filling the Templates
@@ -79,11 +88,14 @@ Dflow ships these templates (do **not** re-inline their content here
79
88
  | Template | Used when |
80
89
  |----------|-----------|
81
90
  | `templates/_index.md` | Creating a feature directory (every feature) |
82
- | `templates/phase-spec.md` | T1 Heavy — new feature / new phase / architectural change |
83
- | `templates/lightweight-spec.md`| T2 Light — bug fix / small tweak with BR Delta |
91
+ | `templates/phase-spec.md` | T1 Heavy |
92
+ | `templates/lightweight-spec.md`| T2 Light — classic BR-delta form, or one of the no-BR family variants (presentation, non-breaking contract, operational / security, performance, implementation defect, intentional change) |
84
93
  | `templates/context-definition.md` | When a new Bounded Context is introduced |
85
94
  | `templates/behavior.md` | BC-level consolidated behavior spec |
86
95
 
96
+ Which tier applies is decided by the cascade in `AI-AGENT-GUIDE.md` § Ceremony
97
+ Scaling, never by this table — these rows only say which template a tier uses.
98
+
87
99
  Project-specific guidance when filling these templates:
88
100
 
89
101
  - {e.g. "Always reference existing BRs in the BR Snapshot inherited
@@ -99,20 +111,30 @@ Project-specific guidance when filling these templates:
99
111
 
100
112
  ## Ceremony Scaling (Project Application)
101
113
 
102
- Dflow defines three tiers — **T1 Heavy / T2 Light / T3
103
- Trivial**. See `AI-AGENT-GUIDE.md` § Ceremony Scaling for the full
104
- criteria table. We do not re-define the tier criteria here; this
114
+ Dflow defines three tiers — **T1 Heavy / T2 Light / T3 Trivial** — plus a
115
+ below-workflow level. See `AI-AGENT-GUIDE.md` § Ceremony Scaling for the
116
+ full ordered cascade. We do not re-define the tier criteria here; this
105
117
  section records how *this* project applies them in borderline
106
118
  situations.
107
119
 
120
+ **The cascade result is a floor: rows in this section may only escalate a
121
+ tier, never lower it.** A row may take a change Dflow would call T2 and
122
+ make it T1 for this project; no row may lower a T1, and no row may move a
123
+ tracked change below workflow.
124
+
125
+ Write `T<n> (project convention)` in the Tier column when this project raises the
126
+ tier, and `cascade result` when it adds an obligation but no tier change. Do not
127
+ write a bare tier — that restates the cascade instead of recording a decision,
128
+ and it is how the two drift apart.
129
+
108
130
  | Situation (project-specific) | Tier we default to | Why |
109
131
  |------------------------------|--------------------|-----|
110
- | {e.g. Year-end reporting tweak without BR change} | T2 | Touches logic path even though no new BR; extract to `lightweight-spec.md` for trace |
111
- | {e.g. Pure label / display text translation} | T3 | No BR change; inline row in `_index.md` |
112
- | {e.g. UI refresh across multiple entrypoints} | T1 (project convention) | We treat multi-entrypoint UI/API refresh as T1 for this project even though Dflow default would be T2, because these changes often leak into business logic embedded in delivery/entrypoint code (presentation/UI layer, controllers, handlers, jobs, message consumers, data pipelines, or stored procedures) |
132
+ | {e.g. Year-end reporting tweak without BR change} | cascade result + a `lightweight-spec.md` trace | No tier change — we want the trace even when the cascade would not require a spec file, because the reporting logic path is where our defects hide |
133
+ | {e.g. Pure label / display text wording polish on one screen} | cascade result + note in the extraction log | No tier change — we add the note so the delivery-layer sweep can find it later |
134
+ | {e.g. UI refresh across multiple entrypoints} | T1 (project convention) | We escalate multi-entrypoint UI/API refresh above whatever the cascade returns, because these changes often leak into business logic embedded in delivery/entrypoint code (presentation/UI layer, controllers, handlers, jobs, message consumers, data pipelines, or stored procedures) |
113
135
 
114
136
  If the team disagrees on tier classification for a specific change,
115
- run through the T3 four-criteria checklist (in `AI-AGENT-GUIDE.md` §
137
+ run through the ordered cascade (in `AI-AGENT-GUIDE.md` §
116
138
  Ceremony Scaling) and record the decision here the first time it arises.
117
139
 
118
140
  ---
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/_overview.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
 
3
4
  # System Overview — {System Name}
@@ -6,6 +6,7 @@ created: {YYYY-MM-DD}
6
6
  branch: feature/{SPEC-ID}-{slug}
7
7
  # follow-up-of: {原 SPEC-ID} # 選用:本 feature 為某個已 completed feature 的 follow-up 時填入
8
8
  ---
9
+ <!-- dflow-shape: brownfield/_index.md 1 — keep this line: dflow doctor reads it -->
9
10
 
10
11
  <!--
11
12
  Template note (for AI):
@@ -36,7 +37,21 @@ Template note (for AI):
36
37
  Minimal usage:
37
38
  For a 1-commit / 1-phase feature this template can be ~30 lines —
38
39
  fill metadata + a short Goals & Scope + one row in Phase Specs +
39
- initial BR Snapshot + Resume Pointer. The other sections can stay empty.
40
+ Resume Pointer, and the BR Snapshot **only when this host carries a BR
41
+ delta** — a phase-bearing host whose phase-specs establish none leaves it
42
+ empty, and that section states the rule. The other sections can stay empty.
43
+
44
+ Minimal HOST usage (zero-phase) — a different shape, not a smaller one:
45
+ A minimal host (references/modify-existing-flow.md Step 1.7 standalone, or
46
+ its Step 1.6 follow-up minimal variant, or Step 1.8's post-hoc hotfix, whose
47
+ linkage resolves to one of those two) carries NO phase-spec, and its
48
+ Phase Specs table stays EMPTY. Its record lives in Lightweight Changes
49
+ instead — at least one row, written before the first commit. Do NOT add a
50
+ Phase Specs row or create a phase-spec to make the "1-phase" wording above
51
+ fit: a host that carries a phase is certified as phase-bearing whatever it
52
+ was intended to be, and closeout then checks it as one
53
+ (references/finish-feature-flow.md Step 1). A baseline capture uses this
54
+ same zero-phase shape, with a `Tier = baseline` row.
40
55
  -->
41
56
 
42
57
  <!-- 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. -->
@@ -71,24 +86,103 @@ Template note (for AI):
71
86
  > 歷史由各 phase-spec 的「Delta from prior phases」段串接閱讀;feature
72
87
  > 完成時 `/dflow:finish-feature` 把本表推進到對應 BC 的 `rules.md` /
73
88
  > `behavior.md`(延續 Step 8.3 既有 sync 機制)。
89
+ >
90
+ > ⚠ **這張表在範本裡是空的(只有表頭),那是對的 —— 不要為了讓它看起來有東西而造一列。**
91
+ > 只有這個 host 真的帶著 BR delta 時才會有列(`/dflow:new-feature` 會拿第一個 phase
92
+ > 規劃中的 BR 初始化它;phase-specs 沒有帶出 BR delta 的 host 就留空);**BC-bearing
93
+ > 的 follow-up host** 另外會從 `rules.md` 繼承 in-scope 的列
94
+ > (見 references/modify-existing-follow-up.md)。
95
+ > ⚠ **`Tier = baseline` 的 capture host 是例外:本表留空,而且不繼承** —— 即使它是
96
+ > BC-bearing 的 follow-up 也一樣(見 references/modify-existing-follow-up.md 的
97
+ > baseline 例外,與 references/finish-feature-minimal-host.md 的 baseline 檢查)。
74
98
 
75
99
  | BR-ID | Current Rule | First Seen (phase) | Last Updated (phase) | Status |
76
100
  |---|---|---|---|---|
101
+
102
+ <!--
103
+ 欄位範例(**這不是資料,是給 AI 看的範例**;它在註解裡,所以不會被當成本表的列):
104
+
77
105
  | BR-01 | {規則描述} | phase-1 / inherited from rules.md | phase-N | active / removed |
78
106
 
107
+ ⚠ 本區塊不是規範。`First Seen` / `Last Updated` / `Status` 的權威語義在既有
108
+ flow 規則裡:
109
+ `First Seen` 的 `inherited from rules.md` 見 references/modify-existing-follow-up.md;
110
+ `Status` 的 `active` / `removed` 見 references/finish-feature-flow.md Step 3 與
111
+ references/new-phase-flow.md。有衝突時以那些檔為準。
112
+ -->
113
+
79
114
  <!-- dflow:section lightweight-changes -->
80
115
  ## Lightweight Changes
81
116
 
82
117
  > T2 行:描述含「見 `lightweight-{date}-{slug}.md`」外連
83
118
  > T3 行:inline 完整描述一句話 + 標籤(如 `[cosmetic]` / `[text]` /
84
- > `[format]`);T3 不產獨立 spec 檔
119
+ > `[appearance]`);T3 不產獨立 spec 檔
120
+ >
121
+ > **minimal(zero-phase)host 的每一列都要能指出這次變更碰到的原始碼路徑**
122
+ > (寫到能和一個 commit 比對的粒度即可,不是貼 diff)。T2 由它的
123
+ > lightweight-spec `## Implementation Paths` 段承載,row 只要外連過去;**T3 沒有
124
+ > spec 檔,路徑就直接寫在 Description 裡**,接在一句話描述與標籤後面。
125
+ > `/dflow:finish-feature` 會拿 checkpoint 1 的 diff 和這些路徑比對;
126
+ > **一個路徑都沒宣告會擋下 closeout**,不會當成通過。
127
+ > **掛在 phase-bearing feature 底下的列不受此限**——closeout 對它們不跑這項檢查
128
+ > (`references/finish-feature-flow.md` Step 1 明寫此例外,理由是 hosted row 本來
129
+ > 就沒有被要求宣告路徑)。寫上去仍是好習慣,但那裡沒有 gate、也不會擋。
130
+ > baseline capture 的 `Tier = baseline` 列同理,寫它實際寫入的 BC 層文件或 `domain/analysis.md` 的路徑。
131
+ >
132
+ > **post-hoc hotfix 的 T3 列**(見 references/modify-existing-flow.md Step 1.8):
133
+ > Description 另外標明這是 hotfix,並寫出識別依據(PR/incident/tracker 編號)
134
+ > ——沒有依據的 hash 會擋下 closeout。此時路徑指的是**已合併的那個 hotfix**
135
+ > 碰到的檔案;本列 `Commit` 欄填的是**補文件那個 commit** 的 hash,不是 hotfix
136
+ > 的 hash(後者記在 Checkpoint Log 的 `reconciled (...)` 列)。兩者來源不同,
137
+ > 不可互填。
85
138
  >
86
- > Tier 判準見 AI-AGENT-GUIDE.md § Ceremony Scaling 三層表。
139
+ > Tier 判準見 AI-AGENT-GUIDE.md § Ceremony Scaling 的 ordered cascade(步驟 0–4,先命中者勝)。
87
140
 
88
141
  | Date | Tier | Description | Commit |
89
142
  |---|---|---|---|
90
143
  | {YYYY-MM-DD} | T2 | bug fix XYZ — 見 [`lightweight-{date}-{slug}.md`](./lightweight-{date}-{slug}.md) | {hash} |
91
- | {YYYY-MM-DD} | T3 | 按鈕顏色從藍改綠 `[cosmetic]` | {hash} |
144
+ | {YYYY-MM-DD} | T3 | 按鈕顏色從藍改綠 `[cosmetic]` — `src/Web/Checkout/PayButton.cs` | {hash} |
145
+
146
+ > ⚠ **`Commit` 還沒填就留空 —— 不要寫佔位字串。** 上面的 `{hash}` 是**範本佔位符**
147
+ > (代表「這裡放一個 commit hash」),不是可以留在真實 `_index.md` 裡的值。
148
+ > 自己發明 `{pending}`、`(待 commit)` 這類字樣會讓那一格變成**非空**,而
149
+ > **凡是照「空/非空」判的規則都會把它讀成已經填好**。在 phase-bearing host 上更徹底:
150
+ > `_index.md` 對 checkpoint 1 的那些證據檢查全都標明「minimal host(zero-phase)限定」,
151
+ > 所以**關帳(closeout)那條線上**沒有任何檢查會去看那一格 —— 要到下面說的回填與
152
+ > PR review 才有人真的去讀它。
153
+ > ⚠ **抓得到它的判準是「那條檢查會不會去解析這個值」**,不是照空/非空判的那些。
154
+ > 目前這樣的檢查有:`references/finish-feature-flow.md` **Step 1**(minimal host 的
155
+ > 關帳檢查:把佔位字串判為「**從未填入**」,與「空」和「填錯 hash」分成三種不同訊息,
156
+ > 一律**擋**)、同檔 **Step 4 指令 1**(hosted 列回填:unfilled 指「空**或**放著佔位
157
+ > 字串」,兩者以同一種方式回填)、以及 `references/pr-review-checklist.md` 的**兩項**
158
+ > ——存在性那項要 `git cat-file -t` 逐個 resolve,hosted identity 那項再用
159
+ > `git show --stat` 確認那個 hash 真的是該列自己的實作 commit(該檔明寫這兩項
160
+ > 「不可互換」)。⚠ 另有數處**複述**同一條規則但把執行交給上面那些檢查(例如
161
+ > `references/modify-existing-flow.md` 的「Commit evidence goes to two surfaces」),
162
+ > 那些是指標、不是額外的關卡。**日後新增的檢查照同一個判準算,這裡列幾項不是重點。**
163
+ > 別把它們的存在讀成「所以放佔位字串沒差」:在它們之前,每一條機械規則都已經把
164
+ > 那一格當成填好了。
165
+ > **留空是有名字的狀態;佔位字串不是。**
166
+
167
+ > **post-hoc hotfix 的列自成一個 host,不會和上面的一般列並存**
168
+ > (見 references/modify-existing-flow.md Step 1.8):上面兩列的 checkpoint 1
169
+ > **必須**碰到它們宣告的實作路徑,而 post-hoc host 的 checkpoint 1 是**補文件那個
170
+ > commit**、按約定**不得**碰任何已宣告的實作路徑,同一個 commit 不可能兩者兼具
171
+ > (`references/finish-feature-flow.md` Step 1);Checkpoint Log 的實作列 Result
172
+ > 也只能二擇一(`committed (...)` 或 `reconciled (...)`)。post-hoc host 本身仍
173
+ > **可以是 compound**——多個 post-hoc 列共用同一個補文件 commit——不能混的是一般列:
174
+ >
175
+ > | Date | Tier | Description | Commit |
176
+ > |---|---|---|---|
177
+ > | {YYYY-MM-DD} | T3 | 首頁公告錯字修正 `[text]` — `src/Web/Home/Notice.cs`;post-hoc hotfix,識別依據 INC-2031 | {hash} |
178
+
179
+ > **baseline capture 的表長這樣,而且只有這一列**(bf 專屬)——closeout 對
180
+ > baseline host 要求 Lightweight Changes 表**恰好一列**,所以它不會和上面的
181
+ > T2/T3 列並存:
182
+ >
183
+ > | Date | Tier | Description | Commit |
184
+ > |---|---|---|---|
185
+ > | {YYYY-MM-DD} | baseline | 盤點 Expense 既有行為並寫入 BC 層 — `dflow/specs/domain/expense/rules.md`、`behavior.md` | {hash} |
92
186
 
93
187
  <!-- dflow:section checkpoint-log -->
94
188
  ## Checkpoint Log
@@ -96,18 +190,65 @@ Template note (for AI):
96
190
  > 生命週期 checkpoint 的 commit / skip 時間線(讓三週後回溯不必手動重建)。
97
191
  > 每個 checkpoint 無論 commit 或 skip 都記一列。Tier 決定 checkpoint 數:
98
192
  > T1 三點(spec 完 / impl 完 / closeout)、T2 兩點(spec+impl 合併 / closeout)、
99
- > T3 單一 commit。
193
+ > T3 單一實作 commit(其 inline row 與本列的 hash 由 host 的下一個 commit 一併帶進;
194
+ > host 若沒有後續 commit,允許一個只動 ledger 的 tracking commit 收尾,該 commit 不另成列)。
195
+ > ⚠ **上面那些「點」在本表 `Checkpoint` 欄各有固定的字面值**:spec 里程碑寫
196
+ > **`spec-baseline`**、實作寫 **`implementation`**、關帳寫 **`closeout`**(下表的
197
+ > `spec-baseline` / `implementation` / `closeout` 三列就是這幾個值;表中另有一列
198
+ > `branch-override`——本例排在最前——是紀錄列、不是生命週期 checkpoint,見下方說明)。散文裡的
199
+ > 「spec 完」是在說那個里程碑,**不是欄位值** —— 不要照著它造一個 `spec` 出來。
200
+ > ⚠ 這條只管**本表的 `Checkpoint` 欄**。`references/git-integration.md` 的選配 trailer
201
+ > `Dflow-Checkpoint: {SPEC-ID} {spec|impl|closeout}` 有它自己的三個角色名(其中就有
202
+ > `spec`)——那是另一個地方的詞彙,**不要拿本條去「修正」一個寫對的 trailer**。
203
+ >
204
+ > **Minimal host(zero-phase)例外**——見 references/modify-existing-flow.md
205
+ > Step 1.7(standalone)、Step 1.6 的 follow-up minimal 變體,或 Step 1.8 的
206
+ > post-hoc hotfix(其 linkage 落在前兩者之一):這種 host 之後
207
+ > 沒有別的 commit 可以收攏 T3 的 row,所以**不分 tier 一律記兩個 checkpoint**
208
+ > (implementation——baseline capture 記 `spec-baseline`——然後 closeout),
209
+ > 而且 T3 的 inline row 要**寫進 checkpoint 1 本身**、不是等下一個 commit 帶
210
+ > 進來。上一段「T3 單一實作 commit / 由下一個 commit 帶進」講的是**掛在既有
211
+ > feature 底下的** T3,不適用於 minimal host。
212
+ >
213
+ > Result 的合法值是 `committed ({hash})` / `skipped` / `failed`,外加
214
+ > **`reconciled ({merged-hotfix-hash})`**——只給 post-hoc hotfix(Step 1.8)的
215
+ > implementation 列用,意思是「本 checkpoint 記錄的是一個已經合併的變更」。
216
+ > 括號裡是**那個 hotfix 的 hash**,不是本 host 補文件那個 commit 的 hash
217
+ > (後者填在 Lightweight Changes 該列的 `Commit` 欄)。完整詞彙見
218
+ > references/git-integration.md § Commit Checkpoints, Branch Gate & AI Commits。
219
+ >
220
+ > **`branch-override` 是紀錄列,不是生命週期 checkpoint。** 在 branch gate 選了
221
+ > 「override and stay」時記一列(references/git-integration.md § Branch gate
222
+ > 定義形狀):Checkpoint 寫 `branch-override`、Result 寫
223
+ > `override ({你留下來的分支})`。它**不是 commit**、**不計入 tier 的 checkpoint
224
+ > 數**。closeout 的分支檢查會看**有沒有任何一列**指名你現在所在的分支(不是只看最近
225
+ > 一列——一個 host 可能在不同 phase override 到不同分支,而 closeout 自己不觸發 branch
226
+ > gate、不會補寫新列)。括號裡必須是**分支名**,沒寫分支的紀錄豁免不了任何東西。
227
+ > `branch:` 欄位本身永遠不因 override 而改寫。
228
+ > ⚠ **只會出現在 phase-bearing host。** references/modify-existing-flow.md 對
229
+ > **minimal host**(Step 1.6 follow-up 變體/Step 1.7 standalone/Step 1.8
230
+ > post-hoc)**不提供**這個選項——那些 host 正是拿 `branch:` 當權威來斷言分支相等的。
100
231
  >
101
232
  > commit hash 只在 commit 實際成功後填入;pre-commit hook reject 或 commit
102
233
  > 失敗記 `failed`、不寫假 hash。**例外:closeout 列不填 hash**——closeout
103
234
  > commit 無法自含自身 hash,該列於 commit 前寫入、隨歸檔目錄一起進 commit;
104
235
  > 溯源用 `git log -1 -- completed/{SPEC-ID}-{slug}` 或選配的
105
236
  > `Dflow-Checkpoint` trailer(見 references/git-integration.md)。
237
+ >
238
+ > ⚠⚠ **下表示範的是欄位值,不是這張表的起始狀態——一列都不要預先放。** 每一列
239
+ > 都在它那個 checkpoint **實際走到的當下**才新增(`branch-override` 在 branch gate
240
+ > 選了 override 時;`closeout` 由 references/finish-feature-flow.md Step 4 指令 1
241
+ > 在關帳當下寫入)。
242
+ > ⚠ **尤其不要預先放一列空的 `closeout` 佔位**:minimal host(zero-phase)的關帳
243
+ > 檢查要求 Checkpoint Log 在關帳前**恰好一列**
244
+ > (references/finish-feature-minimal-host.md Step 1),預先放下的那一列會**擋下**
245
+ > 關帳。
106
246
 
107
247
  | Timestamp | Checkpoint | Result |
108
248
  |---|---|---|
249
+ | {YYYY-MM-DD HH:MM} | branch-override | override ({branch-you-stayed-on}) |
109
250
  | {YYYY-MM-DD HH:MM} | spec-baseline | committed ({hash}) / skipped / failed |
110
- | {YYYY-MM-DD HH:MM} | implementation | committed ({hash}) / skipped / failed |
251
+ | {YYYY-MM-DD HH:MM} | implementation | committed ({hash}) / skipped / failed / reconciled ({merged-hotfix-hash}) |
111
252
  | {YYYY-MM-DD HH:MM} | closeout | committed / skipped / failed |
112
253
 
113
254
  ## Resume Pointer
@@ -117,7 +258,10 @@ Template note (for AI):
117
258
  > 下方四個 cursor 欄位是 workflow 進度的**存放層(宣告,claim)**:
118
259
  > 進入 flow 時設 Active Workflow;**每過一個 step gate** 更新 Current Step /
119
260
  > Gates Passed / Awaiting(與該 gate 既有的 `_index.md` 更新合併,不另加儀式);
120
- > closeout / `/dflow:cancel` 時 Active Workflow 設回 `none`。
261
+ > `/dflow:cancel` 時 Active Workflow 設回 `none`;closeout 也設回 `none`,但**是在
262
+ > 歸檔那一步**——`finish-feature-flow.md` Step 4 的 `git mv` 之後緊接著寫,**不是**
263
+ > closeout 一開始就寫。在那之前 closeout 本身仍是進行中的 workflow,後面還有 step
264
+ > gate 要過。
121
265
  > `/dflow:status` 讀 cursor 後會與推導證據(Checkpoint Log、phase-spec
122
266
  > status、git log)交叉,不一致會明確報 mismatch——cursor 是宣告、證據優先。
123
267
  > Phase 粒度進度由上方 Phase Specs 表承載;cursor 只補 workflow step / gate