dflow-sdd-ddd 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +759 -0
  2. package/CONTRIBUTING.md +10 -1
  3. package/README.en.md +155 -210
  4. package/README.md +88 -145
  5. package/TEMPLATE-COVERAGE.md +13 -6
  6. package/TEMPLATE-LANGUAGE-GLOSSARY.md +15 -1
  7. package/bin/dflow.js +15 -4
  8. package/docs/commands.en.md +110 -0
  9. package/docs/commands.md +101 -0
  10. package/docs/doctor-uncertainty.en.md +212 -0
  11. package/docs/doctor-uncertainty.md +212 -0
  12. package/docs/evaluating-dflow.en.md +29 -11
  13. package/docs/evaluating-dflow.md +8 -6
  14. package/docs/release-versioning-policy.md +5 -0
  15. package/docs/upgrading.en.md +196 -0
  16. package/docs/upgrading.md +197 -0
  17. package/docs/using-with-claude-code.en.md +13 -4
  18. package/docs/using-with-claude-code.md +13 -4
  19. package/docs/using-with-codex.en.md +13 -4
  20. package/docs/using-with-codex.md +13 -4
  21. package/docs/using-with-github-copilot.en.md +13 -4
  22. package/docs/using-with-github-copilot.md +13 -4
  23. package/lib/doc-shapes.json +997 -0
  24. package/lib/doctor-checks.js +2500 -24
  25. package/lib/init.js +2846 -143
  26. package/lib/render-diagrams.js +1474 -0
  27. package/lib/render.js +865 -49
  28. package/package.json +2 -2
  29. package/templates/brownfield/references/drift-verification.md +4 -0
  30. package/templates/brownfield/references/finish-feature-flow.md +635 -88
  31. package/templates/brownfield/references/finish-feature-follow-up.md +60 -0
  32. package/templates/brownfield/references/finish-feature-minimal-host.md +406 -0
  33. package/templates/brownfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  34. package/templates/brownfield/references/git-integration.md +160 -15
  35. package/templates/brownfield/references/init-project-flow.md +23 -3
  36. package/templates/brownfield/references/modify-existing-flow.md +412 -87
  37. package/templates/brownfield/references/modify-existing-follow-up.md +121 -0
  38. package/templates/brownfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  39. package/templates/brownfield/references/new-feature-flow.md +61 -6
  40. package/templates/brownfield/references/new-phase-flow.md +57 -7
  41. package/templates/brownfield/references/pr-review-checklist.md +303 -10
  42. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +151 -34
  43. package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +1 -1
  44. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +75 -6
  45. package/templates/brownfield/scaffolding/Git-principles-trunk.md +82 -8
  46. package/templates/brownfield/scaffolding/_conventions.md +50 -28
  47. package/templates/brownfield/scaffolding/_overview.md +1 -0
  48. package/templates/brownfield/templates/_index.md +151 -7
  49. package/templates/brownfield/templates/analysis.md +79 -0
  50. package/templates/brownfield/templates/behavior.md +1 -0
  51. package/templates/brownfield/templates/context-definition.md +1 -0
  52. package/templates/brownfield/templates/context-map.md +2 -1
  53. package/templates/brownfield/templates/glossary.md +1 -0
  54. package/templates/brownfield/templates/lightweight-spec.md +154 -11
  55. package/templates/brownfield/templates/models.md +1 -0
  56. package/templates/brownfield/templates/phase-spec.md +9 -1
  57. package/templates/brownfield/templates/rules.md +1 -0
  58. package/templates/brownfield/templates/tech-debt.md +1 -0
  59. package/templates/common/references/ddd-modeling-guide.md +33 -16
  60. package/templates/{greenfield → common}/references/dflow-feedback-flow.md +2 -1
  61. package/templates/common/references/flow-rationale-registry.md +130 -0
  62. package/templates/common/skill/SKILL.md +13 -11
  63. package/templates/greenfield/references/drift-verification.md +4 -0
  64. package/templates/greenfield/references/finish-feature-flow.md +625 -89
  65. package/templates/greenfield/references/finish-feature-follow-up.md +60 -0
  66. package/templates/greenfield/references/finish-feature-minimal-host.md +363 -0
  67. package/templates/greenfield/references/finish-feature-post-hoc-hotfix.md +95 -0
  68. package/templates/greenfield/references/git-integration.md +148 -15
  69. package/templates/greenfield/references/init-project-flow.md +23 -3
  70. package/templates/greenfield/references/modify-existing-flow.md +378 -85
  71. package/templates/greenfield/references/modify-existing-follow-up.md +103 -0
  72. package/templates/greenfield/references/modify-existing-post-hoc-hotfix.md +82 -0
  73. package/templates/greenfield/references/new-feature-flow.md +67 -4
  74. package/templates/greenfield/references/new-phase-flow.md +56 -7
  75. package/templates/greenfield/references/pr-review-checklist.md +287 -8
  76. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +146 -32
  77. package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +5 -2
  78. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +74 -6
  79. package/templates/greenfield/scaffolding/Git-principles-trunk.md +88 -12
  80. package/templates/greenfield/scaffolding/_conventions.md +50 -28
  81. package/templates/greenfield/scaffolding/_overview.md +6 -2
  82. package/templates/greenfield/templates/_index.md +137 -7
  83. package/templates/greenfield/templates/aggregate-design.md +1 -0
  84. package/templates/greenfield/templates/analysis.md +79 -0
  85. package/templates/greenfield/templates/behavior.md +1 -0
  86. package/templates/greenfield/templates/context-definition.md +1 -0
  87. package/templates/greenfield/templates/context-map.md +2 -1
  88. package/templates/greenfield/templates/events.md +4 -1
  89. package/templates/greenfield/templates/glossary.md +1 -0
  90. package/templates/greenfield/templates/lightweight-spec.md +154 -11
  91. package/templates/greenfield/templates/models.md +1 -0
  92. package/templates/greenfield/templates/phase-spec.md +9 -1
  93. package/templates/greenfield/templates/rules.md +1 -0
  94. package/templates/greenfield/templates/tech-debt.md +1 -0
  95. package/templates/brownfield/references/dflow-feedback-flow.md +0 -251
