dflow-sdd-ddd 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +915 -0
- package/CONTRIBUTING.md +11 -1
- package/README.en.md +160 -212
- package/README.md +93 -147
- package/TEMPLATE-COVERAGE.md +13 -6
- package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
- package/bin/dflow.js +90 -4
- package/docs/closeout-check.en.md +204 -0
- package/docs/closeout-check.md +204 -0
- package/docs/commands.en.md +112 -0
- package/docs/commands.md +103 -0
- package/docs/doctor-uncertainty.en.md +212 -0
- package/docs/doctor-uncertainty.md +212 -0
- package/docs/evaluating-dflow.en.md +29 -11
- package/docs/evaluating-dflow.md +8 -6
- package/docs/release-versioning-policy.md +6 -0
- package/docs/upgrading.en.md +196 -0
- package/docs/upgrading.md +197 -0
- package/docs/using-with-claude-code.en.md +13 -4
- package/docs/using-with-claude-code.md +13 -4
- package/docs/using-with-codex.en.md +13 -4
- package/docs/using-with-codex.md +13 -4
- package/docs/using-with-github-copilot.en.md +13 -4
- package/docs/using-with-github-copilot.md +13 -4
- package/lib/closeout-check.js +690 -0
- package/lib/doc-shapes.json +1195 -0
- package/lib/doctor-checks.js +2500 -24
- package/lib/init.js +2861 -143
- package/lib/render-diagrams.js +1474 -0
- package/lib/render.js +865 -49
- package/package.json +2 -2
- package/templates/brownfield/references/drift-verification.md +4 -0
- package/templates/brownfield/references/finish-feature-flow.md +648 -98
- package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
- package/templates/brownfield/references/finish-feature-minimal-host.md +427 -0
- package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/brownfield/references/finish-feature-record-correction.md +45 -0
- package/templates/brownfield/references/git-integration.md +205 -26
- package/templates/brownfield/references/init-project-flow.md +23 -3
- package/templates/brownfield/references/modify-existing-flow.md +439 -88
- package/templates/brownfield/references/modify-existing-follow-up.md +122 -0
- package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +200 -0
- package/templates/brownfield/references/new-feature-flow.md +110 -37
- package/templates/brownfield/references/new-phase-flow.md +59 -9
- package/templates/brownfield/references/pr-review-checklist.md +328 -10
- package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +151 -34
- package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
- package/templates/brownfield/scaffolding/Git-principles-gitflow.md +85 -8
- package/templates/brownfield/scaffolding/Git-principles-trunk.md +88 -10
- package/templates/brownfield/scaffolding/_conventions.md +50 -28
- package/templates/brownfield/scaffolding/_overview.md +1 -0
- package/templates/brownfield/templates/_index.md +158 -10
- package/templates/brownfield/templates/analysis.md +79 -0
- package/templates/brownfield/templates/behavior.md +1 -0
- package/templates/brownfield/templates/context-definition.md +1 -0
- package/templates/brownfield/templates/context-map.md +2 -1
- package/templates/brownfield/templates/glossary.md +1 -0
- package/templates/brownfield/templates/lightweight-spec.md +154 -11
- package/templates/brownfield/templates/models.md +1 -0
- package/templates/brownfield/templates/phase-spec.md +9 -1
- package/templates/brownfield/templates/rules.md +1 -0
- package/templates/brownfield/templates/tech-debt.md +1 -0
- package/templates/common/references/ddd-modeling-guide.md +33 -16
- package/templates/{greenfield → common}/references/dflow-feedback-flow.md +2 -1
- package/templates/common/references/flow-rationale-registry.md +146 -0
- package/templates/common/skill/SKILL.md +13 -11
- package/templates/greenfield/references/drift-verification.md +4 -0
- package/templates/greenfield/references/finish-feature-flow.md +639 -100
- package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
- package/templates/greenfield/references/finish-feature-minimal-host.md +383 -0
- package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/greenfield/references/finish-feature-record-correction.md +45 -0
- package/templates/greenfield/references/git-integration.md +193 -26
- package/templates/greenfield/references/init-project-flow.md +23 -3
- package/templates/greenfield/references/modify-existing-flow.md +395 -87
- package/templates/greenfield/references/modify-existing-follow-up.md +104 -0
- package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +200 -0
- package/templates/greenfield/references/new-feature-flow.md +116 -35
- package/templates/greenfield/references/new-phase-flow.md +58 -9
- package/templates/greenfield/references/pr-review-checklist.md +311 -8
- package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +146 -32
- package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
- package/templates/greenfield/scaffolding/Git-principles-gitflow.md +84 -8
- package/templates/greenfield/scaffolding/Git-principles-trunk.md +94 -14
- package/templates/greenfield/scaffolding/_conventions.md +50 -28
- package/templates/greenfield/scaffolding/_overview.md +6 -2
- package/templates/greenfield/templates/_index.md +145 -10
- package/templates/greenfield/templates/aggregate-design.md +1 -0
- package/templates/greenfield/templates/analysis.md +79 -0
- package/templates/greenfield/templates/behavior.md +1 -0
- package/templates/greenfield/templates/context-definition.md +1 -0
- package/templates/greenfield/templates/context-map.md +2 -1
- package/templates/greenfield/templates/events.md +4 -1
- package/templates/greenfield/templates/glossary.md +1 -0
- package/templates/greenfield/templates/lightweight-spec.md +154 -11
- package/templates/greenfield/templates/models.md +1 -0
- package/templates/greenfield/templates/phase-spec.md +9 -1
- package/templates/greenfield/templates/rules.md +1 -0
- package/templates/greenfield/templates/tech-debt.md +1 -0
- package/templates/brownfield/references/dflow-feedback-flow.md +0 -251
|
@@ -59,7 +59,32 @@ AI must locate the target feature and load its current state:
|
|
|
59
59
|
completed = frozen-history semantic and produces confusing dir-rename
|
|
60
60
|
history. The follow-up path is the only correct route.
|
|
61
61
|
|
|
62
|
-
3. **
|
|
62
|
+
3. **Refuse if the target is a minimal (zero-phase) host**
|
|
63
|
+
|
|
64
|
+
Decide this from the **persisted shape** — the same selector closeout
|
|
65
|
+
uses: an **empty Phase Specs table and no `phase-spec-*` file** in the host
|
|
66
|
+
directory. Such a host records a small standalone or follow-up change and
|
|
67
|
+
is *defined* by carrying no phase. Adding one does not extend it; it turns
|
|
68
|
+
the host into a phase-bearing feature, and closeout will then check it as
|
|
69
|
+
one. Refuse with:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
"`{SPEC-ID}-{slug}` is a minimal (zero-phase) host — it records a
|
|
73
|
+
lightweight change and carries no phase-specs, so a phase cannot be
|
|
74
|
+
added to it.
|
|
75
|
+
|
|
76
|
+
If this is genuinely new T1 work, run /dflow:new-feature. If it is
|
|
77
|
+
another small change, run /dflow:modify-existing — its Step 1 decides
|
|
78
|
+
the tier and finds or opens the right host for it."
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Do NOT create a first phase-spec here to "make the host usable", and do
|
|
82
|
+
not jump into `modify-existing-flow.md` Step 1.6, 1.7 or 1.8 from here:
|
|
83
|
+
each of those assumes Step 1's tier decision and host-linkage scan have
|
|
84
|
+
already run, and entering one directly skips both. Send the developer to
|
|
85
|
+
Step 1, exactly as `new-feature-flow.md` Step 4's guard does.
|
|
86
|
+
|
|
87
|
+
4. **Load context for the new phase**
|
|
63
88
|
- Read the feature's `_index.md` — Metadata, Goals & Scope, Phase Specs,
|
|
64
89
|
Current BR Snapshot, Resume Pointer
|
|
65
90
|
- Read the most recent phase-spec to understand where the prior phase
|
|
@@ -74,7 +99,7 @@ AI must locate the target feature and load its current state:
|
|
|
74
99
|
not visible on this branch — if the incoming phase scope overlaps one
|
|
75
100
|
of them, surface it before writing the phase-spec.
|
|
76
101
|
|
|
77
|
-
|
|
102
|
+
5. **Branch gate — ensure you are on this feature's branch (before any commit)**
|
|
78
103
|
|
|
79
104
|
This phase's commits must land on the active feature's
|
|
80
105
|
`feature/{SPEC-ID}-{slug}` branch. If you are not already on it (you
|
|
@@ -113,9 +138,20 @@ Walk the developer through what the new phase covers:
|
|
|
113
138
|
4. **Cross-context impact?** Does this phase introduce / change Domain
|
|
114
139
|
Events that other contexts consume? (If yes, plan for `context-map.md`
|
|
115
140
|
updates at finish-feature time.)
|
|
116
|
-
5. **
|
|
141
|
+
5. **System-level knowledge?** Does this phase add, change, or uncover an
|
|
142
|
+
ordered cross-context flow (always the domain-root copy), the lifecycle a
|
|
143
|
+
status field moves through, a figure computed rather than stored, a
|
|
144
|
+
mechanism no single rule explains, or which roles reach a function (always
|
|
145
|
+
the domain-root copy) — or work around a spot in one of them that only a
|
|
146
|
+
domain decision will settle (in the copy that holds the knowledge it is
|
|
147
|
+
stuck on)? If yes, record it in
|
|
148
|
+
`dflow/specs/domain/{context}/analysis.md` when one context owns what it
|
|
149
|
+
describes and in `dflow/specs/domain/analysis.md` when no one context does,
|
|
150
|
+
creating the file from `templates/analysis.md` the first time there is
|
|
151
|
+
something to record.
|
|
152
|
+
6. **Data structure impact?** New tables, columns, indices, EF
|
|
117
153
|
configuration changes?
|
|
118
|
-
|
|
154
|
+
7. **Why now?** Priority — informs sequencing relative to other phases.
|
|
119
155
|
|
|
120
156
|
This is also the moment to ask: "Should this be its own follow-up feature
|
|
121
157
|
instead of a phase here?" — useful when the scope drift suggests a
|
|
@@ -126,7 +162,7 @@ separate concern (different Aggregate, different BC, etc.).
|
|
|
126
162
|
## Step 3: Phase Slug Confirmation
|
|
127
163
|
|
|
128
164
|
AI proposes the new phase-spec filename and asks the developer to confirm
|
|
129
|
-
before
|
|
165
|
+
before the phase-spec is written.
|
|
130
166
|
|
|
131
167
|
> "Proposed phase-spec for `{SPEC-ID}-{slug}`:
|
|
132
168
|
>
|
|
@@ -169,6 +205,8 @@ frontmatter `status` to `in-progress`. Phase-2-onward specs **must** fill
|
|
|
169
205
|
in the **Delta from prior phases** section (the first phase typically has
|
|
170
206
|
just "首 phase,無前置 Delta"; this is phase 2+, so the section is required).
|
|
171
207
|
|
|
208
|
+
> **Table-cell formatting**: keep table cells concise — separate multiple short items with `<br>` (never chain them into one line with ;/; separators), and move long narrative detail out of the cell into a document section (full convention: the formatting comment at each spec doc's head).
|
|
209
|
+
|
|
172
210
|
Walk the developer through each section, in the same way `new-feature-flow`
|
|
173
211
|
Step 4 does — Behavior (with Aggregate state transitions and Domain
|
|
174
212
|
Events) / Business Rules / Delta / Edge Cases / Domain Events / layer-by-
|
|
@@ -200,6 +238,8 @@ Wait for confirmation before entering Step 5.
|
|
|
200
238
|
|
|
201
239
|
Update the feature's `_index.md`:
|
|
202
240
|
|
|
241
|
+
> **Table-cell formatting**: keep table cells concise — separate multiple short items with `<br>` (never chain them into one line with ;/; separators), and move long narrative detail out of the cell into a document section (full convention: the formatting comment at each spec doc's head).
|
|
242
|
+
|
|
203
243
|
1. **Phase Specs table** — add a new row for this phase:
|
|
204
244
|
```
|
|
205
245
|
| {N+1} | {YYYY-MM-DD} | {phase-slug} | in-progress | [phase-spec-{date}-{phase-slug}.md](./phase-spec-{date}-{phase-slug}.md) |
|
|
@@ -256,6 +296,14 @@ During implementation, continuously verify:
|
|
|
256
296
|
- [ ] No business logic leaks into Application handlers, Infrastructure queries,
|
|
257
297
|
or Presentation controllers
|
|
258
298
|
- [ ] Test failures have been resolved or explicitly recorded as follow-up
|
|
299
|
+
- [ ] Every entry Step 2 recorded in `analysis.md`, at either of its two
|
|
300
|
+
paths, still holds. An entry for what this phase was to add or change
|
|
301
|
+
matches what it built — corrected where the build differs, removed
|
|
302
|
+
where nothing was built for it. An entry for something Step 2
|
|
303
|
+
uncovered in the existing system matches the system as it now stands —
|
|
304
|
+
corrected where this phase changed it, never removed because this
|
|
305
|
+
phase built nothing for it. Read the entries in the file, not in a
|
|
306
|
+
diff: a checkpoint may already have committed them.
|
|
259
307
|
|
|
260
308
|
If implementation changes the agreed Delta, update the phase-spec before
|
|
261
309
|
continuing. Do not let code and spec diverge silently.
|
|
@@ -293,10 +341,11 @@ Update the feature artifacts:
|
|
|
293
341
|
|
|
294
342
|
The bounded context's `rules.md` / `behavior.md` / `events.md` and the
|
|
295
343
|
feature directory move to `completed/` remain `/dflow:finish-feature`
|
|
296
|
-
responsibilities
|
|
297
|
-
|
|
344
|
+
responsibilities; Step 2 records into `analysis.md` directly, at whichever of
|
|
345
|
+
its two paths the entry belongs to. Do not sync any other BC-level current
|
|
346
|
+
state or archive the whole feature from `/dflow:new-phase`.
|
|
298
347
|
|
|
299
348
|
After completion, summarize for the developer:
|
|
300
349
|
> "Phase {N+1} is implemented and marked completed. `_index.md` is refreshed.
|
|
301
|
-
> If another slice is needed, run `/dflow:new-phase`; if
|
|
302
|
-
> run `/dflow:finish-feature
|
|
350
|
+
> If another slice is needed, run `/dflow:new-phase`; if there is no further
|
|
351
|
+
> phase, run `/dflow:finish-feature` to close the feature out."
|
|
@@ -37,20 +37,42 @@ touches and read them all.
|
|
|
37
37
|
`/dflow:finish-feature` time)
|
|
38
38
|
- [ ] Only then proceed to the code-review sections below
|
|
39
39
|
|
|
40
|
-
If the PR
|
|
40
|
+
If the PR records its change in neither a spec nor an `_index.md` row (a T3
|
|
41
|
+
inline row is such a record, and a host that carries one needs no spec), first
|
|
42
|
+
check whether it only records existing behaviour or a decision in Domain
|
|
43
|
+
documents, without changing output. That PR is observation-only and wants no
|
|
44
|
+
host: answer it with the reply under "If the spec is missing or incomplete" at
|
|
45
|
+
the end of Spec Compliance, not with the one below. For any other PR with no
|
|
46
|
+
such record:
|
|
41
47
|
```
|
|
42
48
|
"I don't see a feature directory or _index.md for this PR. Before I
|
|
43
|
-
review the code, can you point me to
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
review the code, can you point me to the host feature? SDD relies on
|
|
50
|
+
the recorded change being the review anchor.
|
|
51
|
+
|
|
52
|
+
If this change belongs to a feature that already exists, run
|
|
53
|
+
/dflow:modify-existing or /dflow:bug-fix against it — the cascade
|
|
54
|
+
decides what gets recorded (a T2 lands a lightweight spec; a T3 lands
|
|
55
|
+
one inline row in that feature's _index.md and no spec file). If it is
|
|
56
|
+
genuinely new work, run /dflow:new-feature.
|
|
57
|
+
|
|
58
|
+
If no feature exists to host it at all, that is the standalone case and
|
|
59
|
+
it has a defined route: /dflow:modify-existing opens a minimal
|
|
60
|
+
(zero-phase) host for it — Step 1.7, or Step 1.6's minimal variant when
|
|
61
|
+
the change is a follow-up on a completed feature. Send it there rather
|
|
62
|
+
than inventing a home by hand."
|
|
47
63
|
```
|
|
48
64
|
|
|
49
65
|
## Spec Compliance
|
|
50
66
|
|
|
51
67
|
Per-feature checks:
|
|
52
68
|
- [ ] **Feature directory exists** with `_index.md` + at least one
|
|
53
|
-
phase-spec (or one lightweight-spec for a T2-only feature)
|
|
69
|
+
phase-spec (or one lightweight-spec for a T2-only feature).
|
|
70
|
+
**Exception — a host whose whole record is inline.** A **T3-only**
|
|
71
|
+
minimal host has no spec file by design: a T3 never produces one.
|
|
72
|
+
What it must have instead is at least one `_index.md` Lightweight
|
|
73
|
+
Changes row. Check for that, and do **not** ask the author to add a
|
|
74
|
+
spec to satisfy this line — that manufactures the artifact the tier
|
|
75
|
+
forbids and the flow refuses to write.
|
|
54
76
|
- [ ] **`_index.md` Current BR Snapshot is up to date** — reflects
|
|
55
77
|
the cumulative effect of all phase-specs / lightweight-specs in
|
|
56
78
|
the directory
|
|
@@ -58,7 +80,9 @@ Per-feature checks:
|
|
|
58
80
|
phase-spec file exists and its `status` matches the row's claim
|
|
59
81
|
- [ ] **For follow-up features**: `_index.md` Metadata has `follow-up-of:
|
|
60
82
|
{原 SPEC-ID}` AND the original feature's `_index.md`
|
|
61
|
-
Follow-up Tracking row references this feature
|
|
83
|
+
Follow-up Tracking row references this feature. **`follow-up-of` may
|
|
84
|
+
be a YAML array** — when it names several originals, *each* of them
|
|
85
|
+
must carry the row; checking the first one is not checking the field.
|
|
62
86
|
|
|
63
87
|
Per-phase-spec / lightweight-spec checks (run for **each** spec file the
|
|
64
88
|
PR touches, not just one):
|
|
@@ -67,6 +91,23 @@ PR touches, not just one):
|
|
|
67
91
|
Aggregate state transitions and Domain Events)
|
|
68
92
|
- [ ] All business rules (BR-*) in this spec implemented
|
|
69
93
|
- [ ] Edge cases (EC-*) in this spec handled
|
|
94
|
+
- [ ] **No-BR family evidence** — for a lightweight-spec whose Behavior
|
|
95
|
+
Delta is a `BR:` / `BR Delta:` none line, check that family's
|
|
96
|
+
evidence instead: (a) `Output Footprint`; (b) `Contract Delta` +
|
|
97
|
+
`**Downstream consumers**`; (c) `Operational Rationale` +
|
|
98
|
+
`**Trace**`; (d) `Performance Delta` + `**SLA / resource
|
|
99
|
+
context**`; (e) `Governing BR-IDs` + root cause + a regression
|
|
100
|
+
check; (f) `Change Rationale` with `**Before**` / `**After**` +
|
|
101
|
+
`**Regression**`. The BR / scenario / Domain items above are
|
|
102
|
+
**N/A** for these specs — record them as N/A rather than passing
|
|
103
|
+
them on an empty rule set, and never fabricate a BR or a
|
|
104
|
+
`behavior.md` anchor to make one pass. Domain documents are not
|
|
105
|
+
automatically N/A: an added event field still belongs in
|
|
106
|
+
`events.md`. A spec in the
|
|
107
|
+
classic BR-delta form, or a bug spec with the older single `BR:`
|
|
108
|
+
line, is accepted as-is. A change matching more than one family
|
|
109
|
+
must carry **every** matching family's evidence, not just the one
|
|
110
|
+
its BR line names
|
|
70
111
|
- [ ] **Delta integrity** (phase 2+ only) — the Delta-from-prior-phases
|
|
71
112
|
section's ADDED / MODIFIED / REMOVED / RENAMED entries actually
|
|
72
113
|
match the diff against the prior phase-spec's BR set
|
|
@@ -75,11 +116,272 @@ If the closeout commit is in this PR (`/dflow:finish-feature` was run):
|
|
|
75
116
|
- [ ] **BC layer sync landed** — `dflow/specs/domain/{context}/rules.md` /
|
|
76
117
|
`behavior.md` / `events.md` / `context-map.md` reflect the
|
|
77
118
|
feature's net effect (compare against `_index.md` Current BR
|
|
78
|
-
Snapshot)
|
|
119
|
+
Snapshot). **N/A for a no-BC host** — one whose Goals & Scope
|
|
120
|
+
declares no bounded context has nothing to sync, and a `{context}`
|
|
121
|
+
document invented to satisfy this line is a fiction, never a pass.
|
|
122
|
+
⚠ **Do not read that as "closeout already blocks it" without qualifying
|
|
123
|
+
where.** Closeout blocks it in the commits it actually reads — checkpoint
|
|
124
|
+
1, and only on a **minimal** host, plus the closeout commit on any host.
|
|
125
|
+
A fiction committed by a **phase-bearing** no-BC host anywhere in between
|
|
126
|
+
passes every closeout check. That range is the *"A no-BC host committed no
|
|
127
|
+
BC-scoped Domain material"* item in the delegated block below, and it is
|
|
128
|
+
yours to run. **N/A for a T3**, which does no Domain work at all.
|
|
129
|
+
Record the N/A rather than ticking it against an empty comparison — but
|
|
130
|
+
note `glossary.md` and the tech-debt file belong to no bounded context
|
|
131
|
+
and stay in a no-BC host's sweep, so this N/A does not reach them.
|
|
79
132
|
- [ ] **Whole feature directory `git mv`'d** to `completed/` — git
|
|
80
133
|
shows `renamed:` (not `deleted:` + `new file:`)
|
|
81
134
|
- [ ] **Integration Summary** was emitted to the conversation (not
|
|
82
135
|
written to a file — it's ephemeral)
|
|
136
|
+
- [ ] **Every Lightweight Changes row now carries a `Commit` hash.**
|
|
137
|
+
Before closeout an empty cell can be legitimate: on a **hosted** row
|
|
138
|
+
the cell is filled by the host's *next* commit, and closeout is
|
|
139
|
+
allowed to be that commit. (A **minimal** host is stricter — its rows
|
|
140
|
+
are written before checkpoint 1, and closeout already refuses an empty
|
|
141
|
+
cell there.) After closeout there is no next commit, so an empty cell
|
|
142
|
+
will never be filled. That is why closeout does not assert this for a
|
|
143
|
+
phase-bearing host, and why it is checkable here.
|
|
144
|
+
⚠ **A placeholder is not a hash.** `{hash}`, `{pending}`, `(待 commit)` —
|
|
145
|
+
anything that is not a resolvable commit — counts as **unfilled**, not as
|
|
146
|
+
carrying one. It is the harder case to catch precisely because the cell is
|
|
147
|
+
**non-empty**: every rule downstream is written against empty / non-empty,
|
|
148
|
+
so a placeholder reads as filled to all of them. Resolve each value
|
|
149
|
+
(`git cat-file -t {value}`) rather than checking the cell is populated.
|
|
150
|
+
|
|
151
|
+
**Delegated to review by `finish-feature-flow.md`.** Closeout states at each
|
|
152
|
+
of these sites that it does *not* prove them. They are not optional extras:
|
|
153
|
+
skip one and the boundary is a hole rather than a division of labour.
|
|
154
|
+
|
|
155
|
+
- [ ] **Whole history — no stray host-open commit.** Closeout validates the
|
|
156
|
+
hashes the record names and cannot see the rest of the branch. List
|
|
157
|
+
this branch's commits that touch the host directory:
|
|
158
|
+
`git log --oneline {base}..HEAD -- 'dflow/specs/features/active/{SPEC-ID}-{slug}/' 'dflow/specs/features/completed/{SPEC-ID}-{slug}/'`
|
|
159
|
+
For a **minimal (zero-phase) host** there must be exactly two —
|
|
160
|
+
checkpoint 1 and closeout. A third, typically a host opened in its own
|
|
161
|
+
commit before the work landed, breaks the two-commit lifecycle while
|
|
162
|
+
every closeout check still passes, because those read only the hashes
|
|
163
|
+
the record points at. A third that comes **after** checkpoint 1 and
|
|
164
|
+
changes only the host's record text — no implementation path, no new
|
|
165
|
+
host file — may instead be a correction committed on its own once
|
|
166
|
+
checkpoint 1 was pushed (`references/finish-feature-record-correction.md`
|
|
167
|
+
sends the developer that way and asks for it to be named in the PR
|
|
168
|
+
description). Report which it is: the count still fails, and
|
|
169
|
+
accepting a correction commit is your call after reading its full
|
|
170
|
+
diff and the PR description. A correction that touches only a record
|
|
171
|
+
outside this directory — a follow-up's original `_index.md` — is not in
|
|
172
|
+
this walk; the PR description is where it is named.
|
|
173
|
+
**This count applies to a minimal host only**: a
|
|
174
|
+
phase-bearing feature has no fixed number of host commits, so there is
|
|
175
|
+
nothing to count there and this item is N/A.
|
|
176
|
+
This is also where closeout's minimal-host **selector** boundary is
|
|
177
|
+
settled: that selector reads the host's persisted shape today and
|
|
178
|
+
cannot know it was opened minimal and *stayed* minimal. The commit walk
|
|
179
|
+
is what shows that.
|
|
180
|
+
- [ ] **Hotfix identity — confirm it against the cited source** (post-hoc
|
|
181
|
+
host only). Closeout tests **plausibility only**, and says so: which
|
|
182
|
+
commit was *the* merged hotfix is not derivable from inside the flow,
|
|
183
|
+
so the developer asserts it and cites what the claim rests on —
|
|
184
|
+
`hotfix-identity:` beside `hotfix-branch:` in a **T2**'s
|
|
185
|
+
lightweight-spec frontmatter, or in the row Description for a **T3**.
|
|
186
|
+
**Open that source** — PR, incident, or tracker — and confirm that it
|
|
187
|
+
names **this fix** and the recorded commit: one of the fix's own
|
|
188
|
+
commits, or the squash commit when the fix was squashed
|
|
189
|
+
(`references/modify-existing-post-hoc-hotfix.md` item 3). A fix merged
|
|
190
|
+
without a PR is identified by the source and the base branch's history
|
|
191
|
+
instead — the hotfix branch's name or the source's number in commit
|
|
192
|
+
messages.
|
|
193
|
+
**When the fix landed as a copy** — a rebase merge or a cherry-pick —
|
|
194
|
+
the recorded commit is that copy, and the source may not name it. Git
|
|
195
|
+
cannot prove that one commit is a copy of another: ancestry does not
|
|
196
|
+
follow copies, and patch comparison (`git cherry`, `git patch-id`)
|
|
197
|
+
cannot tell apart two changes that differ only in whitespace. A
|
|
198
|
+
`(cherry picked from commit …)` line or a matching subject is a clue,
|
|
199
|
+
not proof — settle it with the author from the source, and do not
|
|
200
|
+
report the identity as confirmed from command output alone.
|
|
201
|
+
Closeout already checked that a citation is *present*; only review can
|
|
202
|
+
check that it *matches*.
|
|
203
|
+
- [ ] **Hosted `Commit` cell identity — confirm each hash is *that row's own*
|
|
204
|
+
implementation commit** (phase-bearing host only). `finish-feature-flow.md`
|
|
205
|
+
Step 4 instruction 1 orders the value ("each row's own implementation
|
|
206
|
+
hash, never the closeout hash") and its post-commit verification **states
|
|
207
|
+
that it cannot decide it**: the hash-evidence test is minimal-host-only
|
|
208
|
+
and lives in `finish-feature-minimal-host.md`, so on a phase-bearing host
|
|
209
|
+
no closeout check reads the value at all. The presence item above asserts the cell is **non-empty**;
|
|
210
|
+
this one asserts it is **right**, and the two are not interchangeable.
|
|
211
|
+
For each Lightweight Changes row, take its `Commit` hash and run
|
|
212
|
+
`git show --stat {hash}`. **Take the row's declaration if it has one and
|
|
213
|
+
require the commit to touch it** — a **T2**'s own spec file
|
|
214
|
+
(`lightweight-*.md` / `BUG-*.md`), a **T3**'s Description implementation
|
|
215
|
+
paths.
|
|
216
|
+
**If the row declares nothing, that is by design on a hosted row — with
|
|
217
|
+
one exception.** A **T2** links to its own spec file on *every* host shape:
|
|
218
|
+
`finish-feature-flow.md` Step 1 carries two `Tier = T2` checks that no
|
|
219
|
+
host shape escapes — one of them says so in as many words — and
|
|
220
|
+
`finish-feature-minimal-host.md`'s phase-bearing exemption note confirms
|
|
221
|
+
those hosts keep them. A T2 row with
|
|
222
|
+
no such link is **malformed, not exempt** — find the spec file or flag the
|
|
223
|
+
row. For a **T3** the absence is the sanctioned state.
|
|
224
|
+
⚠ The record then gives you nothing to compare against, and that is
|
|
225
|
+
deliberate: `finish-feature-minimal-host.md` exempts phase-bearing hosts
|
|
226
|
+
from its `Commit` cell path check for exactly this reason
|
|
227
|
+
(`references/flow-rationale-registry.md`, `R-FF-COMMITCELL-02`:
|
|
228
|
+
"demanding it would reject every hosted T3"), and `templates/_index.md`
|
|
229
|
+
calls declaring them good
|
|
230
|
+
practice with no gate behind it. **Do not read "touches nothing declared"
|
|
231
|
+
as a pass.** What remains checkable is weaker and is worth stating as
|
|
232
|
+
such: the hash is **not** the closeout commit, is **not** any other row's
|
|
233
|
+
hash, and is a commit on this host's branch — or on a branch this host
|
|
234
|
+
recorded a `branch-override` for. Past that, ask the developer which
|
|
235
|
+
commit carried the row. If identity matters for this change, say so and
|
|
236
|
+
have the row declare its paths — that is what makes the comparison above
|
|
237
|
+
available.
|
|
238
|
+
⚠ **Do not let tier decide that a declaration must exist.** Tier says what
|
|
239
|
+
the declaration would have been; the one hosted row required to carry one
|
|
240
|
+
is a T2, and that requirement lives in Step 1, not here. Keying this check
|
|
241
|
+
on tier produced three successive versions that each named an input some
|
|
242
|
+
row was not required to carry.
|
|
243
|
+
A cell holding the **closeout** hash, or another row's hash, is what this
|
|
244
|
+
exists to catch: both are non-empty, both pass every closeout check, and
|
|
245
|
+
neither is what instruction 1 ordered.
|
|
246
|
+
**N/A for a minimal host** — there `finish-feature-minimal-host.md`'s
|
|
247
|
+
hash-evidence test already runs (a)–(c) against every row before closeout
|
|
248
|
+
may proceed.
|
|
249
|
+
- [ ] **A recorded branch override still matches where the closeout landed**
|
|
250
|
+
(only when the host carries a `branch-override` row). Closeout passes its
|
|
251
|
+
branch check on any such row naming the branch it ran on, and **states
|
|
252
|
+
that it cannot decide** whether that override was still the current
|
|
253
|
+
intent — nothing expires a row once the developer returns to the host's
|
|
254
|
+
own branch. You can see what closeout cannot: which branch the closeout
|
|
255
|
+
commit sits on, and whether the work had already been integrated by then.
|
|
256
|
+
Two cases to reject, both instances of one rule — **the branch this
|
|
257
|
+
closeout landed on is not where this host's work was finished**: a closeout
|
|
258
|
+
committed onto the **base** branch *after* the feature branch was merged
|
|
259
|
+
(that puts the closeout commit outside the review this checklist is part
|
|
260
|
+
of), and a closeout committed onto a branch an override named earlier but
|
|
261
|
+
which the work has since **left** — a superseded spike, say. Closeout
|
|
262
|
+
distinguishes neither, because it accepts any branch this host ever
|
|
263
|
+
recorded an override for.
|
|
264
|
+
- [ ] **The closeout commit carries only this host's delta.** Closeout's spill
|
|
265
|
+
check derives its permitted set from Step 4 instruction 2, which defines
|
|
266
|
+
one for **every** host shape — so there is a permitted-file list to read
|
|
267
|
+
against on a phase-bearing feature too. What that does not change is what
|
|
268
|
+
the check proves: it is a **path-level spill check**, so it proves no
|
|
269
|
+
unpermitted *file* entered the commit, never that the permitted ones carry
|
|
270
|
+
only this change. An unrelated BR edit inside `rules.md`, or another
|
|
271
|
+
feature's term in `glossary.md`, passes it untouched. Read each
|
|
272
|
+
permitted file's diff against the intended delta in the spec / row.
|
|
273
|
+
- [ ] **A follow-up's reverse link was opened, not only closed.** The
|
|
274
|
+
original's Follow-up Tracking row must have existed as `in-progress`
|
|
275
|
+
before the flip to `completed` — `absent → in-progress → completed`,
|
|
276
|
+
never created straight as `completed`. Closeout verifies this for a
|
|
277
|
+
**minimal** host only, from checkpoint 1's committed blob; a
|
|
278
|
+
**phase-bearing** follow-up has no single commit required to carry the
|
|
279
|
+
row, so closeout has nothing equivalent to read and the opening half
|
|
280
|
+
arrives here unchecked. Confirm it from the branch history
|
|
281
|
+
(`git log -p {base}..HEAD -- <original feature>/_index.md`), for
|
|
282
|
+
**each** SPEC-ID in `follow-up-of`.
|
|
283
|
+
- [ ] **A no-BC host committed no BC-scoped Domain material** — **every no-BC
|
|
284
|
+
host, not only phase-bearing ones.** Closeout inspects only **checkpoint
|
|
285
|
+
1's** diff on a minimal host, and a phase-bearing host has no single
|
|
286
|
+
checkpoint to inspect at all. **Neither is a branch-range proof**, and
|
|
287
|
+
closeout cannot take a range — it needs a base branch and Dflow
|
|
288
|
+
deliberately does not know yours. **You have it.** Run
|
|
289
|
+
`git diff --name-status {base}..HEAD -- 'dflow/specs/domain/'` and confirm
|
|
290
|
+
no BC-scoped Domain document — **the set this track's own no-BC closeout
|
|
291
|
+
check names**, `context-map.md` included — was **added, modified, deleted
|
|
292
|
+
or renamed**. A no-BC host is one whose Goals & Scope declares no bounded
|
|
293
|
+
context, the same identification the BC-layer-sync item above uses.
|
|
294
|
+
⚠ **The path filter is deliberately wider than the prohibited set, so read
|
|
295
|
+
the output before flagging.** `context-map.md` sits at `domain/` top level
|
|
296
|
+
rather than under `{context}/`, and the filter has to reach up there to
|
|
297
|
+
catch it — which means it also lists `domain/glossary.md` and
|
|
298
|
+
`domain/analysis.md`. Both belong to no bounded context and stay
|
|
299
|
+
legitimate for a no-BC host: **seeing either here is not a finding.** The
|
|
300
|
+
tech-debt file is outside the filter entirely and never appears.
|
|
301
|
+
|
|
302
|
+
If the spec is missing or incomplete:
|
|
303
|
+
```
|
|
304
|
+
"I notice this PR doesn't have a matching feature directory / _index.md.
|
|
305
|
+
Can you describe what this change does? If it only records existing
|
|
306
|
+
behaviour or a decision without changing output, it is observation-only
|
|
307
|
+
and wants no host at all — it is captured in the relevant Domain
|
|
308
|
+
document (models.md / rules.md / behavior.md / events.md / analysis.md)
|
|
309
|
+
and stops
|
|
310
|
+
there. Otherwise: if it belongs to a feature we already have, we can
|
|
311
|
+
record it there retroactively — the tier decides what gets written
|
|
312
|
+
(T2: a lightweight spec; T3: one inline _index.md row). If it's
|
|
313
|
+
genuinely new work, it wants /dflow:new-feature. If
|
|
314
|
+
there's no feature it could belong to, that's the standalone case and
|
|
315
|
+
it has a defined route: /dflow:modify-existing opens a minimal
|
|
316
|
+
(zero-phase) host for it — Step 1.7, or Step 1.6's minimal variant when
|
|
317
|
+
it's a follow-up on a completed feature. Let's send it there rather
|
|
318
|
+
than inventing a home by hand."
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Risk-Triggered Modeling Review
|
|
322
|
+
|
|
323
|
+
Use the spec and diff to identify these risks; do not use a keyword-only scan.
|
|
324
|
+
Only when a signal matches, read the named section in
|
|
325
|
+
`dflow/specs/shared/dflow-workflows/references/ddd-modeling-guide.md`. If no
|
|
326
|
+
signal matches, skip this section's guide lookup, create no artifact, and
|
|
327
|
+
continue with the existing checklist below.
|
|
328
|
+
|
|
329
|
+
| Risk signal | Read in the modeling guide |
|
|
330
|
+
|---|---|
|
|
331
|
+
| One use case mutates / saves multiple Aggregate roots, or adds a multi-root Unit of Work | Aggregate Design Rules; Domain Services |
|
|
332
|
+
| The diff crosses Bounded Contexts or changes an event payload, handler, or delivery expectation | Domain Events; Event Handling Guidelines |
|
|
333
|
+
| Deadline, retry, out-of-order callback, compensation, business-visible progress, or a claim that local persistence and an external provider / service fact form one atomic outcome | Aggregate Design Rules; Long-Running Processes; failure-path guidance under Event Handling Guidelines |
|
|
334
|
+
| `unique`, `only one active`, or another set-based invariant | Set-Based / Uniqueness Invariants |
|
|
335
|
+
| A Domain Service is added or changed | Domain Services; Long-Running Processes when the flow has state |
|
|
336
|
+
|
|
337
|
+
For every matched risk, record one disposition in the review:
|
|
338
|
+
|
|
339
|
+
- **Finding** — identify the violated guarantee at the boundary named by the
|
|
340
|
+
matched risk, its severity, and merge disposition. For a match involving an
|
|
341
|
+
external provider / service fact, state that a local transaction cannot make
|
|
342
|
+
that fact atomic with local state; implementing or repairing a local
|
|
343
|
+
transaction / Unit of Work alone is not sufficient closure.
|
|
344
|
+
- **Accepted with rationale** — state why the boundary / transaction / service
|
|
345
|
+
role is sound and when to revisit it. For such an exception, the rationale
|
|
346
|
+
must already exist or be added to the feature's `aggregate-design.md` Design
|
|
347
|
+
Decisions; the PR conversation points to that decision and is not its only
|
|
348
|
+
home. This disposition evaluates the rationale's adequacy, not only its
|
|
349
|
+
existence: it must explain how it answers every matched risk. For a
|
|
350
|
+
match involving an external provider / service fact, acknowledge that a local
|
|
351
|
+
transaction cannot make that fact atomic with local state and explain how the
|
|
352
|
+
outcome progresses across the boundary, including idempotency and retry /
|
|
353
|
+
compensation (or a justified equivalent). For a Domain Service match, name
|
|
354
|
+
the actual domain rule or decision the accepted operation carries; an
|
|
355
|
+
operation that only orders root calls is Application-layer orchestration and
|
|
356
|
+
remains a Finding, even when another operation of the same service holds a
|
|
357
|
+
real rule. If the rationale does not cover the matched risk, use Finding
|
|
358
|
+
rather than this disposition.
|
|
359
|
+
- **Not applicable** — explain from the spec and diff why the match is only
|
|
360
|
+
superficial.
|
|
361
|
+
|
|
362
|
+
Multiple signals for the same design decision may share one disposition, but
|
|
363
|
+
every matched risk must have an auditable disposition. This is not a per-signal
|
|
364
|
+
form.
|
|
365
|
+
|
|
366
|
+
### Closing a blocking modeling finding
|
|
367
|
+
|
|
368
|
+
A DDD / domain-correctness blocker must state:
|
|
369
|
+
|
|
370
|
+
- the code boundary or enforcement needed to restore the guarantee;
|
|
371
|
+
- the existing durable surfaces that must change, as applicable — the spec,
|
|
372
|
+
`aggregate-design.md`, domain `context-map.md` / `events.md` / `behavior.md`,
|
|
373
|
+
or `architecture/tech-debt.md`;
|
|
374
|
+
- the automated tests or observable evidence that re-verify the guarantee; and
|
|
375
|
+
- that the unimplemented guarantee must not remain marked `completed` before
|
|
376
|
+
re-review.
|
|
377
|
+
|
|
378
|
+
If the review offers defer as an option, it must list the owner, known risk,
|
|
379
|
+
exit criteria, and `architecture/tech-debt.md` landing surface as closure
|
|
380
|
+
conditions. If the team defers the finding, record those items on that surface;
|
|
381
|
+
rationale, a re-evaluation condition, or "future refactor" alone is not
|
|
382
|
+
closure. The reviewer defines the evidence needed to unblock, not the author's
|
|
383
|
+
implementation. Use the existing PR comment / conversation surface; do not
|
|
384
|
+
create a separate review report.
|
|
83
385
|
|
|
84
386
|
## Domain Layer Quality
|
|
85
387
|
|
|
@@ -127,6 +429,7 @@ If the closeout commit is in this PR (`/dflow:finish-feature` was run):
|
|
|
127
429
|
glossary)? Align it. (Judgment call, not a string match.)
|
|
128
430
|
- [ ] **Context boundaries respected** — no reaching into another context's internals
|
|
129
431
|
- [ ] **Domain Events documented** — events.md updated?
|
|
432
|
+
- [ ] **analysis.md updated** — did this change touch a status transition, an ordered cross-context flow, which roles reach a function, or the way a figure is computed? If so, is the matching entry in `analysis.md` updated? Open one Evidence source it cites and confirm it still says what the entry claims. An entry whose Evidence is `inferred` names what its conclusion was drawn from: confirm instead that the conclusion still follows from it. An entry whose Evidence is `assumed` has no source to open: confirm instead that it says what it rests on and that the question of confirming it is recorded where the Evidence convention at the top of `analysis.md` says, and report it as unverified rather than blocking on it.
|
|
130
433
|
- [ ] **Tests cover invariants** — not just happy path
|
|
131
434
|
|
|
132
435
|
## Architecture Score
|