devflow-kit 3.3.0 → 3.5.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 (220) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +8 -4
  3. package/dist/agents/code-edit.md +133 -0
  4. package/dist/agents/code-fix.md +165 -0
  5. package/dist/agents/code.md +187 -0
  6. package/{src/assets → dist}/agents/design.md +1 -2
  7. package/{src/assets → dist}/agents/diagnose.md +1 -2
  8. package/dist/agents/git.md +33 -722
  9. package/{src/assets → dist}/agents/knowledge.md +12 -5
  10. package/{src/assets → dist}/agents/research.md +8 -2
  11. package/{src/assets → dist}/agents/review.md +8 -11
  12. package/{src/assets → dist}/agents/scrutinize.md +4 -1
  13. package/dist/agents/skim.md +152 -0
  14. package/{src/assets → dist}/agents/triage.md +9 -2
  15. package/dist/cli/commands/behavior-prompts.js +77 -0
  16. package/dist/cli/commands/behavior.js +123 -0
  17. package/dist/cli/commands/flags.js +8 -2
  18. package/dist/cli/commands/init-seed.js +7 -3
  19. package/dist/cli/commands/init.js +134 -5
  20. package/dist/cli/commands/learning.js +38 -3
  21. package/dist/cli/commands/rules.js +16 -0
  22. package/dist/cli/commands/tracker.js +112 -15
  23. package/dist/cli/commands/uninstall.js +42 -1
  24. package/dist/cli.js +2 -0
  25. package/dist/commands/bug-analysis.md +35 -13
  26. package/dist/commands/code-review.md +155 -69
  27. package/dist/commands/debug.md +39 -16
  28. package/dist/commands/dynamic-build.md +61 -72
  29. package/dist/commands/dynamic-plan.md +39 -25
  30. package/dist/commands/dynamic-profile.md +28 -14
  31. package/dist/commands/dynamic-tickets.md +38 -22
  32. package/dist/commands/explore.md +18 -16
  33. package/dist/commands/implement.md +121 -150
  34. package/dist/commands/plan.md +51 -31
  35. package/dist/commands/release.md +72 -6
  36. package/dist/commands/research.md +49 -20
  37. package/dist/commands/resolve.md +54 -46
  38. package/dist/commands/self-review.md +38 -25
  39. package/dist/core/agent-frontmatter.js +101 -0
  40. package/dist/core/agent-models.js +43 -0
  41. package/dist/core/assets.js +55 -10
  42. package/dist/core/claude-md-audit.js +190 -0
  43. package/dist/core/compliance-compose.js +6 -4
  44. package/dist/core/feature-switch.js +41 -1
  45. package/dist/core/flags.js +139 -0
  46. package/dist/core/fs-atomic.js +8 -3
  47. package/dist/core/learning-variants.js +213 -0
  48. package/dist/core/manifest.js +73 -3
  49. package/dist/core/mds-variants.js +98 -19
  50. package/dist/core/plugins.js +111 -72
  51. package/dist/core/project-paths.js +0 -4
  52. package/dist/core/proxy-log.js +1 -1
  53. package/dist/core/proxy-state.js +1 -1
  54. package/dist/core/tracker.js +120 -2
  55. package/dist/learning-off/agents/code-edit.md +129 -0
  56. package/dist/learning-off/agents/code-fix.md +161 -0
  57. package/dist/learning-off/agents/code.md +183 -0
  58. package/dist/learning-off/agents/design.md +118 -0
  59. package/dist/learning-off/agents/diagnose.md +210 -0
  60. package/dist/learning-off/agents/knowledge.md +96 -0
  61. package/dist/learning-off/agents/research.md +155 -0
  62. package/dist/learning-off/agents/review.md +224 -0
  63. package/dist/learning-off/agents/scrutinize.md +120 -0
  64. package/{src/assets → dist/learning-off}/agents/skim.md +5 -8
  65. package/dist/learning-off/agents/triage.md +170 -0
  66. package/dist/learning-off/commands/bug-analysis.md +420 -0
  67. package/dist/learning-off/commands/code-review.md +530 -0
  68. package/dist/learning-off/commands/debug.md +314 -0
  69. package/dist/learning-off/commands/dynamic-build.md +1245 -0
  70. package/dist/learning-off/commands/dynamic-plan.md +426 -0
  71. package/dist/learning-off/commands/dynamic-profile.md +214 -0
  72. package/dist/learning-off/commands/dynamic-tickets.md +634 -0
  73. package/dist/learning-off/commands/explore.md +210 -0
  74. package/dist/learning-off/commands/implement.md +774 -0
  75. package/dist/learning-off/commands/plan.md +671 -0
  76. package/dist/learning-off/commands/release.md +311 -0
  77. package/dist/learning-off/commands/research.md +229 -0
  78. package/dist/learning-off/commands/resolve.md +842 -0
  79. package/dist/learning-off/commands/self-review.md +266 -0
  80. package/dist/skills/git/references/learn-conventions.md +27 -3
  81. package/dist/skills/git/references/pr/check-ci-status.md +19 -0
  82. package/dist/skills/git/references/pr/check-merge-readiness.md +19 -0
  83. package/dist/skills/git/references/pr/fetch-review-threads.md +30 -0
  84. package/dist/skills/git/references/pr/post-resolution-summary.md +18 -0
  85. package/dist/skills/git/references/pr/post-review-summary.md +20 -0
  86. package/dist/skills/git/references/pr/resolve-review-threads.md +28 -4
  87. package/dist/skills/git/references/pr/update-pr-evidence.md +13 -0
  88. package/dist/skills/git/references/pr/validate-branch.md +26 -0
  89. package/dist/skills/git/references/tracker/_contract.md +33 -0
  90. package/dist/skills/git/references/tracker/github/associate-release.md +24 -0
  91. package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +31 -1
  92. package/dist/skills/git/references/tracker/github/create-release.md +30 -1
  93. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +89 -2
  94. package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +16 -1
  95. package/dist/skills/git/references/tracker/github/fetch-issue.md +38 -1
  96. package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +49 -1
  97. package/dist/skills/git/references/tracker/github/gather-release-evidence.md +29 -0
  98. package/dist/skills/git/references/tracker/github/manage-debt.md +21 -1
  99. package/dist/skills/git/references/tracker/github/post-wave-report.md +17 -1
  100. package/dist/skills/git/references/tracker/github/setup-task.md +53 -1
  101. package/dist/skills/git/references/tracker/jira/associate-release.md +25 -1
  102. package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +31 -1
  103. package/dist/skills/git/references/tracker/jira/create-release.md +30 -1
  104. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +89 -2
  105. package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +16 -1
  106. package/dist/skills/git/references/tracker/jira/fetch-issue.md +38 -1
  107. package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +49 -1
  108. package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +29 -0
  109. package/dist/skills/git/references/tracker/jira/manage-debt.md +21 -1
  110. package/dist/skills/git/references/tracker/jira/post-wave-report.md +17 -1
  111. package/dist/skills/git/references/tracker/jira/setup-task.md +55 -3
  112. package/dist/skills/git/references/tracker/linear/associate-release.md +25 -1
  113. package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +31 -1
  114. package/dist/skills/git/references/tracker/linear/create-release.md +30 -1
  115. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +89 -2
  116. package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +16 -1
  117. package/dist/skills/git/references/tracker/linear/fetch-issue.md +38 -1
  118. package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +49 -1
  119. package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +29 -0
  120. package/dist/skills/git/references/tracker/linear/manage-debt.md +21 -1
  121. package/dist/skills/git/references/tracker/linear/post-wave-report.md +17 -1
  122. package/dist/skills/git/references/tracker/linear/setup-task.md +55 -3
  123. package/dist/targets/claude-code/agent-type-owner.js +164 -0
  124. package/dist/targets/claude-code/behavior-install.js +111 -0
  125. package/dist/targets/claude-code/installer.js +140 -37
  126. package/dist/targets/claude-code/language-stamp.js +185 -0
  127. package/dist/targets/claude-code/learning-install.js +497 -0
  128. package/dist/targets/claude-code/tracker-install.js +33 -16
  129. package/dist/targets/claude-code/tracker-servers.js +107 -0
  130. package/package.json +4 -3
  131. package/src/assets/agents/_partials/_code.mds +109 -0
  132. package/src/assets/agents/code-edit.mds +81 -0
  133. package/src/assets/agents/code-fix.mds +113 -0
  134. package/src/assets/agents/code.mds +135 -0
  135. package/src/assets/agents/design.mds +148 -0
  136. package/src/assets/agents/diagnose.mds +225 -0
  137. package/src/assets/agents/evaluate.md +8 -4
  138. package/src/assets/agents/explore.md +58 -0
  139. package/src/assets/agents/git.mds +33 -722
  140. package/src/assets/agents/knowledge.mds +131 -0
  141. package/src/assets/agents/learning.md +19 -69
  142. package/src/assets/agents/research.mds +182 -0
  143. package/src/assets/agents/review.mds +282 -0
  144. package/src/assets/agents/scrutinize.mds +135 -0
  145. package/src/assets/agents/simplify.md +3 -0
  146. package/src/assets/agents/skim.mds +165 -0
  147. package/src/assets/agents/synthesize.md +26 -352
  148. package/src/assets/agents/test.md +8 -0
  149. package/src/assets/agents/tracker.md +20 -7
  150. package/src/assets/agents/triage.mds +201 -0
  151. package/src/assets/agents/validate.md +13 -6
  152. package/src/assets/commands/_partials/_compliance.mds +41 -4
  153. package/src/assets/commands/_partials/_decisions.mds +33 -2
  154. package/src/assets/commands/_partials/_engine.mds +14 -6
  155. package/src/assets/commands/_partials/_knowledge.mds +25 -12
  156. package/src/assets/commands/_partials/_preamble.mds +34 -10
  157. package/src/assets/commands/_partials/_publication.mds +5 -4
  158. package/src/assets/commands/_partials/_roster.mds +4 -3
  159. package/src/assets/commands/_partials/_settings.mds +13 -5
  160. package/src/assets/commands/_partials/_wave.mds +9 -1
  161. package/src/assets/commands/bug-analysis.mds +27 -5
  162. package/src/assets/commands/code-review.mds +158 -52
  163. package/src/assets/commands/debug.mds +41 -3
  164. package/src/assets/commands/dynamic-build.mds +47 -27
  165. package/src/assets/commands/dynamic-plan.mds +40 -9
  166. package/src/assets/commands/dynamic-profile.mds +9 -1
  167. package/src/assets/commands/dynamic-tickets.mds +21 -6
  168. package/src/assets/commands/explore.mds +28 -2
  169. package/src/assets/commands/implement.mds +126 -129
  170. package/src/assets/commands/plan.mds +58 -14
  171. package/src/assets/commands/{release.md → release.mds} +30 -26
  172. package/src/assets/commands/research.mds +42 -11
  173. package/src/assets/commands/resolve.mds +59 -15
  174. package/src/assets/commands/self-review.mds +30 -5
  175. package/src/assets/mds/.mdsroot +4 -0
  176. package/src/assets/mds/git/_ops.mds +383 -0
  177. package/src/assets/mds/git/_ops_pr.mds +201 -0
  178. package/src/assets/mds/git/_ops_steps.mds +147 -0
  179. package/src/assets/mds/git/_pr.mds +39 -44
  180. package/src/assets/mds/git/_references.mds +6 -3
  181. package/src/assets/mds/tracker/_common.mds +5 -5
  182. package/src/assets/mds/tracker/_contract.mds +73 -0
  183. package/src/assets/mds/tracker/_github.mds +73 -16
  184. package/src/assets/mds/tracker/_jira.mds +77 -19
  185. package/src/assets/mds/tracker/_linear.mds +77 -19
  186. package/src/assets/mds/tracker/_mcp.mds +2 -2
  187. package/src/assets/mds/tracker/_steps.mds +98 -0
  188. package/src/assets/rules/behavior.md +6 -0
  189. package/src/assets/rules/context-economy.md +10 -0
  190. package/src/assets/rules/go.md +1 -0
  191. package/src/assets/rules/java.md +1 -0
  192. package/src/assets/rules/python.md +1 -0
  193. package/src/assets/rules/rust.md +1 -0
  194. package/src/assets/rules/typescript.md +1 -0
  195. package/src/assets/scripts/claude-md-audit.cjs +611 -0
  196. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +3 -3
  197. package/src/assets/scripts/hooks/json-helper.cjs +13 -5
  198. package/src/assets/scripts/hooks/json-parse +34 -10
  199. package/src/assets/scripts/hooks/lib/project-paths.cjs +0 -6
  200. package/src/assets/scripts/hooks/session-start-context +315 -7
  201. package/src/assets/skills/agent-references/SKILL.md +33 -0
  202. package/src/assets/skills/agent-references/references/learning/maintain.md +71 -0
  203. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +71 -0
  204. package/src/assets/skills/agent-references/references/synthesize/design.md +36 -0
  205. package/src/assets/skills/agent-references/references/synthesize/exploration.md +38 -0
  206. package/src/assets/skills/agent-references/references/synthesize/planning.md +60 -0
  207. package/src/assets/skills/agent-references/references/synthesize/research.md +61 -0
  208. package/src/assets/skills/agent-references/references/synthesize/review.md +76 -0
  209. package/src/assets/skills/apply-decisions/SKILL.md +1 -1
  210. package/src/assets/skills/apply-feature-knowledge/SKILL.md +5 -5
  211. package/src/assets/skills/compliance/SKILL.md +1 -1
  212. package/src/assets/skills/docs-framework/SKILL.md +80 -83
  213. package/src/assets/skills/docs-framework/references/patterns.md +14 -25
  214. package/src/assets/skills/docs-framework/references/violations.md +7 -7
  215. package/src/assets/skills/feature-knowledge/SKILL.md +43 -12
  216. package/src/assets/skills/gap-analysis/SKILL.md +0 -16
  217. package/src/assets/skills/gap-analysis/references/compliance.md +19 -0
  218. package/src/assets/skills/quality-gates/SKILL.md +1 -1
  219. package/dist/skills/git/references/pr/ensure-pr-ready.md +0 -24
  220. package/src/assets/agents/code.md +0 -330