@@ -0,0 +1,79 @@
1
+ <!-- dflow-shape: brownfield/analysis.md 1 — keep this line: dflow doctor reads it -->
2
+ <!-- Seeded by Dflow. -->
3
+ <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
4
+ <!-- Placement: this template fills two paths. Cross-Context Flows and Function / Role Index are always written in the domain-root copy, `dflow/specs/domain/analysis.md`, whoever owns what they describe, and left as they are in a per-context copy - including in a project that has only one context. A hotspot row goes in the copy that holds the knowledge it is stuck on, and a question row in the copy that will hold its answer, as Open Questions and Hotspots says. For every other entry, ask who owns what the entry describes. A status field is owned by the context whose entity stores it - the entity that context's models.md defines, or will define. A derived figure is owned by the context that computes it, not the ones it reads from. A mechanism is owned by the context whose behavior it explains. One context owns it: the entry goes in that context's `dflow/specs/domain/{context}/analysis.md`, and it stays there even when another context reads the field, triggers a transition or supplies data it reads. No one context owns it - two contexts own it equally, or a mechanism explains behavior in several contexts equally: it goes in the domain-root copy. -->
5
+ <!-- Entry ids: every entry carries one. The prefix is the section's - `FL` flows, `LC` lifecycles, `RM` read models, `MX` mechanisms, `FN` function rows, `HS` hotspot rows - and the number is sequential within this copy and never reused; an entry keeps its id for as long as it stays in this copy. A subsection carries its id in its heading (`### LC-01: {name}`); in the two tables that have an ID column, each row carries it there. Cite an entry in this file by its id alone. Cite one in the other copy, or from any other document, as path then id: `domain/{context}/analysis.md LC-01`. Until what a citation points at has an entry of its own, write its name instead of an id; whenever you update this file, replace any such name whose entry now exists. An entry that moves to the other copy takes a new id there: update whatever cited the old one - search for its path and id together, and for the bare id in the file it left. -->
6
+ <!-- Referring to a rule: a cell that names what a business rule decides refers to that rule and holds nothing else; a cell decided by no rule holds the value itself. Refer to a rule by its BR-ID alone only from the copy of the context whose rules.md holds it; anywhere else - always in the domain-root copy - write its path then its id: `domain/{context}/rules.md BR-001`. A cell holding a BR-ID, alone or after its path, is a reference; anything else is a recorded value. Until a rule has a BR-ID, write the rule's name; whenever you update this file, replace any rule name whose BR-ID now exists. -->
7
+ <!-- Evidence convention: every entry carries its evidence in the form `{code | data | confirmed by {role} | document | inferred | assumed} - {what to open to re-check} ({date})`. A subsection ends with one line that reads `Evidence: ` followed by it; a table row puts it in its Evidence column. For `data`, the middle slot also carries the counting rule the number used. `document` is a written source outside the code - a regulation, an internal policy, a ticket, an existing spec. When you take a claim from one of these rather than re-checking it yourself, the date is the one that source gives, not the day you copied it. `inferred` is a conclusion drawn from code or data rather than read off either one: name what it was drawn from. A question nobody has answered yet is not an entry: record it where the Open Questions and Hotspots section says. A value that rests on nothing a reader can open is `assumed`: a guess, or a fact whose only home is a private note, a chat log, or one person's AI memory. Say what it rests on, and record the question of confirming it where the Open Questions and Hotspots section says. -->
8
+
9
+ # Domain Analysis
10
+
11
+ > System-level knowledge that no single bounded-context document holds: flows
12
+ > that cross contexts, the lifecycle a status field moves through, how derived
13
+ > figures are computed, the mechanisms behind observed behavior, who reaches
14
+ > which function, and the spots in them this project keeps working around.
15
+ > Where an entry relies on a rule, a model, or a context relationship, link it
16
+ > by BR-ID, model name, or section instead of restating it. Where this project
17
+ > has already written one of these somewhere else, leave that text where it is
18
+ > and give it a row or a subsection here that says where to read it.
19
+
20
+ ## Cross-Context Flows
21
+
22
+ <!-- Global file only. One subsection per ordered flow whose steps are owned by more than one context; the table is the record, one row per step in order. A flow that stays inside one context is not recorded here: its scenarios belong in behavior.md. A statement about two contexts that holds without an order - which one is upstream, how they integrate, what data passes between them - belongs in context-map.md: link it instead of repeating it. A derived figure is not a flow, however many contexts it reads from - it goes in Read Models and Derived Figures. Keep one row per step and no branches: where the flow genuinely forks, either write the fork as its own flow or record the condition as a rule in rules.md and link it. -->
23
+
24
+ ### FL-01: {流程名稱}
25
+
26
+ | # | From | To | Handed over | State change | Evidence |
27
+ |---|---|---|---|---|---|
28
+ | 1 | {Context A} | {Context B} | {交出去的是什麼:欄位、識別鍵或事件} | {造成什麼狀態變化} | {code|data|confirmed by {role}|document|inferred|assumed} - {複查入口} ({date}) |
29
+
30
+ ## Lifecycles
31
+
32
+ <!-- One subsection per status field: name the field and where it is stored, list every value it can hold, then one row per transition. List the values even when the code admits more than the workflow uses: a value seen only in stored data is still a state. A transition triggered from another context stays here - name that flow in Cross-Context Flows and cite it rather than restating the handover (from a per-context copy: `domain/analysis.md FL-01`). Whether a single transition is allowed to happen is a business rule: it belongs in rules.md and the Guard cell carries its BR-ID. One scenario played out end to end belongs in behavior.md. The entity that stores the field is defined in models.md. The Means cell says what the state allows or blocks next; when the value's name is also a term the business uses elsewhere, its definition belongs in glossary.md and this cell stays short. Evidence sits on the transitions, not on the state list. -->
33
+
34
+ ### LC-01: {狀態欄位名稱}(`{存放位置:資料表.欄位,或 Aggregate 屬性}`)
35
+
36
+ | State | Means |
37
+ |---|---|
38
+ | `{狀態值}` | {這個狀態允許或擋住接下來的什麼} |
39
+
40
+ | From | Trigger | To | Guard | Evidence |
41
+ |---|---|---|---|---|
42
+ | `{原狀態}` | {誰做了什麼} | `{新狀態}` | {決定它的 BR-ID;沒有規則決定就寫必須成立的條件,都沒有就留空} | {code|data|confirmed by {role}|document|inferred|assumed} - {複查入口} ({date}) |
43
+
44
+ ## Read Models and Derived Figures
45
+
46
+ <!-- One subsection per figure or read model: its definition, which records or states it counts, and the point at which an amount moves from one figure to another. A stored entity or value object is defined in models.md; this section holds what is computed from them. Say what a missing, duplicated or stale record does to the figure. -->
47
+
48
+ ### RM-01: {數字或 read model 名稱}
49
+
50
+ {定義、計入條件、切換點}
51
+
52
+ Evidence: {code | data | confirmed by {role} | document | inferred | assumed} - {要開哪一支才能複查} ({date})
53
+
54
+ ## Mechanisms
55
+
56
+ <!-- One subsection per mechanism: how the system produces a behavior that no single rule explains - versioned records, deferred updates, recalculation on read. A behavior that one rule or one Given/When/Then scenario can state belongs in rules.md / behavior.md instead. The Affects line lists what this mechanism changes the meaning of - BR-IDs and entry ids, each cited as Entry ids and Referring to a rule say. -->
57
+
58
+ ### MX-01: {機制名稱}
59
+
60
+ {運作方式與影響}
61
+
62
+ Affects: {BR-ID 與 FL-/LC-/RM- 編號(照檔頭的引用規則帶路徑;還沒有編號的寫名稱),逗號分隔;沒有就寫 none}
63
+ Evidence: {code | data | confirmed by {role} | document | inferred | assumed} - {要開哪一支才能複查} ({date})
64
+
65
+ ## Function / Role Index
66
+
67
+ <!-- Global file only. One row per function: which roles reach it and the data scope each one sees. When a business rule decides who may use the function or what a role sees, that rule is the source of truth: write its BR-ID in that cell instead of restating the rule. Write roles or a data scope directly only where no rule decides them - a menu entry every role sees, a report anyone can run. -->
68
+
69
+ | Function | ID | Entry point | Bounded Context | Roles | Data scope | Evidence |
70
+ |---|---|---|---|---|---|---|
71
+ | {功能名稱} | FN-01 | `{route / page / job}` | {Context name} | {角色,或決定它的 BR-ID(帶路徑)} | {看得到的資料範圍,或決定它的 BR-ID(帶路徑)} | {code|data|confirmed by {role}|document|inferred|assumed} - {複查入口} ({date}) |
72
+
73
+ ## Open Questions and Hotspots
74
+
75
+ <!-- One row per hotspot: a spot in the knowledge above this project keeps working around until a domain decision settles it. It goes in whichever copy of this file holds the knowledge it is stuck on - or, when that is a business rule or knowledge not recorded yet, the copy the Placement question gives it - and the Affects column names that knowledge by id, cited as Entry ids and Referring to a rule say. Also one row per question that comes up while writing an entry here and whose own answer will be a cross-context flow, a lifecycle, a derived figure, a mechanism or role reach. A question the current feature must answer before it can finish stays in that feature's spec, and a question whose answer belongs to another document's subject stays in that document's own Open Questions section - a term in glossary.md, a business rule in the owning context's rules.md, and a context boundary, an integration responsibility or the question of which context owns a rule in context-map.md. A question that stays in this file goes in the copy that will hold its answer - the copy Placement gives that answer - whichever copy the entry that raised it is in. When a code change would settle an item - a missing check, a known shortcut - it is tech debt: record it in tech-debt.md instead. A hotspot whose pending decision is recorded elsewhere links to it. A settled item stays as a resolved row that says what settled it, with the settling decision in its Evidence cell. -->
76
+
77
+ | Item | ID | Affects | Why it matters | Status | Evidence |
78
+ |---|---|---|---|---|---|
79
+ | {待確認事項或熱點} | HS-01 | {FL-/LC-/RM-/MX-/FN- 編號或 BR-ID(照檔頭的引用規則帶路徑;還沒有編號的寫名稱)} | {影響} | open / resolved | {code|data|confirmed by {role}|document|inferred|assumed} - {複查入口} ({date}) |
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/behavior.md 1 — keep this line: dflow doctor reads it -->
1
2
  # {Bounded Context} — Behavior Specification
