dflow-sdd-ddd 0.13.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.
- package/CHANGELOG.md +824 -1
- package/CONTRIBUTING.md +16 -10
- package/README.en.md +156 -200
- package/README.md +89 -144
- package/TEMPLATE-COVERAGE.md +15 -8
- package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
- package/bin/dflow.js +36 -4
- package/docs/commands.en.md +110 -0
- package/docs/commands.md +101 -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/npm-publish-checklist.md +3 -1
- package/docs/release-versioning-policy.md +8 -2
- package/docs/upgrading.en.md +196 -0
- package/docs/upgrading.md +197 -0
- package/docs/using-with-claude-code.en.md +25 -10
- package/docs/using-with-claude-code.md +20 -7
- package/docs/using-with-codex.en.md +18 -6
- package/docs/using-with-codex.md +16 -5
- package/docs/using-with-github-copilot.en.md +25 -10
- package/docs/using-with-github-copilot.md +21 -8
- package/lib/doc-shapes.json +997 -0
- package/lib/doctor-checks.js +2654 -0
- package/lib/init.js +3583 -107
- 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 +635 -88
- package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
- package/templates/brownfield/references/finish-feature-minimal-host.md +406 -0
- package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/brownfield/references/git-integration.md +160 -15
- package/templates/brownfield/references/init-project-flow.md +26 -4
- package/templates/brownfield/references/modify-existing-flow.md +412 -87
- package/templates/brownfield/references/modify-existing-follow-up.md +121 -0
- package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +82 -0
- package/templates/brownfield/references/new-feature-flow.md +61 -6
- package/templates/brownfield/references/new-phase-flow.md +57 -7
- package/templates/brownfield/references/pr-review-checklist.md +303 -10
- package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +158 -34
- package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
- package/templates/brownfield/scaffolding/Git-principles-gitflow.md +75 -6
- package/templates/brownfield/scaffolding/Git-principles-trunk.md +82 -8
- package/templates/brownfield/scaffolding/_conventions.md +50 -28
- package/templates/brownfield/scaffolding/_overview.md +1 -0
- package/templates/brownfield/templates/_index.md +151 -7
- 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 +130 -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 +625 -89
- package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
- package/templates/greenfield/references/finish-feature-minimal-host.md +363 -0
- package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
- package/templates/greenfield/references/git-integration.md +148 -15
- package/templates/greenfield/references/init-project-flow.md +28 -8
- package/templates/greenfield/references/modify-existing-flow.md +378 -85
- package/templates/greenfield/references/modify-existing-follow-up.md +103 -0
- package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +82 -0
- package/templates/greenfield/references/new-feature-flow.md +67 -4
- package/templates/greenfield/references/new-phase-flow.md +56 -7
- package/templates/greenfield/references/pr-review-checklist.md +287 -8
- package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +153 -32
- package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
- package/templates/greenfield/scaffolding/Git-principles-gitflow.md +74 -6
- package/templates/greenfield/scaffolding/Git-principles-trunk.md +88 -12
- package/templates/greenfield/scaffolding/_conventions.md +50 -28
- package/templates/greenfield/scaffolding/_overview.md +6 -2
- package/templates/greenfield/templates/_index.md +137 -7
- 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
|
@@ -15,26 +15,35 @@ state, archives the feature directory, and emits a Git-strategy-neutral
|
|
|
15
15
|
own. Merge strategy follows the team's selected Git policy (`gitflow` /
|
|
16
16
|
`trunk`, recorded in `dflow/specs/shared/_conventions.md` § Git Policy).
|
|
17
17
|
- Closeout is split into two gates so it works offline: a **Local-closeout
|
|
18
|
-
gate** (Steps 1–4: validation, status flip, BC sync, archive +
|
|
19
|
-
commit checkpoint — all doable with no network
|
|
18
|
+
gate** (Steps 1–4: validation, status flip, BC sync, archive + the closeout
|
|
19
|
+
commit checkpoint — all doable with no network; what is optional is **who**
|
|
20
|
+
runs that commit, not whether it happens, see Step 4) and an **Integration / PR
|
|
20
21
|
gate** (Step 5: push / merge / PR — needs network; the AI only runs
|
|
21
22
|
`git push` / `gh pr create` when you explicitly ask).
|
|
22
23
|
- At the archive checkpoint the AI may offer to commit using your Git identity;
|
|
23
24
|
you can always decline. The commit marker mode is read from `_conventions.md`
|
|
24
25
|
§ AI Commit Policy. This replaces Dflow's earlier "the AI never commits"
|
|
25
26
|
stance — the AI helps at natural checkpoints, you keep the final say.
|
|
26
|
-
- The BC-layer sync in Step 3 **reuses the existing
|
|
27
|
-
from `new-feature-flow` (
|
|
28
|
-
5.3) — it does not introduce a new sync flow. Treat it as "lift
|
|
29
|
-
|
|
27
|
+
- The BC-layer sync in Step 3 **reuses the existing documentation-sync
|
|
28
|
+
mechanism** from `new-feature-flow` (§ 8.3) and `modify-existing-flow`
|
|
29
|
+
(Step 5.3) — it does not introduce a new sync flow. Treat it as "lift
|
|
30
|
+
that step out of the per-phase checklist and run it once at feature
|
|
30
31
|
closeout, with the `_index.md` Current BR Snapshot as input."
|
|
31
32
|
|
|
32
33
|
**Step Gates** in this flow (stop-and-confirm before proceeding):
|
|
33
34
|
- Step 1 → Step 2 (validation passed → flip status)
|
|
34
35
|
- Step 3 → Step 4 (BC sync done → archive)
|
|
35
|
-
- Step 5 → Step 6 (Integration Summary emitted → optional follow-up reverse-link)
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
**Step 5 → Step 6 is deliberately not a step gate, and when the host carries a
|
|
38
|
+
`follow-up-of` field it also stops and waits.** It is this flow's
|
|
39
|
+
**post-Local-closeout confirmation**: by the time it is reached the archive has
|
|
40
|
+
landed and this feature's cursor reads `none`, so the step-gate protocol —
|
|
41
|
+
`/dflow:next` and `/dflow:cancel`, and the cursor update a step gate carries —
|
|
42
|
+
does not apply to it. **Step 5 states what it accepts.** Do not treat it as a
|
|
43
|
+
step gate because it waits, and do not treat it as step-internal because it is
|
|
44
|
+
not a gate.
|
|
45
|
+
|
|
46
|
+
**Every other step transition is step-internal**: announce "Step N complete,
|
|
38
47
|
entering Step N+1" and proceed without waiting. See AI-AGENT-GUIDE.md § Workflow
|
|
39
48
|
Transparency for the full transparency protocol and confirmation signals.
|
|
40
49
|
|
|
@@ -49,12 +58,59 @@ AI runs mechanical checks first. Report `✓` / `✗` for every item; if any
|
|
|
49
58
|
`✗` appears, **stop here** and ask the developer to address them before
|
|
50
59
|
proceeding (do not flip status, do not archive, do not emit summary).
|
|
51
60
|
|
|
61
|
+
**What these checks do and do not prove.** They catch the mistakes that actually
|
|
62
|
+
happen — work left uncommitted, a placeholder hash never replaced, a row copied
|
|
63
|
+
from another feature — not a record deliberately built to look right.
|
|
64
|
+
Whole-history assertions ("no stray host-open commit exists anywhere on this
|
|
65
|
+
branch") are **review's** job, not closeout's: they belong to
|
|
66
|
+
`references/pr-review-checklist.md`.
|
|
67
|
+
|
|
68
|
+
> **Reconcile a mainline hotfix BEFORE you run these checks.** If a T2 / T3
|
|
69
|
+
> post-hoc hotfix (`modify-existing-flow.md` Step 1.8) landed on the mainline
|
|
70
|
+
> while this feature was in flight and the two touched the same code, settle the
|
|
71
|
+
> overlap **now, before the checklist below** — anything *recorded into this
|
|
72
|
+
> host* after the checks have passed has bypassed them.
|
|
73
|
+
>
|
|
74
|
+
> **Open `references/finish-feature-post-hoc-hotfix.md` and follow it there.**
|
|
75
|
+
> What you classify, where the merge-resolution delta may be recorded, and what
|
|
76
|
+
> must not be routed mid-closeout are decided there.
|
|
77
|
+
|
|
78
|
+
**What "Minimal host (zero-phase) only" selects, and what that does not prove.**
|
|
79
|
+
A minimal host takes extra checks and extra field rules. Decide it from the
|
|
80
|
+
**persisted shape**: an **empty Phase Specs table and no `phase-spec-*` file**
|
|
81
|
+
in the host directory. There is no other selector — closeout runs in a fresh
|
|
82
|
+
session and cannot know which flow step created the host. So a host that
|
|
83
|
+
*carries* a phase is certified as **phase-bearing**, whatever it was intended to
|
|
84
|
+
be, and takes the ordinary checks rather than these. This gate does **not**
|
|
85
|
+
prove "this host was opened as minimal and stayed that way"; that is a claim
|
|
86
|
+
about history, and it belongs with the whole-history assertion already assigned
|
|
87
|
+
to `references/pr-review-checklist.md`.
|
|
88
|
+
|
|
89
|
+
**Open `references/finish-feature-minimal-host.md` and follow it there.** Do
|
|
90
|
+
this only when this host is minimal; a **phase-bearing** host does not open it
|
|
91
|
+
at all. Its rules live in that file and are not repeated here, and it stays open
|
|
92
|
+
for the whole closeout — it adds to this checklist, to Step 3's sync input, to
|
|
93
|
+
Step 4's post-commit verification, and to Step 5's Integration Summary fields.
|
|
94
|
+
|
|
52
95
|
- [ ] Locate the feature directory at `dflow/specs/features/active/{SPEC-ID}-{slug}/`
|
|
53
|
-
- [ ] `_index.md` exists and parses (YAML front matter intact, seven
|
|
54
|
-
sections present
|
|
96
|
+
- [ ] `_index.md` exists and parses (YAML front matter intact, and all seven
|
|
97
|
+
required sections present — the Metadata front matter, Goals & Scope,
|
|
98
|
+
Phase Specs, Current BR Snapshot, Lightweight Changes, Checkpoint Log,
|
|
99
|
+
Resume Pointer; `Follow-up Tracking` is `templates/_index.md`'s optional
|
|
100
|
+
eighth section and appears only when this feature has follow-ups)
|
|
55
101
|
- [ ] Every row in `_index.md` Phase Specs table has Status = `completed`
|
|
56
102
|
- [ ] Every phase-spec file referenced in the Phase Specs table exists at
|
|
57
103
|
the path the table claims
|
|
104
|
+
- [ ] **And the other direction, for every host shape: every spec file in the
|
|
105
|
+
host directory is named by a row** — each `phase-spec-*` by a Phase Specs
|
|
106
|
+
row, each `lightweight-*.md` / `BUG-*.md` by a `Tier = T2` Lightweight
|
|
107
|
+
Changes row. An orphan file **blocks** — either it belongs to a phase or
|
|
108
|
+
change whose row is missing, or it belongs to nothing and does not belong
|
|
109
|
+
in the host.
|
|
110
|
+
**One file is legitimately unrowed**: an `aggregate-design.md` worksheet,
|
|
111
|
+
which `AI-AGENT-GUIDE.md` § Ceremony Scaling orders into the feature
|
|
112
|
+
directory for a T1 introducing a new Aggregate / BC. No table names it by
|
|
113
|
+
design — it is a worksheet, not a spec — so it is not an orphan.
|
|
58
114
|
- [ ] Every phase-spec file's frontmatter has `status: completed`
|
|
59
115
|
- [ ] Every Tier = T2 row in `_index.md` Lightweight Changes references an
|
|
60
116
|
existing `lightweight-*.md` / `BUG-*.md` file in the feature directory
|
|
@@ -62,8 +118,53 @@ proceeding (do not flip status, do not archive, do not emit summary).
|
|
|
62
118
|
`status: completed`
|
|
63
119
|
- [ ] `_index.md` has no obvious open items in Resume Pointer (e.g. "phase-N
|
|
64
120
|
drafting" / "implementation pending" / "TODO" markers)
|
|
65
|
-
- [ ]
|
|
66
|
-
|
|
121
|
+
- [ ] **You are on this host's branch — or on a branch this host recorded a
|
|
122
|
+
sanctioned override for.** `git rev-parse --abbrev-ref HEAD`
|
|
123
|
+
equals `_index.md`'s `branch:` value — that field is authoritative for the
|
|
124
|
+
whole host — and for a **T2** the lightweight-spec's frontmatter `branch:`
|
|
125
|
+
equals it too. Steps 1–4 run *before* the merge / PR gate, so a mismatch
|
|
126
|
+
means you are closing out somewhere other than where the work was
|
|
127
|
+
finished, and it **blocks**. A hash check does not cover this: any sibling
|
|
128
|
+
branch descended from checkpoint 1 carries the same commits, so every hash
|
|
129
|
+
this host's record names still resolves and is still an ancestor. For a
|
|
130
|
+
**post-hoc** host this compares against the **documentation** branch,
|
|
131
|
+
never `hotfix-branch:`, which names the already-merged hotfix.
|
|
132
|
+
**Before blocking on a mismatch, read the Checkpoint Log for a
|
|
133
|
+
`branch-override` row.** `references/git-integration.md` § Branch gate
|
|
134
|
+
offers "override and stay" as a sanctioned third option and defines that
|
|
135
|
+
row's shape. If **any** such row's Result names the branch `HEAD` is on,
|
|
136
|
+
the mismatch is one this host recorded deliberately and this item
|
|
137
|
+
**passes**. `branch:` itself is still never rewritten — the override is a
|
|
138
|
+
record beside it, not a correction of it.
|
|
139
|
+
⚠ **The row must name *this* branch** — that is the whole test.
|
|
140
|
+
⚠ **What this cannot decide, stated rather than patched:** whether a
|
|
141
|
+
recorded override is still the current intent. **Nothing expires a row at
|
|
142
|
+
all** — not when the developer moves back to the host's own branch, not
|
|
143
|
+
when a later override supersedes it, and not with distance from closeout.
|
|
144
|
+
So *any* branch this host ever recorded an override for stays acceptable
|
|
145
|
+
here: closing out on the base branch after the feature branch was merged
|
|
146
|
+
passes, and so does closing out on an abandoned spike branch that an
|
|
147
|
+
override named two phases ago. Closeout cannot separate either from a
|
|
148
|
+
legitimate override. **Which branch the closeout commit actually landed on
|
|
149
|
+
is visible in the PR**, and it is judged by
|
|
150
|
+
`references/pr-review-checklist.md`'s **"A recorded branch override still
|
|
151
|
+
matches where the closeout landed"** item.
|
|
152
|
+
**When it passes this way, say so in the conversation** — name the row and
|
|
153
|
+
the branch, e.g. *"HEAD differs from `branch:`; found a recorded
|
|
154
|
+
`branch-override` for `{branch}` in the Checkpoint Log — verified and
|
|
155
|
+
passing."*
|
|
156
|
+
- [ ] Current BR Snapshot table is non-empty — **or this host's own record
|
|
157
|
+
carries no BR delta**, which is what makes an empty one legitimate.
|
|
158
|
+
That condition is the check; the shapes below are illustrations of it,
|
|
159
|
+
not the list of them: a **T3** host; a **no-BR family T2** whose Behavior
|
|
160
|
+
Delta is a `BR:` / `BR Delta:` none line; a **phase-bearing** host —
|
|
161
|
+
**including a T1** — whose phase-specs establish no BR delta, which the
|
|
162
|
+
cascade explicitly allows; or a shape added later that likewise carries
|
|
163
|
+
none. Decide from the artifact, not from a declaration:
|
|
164
|
+
"the feature is intentionally no-BR" is a claim, and the record is what
|
|
165
|
+
settles it. A classic BR-delta spec carrying ADDED / MODIFIED / RENAMED
|
|
166
|
+
entries **and** an empty Snapshot means finalization never refreshed it
|
|
167
|
+
(`modify-existing-flow.md` Step 1.7) and **blocks**.
|
|
67
168
|
|
|
68
169
|
If any check fails:
|
|
69
170
|
> "Cannot finish feature `{SPEC-ID}-{slug}` yet — {N} validation issues
|
|
@@ -75,6 +176,50 @@ If any check fails:
|
|
|
75
176
|
> Address these (run `/dflow:new-phase` to add missing work, or fix the
|
|
76
177
|
> stale status manually), then re-run `/dflow:finish-feature`."
|
|
77
178
|
|
|
179
|
+
**Once every item above — and every item this host's branch file added — reads
|
|
180
|
+
`✓`, record the baseline the post-commit check compares against.** Capture
|
|
181
|
+
**exactly the set the closeout commit will carry**:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
git ls-files --cached --others --exclude-standard -- dflow/specs/features/active/{SPEC-ID}-{slug}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
⚠ **Both halves of that command are load-bearing, because it has to agree with
|
|
188
|
+
`git add`.** It **excludes** ignored files, which `git add` also skips and the
|
|
189
|
+
span can never carry. It **includes** untracked files that are not ignored,
|
|
190
|
+
which `git add` **does** stage — a host file created during this feature and
|
|
191
|
+
never committed still rides into the closeout commit, so a baseline without it
|
|
192
|
+
reports that file as added after Step 1 and blocks a correct closeout.
|
|
193
|
+
|
|
194
|
+
Run `git hash-object -w {path}` for each and build the `path → blob` list, one
|
|
195
|
+
entry per line. **Strip the host-directory prefix**: that command prints paths
|
|
196
|
+
from the repository root, and Step 4 matches on the path *relative to the host
|
|
197
|
+
directory*.
|
|
198
|
+
⚠ **The `-w` is mandatory.** It writes each blob into the object database, so
|
|
199
|
+
Step 4 can read the baseline's **content** back; without it the list is
|
|
200
|
+
fingerprints only, and Step 4's comparison cannot run.
|
|
201
|
+
|
|
202
|
+
**Then anchor the list itself where Step 4 can recompute its name** — pipe the
|
|
203
|
+
list into `git hash-object -w --stdin`, then:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
git update-ref refs/dflow/closeout-baseline/{SPEC-ID}-{slug} {list blob}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**That ref is the baseline.** Step 4 resolves it by recomputing the same name
|
|
210
|
+
from this host's own `{SPEC-ID}-{slug}`; if `git check-ref-format` rejects that
|
|
211
|
+
name, say so and stop.
|
|
212
|
+
⚠ **Trim both fields of every entry, on write and on read.** A shell that
|
|
213
|
+
writes CRLF leaves a trailing `\r` on each line, and `git cat-file -p` on a blob
|
|
214
|
+
id carrying one fails as an invalid object name — which would report a healthy
|
|
215
|
+
baseline as unreadable.
|
|
216
|
+
⚠ **`refs/dflow/**` is local by construction.** A default `git push`, `git fetch`
|
|
217
|
+
and a plain `git clone` all leave it behind; an explicit refspec or
|
|
218
|
+
`clone --mirror` does carry it. Do not add it to any refspec.
|
|
219
|
+
⚠ **`HEAD^` is not a substitute** — at this point the working tree legitimately
|
|
220
|
+
carries uncommitted finalization edits, so comparing against the parent commit
|
|
221
|
+
reports every one of them as a difference no step ordered.
|
|
222
|
+
|
|
78
223
|
**→ Step Gate: Step 1 → Step 2**
|
|
79
224
|
|
|
80
225
|
If all checks pass:
|
|
@@ -94,45 +239,117 @@ spec-id: SPEC-{YYYYMMDD}-{NNN}
|
|
|
94
239
|
slug: {slug}
|
|
95
240
|
status: completed # ← flipped from in-progress
|
|
96
241
|
created: {YYYY-MM-DD}
|
|
97
|
-
branch: feature/{SPEC-ID}-{slug}
|
|
242
|
+
branch: {unchanged — keep the host's existing value} # feature/{SPEC-ID}-{slug} or bugfix/BUG-{NUMBER}-{slug}; never rewrite
|
|
98
243
|
---
|
|
99
244
|
```
|
|
100
245
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
246
|
+
**Flip `status` only — never rewrite `branch:`.** The `branch:` field is
|
|
247
|
+
authoritative and stays exactly as opened: a bugfix host keeps its
|
|
248
|
+
`bugfix/BUG-{NUMBER}-{slug}` value, a **non-bug** standalone / follow-up host
|
|
249
|
+
keeps its `feature/{SPEC-ID}-{slug}` value. The branch follows the **change
|
|
250
|
+
class**, not how the host was opened — a functional-bug standalone or
|
|
251
|
+
follow-up host **is** a bugfix host and keeps `bugfix/BUG-*` (the branch-by-class
|
|
252
|
+
rule at `modify-existing-flow.md` Step 1.7 step 4). Overwriting it to
|
|
253
|
+
`feature/...` at closeout would break branch equality for a bugfix host.
|
|
254
|
+
|
|
255
|
+
Also update the **Resume Pointer** — to the state closeout is actually in, which
|
|
256
|
+
is **not** the terminal state. **Step 4 writes the terminal value**, immediately
|
|
257
|
+
after the `git mv`:
|
|
104
258
|
|
|
105
259
|
```
|
|
106
|
-
**Current Progress**:
|
|
107
|
-
**Next Action**:
|
|
108
|
-
**Active Workflow**:
|
|
109
|
-
**Current Step**:
|
|
110
|
-
**Gates Passed**:
|
|
111
|
-
**Awaiting**: none
|
|
260
|
+
**Current Progress**: status flipped to `completed` ({date}); closeout in progress.
|
|
261
|
+
**Next Action**: continue closeout — sync the BR Snapshot to the BC layer (Step 3).
|
|
262
|
+
**Active Workflow**: finish-feature
|
|
263
|
+
**Current Step**: Step 3 — sync BR Snapshot to BC layer
|
|
264
|
+
**Gates Passed**: 1→2
|
|
265
|
+
**Awaiting**: none (mid-step)
|
|
112
266
|
```
|
|
113
267
|
|
|
268
|
+
⚠ **`Awaiting` is `none (mid-step)`, never `gate 3→4`.** Step 3 has not run at
|
|
269
|
+
this point, and a cursor that says otherwise sends the next session to
|
|
270
|
+
`/dflow:next`, **skipping the BC sync**. `none (mid-step)` is the existing
|
|
271
|
+
convention for this position.
|
|
272
|
+
|
|
114
273
|
**→ Transition (step-internal)**: Step 2 complete. Announce "Step 2 complete (status flipped). Entering Step 3: Sync BR Snapshot to BC layer." and continue.
|
|
115
274
|
|
|
116
275
|
## Step 3: Sync `_index.md` Current BR Snapshot to BC Layer
|
|
117
276
|
|
|
118
|
-
|
|
277
|
+
**First, branch on what this host actually carries** — "zero-phase" (no
|
|
278
|
+
phase-spec) is independent of whether a bounded context exists, so a minimal
|
|
279
|
+
host may still touch a real BC, or none at all:
|
|
280
|
+
|
|
281
|
+
- **(i) BC-bearing** — the host touched a real bounded context, **whether or
|
|
282
|
+
not it carries a BR delta** (BC presence and BR presence are independent). Run
|
|
283
|
+
the sync below **for the documents this host actually changed**: a BR delta
|
|
284
|
+
updates `rules.md` / `behavior.md`; a no-BR family that touched, say, an event
|
|
285
|
+
field updates `events.md`. Fill the Integration Summary's BC field with the
|
|
286
|
+
context, and its BR-IDs with whatever applies — a real set, the per-family
|
|
287
|
+
no-BR marker, or empty. Do **not** skip, and do **not** manufacture a BR delta
|
|
288
|
+
a no-BR host does not have.
|
|
289
|
+
**`Aggregates affected:` and `Domain Events Changes:` take what this host
|
|
290
|
+
actually changed — and `none` for those it did not.** ⚠ **Every BC-bearing
|
|
291
|
+
host, not only a zero-phase one.** An unchanged field is reported `none`, never
|
|
292
|
+
left blank: a blank cannot be told apart from a field nobody filled in, and the
|
|
293
|
+
reader of this summary has no other source for the difference. The same shape
|
|
294
|
+
for a zero-phase host, and the authority on that host's *whole* field set, is
|
|
295
|
+
`references/finish-feature-minimal-host.md` § Step 5. This sentence is the
|
|
296
|
+
half that was only ever written there.
|
|
297
|
+
- **(ii) no-BC** — the host touched **no** bounded context at all (a display
|
|
298
|
+
T3, an appearance sweep). **Skip this sync entirely** — do **not** create
|
|
299
|
+
`rules.md` / `behavior.md` / `events.md`, and do not invent a BC to sync into.
|
|
300
|
+
Its Integration Summary sets the fields that **report a sync** to `none` —
|
|
301
|
+
`BC`, `Aggregates affected`, `Domain Events Changes`. **`Related BR-IDs` is
|
|
302
|
+
not one of those**: it reports what this change's own record carries, so it
|
|
303
|
+
stays empty or keeps the per-family no-BR marker. Step 5's "exact fields"
|
|
304
|
+
block is the authority on the shape; do not flatten it to "all BR fields are
|
|
305
|
+
none" here.
|
|
306
|
+
|
|
307
|
+
For a **BC-bearing** host, continue with the sync. This step **reuses the
|
|
308
|
+
existing sync mechanism** from `new-feature-flow`
|
|
119
309
|
Step 8.3 / `modify-existing-flow` Step 5.3 (`dflow/specs/domain/{context}/rules.md`
|
|
120
310
|
+ `behavior.md` + `events.md` + `context-map.md` updates). The input is
|
|
121
311
|
the feature's `_index.md` Current BR Snapshot table; the output is the
|
|
122
312
|
BC's `rules.md` / `behavior.md` updated to reflect the feature's net
|
|
123
313
|
effect.
|
|
124
314
|
|
|
125
|
-
|
|
315
|
+
**Minimal host — sync input.** A minimal host reads "phase-spec" differently
|
|
316
|
+
here, and a phase-bearing host must not apply that reading. The rule is
|
|
317
|
+
`references/finish-feature-minimal-host.md` § Step 3.
|
|
318
|
+
|
|
319
|
+
**Every host — lightweight-spec deltas are a sync input too.** A host of **any**
|
|
320
|
+
shape may carry hosted Lightweight Changes rows: Step 1's two `Tier = T2` checks
|
|
321
|
+
carry no host-shape restriction, so they reach a phase-bearing host as well. So
|
|
322
|
+
a phase-bearing host can hold a hosted T2 whose delta belongs in
|
|
323
|
+
this sync, and **the steps below name phase-specs only**. Read them as *this
|
|
324
|
+
feature's phase-specs **and** its hosted lightweight-specs' recorded deltas*.
|
|
325
|
+
⚠ **Where this bites hardest is a no-BR family**, because the BR-driven sections
|
|
326
|
+
below cannot reach it at all: with no BR-ID there is no Current BR Snapshot row
|
|
327
|
+
to iterate, so a hosted T2 that changed only an event field or a documented
|
|
328
|
+
behaviour is visible **solely** in its own recorded delta. Miss it and
|
|
329
|
+
`events.md` / `behavior.md` silently lose a change that a phase-bearing closeout
|
|
330
|
+
had no other instruction to look for.
|
|
331
|
+
|
|
332
|
+
Before syncing, ensure the BC files **this sync actually writes** exist; create a
|
|
333
|
+
missing one from its template **only when this host's delta writes to it**. Case
|
|
334
|
+
(i) above syncs "the documents this host actually changed", so a document this
|
|
335
|
+
host does not touch is **not** created — a BC-bearing **T3**, or a no-BR family
|
|
336
|
+
that touched only one of them, leaves the others absent, exactly as case (ii)
|
|
337
|
+
does for a no-BC host. Creating one anyway plants the same fiction the no-BC
|
|
338
|
+
guard refuses:
|
|
126
339
|
- `dflow/specs/domain/{context}/rules.md` → `templates/rules.md`
|
|
127
340
|
- `dflow/specs/domain/{context}/behavior.md` → `templates/behavior.md`
|
|
128
341
|
- `dflow/specs/domain/{context}/events.md` → `templates/events.md`
|
|
129
342
|
|
|
130
|
-
|
|
343
|
+
> **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).
|
|
131
344
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
345
|
+
For each row in Current BR Snapshot — **every** row, not only the `active` ones.
|
|
346
|
+
The branch below is selected by the row's own Status, and a `removed` row is
|
|
347
|
+
exactly what the REMOVED branch needs:
|
|
348
|
+
|
|
349
|
+
- Status `active`, and the BR-ID is **not yet in `rules.md`** → add it (new
|
|
350
|
+
ADDED rule introduced by this feature)
|
|
351
|
+
- Status `active`, and the BR-ID is **already in `rules.md`** but the rule text
|
|
352
|
+
differs → update it (MODIFIED rule, reflect the new text)
|
|
136
353
|
- If the BR-ID was previously in `rules.md` and is now in Current BR
|
|
137
354
|
Snapshot with Status = `removed` → remove the corresponding section in
|
|
138
355
|
`rules.md` (REMOVED rule)
|
|
@@ -151,7 +368,10 @@ For `behavior.md`:
|
|
|
151
368
|
`behavior.md`
|
|
152
369
|
|
|
153
370
|
For `events.md`:
|
|
154
|
-
- Add any new Domain Events introduced by phase-specs
|
|
371
|
+
- Add any new Domain Events introduced by this feature — **by its phase-specs
|
|
372
|
+
and by its hosted lightweight-specs alike** (see "Every host — lightweight-spec
|
|
373
|
+
deltas are a sync input too" above; a hosted no-BR T2 appears in neither the
|
|
374
|
+
phase-specs nor the BR Snapshot)
|
|
155
375
|
- Remove events that were REMOVED across the feature's net delta
|
|
156
376
|
- Update producers / consumers if Aggregate ownership shifted
|
|
157
377
|
|
|
@@ -164,7 +384,8 @@ rules.md ↔ behavior.md drift check (see `references/drift-verification.md`).
|
|
|
164
384
|
|
|
165
385
|
Cross-reference each phase-spec's Delta-from-prior-phases section to
|
|
166
386
|
double-check the net result; the Snapshot is the SSOT but the per-phase
|
|
167
|
-
Deltas are the audit trail.
|
|
387
|
+
Deltas are the audit trail. **Cross-reference each hosted lightweight-spec's
|
|
388
|
+
recorded delta the same way** — on any host shape, for the reason above.
|
|
168
389
|
|
|
169
390
|
> Note: this step does NOT read individual phase-specs to re-derive the BR
|
|
170
391
|
> set — that work was already reconciled by `/dflow:new-phase` Step 7 each
|
|
@@ -173,10 +394,12 @@ Deltas are the audit trail.
|
|
|
173
394
|
> and the phase-specs, fix `_index.md` first, then re-run
|
|
174
395
|
> `/dflow:finish-feature`.
|
|
175
396
|
|
|
176
|
-
Also update `architecture/tech-debt.md` / `models.md` / `glossary.md`
|
|
177
|
-
discovered during the feature (the same items listed in
|
|
178
|
-
`new-feature-flow.md` Step 8.3
|
|
179
|
-
|
|
397
|
+
Also update `architecture/tech-debt.md` / `models.md` / `glossary.md` /
|
|
398
|
+
`analysis.md` as discovered during the feature (the same items listed in
|
|
399
|
+
`new-feature-flow.md` Step 8.3; `analysis.md` is two files — the owning
|
|
400
|
+
context's and the domain-root one — each created from `templates/analysis.md`
|
|
401
|
+
the first time there is something to record) — these may have been touched per
|
|
402
|
+
phase already; this is the closeout sweep.
|
|
180
403
|
|
|
181
404
|
**→ Step Gate: Step 3 → Step 4**
|
|
182
405
|
|
|
@@ -186,25 +409,78 @@ already; this is the closeout sweep.
|
|
|
186
409
|
> `context-map.md` {updated / unchanged}, `last-updated` set to {date}.
|
|
187
410
|
> Ready to archive the feature directory? `/dflow:next` to proceed."
|
|
188
411
|
|
|
412
|
+
For a **no-BC host** (case ii) the sync was skipped — do **not** announce a
|
|
413
|
+
sync that did not happen. Say instead: "No BC-scoped sync was performed (no-BC
|
|
414
|
+
host). Ready to archive the feature directory? `/dflow:next` to proceed." Do
|
|
415
|
+
**not** say "nothing was written to the Domain layer" — a no-BC host may still
|
|
416
|
+
have updated a **global** document (`glossary.md`, `domain/analysis.md`,
|
|
417
|
+
`architecture/tech-debt.md`); those belong to no bounded context and Step 4
|
|
418
|
+
must still stage them.
|
|
419
|
+
|
|
189
420
|
Wait for confirmation before entering Step 4.
|
|
190
421
|
|
|
191
422
|
## Step 4: Archive — `git mv` the Feature Directory
|
|
192
423
|
|
|
193
|
-
|
|
424
|
+
**First, confirm the baseline is still readable — before anything irreversible:**
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
git cat-file -p refs/dflow/closeout-baseline/{SPEC-ID}-{slug}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
If it does not resolve, or the list it prints names a blob that does not read
|
|
431
|
+
back, **stop here** and say the baseline is unavailable: closeout restarts from
|
|
432
|
+
Step 1, which re-captures it. **Nothing is committed yet and the host is still in
|
|
433
|
+
`active/`** — that is what this position buys.
|
|
434
|
+
⚠ **This does not prove the baseline is the *right* one** — only that it is
|
|
435
|
+
readable. Reading back and *judging* is the post-commit item's job.
|
|
436
|
+
|
|
437
|
+
Then AI runs:
|
|
194
438
|
|
|
195
439
|
```bash
|
|
196
440
|
git mv dflow/specs/features/active/{SPEC-ID}-{slug} \
|
|
197
441
|
dflow/specs/features/completed/{SPEC-ID}-{slug}
|
|
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
|
|
200
442
|
```
|
|
201
443
|
|
|
202
|
-
`git mv` is mandatory — never use plain `mv` + `git add`.
|
|
203
|
-
git's directory rename detection so `git log --follow` / `git blame` /
|
|
204
|
-
PR diff quality stays intact across the move. See
|
|
444
|
+
`git mv` is mandatory — never use plain `mv` + `git add`. See
|
|
205
445
|
`references/git-integration.md` § "Directory Moves Must Use git mv" for
|
|
206
446
|
the full rule set.
|
|
207
447
|
|
|
448
|
+
**Immediately after the `git mv`, write the Resume Pointer's terminal value into
|
|
449
|
+
the moved `_index.md`.** Write both prose lines and all four cursor fields —
|
|
450
|
+
all six lines of the Resume Pointer — because this write sets each line's final
|
|
451
|
+
value:
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
**Current Progress**: feature completed ({date}); all phase-specs status = completed.
|
|
455
|
+
**Next Action**: integration — push / merge / PR per the selected Git policy.
|
|
456
|
+
**Active Workflow**: none
|
|
457
|
+
**Current Step**: n/a
|
|
458
|
+
**Gates Passed**: n/a
|
|
459
|
+
**Awaiting**: none
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
⚠⚠ **The `git mv` and this write are one uninterruptible pair.** Nothing goes
|
|
463
|
+
between them — not the Y / N prompt below, not a question to the developer, not
|
|
464
|
+
a tool call that can wait on input, and not the `git status` check below.
|
|
465
|
+
|
|
466
|
+
⚠ **From here the cursor is terminal and closeout does not edit it again** — not
|
|
467
|
+
when the developer declines the commit (N), not when the commit fails, not when
|
|
468
|
+
the post-commit verification below reports `✗`. **Do not restore it to an
|
|
469
|
+
in-progress value on any of those paths.** What surfaces a closeout that failed
|
|
470
|
+
after this point is `git status` — the staged rename plus the uncommitted
|
|
471
|
+
`_index.md` edits — not the cursor, which no global scan reads once the host has
|
|
472
|
+
left `active/`.
|
|
473
|
+
|
|
474
|
+
**Now check the rename landed**, with the terminal cursor already written:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
git status --short # confirm rename detection AND check for `RM` — an `M`
|
|
478
|
+
# next to a rename means unstaged edits you must re-add
|
|
479
|
+
# before committing. --short is required: the default long
|
|
480
|
+
# format lists the rename and the modification separately
|
|
481
|
+
# and never prints a two-column `RM`.
|
|
482
|
+
```
|
|
483
|
+
|
|
208
484
|
**Closeout commit checkpoint** (completes the offline Local-closeout gate):
|
|
209
485
|
|
|
210
486
|
```
|
|
@@ -225,6 +501,14 @@ Then, in this order:
|
|
|
225
501
|
still applies to spec / implementation rows — closeout is the documented
|
|
226
502
|
exception (see `references/git-integration.md` § Commit Checkpoints,
|
|
227
503
|
Branch Gate & AI Commits).
|
|
504
|
+
**Backfill any unfilled hosted `Commit` cell in the same edit** — a hosted row
|
|
505
|
+
waits for the host's *next* commit and this is it. **Unfilled means empty *or*
|
|
506
|
+
holding a placeholder** — `{hash}`, `{pending}`, `(待 commit)`, anything that
|
|
507
|
+
`git cat-file -t` cannot resolve; both are backfilled the same way, and the
|
|
508
|
+
placeholder case matters more because the cell is **non-empty** and therefore
|
|
509
|
+
invisible to every rule written against empty / non-empty. Write each row's own
|
|
510
|
+
implementation hash, never the closeout hash. After closeout there is no next
|
|
511
|
+
commit, which is why `references/pr-review-checklist.md` asserts it there.
|
|
228
512
|
2. **Stage the whole archived feature directory:**
|
|
229
513
|
|
|
230
514
|
```bash
|
|
@@ -232,14 +516,68 @@ Then, in this order:
|
|
|
232
516
|
```
|
|
233
517
|
|
|
234
518
|
This is required, not optional: `git mv` stages the rename with the
|
|
235
|
-
**last-committed** content, so working-tree
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
519
|
+
**last-committed** content, so **every** working-tree edit to the moved files
|
|
520
|
+
stays **unstaged** until this `git add` — Step 2's status flip and its
|
|
521
|
+
in-progress cursor, whatever gate 3 → 4 wrote to the cursor, the terminal
|
|
522
|
+
cursor value written just after the `git mv`, and **everything instruction 1
|
|
523
|
+
ordered**: the checkpoint row *and* every hosted `Commit` cell it backfilled.
|
|
524
|
+
Take that last part from instruction 1 itself, not from this sentence — it is
|
|
525
|
+
the step that orders those edits, and a copy kept here would go stale the
|
|
526
|
+
next time it changes. In `git status --short`, the moved `_index.md` showing
|
|
527
|
+
`RM` instead of plain `R` is exactly this signal — the same `--short`
|
|
528
|
+
requirement as the rename check above. Then also `git add`
|
|
529
|
+
**every external document this closeout carries.** That set is defined
|
|
530
|
+
here, once, and it covers **every** host shape — take it from this
|
|
531
|
+
instruction, not from a list kept somewhere else:
|
|
532
|
+
**(a)** whatever Step 3 wrote (`rules.md`, `behavior.md`, `events.md`,
|
|
533
|
+
`models.md`, `analysis.md`, `context-map.md`, `glossary.md`,
|
|
534
|
+
`domain/analysis.md`, `architecture/tech-debt.md`) — Step 3 is **skipped
|
|
535
|
+
entirely for a no-BC host**, so this half is empty there; **and**
|
|
536
|
+
**(b)** the **documentation-sweep step of the flow that produced *this
|
|
537
|
+
change*** — take the paths from that step, not from a list kept here. It runs
|
|
538
|
+
*after* that flow's implementation checkpoint and *before* closeout, so **that
|
|
539
|
+
flow** leaves its deltas uncommitted; a later phase's own checkpoints may have
|
|
540
|
+
committed them since, and staging an already-committed path is a no-op.
|
|
541
|
+
Those sweeps reach
|
|
542
|
+
Domain-layer documents under `dflow/specs/domain/`, plus the **global**
|
|
543
|
+
documents `glossary.md`, `domain/analysis.md` and
|
|
544
|
+
`architecture/tech-debt.md`, which belong to no bounded context and stay
|
|
545
|
+
legitimate for a **no-BC** host. The two that exist
|
|
546
|
+
today —
|
|
547
|
+
`modify-existing-flow.md` **Step 5.3** and `new-feature-flow.md`
|
|
548
|
+
**§ 8.3 Documentation updates** — are **illustrations, not the definition**:
|
|
549
|
+
a later flow with a sweep of its own is covered without editing this line.
|
|
550
|
+
⚠ **Key it on the flow that produced the change, never on the flow that
|
|
551
|
+
opened the host.**
|
|
552
|
+
⚠ **How to tell which flow produced a change: it is determined by the
|
|
553
|
+
artifact, and it is recorded nowhere.** The ledger has no producer column, so
|
|
554
|
+
read it off the shape.
|
|
555
|
+
A **Lightweight Changes row** was produced by `modify-existing-flow.md` **by
|
|
556
|
+
construction** — that is the only flow that writes such a row
|
|
557
|
+
(`/dflow:bug-fix` routes into the same file, and `new-feature-flow.md` creates
|
|
558
|
+
that table empty and never adds to it), so its sweep is **Step 5.3**.
|
|
559
|
+
A **phase-spec** has three possible producers — `/dflow:new-feature`,
|
|
560
|
+
`/dflow:new-phase`, or `modify-existing-flow.md` Step 1.6 / 5.4 routing into
|
|
561
|
+
the new-feature machinery — and **you do not have to tell them apart**:
|
|
562
|
+
§ 8.3 and Step 5.3 enumerate the same external paths, and `/dflow:new-phase`
|
|
563
|
+
has no sweep at all, so every branch yields that same set or a part of it.
|
|
564
|
+
⚠ **`/dflow:new-phase` has no sweep and is deliberately absent here.**
|
|
565
|
+
`new-phase-flow.md` Step 7 updates the phase-spec and this host's own tables,
|
|
566
|
+
and says so in as many words — "The bounded context's `rules.md` /
|
|
567
|
+
`behavior.md` / `events.md` and the feature directory move to `completed/`
|
|
568
|
+
remain `/dflow:finish-feature` responsibilities; Step 2 records into
|
|
569
|
+
`analysis.md` directly, at whichever of its two paths the entry belongs to.
|
|
570
|
+
**Do not sync any other BC-level current state**". The external writes its
|
|
571
|
+
own steps call for are those two paths, and the set above already carries
|
|
572
|
+
them.
|
|
573
|
+
On a **minimal host** that set is exactly what allow-list member (iii) of
|
|
574
|
+
`references/finish-feature-minimal-host.md`'s uncommitted-source check
|
|
575
|
+
admitted. On a **phase-bearing** host there is **no allow-list at all** —
|
|
576
|
+
that check is minimal-host-only and lives in that branch file — so read the
|
|
577
|
+
sweep directly and **do not go looking for a list that host never produced.**
|
|
578
|
+
Read each delta (`git diff -- {path}`) and scope every one to *this change*;
|
|
579
|
+
staging only "what Step 3 wrote" leaves a no-BC host's global delta dirty and
|
|
580
|
+
the post-commit clean-tree check fails.
|
|
243
581
|
3. **Commit (Y) or stop (N).** For Y the AI commits. If a pre-commit hook
|
|
244
582
|
rejects it or the commit fails, flip the checkpoint row to `failed` (the
|
|
245
583
|
row is not committed yet — edit it directly), surface the error, and
|
|
@@ -249,19 +587,159 @@ Then, in this order:
|
|
|
249
587
|
declaring the Local-closeout gate satisfied, AI runs and reports `✓` / `✗` for
|
|
250
588
|
every item:
|
|
251
589
|
|
|
252
|
-
- [ ] `git
|
|
253
|
-
—
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
590
|
+
- [ ] `git ls-tree -r --name-only HEAD
|
|
591
|
+
dflow/specs/features/completed/{SPEC-ID}-{slug}/` — **every file this
|
|
592
|
+
commit carries from inside the archived host directory** — then
|
|
593
|
+
`git show HEAD:<path>` for each.
|
|
594
|
+
**The span, fixed once and used by both halves of this check:** exactly
|
|
595
|
+
that set — nothing wider, nothing narrower. What may differ, and what
|
|
596
|
+
blocks, are judged over exactly that span.
|
|
597
|
+
⚠ **The span is derived from what was staged, not from what the tables
|
|
598
|
+
name.**
|
|
599
|
+
**The baseline is the `path → blob` list Step 1 anchored** — read it with
|
|
600
|
+
`git cat-file -p refs/dflow/closeout-baseline/{SPEC-ID}-{slug}`, recomputing
|
|
601
|
+
that name from this host's own `{SPEC-ID}-{slug}`. The tree Step 1 read is
|
|
602
|
+
captured rather than remembered, **and so is the way back into it** — this
|
|
603
|
+
resolves in a session that never saw Step 1. **Both sides are already
|
|
604
|
+
host-relative** — Step 1 stripped its prefix, this span is read under
|
|
605
|
+
`completed/{SPEC-ID}-{slug}/`, and the `git mv` preserved the relative
|
|
606
|
+
tree — so the two sets line up with no prefix left to reconcile.
|
|
607
|
+
**Compare the two path sets first, before any blob comparison.** A
|
|
608
|
+
**baseline path missing from the span** is a file removed after Step 1; a
|
|
609
|
+
**span path with no baseline entry** is a file added after Step 1. Each is
|
|
610
|
+
a difference and is judged like any other: it **blocks** unless a step of
|
|
611
|
+
this closeout ordered it.
|
|
612
|
+
Then, for each path the two sets share, take the committed blob with
|
|
613
|
+
`git rev-parse HEAD:{completed path}`. **Different from that path's
|
|
614
|
+
baseline blob → read the delta itself with `git diff {baseline blob}
|
|
615
|
+
HEAD:{completed path}`, and judge that delta against the derived
|
|
616
|
+
condition below. Equal → that path carries no difference at all, which
|
|
617
|
+
satisfies the condition only where no step of this closeout ordered an
|
|
618
|
+
edit to it; where one did, the ordered edit never landed and this
|
|
619
|
+
**blocks**.**
|
|
620
|
+
⚠ **Blob ids alone answer only *same* / *different*, which cannot decide
|
|
621
|
+
the derived condition.**
|
|
622
|
+
⚠ **If the ref does not resolve, or the content behind it does not read
|
|
623
|
+
back, report this check as degraded and say so — do not substitute
|
|
624
|
+
`HEAD^`.** The probe at the top of this step exists so that this is reached
|
|
625
|
+
only when the baseline became unreadable *during* closeout.
|
|
626
|
+
Step 1 blocks unless every
|
|
627
|
+
spec in the host already reads `status: completed`, so those flips are
|
|
628
|
+
already **in** the baseline whichever step made them: a **minimal** host's
|
|
629
|
+
come from `modify-existing-flow.md` Step 1.7's finalization, a
|
|
630
|
+
**phase-bearing** host's from its phases completing — no phase-bearing
|
|
631
|
+
host runs Step 1.7, whichever flow opened it. Either way the
|
|
632
|
+
flips are not differences this check admits; they are part of what it
|
|
633
|
+
compares against. (They are differences only from the *pre-flip* record,
|
|
634
|
+
which is not the baseline.)
|
|
635
|
+
**The condition is derived, not listed:** within that span, the committed
|
|
636
|
+
blob differs from that baseline in **exactly the edits Step 2, Step 4's
|
|
637
|
+
terminal Resume Pointer write and Step 4 instruction 1 ordered, carrying
|
|
638
|
+
the values those steps state, and in nothing else**. Read those three
|
|
639
|
+
steps and compute the set — do not keep a second copy of it here. Any
|
|
640
|
+
difference **no step of this closeout ordered** is edit fallout and
|
|
641
|
+
**blocks**.
|
|
642
|
+
⚠ **This compares final net state, not the sequence of edits.** Step 2
|
|
643
|
+
writes an in-progress cursor and gate 3 → 4 updates it again; Step 4's
|
|
644
|
+
terminal write then overwrites every field either of them touched, so
|
|
645
|
+
neither appears in the committed blob. **Their absence is not a difference
|
|
646
|
+
and must not block**, and nothing here proves they happened. What this
|
|
647
|
+
comparison settles is that the *terminal* value is the one that landed.
|
|
648
|
+
**What this check cannot decide — stated, not asserted:** whether a
|
|
649
|
+
backfilled hosted `Commit` cell holds *that row's own* implementation hash
|
|
650
|
+
rather than some other commit's. Instruction 1 orders that value and is
|
|
651
|
+
the single place the constraint lives; closeout cannot verify it, because
|
|
652
|
+
the hash-evidence test is minimal-host-only and lives in
|
|
653
|
+
`references/finish-feature-minimal-host.md`, so on a phase-bearing host
|
|
654
|
+
**no check reads the value at all**.
|
|
655
|
+
**Identity is confirmed by `references/pr-review-checklist.md`'s
|
|
656
|
+
"Hosted `Commit` cell identity" item**, which sits in that file's
|
|
657
|
+
*Delegated to review by `finish-feature-flow.md`* block — the same
|
|
658
|
+
assignment this file already makes for the whole-history assertion.
|
|
659
|
+
⚠ **Not** its "Every Lightweight Changes row now carries a `Commit` hash"
|
|
660
|
+
item: that one asserts **presence, not correctness**, and pointing a
|
|
661
|
+
boundary at a presence check would make it a hole rather than a division
|
|
662
|
+
of labour.
|
|
663
|
+
⚠ **An empty hosted `Commit` cell does not fail this item — and this item
|
|
664
|
+
does not clear it either.** `references/pr-review-checklist.md` asserts that
|
|
665
|
+
cell and says so in its own words; **this check does not**. Judge the item on
|
|
666
|
+
the rest of its evidence and leave the cell to that checklist.
|
|
667
|
+
**Scope, stated so it is not read as more than it is:** this gate re-reads
|
|
668
|
+
the archived **host directory** only — what the archived record *says*.
|
|
669
|
+
What the closeout **commit** *contains* is the separate item below; neither
|
|
670
|
+
implies the other, and "closeout is clean" needs both.
|
|
671
|
+
What has actually gone missing this way: `branch:` rewritten away from the
|
|
672
|
+
value the host opened with (Step 2 flips `status` only, so a bugfix host
|
|
673
|
+
still reads `bugfix/BUG-{NUMBER}-{slug}`); `follow-up-of` dropped by the
|
|
674
|
+
Step 2 metadata edit — Step 5 skips Step 6 when it is absent, so losing it
|
|
675
|
+
strands the original feature's reverse link at `in-progress` while
|
|
676
|
+
closeout reports success; a Lightweight Changes row, or its `Commit` cell,
|
|
677
|
+
deleted — archiving an effectively empty host.
|
|
678
|
+
The closeout row's Result must also **not** be `failed`: you are reading a
|
|
679
|
+
commit that landed, so `failed` contradicts reality (`committed` on the Y
|
|
680
|
+
path, `skipped` when the developer declined and committed it themselves).
|
|
681
|
+
**Minimal host (zero-phase) additionally**: one further condition on the
|
|
682
|
+
first checkpoint row, stated in
|
|
683
|
+
`references/finish-feature-minimal-host.md` § Step 4.
|
|
684
|
+
- [ ] **The closeout commit contains only what closeout is allowed to write.**
|
|
685
|
+
Read its changed paths (`git show --stat HEAD`) and admit **only** the
|
|
686
|
+
archived host directory — the `git mv` rename plus the finalization
|
|
687
|
+
Step 1 validated — **plus exactly the external paths Step 4 instruction 2
|
|
688
|
+
ordered staged**. That is the same derivation the item above uses, applied
|
|
689
|
+
to the other half of "closeout is clean": the permitted set comes from the
|
|
690
|
+
instruction that ordered the staging, never from a copy kept here.
|
|
691
|
+
**Anything else blocks**: implementation source, or a
|
|
692
|
+
`domain/{context}/…` document under a host that declared itself no-BC.
|
|
693
|
+
⚠ **That no-BC half applies to every host shape, and on a phase-bearing
|
|
694
|
+
host this is the only place closeout tests it.** The
|
|
695
|
+
`Minimal host (zero-phase), no-BC only` check in
|
|
696
|
+
`references/finish-feature-minimal-host.md` reads checkpoint 1, and a
|
|
697
|
+
phase-bearing host has no checkpoint 1 to read — so do not treat "the
|
|
698
|
+
minimal-host branch file already covers no-BC" as true here. It is true
|
|
699
|
+
only for a minimal host.
|
|
700
|
+
**Scope: this is a path-level spill check and nothing more.** It proves no
|
|
701
|
+
unpermitted *file* entered the commit — not that the permitted ones carry
|
|
702
|
+
only this host's delta. Judging a hunk inside `rules.md` as "this host's
|
|
703
|
+
change rather than an unrelated BR edit" needs the intended delta, which
|
|
704
|
+
lives in the spec and the row, not in a path list. The item that reads one
|
|
705
|
+
against the other is `references/pr-review-checklist.md`'s
|
|
706
|
+
**"The closeout commit carries only this host's delta"**.
|
|
707
|
+
Say that plainly rather than implying the stronger claim.
|
|
708
|
+
⚠ **And its span is this one commit.** Between them, the minimal-host
|
|
709
|
+
branch file's check (a minimal host's checkpoint 1) and this one (any
|
|
710
|
+
host's closeout commit)
|
|
711
|
+
still leave every *other* commit on the branch unread — where a no-BC host
|
|
712
|
+
is concerned, that gap is closed by
|
|
713
|
+
`references/pr-review-checklist.md`'s **"A no-BC host committed no
|
|
714
|
+
BC-scoped Domain material"** item, which takes the branch range for
|
|
715
|
+
**every** no-BC host.
|
|
258
716
|
- [ ] `dflow/specs/features/active/{SPEC-ID}-{slug}/` no longer exists (the
|
|
259
717
|
directory was moved, not copied)
|
|
260
718
|
- [ ] `git status --short` shows no leftovers related to this feature
|
|
261
719
|
(working tree clean; identify any unrelated dirty files explicitly)
|
|
262
720
|
|
|
263
|
-
|
|
264
|
-
|
|
721
|
+
**Once every item above reads `✓`, release the baseline anchor:**
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
git update-ref -d refs/dflow/closeout-baseline/{SPEC-ID}-{slug}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
⚠ **Only after they all pass** — a failed item is fixed and re-verified against
|
|
728
|
+
this same evidence, so the anchor has to survive it.
|
|
729
|
+
⚠ **Deleting the ref is not a worktree edit**, so it is not a difference the
|
|
730
|
+
item above could have seen.
|
|
731
|
+
|
|
732
|
+
If any item fails, do **not** declare closeout complete — fix it and re-verify.
|
|
733
|
+
**How you may fix it depends on the host.** A **phase-bearing** feature has no
|
|
734
|
+
fixed commit count, so re-add and amend *or* a follow-up commit both work; the
|
|
735
|
+
developer chooses. A **minimal (zero-phase) host** does not: its lifecycle is
|
|
736
|
+
exactly checkpoint 1 plus closeout, and every check its branch file adds rests
|
|
737
|
+
on that, so the
|
|
738
|
+
repair must go **into the closeout commit itself** — re-add and amend. Nothing
|
|
739
|
+
has been pushed yet (integration is Step 5), so amending is safe here. A
|
|
740
|
+
follow-up commit would give the host a third host-mutating commit while its
|
|
741
|
+
ledger still claims two; the Step 6 flip is the *only* sanctioned
|
|
742
|
+
post-closeout commit, and it touches the original feature, not this host.
|
|
265
743
|
|
|
266
744
|
The Local-closeout gate is satisfied **only when the closeout is committed and
|
|
267
745
|
the verification above passes**. If you declined the commit (chose N) or it
|
|
@@ -277,7 +755,32 @@ offline; integration happens in Step 5 when you have network.
|
|
|
277
755
|
|
|
278
756
|
## Step 5: Emit Integration Summary (Git-strategy-neutral)
|
|
279
757
|
|
|
280
|
-
|
|
758
|
+
**First, print the closeout verification's derivation.** Before the summary
|
|
759
|
+
itself, state in the conversation *how* Step 4's post-commit verification
|
|
760
|
+
reached its result — not that it passed, but how it was computed:
|
|
761
|
+
|
|
762
|
+
1. **The baseline** — the tree Step 1 read, and which step put each spec's
|
|
763
|
+
`status: completed` flip into it (Step 1.7's finalization on a minimal host;
|
|
764
|
+
the phases completing on a phase-bearing one, which never runs Step 1.7).
|
|
765
|
+
2. **The differences you derived, each with the step that ordered it** —
|
|
766
|
+
Step 2, Step 4's terminal Resume Pointer write, or Step 4 instruction 1. This
|
|
767
|
+
is the set the check *derives* instead of listing, so printing it is what
|
|
768
|
+
lets a reader check the derivation rather than trust it. Say as well that the
|
|
769
|
+
comparison reads final net state, so the transitional cursor values Step 2
|
|
770
|
+
and gate 3 → 4 wrote are outside what it can show.
|
|
771
|
+
3. **The external paths admitted, and which half of Step 4 instruction 2 each
|
|
772
|
+
came from** — what Step 3 wrote, or this change's own sweep.
|
|
773
|
+
4. **What the check could not decide, and who holds it** — for a backfilled
|
|
774
|
+
hosted `Commit` cell, that closeout cannot decide whether the value is that
|
|
775
|
+
row's own implementation hash, and that
|
|
776
|
+
`references/pr-review-checklist.md`'s **"Hosted `Commit` cell identity"**
|
|
777
|
+
item is where it gets confirmed (not its presence check). Say this **even
|
|
778
|
+
when there was nothing to backfill**, so its absence is visible rather than
|
|
779
|
+
silent.
|
|
780
|
+
5. **Any check that passed on a recorded exception** — e.g. a branch mismatch
|
|
781
|
+
honoured by a `branch-override` row, naming the row and the branch.
|
|
782
|
+
|
|
783
|
+
Then produce a plain-text summary of what this feature did. The summary is
|
|
281
784
|
**not** a commit message template — it is reference material the
|
|
282
785
|
developer adapts to whichever merge strategy their project uses
|
|
283
786
|
(merge commit, squash, rebase, fast-forward — Dflow stays neutral).
|
|
@@ -322,17 +825,70 @@ Next Steps (developer) — Integration / PR gate (needs network):
|
|
|
322
825
|
you, but only when you explicitly ask; it never pushes on its own
|
|
323
826
|
```
|
|
324
827
|
|
|
828
|
+
**Zero-phase minimal host — exact fields.** A zero-phase host does not fill the
|
|
829
|
+
format above the way a phase-bearing one does. Its whole field set, and the
|
|
830
|
+
authority on that shape, is `references/finish-feature-minimal-host.md`
|
|
831
|
+
§ Step 5.
|
|
832
|
+
|
|
325
833
|
Print the summary to the conversation; do not write it to a file (it is
|
|
326
834
|
ephemeral closeout output).
|
|
327
835
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
836
|
+
**A mainline hotfix that overlapped this feature.** Step 4 has already archived
|
|
837
|
+
and committed this host — **it is frozen**, so anything a merge with the
|
|
838
|
+
mainline leaves over is routed elsewhere and never recorded back into it. What
|
|
839
|
+
is left over, how to classify it, and where each class goes are decided in
|
|
840
|
+
`references/finish-feature-post-hoc-hotfix.md` § After closeout — the same
|
|
841
|
+
branch file Step 1's hotfix callout dispatches to.
|
|
842
|
+
|
|
843
|
+
**→ Post-Local-closeout confirmation: Step 5 → Step 6**
|
|
844
|
+
|
|
845
|
+
**This stops and waits, and it is not a step gate.** By now the host is in
|
|
846
|
+
`completed/` and its cursor reads `none`. **Confirming here updates no cursor**,
|
|
847
|
+
and nothing below re-opens the workflow.
|
|
848
|
+
|
|
849
|
+
If the feature has `follow-up-of` in its Metadata, prompt the developer —
|
|
850
|
+
naming **every** original it lists (the field may be a YAML array):
|
|
851
|
+
> "This feature is a follow-up of `{原 SPEC-ID}` (and `{原 SPEC-ID-2}`, …).
|
|
852
|
+
> Ready to update **each** original feature's `_index.md` Follow-up Tracking row
|
|
853
|
+
> to mark this follow-up as `completed`? (Or tell me you'll do it manually —
|
|
854
|
+
> either way this tracking commit is **required** before closeout is complete;
|
|
855
|
+
> see Step 6.)"
|
|
856
|
+
|
|
857
|
+
**What this confirmation accepts** — this governs the follow-up prompt above; a
|
|
858
|
+
host with no `follow-up-of` never reaches it and takes the branch below instead:
|
|
859
|
+
|
|
860
|
+
- **Any affirmative reply → enter Step 6.** The verbal signals the guide lists
|
|
861
|
+
(`AI-AGENT-GUIDE.md` § Confirmation Signals) are illustrations, **not the
|
|
862
|
+
list**: a reply that plainly means "go ahead" counts however it is worded.
|
|
863
|
+
**Implicit confirmation counts too** — a developer who supplies what Step 6
|
|
864
|
+
needs, by naming the originals or by saying they will make the commit
|
|
865
|
+
themselves, has confirmed, not declined.
|
|
866
|
+
- **`/dflow:next` and `/dflow:cancel` do not apply here.** No workflow is
|
|
867
|
+
active, and the guide requires both to be refused in that state. Refuse as the
|
|
868
|
+
guide says, **then ask this question again in plain language**. ⚠ **Do not
|
|
869
|
+
read either as a decline** — every earlier gate in this flow asked for
|
|
870
|
+
`/dflow:next`, so typing it here is a trained reflex, not an answer.
|
|
871
|
+
- **A request to change something first** — revise what was asked about, then
|
|
872
|
+
ask again. Nothing already committed is undone, and the Local-closeout gate
|
|
873
|
+
stays satisfied.
|
|
874
|
+
- **A plain decline, or "not now" — stop, and do not ask again.** There is
|
|
875
|
+
nothing to revise, so re-asking is the same question and reads as pressure.
|
|
876
|
+
Report that closeout is **not complete**, name the Step 6 flip as what is
|
|
877
|
+
still owed, and leave restarting to the developer.
|
|
878
|
+
|
|
879
|
+
⚠ **Declining does not waive the tracking commit** — Step 6's flip is required
|
|
880
|
+
either way. `references/finish-feature-follow-up.md` holds that rule and states
|
|
881
|
+
what it takes to satisfy it.
|
|
882
|
+
|
|
883
|
+
**The reverse link must have been opened, not only closed — and that is not
|
|
884
|
+
minimal-host-only.** This confirmation point puts no host shape on
|
|
885
|
+
`follow-up-of`, so a phase-bearing host can carry it too, but it has no single
|
|
886
|
+
commit required to carry the row. The requirement still holds for that host; nothing in closeout
|
|
887
|
+
tests it. **The opening half is confirmed for a phase-bearing follow-up host by
|
|
888
|
+
`references/pr-review-checklist.md`'s "A follow-up's reverse link was opened,
|
|
889
|
+
not only closed" item**, in that file's *Delegated to review by
|
|
890
|
+
`finish-feature-flow.md`* block — it reads the branch history, which is exactly
|
|
891
|
+
what closeout cannot take.
|
|
336
892
|
|
|
337
893
|
If no `follow-up-of` field, skip Step 6 and announce closeout complete:
|
|
338
894
|
> "`/dflow:finish-feature` complete for `{SPEC-ID}-{slug}`. Feature
|
|
@@ -344,29 +900,9 @@ If no `follow-up-of` field, skip Step 6 and announce closeout complete:
|
|
|
344
900
|
**In-flight reminder** — after the closeout announcement (with or without
|
|
345
901
|
Step 6), run the in-flight overview scan (see `AI-AGENT-GUIDE.md` § Status /
|
|
346
902
|
Control Commands) and list any other unfinished features in `active/` and any
|
|
347
|
-
in-flight feature / bugfix branches.
|
|
348
|
-
attention is about to move elsewhere, and this is exactly where half-done work
|
|
349
|
-
sinks.
|
|
903
|
+
in-flight feature / bugfix branches.
|
|
350
904
|
|
|
351
905
|
## Step 6: Reverse-Update Follow-up Tracking (only if follow-up)
|
|
352
906
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
Follow-up Tracking table.
|
|
356
|
-
|
|
357
|
-
1. Locate `dflow/specs/features/completed/{原 SPEC-ID}-{原 slug}/_index.md`
|
|
358
|
-
2. Find the Follow-up Tracking section's row for this feature's SPEC-ID
|
|
359
|
-
3. Flip Status → `completed`
|
|
360
|
-
|
|
361
|
-
```bash
|
|
362
|
-
# The AI makes the edit and may offer to commit it (Y / N), per the AI commit policy
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
After the update:
|
|
366
|
-
> "Follow-up Tracking row in `{原 SPEC-ID}-{原 slug}/_index.md` updated
|
|
367
|
-
> to Status = `completed`. Closeout complete."
|
|
368
|
-
|
|
369
|
-
The connection is bidirectional and weakly redundant: the new feature's
|
|
370
|
-
`follow-up-of` field is the authoritative source; the old feature's
|
|
371
|
-
Follow-up Tracking row is a derived index. If they ever disagree, trust
|
|
372
|
-
`follow-up-of`.
|
|
907
|
+
**Open `references/finish-feature-follow-up.md` and follow it there.** This
|
|
908
|
+
step's rules live in that file and are not repeated here.
|