@@ -32,8 +32,28 @@ $ARGUMENTS
32
32
 
33
33
  **Produces:** DECISIONS_CONTEXT
34
34
 
35
+ **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
36
+
37
+ ```bash
38
+ node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
39
+ ```
40
+
41
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
42
+
43
+ The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
44
+
45
+ From the same line: `COMPLIANCE_FRAMEWORKS` is the settings line's `COMPLIANCE` with `generic` written `none`: `off`, `none`, or the framework ids the machine and this repository declare.
46
+
47
+ **Set the compliance directive** from `COMPLIANCE_FRAMEWORKS`. `COMPLIANCE_DIRECTIVE` is empty when `COMPLIANCE_FRAMEWORKS` is `off`: a spawn line that holds only `{COMPLIANCE_DIRECTIVE}` is then left out, so a lens-off spawn carries nothing about compliance. Otherwise `COMPLIANCE_DIRECTIVE` is this one line, with `<ids>` replaced by the value of `COMPLIANCE_FRAMEWORKS`:
48
+
49
+ > When the task touches regulated surface (data models, auth flows, logging/observability, payments, IaC, retention), invoke Skill(skill="devflow:compliance") and load references/{id}.md only for the ids in <ids> (none: generic controls only); never fabricate guidance for a framework you were not given.
50
+
51
+ Phase 7 passes `COMPLIANCE_DIRECTIVE` to the Code agent that implements the fix, on a line of its own.
52
+
35
53
  ### Load DECISIONS_CONTEXT