2
3
 
3
4
  > **Purpose**: Consolidated source of truth for this context's current behavior.
@@ -4,6 +4,7 @@ chinese-name: {中文名稱}
4
4
  owner: {負責的開發者或團隊}
5
5
  created: {YYYY-MM-DD}
6
6
  ---
7
+ <!-- dflow-shape: brownfield/context-definition.md 1 — keep this line: dflow doctor reads it -->
7
8
 
8
9
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
9
10
 
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/context-map.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
3
4
 
@@ -27,7 +28,7 @@
27
28
 
28
29
  ## Integration Notes
29
30
 
30
- - {跨 context 的資料流、權責邊界或 legacy coupling}
31
+ - {沒有先後順序也成立的跨 context 資料交換、權責邊界或 legacy coupling;有先後順序的跨 context 交手流程記在 analysis.md}
31
32
 
32
33
  ## Open Questions
33
34
 
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/glossary.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
3
4
 
@@ -2,10 +2,13 @@
2
2
  id: BUG-{NUMBER} # bug-type T2 only; a non-bug T2 (lightweight-{date}-{slug}.md) carries no id — the filename identifies it
3
3
  title: {簡述問題}
4
4
  status: in-progress # in-progress | completed
5
- bounded-context: {ContextName}
5
+ bounded-context: {ContextName} # or `none` when the host has no meaningful bounded context
6
6
  created: {YYYY-MM-DD}
7
- branch: bugfix/BUG-{NUMBER}-{slug}
7
+ branch: bugfix/BUG-{NUMBER}-{slug} # must equal the host _index.md `branch:` value (that field is authoritative) — a non-bug T2 therefore carries the host's feature/{SPEC-ID}-{slug} branch
8
+ # hotfix-branch: hotfix/{name} # ADD (uncomment) only for a post-hoc T2 — references/modify-existing-flow.md Step 1.8. The already-merged emergency fix's own branch, kept even after that branch was deleted.
9
+ # hotfix-identity: {PR / incident / tracker reference} # ADD only for a post-hoc T2 — the source the asserted `reconciled ({merged-hotfix-hash})` identity rests on; an uncited hash blocks closeout. Keep it adjacent to hotfix-branch.
8
10
  ---
11
+ <!-- dflow-shape: brownfield/lightweight-spec.md 1 — keep this line: dflow doctor reads it -->
9
12
 
10
13
  <!--
11
14
  Template note (for AI):
@@ -16,7 +19,7 @@ Template note (for AI):
16
19
  - T1 Heavy → use templates/phase-spec.md instead
17
20
  - T2 Light → THIS template; produces an independent file
18
21
  - T3 Trivial → no independent file; just one inline row in _index.md
19
- Lightweight Changes with a tag like [cosmetic] / [text] / [format]
22
+ Lightweight Changes with a tag like [cosmetic] / [text] / [appearance]
20
23
 