36
54
 
55
+ When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
56
+
37
57
  The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
38
58
 
39
59
  ```bash
@@ -57,9 +77,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
57
77
 
58
78
  The index is one direct file read, written at render time by `render-decisions.cjs` alongside `decisions.md`/`pitfalls.md` — no `.cjs` script runs here, and the index's own footer names the files that hold each entry's full body.
59
79
 
60
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
80
+ **Step 2 — Agents apply the decisions:**
61
81
 
62
- When `DECISIONS_CONTEXT` is not `(none)`, follow `devflow:apply-decisions` to scan the index, identify plausibly-relevant entries, Read full entry bodies on demand, and cite verbatim IDs in downstream agent prompts and reasoning.
82
+ When `DECISIONS_CONTEXT` is not `(none)`, hand it only to the agents whose contract declares it. They follow `devflow:apply-decisions`: scan the index, identify plausibly-relevant entries, Read full entry bodies on demand, and cite verbatim IDs in their reasoning. A host that uses the index in its own thread says so in its own step.
63
83
 
64
84
  The orchestrator uses `DECISIONS_CONTEXT` locally when generating hypotheses (Phase 2) — prior pitfalls and decisions can suggest specific root causes to investigate. Follow `devflow:apply-decisions` to Read full entry bodies on demand. **Do NOT pass `DECISIONS_CONTEXT` to Explore investigators** — decisions context stays in the orchestrator; investigators examine code directly.
65
85
 
@@ -214,7 +234,20 @@ Produce the final report:
214
234
 
215
235
  Ask user via AskUserQuestion: "Want me to implement this fix?"
216
236
 
217
- - **YES** → Load `devflow:patterns` and `devflow:test-driven-development` skills, implement the fix, then spawn `Agent(subagent_type="Simplify")` on changed files.
237
+ - **YES** → implement the fix through the Code agent, never in this thread, then spawn `Agent(subagent_type="Simplify")` on the changed files. /debug loads no feature knowledge, so the fix gets none:
238
+
239
+ ```
240
+ Agent(subagent_type="Code"):
241
+ "OPERATION: implement
242
+ TASK_ID: debug-{YYYY-MM-DD_HHMM}
243
+ TASK_DESCRIPTION: Implement the fix for this root cause: {bug description}
244
+ EXECUTION_PLAN: {Root Cause, Key Findings and Recommended Fix from Phase 6, with their file:line references}
245
+ PUSH: false
246
+ DECISIONS_CONTEXT: {decisions_context}
247
+ FEATURE_KNOWLEDGE: (none)
248
+ {COMPLIANCE_DIRECTIVE}"
249
+ ```
250
+
218
251
  - **NO** → Done. Report stands as documentation.
219
252
 
220
253
  ### Feature Knowledge Write-Back (Conditional)
@@ -227,17 +260,7 @@ git -C "{start}" rev-parse --show-toplevel
227
260
 
228
261
  and use its one-line output. If the command fails (outside a git repository), `{worktree}` is the start directory itself. All paths below are relative to `{worktree}`.
229
262
 
230
- **Step 1 — Check the opt-out gate, with `{root}` = `{worktree}`:**
231
-
232
- **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
233
-
234
- ```bash
235
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
236
- ```
237
-
238
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
239
-
240
- The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
263
+ **Step 1 — Check the opt-out gate, with `{root}` = `{worktree}`:** take the settings line resolved above for that root, resolving it with the settings block when this run has not yet.
241
264
 
242
265
  If the settings line says `KNOWLEDGE=off`, skip write-back entirely. The machine switch (`devflow knowledge --disable`), the repository and the personal settings can each turn knowledge off, and none can turn it back on (D-FEATURES-NARROW-ONLY). The fail-closed line says `KNOWLEDGE=off` too, so an unresolvable line skips write-back.
243
266
 
@@ -267,7 +290,7 @@ Write the knowledge base to:
267
290
  Then update the index cache by performing a read-modify-write on:
268
291
  {worktree}/.devflow/features/index.md
269
292
 
270
- Index line format: `- **{slug}** — {areas} — {Use-when description}`
293
+ Index line format: `- **{slug}** — {areas} — {Use-when description}` — at most 300 characters, the description at most 220; reword a longer one, never cut it.
271
294
 
272
295
  If the line for this slug already exists in index.md, replace it. If it does not exist, append it. If index.md does not exist, create it with just this line.
273
296
 
@@ -305,7 +328,7 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
305
328
  ├─ Phase 6: Root cause report with confidence level
306
329
  │
307
330
  ├─ Phase 7: Offer Fix
308
- │ └─ AskUserQuestion → implement fix + Simplify agent, or done
331
+ │ └─ AskUserQuestion → Code agent (fix) + Simplify agent, or done
309
332
  │
310
333
  └─ Feature Knowledge Write-Back (Conditional)
311
334
  └─ Knowledge agent (if investigation surfaced durable cross-cutting knowledge)
@@ -41,7 +41,7 @@ Spawn devflow agents with:
41
41
  agent("your prompt here", { agentType: "Code" })
42
42
  ```
43
43
 
44
- Valid `agentType` values: Code, Validate, Simplify, Scrutinize, Evaluate, Test, Review, Git, Synthesize, Knowledge, Design.
44
+ Valid `agentType` values: Code, Code-fix, Validate, Simplify, Scrutinize, Evaluate, Test, Review, Git, Synthesize, Knowledge, Design.
45
45
 
46
46
  **OMIT `opts.model` whenever `agentType` is set.** The agent frontmatter carries its own model tier (Code→sonnet, Validate→haiku, Review→opus, etc.) and that tier is honored automatically. Passing `opts.model` overrides it — always a mistake.
47
47
 