21
24
  Instance file location and naming:
22
25
  Place the instantiated file inside the corresponding feature directory:
@@ -24,17 +27,136 @@ Template note (for AI):
24
27
  or, when the lightweight change is a tracked bug:
25
28
  dflow/specs/features/active/{SPEC-ID}-{slug}/BUG-{NUMBER}-{slug}.md
26
29
 
27
- If the change is a standalone bug not yet attached to any existing
28
- feature, /dflow:bug-fix must first create a feature directory (with a
29
- minimal _index.md) before placing the lightweight-spec instance inside.
30
- This keeps the structure invariant: every spec file lives under some
31
- feature directory.
32
-
33
- After finalizing this lightweight-spec, AI must:
30
+ A standalone change not yet attached to any existing feature needs a
31
+ minimal host feature directory (with a minimal _index.md) to hold this
32
+ instance — the structure invariant is that every spec file lives under
33
+ some feature directory. That minimal host has a defined create / record /
34
+ close-out lifecycle: references/modify-existing-flow.md Step 1.7 for a
35
+ standalone change, or its Step 1.6 minimal variant when the change is a
36
+ follow-up on a completed feature; a post-hoc hotfix reaches the same
37
+ lifecycle through Step 1.8, which resolves its linkage to one of those two.
38
+ Follow that lifecycle rather than
39
+ improvising one, and do not leave an empty host behind.
40
+
41
+ No-BR variants (a T2 that carries no BR delta at all):
42
+ The default shape below is the classic BR-delta form (Behavior Delta with
43
+ BR-NN entries + Root Cause). It stays valid and is the right shape whenever
44
+ the change does have a BR delta.
45
+ A change can reach T2 carrying no BR delta at all. These families describe
46
+ how to write a spec the cascade has ALREADY placed at T2; none of them
47
+ makes a change T2, and none of them overrides an earlier cascade step. Do
48
+ NOT invent a BR-NN and do NOT write a fake root cause. Pick the matching
49
+ family, put its BR line under `## Behavior Delta` in place of the
50
+ ADDED / MODIFIED / REMOVED / RENAMED subsections, and replace `## Root Cause`
51
+ with that family's evidence section:
52
+
53
+ (a) presentation — any copy / appearance change T3 would not take: a sweep
54
+ beyond the local unit, a whole-screen rewrite, a local edit to
55
+ high-consequence content (a security warning, a password hint,
56
+ consent text, a payment or legal notice), or one that changes what the output *means* (an
57
+ instruction reversed from "company email only" to "any email", a
58
+ danger / status colour flipped)
59
+ BR line: `BR: none — presentation`
60
+ Evidence: `## Output Footprint` — which screens / outputs the change
61
+ actually reaches, and **what it now says or signals** whenever the
62
+ meaning moved or the content is high-consequence
63
+ (b) contract change — non-breaking structured-log / export / API / event
64
+ field or semantics change
65
+ BR line: `BR: none — contract change`
66
+ Evidence: `## Contract Delta`, including a `**Downstream consumers**:`
67
+ line naming who reads it
68
+ (c) operational — CVE / security dependency bump, or a behavior-preserving
69
+ refactor on an auth / payment / resilience / compliance path
70
+ BR line: `BR: none — operational`
71
+ Evidence: `## Operational Rationale`, including a `**Trace**:` line
72
+ (advisory / ticket / audit reference; a self-initiated case with nothing
73
+ to cite records `**Trace**: none — self-initiated` — the line is never
74
+ omitted)
75
+ (d) performance — a runtime performance / resource / SLA change, including
76
+ one no audience perceives
77
+ BR line: `BR: none — performance`
78
+ Evidence: `## Performance Delta`, including an
79
+ `**SLA / resource context**:` line
80
+ (e) implementation defect — the rule is unchanged; the implementation was
81
+ wrong
82
+ BR lines: `BR Delta: none — implementation defect` AND
83
+ `Governing BR-IDs: {BR-NN, BR-NN | none}`
84
+ Evidence: keep `## Problem` / `## Root Cause` / `## Fix Approach`, plus a
85
+ regression task
86
+ (f) intentional change — a planned, non-normative functional or interaction
87
+ change with no rule in the catalogue (default tab, redirect target,
88
+ ordering, a presentation / interaction control)
89
+ BR line: `BR: none — intentional change`
90
+ Evidence: `## Change Rationale`, including `**Before**` / `**After**`
91
+ behavior lines and a `**Regression**:` line
92
+
93
+ More than one family can fit — a behaviour-preserving payment-retry refactor
94
+ that also shifts runtime SLA is both (c) and (d). Do not pick one and drop
95
+ the other's evidence: write the BR line of the family with the higher
96
+ consequence (order: (e) → (c) → (b) → (d) → (f) → (a)) and include **every**
97
+ matching family's evidence section. A reviewer needs the operational trace
98
+ and the SLA context, not whichever one the author chose first.
99
+
100
+ Family (e) keeps two fields on purpose: "no BR delta" is not "no governing
101
+ rule". `Governing BR-IDs:` lists the rules the defect sits under, so the rule
102
+ the fix answers to stays traceable; a genuinely uncatalogued defect records
103
+ `none`. Never collapse the pair into a single `BR: none`.
104
+ Family (f) is non-normative only. A new normative constraint (permission /
105
+ eligibility / threshold / approval / required outcome) is a new BR — take it
106
+ back to the cascade rather than filing it here, even when the catalogue has
107
+ no BR-ID for it yet.
108
+
109
+ Legacy shapes are accepted, never migrated. Readers and gates take both the
110
+ classic BR-delta form and the older single `BR:` line bug form (written
111
+ before the `BR Delta:` / `Governing BR-IDs:` split) as they are; leave
112
+ existing specs alone and use the shapes above for new ones.
113
+
114
+ After drafting this lightweight-spec, AI must:
34
115
  1. Add an outbound-link row to the feature's _index.md Lightweight Changes table
35
- (Tier = T2; description includes the link to this file)
116
+ (Tier = T2; description includes the link to this file).
117
+ TIMING — do this BEFORE the implementation commit, not after. On a
118
+ minimal (zero-phase) host the row must already be inside checkpoint 1,
119
+ and closeout's allow-list admits only the row's `Commit` cell
120
+ afterwards, so a row added later blocks. "Drafting", not the Step 1.7
121
+ "Finalize + close" sub-step, which runs after that commit and only fills
122
+ the cell in.
36
123
  2. Refresh the feature's _index.md Current BR Snapshot table to reflect
37
124
  any BR ADDED / MODIFIED / REMOVED / RENAMED in this lightweight-spec
125
+ (a no-BR family has nothing to refresh — leave the snapshot as it is)
126
+
127
+ Implementation Paths:
128
+ The `## Implementation Paths` section below names the source paths this
129
+ change touches. Paths, not a diff — enough to compare a commit against.
130
+ REQUIRED on a minimal (zero-phase) host — standalone or follow-up:
131
+ `/dflow:finish-feature` asserts that checkpoint 1's diff touches them, so a
132
+ spec declaring none leaves that check nothing to compare and a missing
133
+ declaration BLOCKS closeout rather than passing vacuously
134
+ (references/finish-feature-flow.md Step 1; written at
135
+ references/modify-existing-flow.md Step 1.7, before the first commit).
136
+ On a HOSTED T2 — one recorded under a phase-bearing feature — closeout runs
137
+ no such check and nothing blocks on it, so the section is good practice
138
+ there, not a gate. Do not read the "blocks" above as applying to a hosted
139
+ spec; it does not.
140
+ Keep the paths OUT of `## Implementation Tasks`: the completion checklist
141
+ may collapse or remove that section once the tasks are done, and the paths
142
+ must still be readable at closeout.
143
+
144
+ Post-hoc hotfix fields — T2 only, references/modify-existing-flow.md Step 1.8:
145
+ `hotfix-branch:` records the already-merged emergency fix's own branch
146
+ (keep the value even when that branch has been deleted); `hotfix-identity:`
147
+ cites what the identity claim rests on — the PR, incident, or tracker
148
+ reference. Both live in the frontmatter, adjacent, so the trace and the
149
+ thing it rests on stay together. They ship COMMENTED OUT: an ordinary
150
+ (non-post-hoc) T2 leaves them commented and they are inert. Uncomment them
151
+ only in post-hoc mode — never fill them with `none`, and never leave them
152
+ live on an ordinary T2, which would claim a hotfix branch that never
153
+ existed on a record nothing else re-checks.
154
+ In post-hoc mode `## Implementation Paths` names the paths the MERGED
155
+ HOTFIX touched: closeout compares them against the `reconciled ({hash})`
156
+ commit, and blocks if this host's own documentation commit touches any of
157
+ them (that would be re-implementation, not reconciliation). Closeout tests
158
+ plausibility only and blocks on an uncited hash; the identity itself is
159
+ confirmed by references/pr-review-checklist.md.
38
160
  -->
39
161
 
40
162
  # {問題簡述}
@@ -46,6 +168,8 @@ Template note (for AI):
46
168
  ## Behavior Delta
47
169
 
48
170
  > 精簡 delta 格式:bug fix 多數只需 MODIFIED;若確實是新增規則可改用 ADDED、移除用 REMOVED、改名用 RENAMED。多項變更時照類別列。
171
+ >
172
+ > 完全沒有 BR delta 時改用 no-BR 家族形(見檔首 Template note 的 No-BR variants):本段只留該家族的 BR 行(例如 `BR: none — presentation`;家族 (e) 是 `BR Delta:` + `Governing BR-IDs:` 兩行),不要為了填滿 delta 子段捏造 BR-NN。
49
173
 