@@ -63,8 +63,34 @@ Then run a cheap syntax gate: write the authored script to a fresh, run-unique s
63
63
 
64
64
  The `budget` global governs depth. Scale Review agent roster and verification votes to `budget`. A low-budget run uses a leaner roster and fewer verification votes; a high-budget run expands both. Never hardcode a roster size — let budget guide it.
65
65
 
66
+ ### Handoff convention for sequential Code agents within a ticket
67
+
68
+ When a ticket requires multiple sequential Code agent phases, each Code agent appends its own `## Phase {N} Implementation Summary` section, at most 8,192 bytes, to `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{branch_slug}` being the ticket's branch name with every `/` replaced by `-`, and `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. It never rewrites an earlier section. The next Code agent reads, via HANDOFF_FILE input, only the section of the phase immediately before its own, not the whole file. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Code is authoritative, summaries are supplementary.
69
+
70
+ ### IRON RULE (LLM-vs-plumbing)
71
+
72
+ **Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
73
+
74
+ ### SAFETY BANNER
75
+
76
+ **NEVER merge to main or master** — the workflow merges to an integration branch only. The user merges to main themselves after reviewing. This rule is absolute and must appear as an `engine_invariants()` note in every workflow that touches git.
77
+
78
+ ### Settings line
79
+
80
+ **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
81
+
82
+ ```bash
83
+ node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
84
+ ```
85
+
86
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
87
+
88
+ The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
89
+
66
90
  ### DECISIONS_CONTEXT — obtain BEFORE authoring
67
91
 
92
+ When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
93
+
68
94
  The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
69
95
 
70
96
  ```bash
@@ -81,19 +107,7 @@ This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT
81
107
 
82
108
  Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
83
109
 
84
- The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
85
-
86
- ### Handoff convention for sequential Code agents within a ticket
87
-
88
- When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
89
-
90
- ### IRON RULE (LLM-vs-plumbing)
91
-
92
- **Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
93
-
94
- ### SAFETY BANNER
95
-
96
- **NEVER merge to main or master** — the workflow merges to an integration branch only. The user merges to main themselves after reviewing. This rule is absolute and must appear as an `engine_invariants()` note in every workflow that touches git.
110
+ The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into the prompts of the Code, Code-fix, Design, Knowledge, Review and Scrutinize agents, using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Those are the agent types above whose contract declares it; every other agent type gets no decisions context.
97
111
 
98
112
  ---
99
113
 
@@ -107,11 +121,12 @@ The following agentType values are valid. Model tiers are shown for reference
107
121
 
108
122
  | agentType | Model tier | Role |
109
123
  |-----------|-----------|------|
110
- | Code | sonnet | Writes all code and all fixes — the ONLY agent that writes code |
124
+ | Code | sonnet | Implements the plan and fixes review findings — the Code family is the ONLY one that writes code |
125
+ | Code-fix | sonnet | Fixes the listed validation, alignment and QA failures — part of the Code family |
111
126
  | Validate | haiku | Build / typecheck / lint / test — fast correctness gate |
112
127
  | Simplify | sonnet | Reduces complexity, removes duplication, improves readability |
113
128
  | Scrutinize | opus | 9-pillar self-review — deep structural analysis |
114
- | Evaluate | opus | Plan-fidelity / alignment — "was the plan implemented correctly?" |
129
+ | Evaluate | sonnet | Plan-fidelity / alignment — "was the plan implemented correctly?" |
115
130
  | Test | sonnet | Scenario-based acceptance tests against acceptance criteria |
116
131
  | Review | opus | Focus-parameterized code review — spawn ONE agent() per focus area |
117
132
  | Git | haiku | Git operations — branches, commits, merges, worktree management |
@@ -121,7 +136,7 @@ The following agentType values are valid. Model tiers are shown for reference
121
136
 
122
137
  ### Agent caveats
123
138
 
124
- - **A Code agent writes every fix** — no other agent type writes code. Finding-verification uses adversarial Review agent-style passes (§6.3 of design doc). Risk-assessment and tech-debt routing live in the post-code pipeline + escalation doctrine.
139
+ - **The Code family writes every fix** — Code for review findings, Code-fix for gate failures; no other agent type writes code. Finding-verification uses adversarial Review agent-style passes (§6.3 of design doc). Risk-assessment and tech-debt routing live in the post-code pipeline + escalation doctrine.
125
140
  - **Always omit `opts.model` with `agentType`.** Each agent honors its own frontmatter model tier; overriding it defeats the per-agent specialization.
126
141
  - **Review agents are focus-parameterized.** Each focus is a SEPARATE `agent()` call with `agentType: "Review"` and the focus baked into the prompt. Do NOT batch multiple focuses into one Review agent call — that defeats parallel specialization.
127
142
  - **Do not depend on agents enumerating their own skills.** Skills are loaded (spike F5 confirmed) but self-reporting is imperfect. Write agent prompts that give the agent full context directly.
@@ -168,21 +183,11 @@ Author the resolved `ISSUE_REQUIRED` and `APPLY_CONVENTIONS` into the workflow s
168
183
 
169
184
  **Produces:** COMPLIANCE_FRAMEWORKS
170
185
 
171
- **Resolve the compliance lens** for each worktree root, from its settings line (every framework reference is installed on every machine, so no file check decides it):
172
-
173
- **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
174
-
175
- ```bash
176
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
177
- ```
178
-
179
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
180
-
181
- The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
186
+ **Resolve the compliance lens** for each worktree root, from its settings line — the line resolved above for that root, by the settings block when this run has not yet resolved it (every framework reference is installed on every machine, so no file check decides it).
182
187
 
183
188
  **Set the compliance lens** from that line: `COMPLIANCE_FRAMEWORKS` is the settings line's `COMPLIANCE` with `generic` written `none`: `off`, `none`, or the framework ids the machine and this repository declare.
184
189
 
185
- Pass it as `complianceFrameworks` when invoking the workflow; the engine hands it to every Code agent.
190
+ Pass it as `complianceFrameworks` when invoking the workflow; the engine builds `COMPLIANCE_DIRECTIVE` from it and hands that to every Code and Code-fix agent.
186
191
 
187
192
  **0c. Resolve the docs root**
188
193
 
@@ -198,7 +203,7 @@ and using its one-line output. If the command fails (outside a git repository),
198
203
 
199
204
  **1. Apply decisions context**
200
205
 
201
- Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded per the preamble above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Code agent and Evaluate agent prompts.
206
+ Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Code agent prompts.
202
207
 
203
208
  **2. Read budget**
204
209
 
@@ -264,7 +269,7 @@ If a number is found, record it as the command-level `ISSUE_NUMBER`:
264
269
  - **SINGLE mode:** it is the ticket's own reference. Pass it as `issueNumber: <number>` when invoking the workflow; the engine hands it to setup-task as `ISSUE_INPUT`. If none is found, pass nothing.
265
270
  - **WAVE mode:** it is the tracking issue. Only steps 2 and 3 after the workflow use it: step 2 for the wave report, step 3 for the wave block's tracking line. It never reaches a ticket: each ticket's engine gets that ticket's own reference as `issueInput`.
266
271
 
267
- Never pass `ISSUE_PR_LINK` into the workflow. The engine binds each ticket's `ISSUE_NUMBER` and `ISSUE_PR_LINK` from that ticket's own setup-task Output (`### Handoff Values`), never from the token it passed in, and hands both to that ticket's Code agents. No `- **PR link line**:` captured ⇒ `ISSUE_PR_LINK` is `"(none)"` and the Code agent emits the `## Related Issues` heading with no reference.
272
+ Never pass `ISSUE_PR_LINK` into the workflow. The engine binds each ticket's `ISSUE_NUMBER` and `ISSUE_PR_LINK` from that ticket's own setup-task Output (`### Handoff Values`), never from the token it passed in, and returns that ticket's `issuePrLink` for the wave PR's related line. No `- **PR link line**:` captured ⇒ `ISSUE_PR_LINK` is `"(none)"` and the ticket's related line carries no closing reference.
268
273
 