50
174
  ### MODIFIED - behavior modified in this fix
51
175
  #### Rule: BR-NN {規則名稱}
@@ -58,12 +182,31 @@ Template note (for AI):
58
182
 
59
183
  ## Root Cause
60
184
 
185
+ > classic BR-delta 形與 no-BR 家族 (e) 保留本段;家族 (a)–(d)、(f) 以該家族的 evidence 段取代本段(見檔首 Template note 的 No-BR variants)。
186
+
61
187
  {為什麼會這樣?是邏輯錯誤?資料問題?還是需求理解有誤?}
62
188
 
63
189
  ## Fix Approach
64
190
 
65
191
  {怎麼修?有沒有抽到 Domain 層的機會?}
66
192
 
193
+ ## Implementation Paths
194
+
195
+ > The source paths this change touches — paths, not a diff, at a granularity a
196
+ > commit can be compared against. **Required on a minimal (zero-phase) host**:
197
+ > `/dflow:finish-feature` asserts checkpoint 1's diff touches them, and
198
+ > declaring none **blocks** closeout instead of passing. On a **hosted** T2 —
199
+ > recorded under a phase-bearing feature — closeout runs no such check, so this
200
+ > is good practice there rather than a gate. Do not move the list into
201
+ > `## Implementation Tasks`: that section may be collapsed once the tasks are
202
+ > done, and these paths must still be readable at closeout.
203
+ >
204
+ > **Post-hoc hotfix (Step 1.8)**: list what the **already-merged hotfix**
205
+ > touched. This host's own documentation commit must **not** touch any of them.
206
+
207
+ - `{src/path/touched}`
208
+ - `{src/another/path/touched}`
209
+
67
210
  <!-- dflow:section implementation-tasks -->
68
211
  ## Implementation Tasks
69
212
 
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/models.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
3
4
 
@@ -7,6 +7,7 @@ created: {YYYY-MM-DD}
7
7
  author: {developer-name}
8
8
  branch: feature/{SPEC-ID}-{slug}
9
9
  ---
10
+ <!-- dflow-shape: brownfield/phase-spec.md 1 — keep this line: dflow doctor reads it -->
10
11
 
11
12
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
12
13
 
@@ -17,7 +18,14 @@ Template note (for AI):
17
18
  This is the **phase-spec** template - one phase-spec captures one full
18
19
  "Kickoff -> Domain -> Design -> Build -> Verify" cycle inside a feature directory.
19
20
  A feature can have 1..N phase-specs; the feature-level dashboard lives in
20
- the sibling `_index.md` (see templates/_index.md). The instance file name is
21
+ the sibling `_index.md` (see templates/_index.md).
22
+ Exception — a **minimal (zero-phase) host** has **0**: it records a small
23
+ standalone or follow-up change and is defined by carrying no phase-spec at
24
+ all (references/modify-existing-flow.md Step 1.7, or its Step 1.6 minimal
25
+ variant). Such a host never uses this template. If you are about to create a
26
+ phase-spec for one, stop: adding a phase makes it a phase-bearing feature,
27
+ and closeout will then check it as one.
28
+ The instance file name is
21
29
  `phase-spec-YYYY-MM-DD-{slug}.md` placed at
22
30
  `dflow/specs/features/active/{SPEC-ID}-{slug}/`.
23
31
 
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/rules.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
3
4
 
@@ -1,3 +1,4 @@
1
+ <!-- dflow-shape: brownfield/tech-debt.md 1 — keep this line: dflow doctor reads it -->
1
2
  <!-- Seeded by Dflow. -->
2
3
  <!-- Formatting convention: keep table cells concise. When one cell holds multiple short items (invariants, rules, steps), separate them with <br> so each renders on its own line - never chain them into one line with ;/; separators. Long narrative detail does not belong in a table cell: keep the cell to a concise summary and put extended detail in an existing section of this document when one fits, or give each item its own row. -->
3
4
 
@@ -76,10 +76,10 @@ never cross:
76
76
  BR-ID in `rules.md` and a scenario in `behavior.md`; `/dflow:verify` works
77
77
  the same. What shrinks is the *domain model's thickness*, not the spec's
78
78
  existence.
79
- 2. **The Tier system is unchanged.** A bug fix in a generic context is still a
80
- T2; a new feature still runs the flow. Subdomain governs *how deep to model
81
- a context*; Tier governs *how much ceremony a single change needs* — they
82
- are orthogonal.
79
+ 2. **The Tier system is unchanged.** A bug fix's tier is whatever the cascade
80
+ decides by the change itself (T3 / T2 / T1), not by the subdomain; a new
81
+ feature still runs the flow. Subdomain governs *how deep to model a context*;
82
+ Tier governs *how much ceremony a single change needs* — they are orthogonal.
83
83
  3. **Criticality is not downgraded.** Security, tests, SLA, audit,