269
274
  ---
270
275
 
@@ -290,6 +295,7 @@ const ISSUE_INPUT = args.issueInput || args.issueNumber || "(none)"; // this ti
290
295
  const ISSUE_REQUIRED = String(args.issueRequired) === "false" ? "false" : "true"; // Pre-authoring step 0; only an explicit false turns it off — absent or unrecognised fails closed, like the resolver
291
296
  const APPLY_CONVENTIONS = String(args.applyConventions) === "false" ? "false" : "true";
292
297
  const COMPLIANCE_FRAMEWORKS = /^(?:off|none|[a-z][a-z0-9-]{0,15}(?:,[a-z][a-z0-9-]{0,15}){0,7})$/.test(String(args.complianceFrameworks)) ? String(args.complianceFrameworks) : "off"; // Pre-authoring step 0b; any other shape is no lens
298
+ const COMPLIANCE_DIRECTIVE = COMPLIANCE_FRAMEWORKS === "off" ? "" : `When the task touches regulated surface (data models, auth flows, logging/observability, payments, IaC, retention), invoke Skill(skill="devflow:compliance") and load references/{id}.md only for the ids in ${COMPLIANCE_FRAMEWORKS} (none: generic controls only); never fabricate guidance for a framework you were not given.`; // D-COMPLIANCE-INJECT: empty when the lens is off, so a Code or Code-fix spawn then carries nothing about compliance; otherwise the one line _compliance.mds renders as compliance_directive(), with the validated ids in place of <ids>
293
299
 
294
300
  // Phase 1: Git setup — declare the operation; the agent owns the process, the branch name included
295
301
  const setup = await phase("setup", () =>
@@ -306,7 +312,7 @@ Return: {"branch": "<the - **Branch name**: value under your ### Branch, or (non
306
312
  // An Issue ID is a bare number or a KEY-number, per the provider; any other value — "none", blank, prose — is no capture, so the stop fails closed.
307
313
  const ISSUE_ID_SHAPE = /^(?:[1-9][0-9]{0,8}|[A-Z][A-Z0-9_]{0,9}-[1-9][0-9]{0,8})$/;
308
314
  const ISSUE_NUMBER = ISSUE_ID_SHAPE.test(String(setup?.issueId ?? "")) ? setup.issueId : "(none)"; // the captured Issue ID; "(none)" ⇒ none was captured
309
- const ISSUE_PR_LINK = setup?.prLinkLine || "(none)"; // "(none)" ⇒ Code emits the heading with no reference
315
+ const ISSUE_PR_LINK = setup?.prLinkLine || "(none)"; // "(none)" ⇒ the wave PR carries no closing reference for this ticket
310
316
  // The branch setup-task created, as it reported it: every later phase and the merge use it. The engine never names one.
311
317
  const BRANCH = typeof setup?.branch === "string" && setup.branch.trim() !== "" && setup.branch.trim() !== "(none)" ? setup.branch : "(none)";
312
318
 
@@ -332,9 +338,7 @@ ${PLAN ? `Implementation plan:\n${PLAN}` : "No plan provided — use best judgme
332
338
  Relevant architectural decisions (apply devflow:apply-decisions algorithm):
333
339
  ${DECISIONS_CONTEXT}
334
340
 
335
- ISSUE_NUMBER: ${ISSUE_NUMBER}
336
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
337
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
341
+ ${COMPLIANCE_DIRECTIVE}
338
342
 
339
343
  When you build or run tests to verify your work, follow your Running commands block.
340
344
 
@@ -368,10 +372,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
368
372
  await agent(`OPERATION: validation-fix
369
373
  Fix the validation failures on branch ${BRANCH}:
370
374
  ${failureDetails}
371
- ISSUE_NUMBER: ${ISSUE_NUMBER}
372
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
373
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
374
- Commit fixes with conventional-commit message.`, { agentType: "Code" });
375
+ DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
376
+ ${COMPLIANCE_DIRECTIVE}
377
+ Commit fixes with conventional-commit message.`, { agentType: "Code-fix" });
375
378
  const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} after fix attempt ${attempt} (follow your Running commands block).
376
379
  Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
377
380
  if (recheck?.verdict === "PASS") break;
@@ -410,10 +413,9 @@ Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answ
410
413
  await agent(`OPERATION: alignment-fix
411
414
  Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
412
415
  ${evaluation?.rationale || "see the Evaluate agent's report"}
413
- ISSUE_NUMBER: ${ISSUE_NUMBER}
414
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
415
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
416
- Self-verify your fix compiles, following your Running commands block. Commit fixes.`, { agentType: "Code" });
416
+ DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
417
+ ${COMPLIANCE_DIRECTIVE}
418
+ Self-verify your fix compiles, following your Running commands block. Commit fixes.`, { agentType: "Code-fix" });
417
419
  evalVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
418
420
  }
419
421
  }
@@ -431,10 +433,9 @@ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scen
431
433
  await agent(`OPERATION: qa-fix
432
434
  Fix the failing acceptance test scenarios on branch ${BRANCH}:
433
435
  ${testResult.failures}
434
- ISSUE_NUMBER: ${ISSUE_NUMBER}
435
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
436
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
437
- Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Code" });
436
+ DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
437
+ ${COMPLIANCE_DIRECTIVE}
438
+ Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Code-fix" });
438
439
  testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
439
440
  }
440
441
  }
@@ -550,9 +551,8 @@ ISSUES:
550
551
  ${chunk.map((f, n) => `${n + 1}. ${f.file ? `${f.file}${f.line ? `:${f.line}` : ""} — ` : ""}${f.description} (${f.severity})`).join("\n")}
551
552
  SCOPE: ${chunk.map((f, n) => `${n + 1}=${CAREFUL_SEVERITIES.has(String(f.severity).toLowerCase()) ? "Careful" : "Standard"}`).join(", ")}
552
553
  PUSH: false
553
- ISSUE_NUMBER: ${ISSUE_NUMBER}
554
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
555
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
554
+ DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
555
+ ${COMPLIANCE_DIRECTIVE}
556
556
  Fix all findings in this batch. Self-verify your fix compiles, following your Running commands block. Commit with conventional-commit message.
557
557
  Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<description of any finding that could not be fixed>"]}`, { agentType: "Code" });
558
558
  chunkResults.push({ chunk, result: r });