84
84
  permissions, and data integrity do **not** drop with the subdomain type —
85
85
  auth or notifications can be generic *and* mission-critical. Generic lowers
@@ -119,9 +119,14 @@ or value objects directly.
119
119
  1. **Protect invariants** — The Aggregate exists to enforce business rules that span
120
120
  multiple objects within it.
121
121
 
122
- 2. **One Aggregate per transaction** — A single operation should modify only ONE
123
- Aggregate. If you need to modify two Aggregates, use Domain Events for eventual
124
- consistency.
122
+ 2. **One Aggregate per transaction — by default** — A single operation should
123
+ normally modify one Aggregate. If it appears to need multiple roots, first
124
+ challenge whether the boundary is wrong and whether Domain Events with eventual
125
+ consistency can separate the work. If a multi-root local transaction remains
126
+ justified, record the domain rationale, the shared atomic boundary / operational
127
+ coupling, and when to re-evaluate the choice. A local database transaction cannot
128
+ make an external provider or service fact atomic with local state; that workflow
129
+ still needs idempotency plus reliable delivery / retry or compensation.
125
130
 
126
131
  3. **Reference other Aggregates by ID only** — Never hold a direct object reference
127
132
  to another Aggregate. Store its ID instead.
@@ -479,9 +484,10 @@ the values that changed — not a snapshot of the whole Aggregate.
479
484
  event** decision (record the Delivery expectation in `events.md`; Brownfield —
480
485
  see the Edition note) and treat
481
486
  the payload as a published contract: add fields if you must, never change
482
- their meaning. Ceremony-wise, extending an event payload that crosses
483
- contexts is a T1 contract change (see the ceremony examples in
484
- `_conventions.md`).
487
+ their meaning. Ceremony-wise it follows the contract axis of the ordered
488
+ cascade — classify the change in AI-AGENT-GUIDE.md § Ceremony Scaling, which
489
+ is where breaking versus non-breaking and the resulting tier are decided; a
490
+ project's own `_conventions.md` may escalate from there.
485
491
 
486
492
  ### Event Flow
487
493
 
@@ -673,13 +679,19 @@ build. (Read model ≠ event sourcing.)
673
679
  ## Domain Services
674
680
 
675
681
  Use Domain Services for operations that:
676
- - Involve multiple Aggregates (read-only access to the second Aggregate)
682
+ - Require a domain decision spanning information from multiple Aggregates
683
+ (read-only access by default)
677
684
  - Require external information (through interfaces) to make domain decisions
678
685
  - Don't naturally belong to any single Entity
679
686
 
680
- A Domain Service can make a **stateless** cross-Aggregate decision. It is
681
- not a home for process progress, retries, deadlines, or compensation state.
682
- When the flow has state, use the "Long-Running Processes" ladder.
687
+ A Domain Service expresses a **stateless domain operation or decision** and may
688
+ read multiple Aggregate roots. A justified multi-root mutation is rare rather
689
+ than automatically invalid: it must trigger a boundary / transaction review and
690
+ the service itself must carry a real domain rule or decision. A service that only
691
+ orders calls to multiple roots is Application-layer orchestration in the wrong
692
+ place. A Domain Service is not a home for process progress, retries, deadlines,
693
+ or compensation state. Application code owns ordinary workflow sequencing; when
694
+ the flow has business-visible state, use the "Long-Running Processes" ladder.
683
695
 
684
696
  ```csharp
685
697
  // Domain Service — in Domain layer
@@ -791,8 +803,13 @@ The less-obvious three — when to reach for them:
791
803
  4. **Business logic in Infrastructure** — Rules in SQL queries or EF configurations
792
804
  → Domain defines WHAT, Infrastructure defines HOW
793
805
 
794
- 5. **Direct cross-Aggregate modification** — One command modifying two Aggregates
795
- → Use Domain Events for the second Aggregate
806
+ 5. **Unreviewed direct cross-Aggregate modification** — One command modifies
807
+ multiple Aggregate roots without challenging the boundaries or recording why
808
+ one local transaction is justified
809
+ → Prefer Domain Events and eventual consistency; if the local-transaction
810
+ exception is justified, record the rationale and re-evaluation condition from
811
+ Aggregate Design Rule 2. External provider / service facts are never atomic
812
+ with the local database transaction.
796
813
 
797
814
  6. **Set-based invariant guarded only in memory** — A "unique" / "only one active"
798
815
  rule enforced solely by an in-app check, with no DB unique constraint or
@@ -77,7 +77,8 @@ Allowed evidence:
77
77
  - Dflow command name
78
78
  - Dflow version if known
79
79
  - Template or reference file name
80
- - Generic project type, such as "existing legacy presentation-framework app"
80
+ - Generic project type, such as "existing legacy presentation-framework app" or
81
+ "new internal service"
81
82
  - Minimal paraphrased symptom
82
83
  - Short sanitized snippets from Dflow-owned files
83
84