@@ -610,10 +610,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
610
610
  await agent(`OPERATION: validation-fix
611
611
  Fix the final validation failures on branch ${BRANCH}:
612
612
  ${failureDetails}
613
- ISSUE_NUMBER: ${ISSUE_NUMBER}
614
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
615
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
616
- Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code" });
613
+ DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
614
+ ${COMPLIANCE_DIRECTIVE}
615
+ Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code-fix" });
617
616
  const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (final gate, after fix attempt ${attempt}; follow your Running commands block).
618
617
  Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
619
618
  if (recheck?.verdict === "PASS") break;
@@ -698,12 +697,12 @@ Run builds, typechecks, lints and tests in the foreground, each with an explicit
698
697
  The standard implementation unit for one ticket. Run in order:
699
698
 
700
699
  ```
701
- Code(agentType:"Code", prompt: "OPERATION: implement" first line, then full task + plan + DECISIONS_CONTEXT + handoff if sequential)
700
+ Code(agentType:"Code", prompt: "OPERATION: implement" first line, then full task + plan + DECISIONS_CONTEXT + COMPLIANCE_DIRECTIVE + handoff if sequential)
702
701
  → gate1_postcode()
703
702
  → gate2_acceptance() ← Gate 2 runs HERE — before the review pass, not after
704
703
  ```
705
704
 
706
- Every Code agent prompt opens with `OPERATION: <mode>` as its first line (`implement`, `issue-fix`, `validation-fix`, `alignment-fix` or `qa-fix`). It must include: task description, implementation plan (if one exists), relevant DECISIONS_CONTEXT (the index you loaded before authoring), the compliance lens (`COMPLIANCE_FRAMEWORKS` — every Code prompt carries it, fix prompts included), and any PRIOR_PHASE_SUMMARY / HANDOFF_FILE for sequential multi-phase tickets.
705
+ Every Code-family agent prompt opens with `OPERATION: <mode>` as its first line: `implement` and `issue-fix` go to `agentType:"Code"`, `validation-fix`, `alignment-fix` and `qa-fix` go to `agentType:"Code-fix"`. It must include: task description, implementation plan (if one exists), relevant DECISIONS_CONTEXT (the index you loaded before authoring), the compliance directive (`COMPLIANCE_DIRECTIVE` — every Code prompt carries it when it is not empty, fix prompts included), and any PRIOR_PHASE_SUMMARY / HANDOFF_FILE for sequential multi-phase tickets.
707
706
 
708
707
  Gate 2 runs at implementation acceptance — this matches devflow's deliberate placement: "evaluation is part of implementation acceptance, not post-review" (§6.1).
709
708
 
@@ -716,7 +715,7 @@ ORDER IS LOAD-BEARING. Run exactly in this sequence:
716
715
  - BLOCKED (a P0 issue cannot be fixed in scope), or a missing or unrecognised status, which counts as BLOCKED → stop the pass and escalate as `scrutiny-blocked`; Validate does not run on code Scrutinize could not accept
717
716
  3. **Validate agent** — build / typecheck / lint / test; returns `{"verdict": "PASS" | "FAIL", "details": "..."}`. It runs LAST and UNCONDITIONALLY: one full run over the HEAD that Simplify and Scrutinize left, so it covers their commits whether or not Scrutinize changed code
718
717
  - Build/test commands follow `build_execution_doctrine()`: the Validate agent's Running commands block, foreground under an explicit Bash timeout.
719
- - FAIL → Code agent fix (max 2 retries), each followed by a Validate agent re-run
718
+ - FAIL → Code-fix agent fix (max 2 retries), each followed by a Validate agent re-run
720
719
  - If still FAIL after 2 retries → escalate as `validation-exhausted` (do not loop endlessly)
721
720
 
722
721
  **Gate 1 contains NO Evaluate agent and NO Test agent.** Those are Gate 2 only.
@@ -737,11 +736,11 @@ Gate 2 inputs are produced by `/devflow:dynamic-plan`'s plan-challenge step —
737
736
 
738
737
  **Evaluate agent** (only if a plan exists):
739
738
  - Run `evaluator_panel()` — see that block for the one spawn and the two lenses it names
740
- - If the Evaluate agent returns FAIL (either lens failed): fix-and-continue — the demanded fixes are applied by a Code agent that self-verifies its own build (batched per the review-pass batching doctrine if numerous). The recorded verdict becomes `FAIL-FIXED` (issues found, fixes applied, not re-evaluated by design); Gate 2 then proceeds. In SINGLE mode the run reports it as `UNVERIFIED`, never PASS.
739
+ - If the Evaluate agent returns FAIL (either lens failed): fix-and-continue — the demanded fixes are applied by a Code-fix agent that self-verifies its own build (batched per the review-pass batching doctrine if numerous). The recorded verdict becomes `FAIL-FIXED` (issues found, fixes applied, not re-evaluated by design); Gate 2 then proceeds. In SINGLE mode the run reports it as `UNVERIFIED`, never PASS.
741
740
 
742
741
  **Test agent** (only if acceptance criteria or a test plan exist):
743
742
  - Scenario-based acceptance tests covering functionality, API contracts, performance
744
- - FAIL → fix-and-continue — a Code agent applies the demanded fixes and self-verifies its own build. The recorded verdict becomes `FAIL-FIXED`; Gate 2 then proceeds. In SINGLE mode the run reports it as `UNVERIFIED`, never PASS.
743
+ - FAIL → fix-and-continue — a Code-fix agent applies the demanded fixes and self-verifies its own build. The recorded verdict becomes `FAIL-FIXED`; Gate 2 then proceeds. In SINGLE mode the run reports it as `UNVERIFIED`, never PASS.
745
744
 
746
745
  **When Gate 2 inputs are absent:**
747
746
  - No plan → skip the Evaluate agent silently (note in output: "Gate 2 Evaluate agent skipped — no plan available")
@@ -860,7 +859,7 @@ If survivors remain: batch the confirmed findings for fixing: group findings by
860
859
 
861
860
  ### Engine invariants (non-negotiable)
862
861
 
863
- 1. **Code is written ONLY by Code agents.** No other agent type writes code — not Review agent, not Evaluate agent.
862
+ 1. **Code is written ONLY by the Code family (Code, Code-fix).** No other agent type writes code — not Review agent, not Evaluate agent.
864
863
  2. **Findings are verified before any fix is written.** The adversarial verification step is not optional; unverified findings are not passed to the Code agent.
865
864
  3. **All written code passes Gate 1.** No code merge, commit, or handoff before Simplify agent + Scrutinize agent + Validate agent (in that order — Validate last, so its one run covers the commits of the two before it).
866
865
  4. **Gate 2 runs once, at implementation acceptance.** It does not re-run after review-fixes.
@@ -1004,7 +1003,7 @@ Two parallel sibling tickets can produce real git conflicts. The resolution is *
1004
1003
  **Resolution procedure:**
1005
1004
 
1006
1005
  1. Git agent detects the conflict and reports the conflicting files + sections
1007
- 2. Spawn a Code agent whose prompt opens with `OPERATION: implement` and carries `COMPLIANCE_FRAMEWORKS`, with FULL intent context:
1006
+ 2. Spawn a Code agent whose prompt opens with `OPERATION: implement` and carries `COMPLIANCE_DIRECTIVE` (when it is not empty) and `DECISIONS_CONTEXT`, with FULL intent context:
1008
1007
  - Both ticket descriptions and plans
1009
1008
  - The conflicting diff sections (both sides)
1010
1009
  - Relevant ADRs from DECISIONS_CONTEXT (loaded by main model before authoring)
@@ -1257,17 +1256,7 @@ git -C "{integration worktree root}" push origin HEAD; echo "exit=$?"
1257
1256
 
1258
1257
  **(d) Refresh.** Resolve the publication value for the integration worktree:
1259
1258
 
1260
- **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
1261
-
1262
- ```bash
1263
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
1264
- ```
1265
-
1266
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
1267
-
1268
- The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
1269
-
1270
- **Resolve `REVIEW_PUBLICATION` per worktree:** take `REVIEW_PUBLICATION` from that worktree's settings line, with `{root}` the worktree's root — multi-worktree repos may resolve different values per worktree. The line already caps the personal choice at the team's (D-PUBLICATION-CEILING), so it is `off`, `auto` or `full`, and `off` when the line was unresolvable.
1259
+ **Resolve `REVIEW_PUBLICATION` per worktree:** take `REVIEW_PUBLICATION` from that worktree's settings line — the line resolved above for `{root}`, the worktree's root, by the settings block when this run has not yet resolved that root; multi-worktree repos may resolve different values per worktree. The line already caps the personal choice at the team's (D-PUBLICATION-CEILING), so it is `off`, `auto` or `full`, and `off` when the line was unresolvable.
1271
1260
 
1272
1261
  **Evidence stub:** only when `EVIDENCE_POLICY` is `required`, a resolved `off` becomes `stub`, so a counts-only record still reaches the PR. `stub` is never a config value: the settings line never carries it.
1273
1262
 
@@ -41,7 +41,7 @@ Spawn devflow agents with:
41
41
  agent("your prompt here", { agentType: "Code" })
42
42
  ```
43
43
 
44
- Valid `agentType` values: Code, Validate, Simplify, Scrutinize, Evaluate, Test, Review, Git, Synthesize, Knowledge, Design.
44
+ Valid `agentType` values: Code, Code-fix, Validate, Simplify, Scrutinize, Evaluate, Test, Review, Git, Synthesize, Knowledge, Design.
45
45
 
46
46
  **OMIT `opts.model` whenever `agentType` is set.** The agent frontmatter carries its own model tier (Code→sonnet, Validate→haiku, Review→opus, etc.) and that tier is honored automatically. Passing `opts.model` overrides it — always a mistake.
47
47
 
@@ -63,8 +63,34 @@ Then run a cheap syntax gate: write the authored script to a fresh, run-unique s
63
63
 
64
64
  The `budget` global governs depth. Scale Review agent roster and verification votes to `budget`. A low-budget run uses a leaner roster and fewer verification votes; a high-budget run expands both. Never hardcode a roster size — let budget guide it.
65
65
 
66
+ ### Handoff convention for sequential Code agents within a ticket
67
+
68
+ When a ticket requires multiple sequential Code agent phases, each Code agent appends its own `## Phase {N} Implementation Summary` section, at most 8,192 bytes, to `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{branch_slug}` being the ticket's branch name with every `/` replaced by `-`, and `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. It never rewrites an earlier section. The next Code agent reads, via HANDOFF_FILE input, only the section of the phase immediately before its own, not the whole file. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Code is authoritative, summaries are supplementary.
69
+
70
+ ### IRON RULE (LLM-vs-plumbing)
71
+
72
+ **Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
73
+
74
+ ### SAFETY BANNER
75
+
76
+ **NEVER merge to main or master** — the workflow merges to an integration branch only. The user merges to main themselves after reviewing. This rule is absolute and must appear as an `engine_invariants()` note in every workflow that touches git.
77
+
78
+ ### Settings line
79
+
80
+ **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
81
+
82
+ ```bash
83
+ node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
84
+ ```
85
+
86
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off` instead.
87
+
88
+ The accepted line is the only source of these values: the script alone folds the committed `.devflow/project.json`, the personal `.devflow/config.json` and the machine manifest.
89
+
66
90
  ### DECISIONS_CONTEXT — obtain BEFORE authoring
67
91
 
92
+ When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
93
+
68
94
  The decisions ledger belongs to the repository, not to one checkout: in a linked worktree it lives in the main worktree, and a session started in a subdirectory reads the copy at the repository root. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
69
95
 
70
96
  ```bash
@@ -81,19 +107,7 @@ This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT
81
107
 
82
108
  Before you author the workflow script, read `{ledger}/.devflow/learning/index.md`. If the file is absent or empty, set `DECISIONS_CONTEXT` to `(none)`; otherwise use the file content as `DECISIONS_CONTEXT`.
83
109
 
84
- The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into agent prompts using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Only agents that need architectural context (Code agent, Evaluate agent, Review agent, Scrutinize agent) need DECISIONS_CONTEXT injected; lightweight agents (Validate agent, Simplify agent) do not.
85
-
86
- ### Handoff convention for sequential Code agents within a ticket
87
-
88
- When a ticket requires multiple sequential Code agent phases, each Code agent writes `{toplevel}/.devflow/docs/handoff-{branch_slug}.md` (branch-scoped to prevent concurrent session clobber), `{toplevel}` being `git rev-parse --show-toplevel` in the checkout the ticket's branch is in — never a subdirectory. The next Code agent reads it via HANDOFF_FILE input. PRIOR_PHASE_SUMMARY is the compact in-context form; the handoff file is the durable form that survives context compaction. Always read the handoff file directly — code is authoritative, summaries are supplementary.
89
-
90
- ### IRON RULE (LLM-vs-plumbing)
91
-
92
- **Author ZERO deterministic feature code.** No parsers, no schedulers, no topological-sort, no dependency-graph helpers, no confidence formulas. ALL issue reading, dependency reasoning, and scheduling decisions are LLM judgment at runtime, performed by the workflow's agents. The recipe is instructions. The workflow script Claude authors IS the runtime logic — keep it free of hand-coded feature algorithms.
93
-
94
- ### SAFETY BANNER
95
-
96
- **NEVER merge to main or master** — the workflow merges to an integration branch only. The user merges to main themselves after reviewing. This rule is absolute and must appear as an `engine_invariants()` note in every workflow that touches git.
110
+ The script body cannot perform this read — you (the main model) do it before authoring. Then inject the relevant DECISIONS_CONTEXT into the prompts of the Code, Code-fix, Design, Knowledge, Review and Scrutinize agents, using the `devflow:apply-decisions` consumption algorithm (scan index → Read relevant entries → cite verbatim IDs in agent prompts). Those are the agent types above whose contract declares it; every other agent type gets no decisions context.
97
111
 
98
112
  ---
99
113
 
@@ -107,11 +121,12 @@ The following agentType values are valid. Model tiers are shown for reference
107
121
 
108
122
  | agentType | Model tier | Role |
109
123
  |-----------|-----------|------|
110
- | Code | sonnet | Writes all code and all fixes — the ONLY agent that writes code |
124
+ | Code | sonnet | Implements the plan and fixes review findings — the Code family is the ONLY one that writes code |
125
+ | Code-fix | sonnet | Fixes the listed validation, alignment and QA failures — part of the Code family |
111
126
  | Validate | haiku | Build / typecheck / lint / test — fast correctness gate |
112
127
  | Simplify | sonnet | Reduces complexity, removes duplication, improves readability |
113
128
  | Scrutinize | opus | 9-pillar self-review — deep structural analysis |
114
- | Evaluate | opus | Plan-fidelity / alignment — "was the plan implemented correctly?" |
129
+ | Evaluate | sonnet | Plan-fidelity / alignment — "was the plan implemented correctly?" |
115
130
  | Test | sonnet | Scenario-based acceptance tests against acceptance criteria |
116
131
  | Review | opus | Focus-parameterized code review — spawn ONE agent() per focus area |
117
132
  | Git | haiku | Git operations — branches, commits, merges, worktree management |
@@ -121,7 +136,7 @@ The following agentType values are valid. Model tiers are shown for reference
121
136
 
122
137
  ### Agent caveats
123
138
 
124
- - **A Code agent writes every fix** — no other agent type writes code. Finding-verification uses adversarial Review agent-style passes (§6.3 of design doc). Risk-assessment and tech-debt routing live in the post-code pipeline + escalation doctrine.
139
+ - **The Code family writes every fix** — Code for review findings, Code-fix for gate failures; no other agent type writes code. Finding-verification uses adversarial Review agent-style passes (§6.3 of design doc). Risk-assessment and tech-debt routing live in the post-code pipeline + escalation doctrine.
125
140
  - **Always omit `opts.model` with `agentType`.** Each agent honors its own frontmatter model tier; overriding it defeats the per-agent specialization.
126
141
  - **Review agents are focus-parameterized.** Each focus is a SEPARATE `agent()` call with `agentType: "Review"` and the focus baked into the prompt. Do NOT batch multiple focuses into one Review agent call — that defeats parallel specialization.
127
142
  - **Do not depend on agents enumerating their own skills.** Skills are loaded (spike F5 confirmed) but self-reporting is imperfect. Write agent prompts that give the agent full context directly.
@@ -154,11 +169,11 @@ git -C "{start}" rev-parse --show-toplevel
154
169
 
155
170
  and using its one-line output. If the command fails (outside a git repository), `{worktree}` is the start directory itself. Every docs path below is written `{worktree}/.devflow/docs/…`; a repo-relative docs path handed to an agent always travels with a `WORKTREE_PATH` naming the checkout it is relative to.
156
171
 
157
- Pass `{worktree}` to the workflow as its `root` argument.
172
+ Pass `{worktree}` to the workflow as its `root` argument, and its `slug` argument: the `{slug}` of the ticket directory (`tickets/{slug}/{ts}/`) when that is the ticket source, otherwise `wave`.
158
173
 
159
174
  **1. Apply decisions context**
160
175
 
161
- Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded per the preamble above: scan the index, Read relevant entries, note verbatim ADR/PF IDs to inject into Design agent and Evaluate agent prompts.
176
+ Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note verbatim ADR/PF IDs to inject into Design agent prompts.
162
177
 
163
178
  **2. Read the preference profile**
164
179
 
@@ -236,7 +251,8 @@ const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before auth
236
251
  const PREFERENCE_PROFILE = args.preferenceProfile || ""; // injected before authoring
237
252
  const TP_CONTRACT = args.tpContract || ""; // the "Test-plan line (TP)" contract below — its paragraph and four bullets — passed verbatim as tpContract when invoking the workflow, never pasted into this script (it holds backticks)
238
253
  const slug = args.slug || "wave";
239
- const ts = new Date().toISOString().slice(0,16).replace(/[-:T]/g, (c) => c === 'T' ? '_' : c === ':' ? '' : c);
254
+ const now = new Date(), two = (n) => String(n).padStart(2, '0');
255
+ const ts = `${now.getFullYear()}-${two(now.getMonth() + 1)}-${two(now.getDate())}_${two(now.getHours())}${two(now.getMinutes())}`;
240
256
  const ROOT = args.root; // {worktree} from Pre-authoring setup: the checkout's toplevel, never cwd
241
257
  const OUTDIR = `${ROOT}/.devflow/docs/design/${slug}/${ts}`;
242
258
 
@@ -269,7 +285,6 @@ const challenged = await phase("plan-challenge", () =>
269
285
 
270
286
  Plan under review: ${JSON.stringify(plan)}
271
287
  Ticket: ${JSON.stringify((tickets || [])[i])}
272
- Decisions context: ${DECISIONS_CONTEXT}
273
288
 
274
289
  Produce:
275
290
  1. List of improvements / gaps / edge cases / side-effects identified.
@@ -280,7 +295,7 @@ Produce:
280
295
  - its verification method is its method: a test committed to the suite is ci; a command run and read (a load test, a script) is local; a step performed and observed is manual;
281
296
  - the paths it exercises, from the plan's affected files, are its files: globs.
282
297
  Setup and expected outcome never go in a line: give them per TP in testScenarios.
283
- 4. A list of genuine design decisions that require user input (not settled by the plan, the preference profile, or existing ADRs).
298
+ 4. A list of genuine design decisions that require user input (not settled by the plan or the preference profile).
284
299
 
285
300
  Test-plan line contract:
286
301
  ${TP_CONTRACT}
@@ -319,10 +334,9 @@ const resolved = await phase("preference-resolve", () =>
319
334
  Preference profile: ${PREFERENCE_PROFILE || "(none — no profile found)"}
320
335
  Open decisions from plan-challenge: ${JSON.stringify((challenged || []).flatMap(c => c.openDecisions || []))}
321
336
  Cross-plan conflicts needing resolution: ${JSON.stringify((crossCritic && crossCritic.conflicts) || [])}
322
- Decisions context (ADRs/PFs already settled): ${DECISIONS_CONTEXT}
323
337
 
324
338
  For each open decision:
325
- - If the preference profile or an existing ADR/PF settles it clearly: auto-resolve and note the rationale.
339
+ - If the preference profile settles it clearly: auto-resolve and note the rationale.
326
340
  - If not settled: add to the DECISIONS-NEEDED list for the user.
327
341
 
328
342
  Return: { autoResolved (array of {decision, resolution, source}), decisionsNeeded (array of {decision, context, options}) }.`, { agentType: "Synthesize" })
@@ -338,7 +352,7 @@ Auto-resolved decisions: ${JSON.stringify(resolved && resolved.autoResolved)}
338
352
  Decisions needed: ${JSON.stringify(resolved && resolved.decisionsNeeded)}
339
353
  Output directory: ${OUTDIR}
340
354
 
341
- For each ticket, write ${OUTDIR}/{ticket-slug}-plan.md containing:
355
+ For each ticket, write ${OUTDIR}/{ticket-slug}-plan.md, {ticket-slug} being the ticket's file name without .md when it came from a ticket directory, otherwise the ticket title run through title.toLowerCase().replace(/[^a-z0-9]+/g, '-') with nothing trimmed, containing:
342
356
  - ## Implementation Plan
343
357
  - The plan body (incorporate cross-plan amendments)
344
358
  - ## Acceptance Criteria (numbered, positive + negative)