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
@@ -22,9 +22,10 @@ Process issues from code review reports: triage every issue through the blast-ra
22
22
 
23
23
  **Produces:** WORKTREES
24
24
 
25
- 1. **Discover resolvable worktrees** using the `devflow:worktree-support` skill discovery algorithm:
26
- - Run `git worktree list --porcelain` → parse, filter (skip protected/detached/mid-rebase), dedup by branch, sort by recent commit
27
- - Invoke the `devflow:worktree-support` skill, then Read its `references/discovery.md` from the skill's base directory for the full 7-step algorithm; the canonical protected branch list stays in the skill
25
+ 1. **Discover resolvable worktrees**:
26
+ - Run `git worktree list --porcelain` and count the worktrees it lists
27
+ - **More than one listed:** Invoke the `devflow:worktree-support` skill, then Read its `references/discovery.md` from the skill's base directory for the full 7-step algorithm; the canonical protected branch list stays in the skill. Parse the list, filter (skip protected/detached/mid-rebase), dedup by branch, sort by recent commit
28
+ - **Exactly one listed:** load neither the skill nor the reference — that worktree is the only candidate, and Step 0b's Git agent validates its branch
28
29
  - Additional filter: must have unresolved reviews (latest review directory has no `resolution-summary.md`)
29
30
  2. **If `--path` flag provided:** use only that worktree, skip discovery
30
31
  **`--path` validation**: Before proceeding, verify the path exists as a directory and appears in `git worktree list` output. If not: report error and stop.
@@ -85,10 +86,22 @@ Set `TARGET_DIR` to the selected review or bug-analysis directory path, and `TAR
85
86
 
86
87
  #### Step 0d: Load Project Decisions
87
88
 
88
- **Produces:** DECISIONS_CONTEXT, FEATURE_KNOWLEDGE, EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL, REVIEW_PUBLICATION, RESOLUTION_TS
89
+ **Produces:** DECISIONS_CONTEXT, FEATURE_KNOWLEDGE, FEATURE_KNOWLEDGE_RULES, EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL, REVIEW_PUBLICATION, RESOLUTION_TS
90
+
91
+ **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:
92
+
93
+ ```bash
94
+ node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
95
+ ```
96
+
97
+ 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.
98
+
99
+ 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
100
 
90
101
  ### Load DECISIONS_CONTEXT
91
102
 
103
+ When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
104
+
92
105
  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`):
93
106
 
94
107
  ```bash
@@ -112,9 +125,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
112
125
 
113
126
  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.
114
127
 
115
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
128
+ **Step 2 — Agents apply the decisions:**
116
129
 
117
- 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.
130
+ 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.
118
131
 
119
132
  This produces a compact index of active ADR/PF entries written at render time — Deprecated/Superseded entries are already stripped. Pass `DECISIONS_CONTEXT` to the Triage agent in Phase 2 and to Code agents in Phase 4.
120
133
 
@@ -146,22 +159,32 @@ Glob `{worktree}/.devflow/features/*/KNOWLEDGE.md`. For each file found, read on
146
159
 
147
160
  Match the current task area and description against each index line (or frontmatter `description` + `directories` on fallback). Select entries whose documented area overlaps the current task. This is a relevance judgment — prefer specificity over breadth.
148
161
 
149
- **Step 4 — Read selected KBs:**
162
+ **Step 4 — Read each selected KB's Rules:**
163
+
164
+ For each selected entry, `{kb}` is `{worktree}/.devflow/features/{slug}/KNOWLEDGE.md`:
150
165
 
151
- For each selected entry, read `{worktree}/.devflow/features/{slug}/KNOWLEDGE.md` in full. When the KB content contradicts the current code you observe, **trust the code** — the code is the freshness mechanism; the KB may lag behind.
166
+ 1. List its `##` headings with line numbers through Bash: `command grep -n '^## ' "{kb}"`. This only locates sections; the text of a KB comes from the Read view alone.
167
+ 2. Read the `## Rules` range (its line to the next heading) with the Read tool, using `offset` and `limit`, and choose the one to three bullets most relevant to the current task. The choice is yours, made per KB.
168
+ 3. If the KB has no `## Rules` section, choose one to three entries from its `## Anti-Patterns` or `## Gotchas` range the same way, and label them by that section's name instead of an ID.
152
169
 
153
- **Step 5 — Set FEATURE_KNOWLEDGE:**
170
+ When a KB contradicts the code you observe, **trust the code** — the code is the freshness mechanism; the KB may lag behind. A missing Rules section, a missing heading list and `(none)` are legitimate states, not errors.
154
171
 
155
- Concatenate the selected KNOWLEDGE.md files under slug headers:
172
+ **Step 5 — Set FEATURE_KNOWLEDGE and FEATURE_KNOWLEDGE_RULES:**
173
+
174
+ Write one block per selected KB. Paste each bullet verbatim from the Read view, never from a shell view. The path is relative to the checkout root; an agent resolves it under `WORKTREE_PATH` when one is provided.
156
175
 
157
176
  ```
158
177
  --- Feature knowledge: {slug} ---
159
- {full KNOWLEDGE.md content}
178
+ KB: .devflow/features/{slug}/KNOWLEDGE.md
179
+ Rules:
180
+ - **KB-AP-2** {bullet text, verbatim}
181
+ - **KB-INV-1** {bullet text, verbatim}
182
+ Headings: L5 Rules · L40 Overview · L62 Anti-Patterns · L118 Key Files
160
183
  ```
161
184
 
162
- If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set `FEATURE_KNOWLEDGE` to `(none)`.
185
+ A KB with no Rules section labels its entries `Rules ({section name}):` and gives them no ID. `FEATURE_KNOWLEDGE` is these blocks; `FEATURE_KNOWLEDGE_RULES` is the same blocks without the `Headings:` line. Both come from this one selection, and each spawn names the variable its recipient takes. If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set both to `(none)`.
163
186
 
164
- **One git call, then direct file reads — no `.cjs` script.** After resolving `{worktree}`, this step is 1 index read (or N frontmatter reads on fallback), bounded by KB count.
187
+ **One git call, then direct reads — no `.cjs` script.** After resolving `{worktree}`, this step is 1 index read (or N frontmatter reads on fallback), plus one heading listing and one Rules read per selected KB, bounded by KB count.
165
188
 
166
189
  Pass `FEATURE_KNOWLEDGE` to the Triage agent in Phase 2.
167
190
 
@@ -176,23 +199,19 @@ Accept the output only when it is exactly two lines: `exit=0` last and, before i
176
199
  Set `EVIDENCE_POLICY`, `ISSUE_REQUIRED`, `APPLY_CONVENTIONS` and `REQUIRE_NON_AUTHOR_APPROVAL` from the accepted line. Pass agents only the three mechanism inputs, never `EVIDENCE_POLICY`. Report `Evidence policy: {EVIDENCE_POLICY} (source: {SOURCE})`, plus any `WARN` tokens as advisory, once in the final report.
177
200
  Reuse this result for every worktree.
178
201
 
179
- **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:
180
-
181
- ```bash
182
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
183
- ```
184
-
185
- 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.
186
-
187
- 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.
188
-
189
- **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.
202
+ **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.
190
203
 
191
204
  **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.
192
205
 
193
206
  Note: `auto` is NOT fail-open — under `auto`, the Git agent probes the repository visibility and treats any error or unrecognised value as PUBLIC (mode STUB). What each value does is decided by the Git agent's publication gate (`references/publication-gate.md` step 2); this partial only resolves the value.
194
207
  Resolve this per worktree — unlike the evidence policy above, the settings line is per worktree root. 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.
195
208
 
209
+ **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`:
210
+
211
+ > 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.
212
+
213
+ Set it per worktree, from that worktree's lens, and pass that worktree's `COMPLIANCE_DIRECTIVE` to each of its Code and Code-fix spawns, on a line of its own.
214
+
196
215
  **Mint `RESOLUTION_TS` once per run:** `date -u +%Y-%m-%dT%H:%M:%SZ`. Every worktree reuses it — the resolution comment's dedup marker is searched per PR, so one run key keeps a retry of this run deduplicated while the next /resolve on the same PR posts. Phase 5 writes it as the summary's `**Date**:` and Phase 9b-2 passes it, so the local file and the posted marker agree.
197
216
 
198
217
  ### Phase 1: Parse Issues
@@ -307,11 +326,10 @@ TASK_ID: resolve-{batch-id}
307
326
  ISSUES: {fix_now_issues_in_batch}
308
327
  SCOPE: {risk_tier_per_issue}
309
328
  PUSH: false
310
- CREATE_PR: false
311
329
  WORKTREE_PATH: {worktree_path} (omit if cwd)
312
330
  DECISIONS_CONTEXT: {decisions_context}
313
331
  FEATURE_KNOWLEDGE: {feature_knowledge}
314
- COMPLIANCE_FRAMEWORKS: {COMPLIANCE_FRAMEWORKS}
332
+ {COMPLIANCE_DIRECTIVE}
315
333
  Fix only the listed pre-classified FIX_NOW issues. Do not re-litigate dispositions.
316
334
  Same-file issues → one commit. Include ## Verification block in report.
317
335
  Regression fix without failing-then-passing regression test → BLOCKED."
@@ -374,17 +392,17 @@ Run build, typecheck, lint, test. Report pass/fail with failure details."
374
392
  1. Extract failure details from Validate agent output
375
393
  2. Increment `validation_retry_count`
376
394
  3. If `validation_retry_count <= 2`:
377
- - Spawn Code agent with fix context:
395
+ - Spawn Code-fix agent with fix context:
378
396
  ```
379
- Agent(subagent_type="Code"):
397
+ Agent(subagent_type="Code-fix"):
380
398
  "OPERATION: validation-fix
381
399
  TASK_ID: resolve-{batch-id}-valfix-{count}
382
400
  VALIDATION_FAILURES: {parsed failures from Validate agent}
383
401
  SCOPE: Fix only the listed failures, no other changes
384
402
  PUSH: false
385
- CREATE_PR: false
386
403
  WORKTREE_PATH: {worktree_path} (omit if cwd)
387
- COMPLIANCE_FRAMEWORKS: {COMPLIANCE_FRAMEWORKS}"
404
+ DECISIONS_CONTEXT: {decisions_context}
405
+ {COMPLIANCE_DIRECTIVE}"
388
406
  ```
389
407
  - Loop back to re-validate
390
408
  4. If `validation_retry_count > 2`: set VERIFICATION_STATUS = FAILED and exit the validation loop — record a blocking callout in the `## Verification` section (never a silent pass).
@@ -394,7 +412,7 @@ Run build, typecheck, lint, test. Report pass/fail with failure details."
394
412
  Record `## Verification` outcome in resolution-summary.md (runs unconditionally after the validation loop exits):
395
413
  - Rewrite the `## Verification` section with | Command | Result | table + `Final gate: PASS | FAILED after N attempts`
396
414
 
397
- Push all commits to remote once (Code agents and validation-fix Code agents ran with PUSH: false — runs whether the gate PASSED or FAILED, so the branch is always visible before Phase 9 or CI runs):
415
+ Push all commits to remote once (Code and Code-fix agents ran with PUSH: false — runs whether the gate PASSED or FAILED, so the branch is always visible before Phase 9 or CI runs):
398
416
  ```bash
399
417
  git -C {worktree} push
400
418
  ```
@@ -432,7 +450,7 @@ Phase 7 has pushed already, so this is a no-op unless the head moved since. `exi
432
450
  3. **If NO_PR or NO_CI** → skip: "No PR/CI configured, skipping CI validation." Proceed to next phase.
433
451
  4. **If PENDING** and fewer than 3 waits have run → wait again (step 1). After the third wait → report "CI still running — verify manually before merging" and proceed.
434
452
  5. **If INDETERMINATE** and fewer than 3 waits have run → wait again (step 1). After the third wait → report "CI status unknown — verify manually before merging" and proceed.
435
- 6. **If FAILING** and fewer than 2 fixes have run → report the failing checks from the line. Spawn `Agent(subagent_type="Code")` whose prompt opens with `OPERATION: ci-fix`, with `COMPLIANCE_FRAMEWORKS`, `CI_FAILURES` and `PUSH: false`; `CI_FAILURES` holds the failing-check names from the line and nothing else, because the Code agent fetches the full names and reads the logs itself and this command reads none. After a fix, push with the command above and, if a wait remains, wait again (step 1); a failed push records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and stops waiting. After the second fix still FAILING → report the failing checks and proceed.
453
+ 6. **If FAILING** and fewer than 2 fixes have run → report the failing checks from the line. Spawn `Agent(subagent_type="Code-fix")` whose prompt opens with `OPERATION: ci-fix`, with `COMPLIANCE_DIRECTIVE` (when it is not empty), `CI_FAILURES`, `DECISIONS_CONTEXT` and `PUSH: false`; `CI_FAILURES` holds the failing-check names from the line and nothing else, because the Code-fix agent fetches the full names and reads the logs itself and this command reads none. After a fix, push with the command above and, if a wait remains, wait again (step 1); a failed push records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and stops waiting. After the second fix still FAILING → report the failing checks and proceed.
436
454
  7. **Budget**: at most 3 waits and 2 fixes in all, per worktree. When one is spent, report the current status and proceed.
437
455
  <!-- /PATTERN: ci-status-gate -->
438
456
 
@@ -642,17 +660,7 @@ git -C "{start}" rev-parse --show-toplevel
642
660
 
643
661
  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}`.
644
662
 
645
- **Step 1 — Check the opt-out gate, with `{root}` = `{worktree}`:**
646
-
647
- **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:
648
-
649
- ```bash
650
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
651
- ```
652
-
653
- 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.
654
-
655
- 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.
663
+ **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.
656
664
 
657
665
  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.
658
666
 
@@ -682,7 +690,7 @@ Write the knowledge base to:
682
690
  Then update the index cache by performing a read-modify-write on:
683
691
  {worktree}/.devflow/features/index.md
684
692
 
685
- Index line format: `- **{slug}** — {areas} — {Use-when description}`
693
+ Index line format: `- **{slug}** — {areas} — {Use-when description}` — at most 300 characters, the description at most 220; reword a longer one, never cut it.
686
694
 
687
695
  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.
688
696
 
@@ -726,10 +734,10 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
726
734
  │
727
735
  ├─ Phase 6: Simplify [Simplify agent]
728
736
  │
729
- ├─ Phase 7: Verification Gate [Validate agent, haiku] + push + Code agent validation-fix loop ≤2
737
+ ├─ Phase 7: Verification Gate [Validate agent, haiku] + push + Code-fix agent validation-fix loop ≤2
730
738
  │
731
739
  ├─ Phase 8: CI Status Gate (conditional — skipped if no fixes or verification FAILED)
732
- │ └─ push, then ci-wait.cjs (inline, one wait ≤ 570 s) → Code agent on FAILING (3 waits + 2 fixes per worktree)
740
+ │ └─ push, then ci-wait.cjs (inline, one wait ≤ 570 s) → Code-fix agent on FAILING (3 waits + 2 fixes per worktree)
733
741
  │
734
742
  ├─ Phase 9: Git agent (manage-debt) — FIX_SEPARATE + TECH_DEBT → backfill Tracked={ISSUE_REF} (or TRACEABILITY: DEGRADED on failure)
735
743
  │ SEQUENTIAL across worktrees
@@ -14,7 +14,7 @@ Run Simplify agent and Scrutinize agent sequentially on changed files for post-i
14
14
 
15
15
  ### Phase 0: Context Gathering
16
16
 
17
- **Produces:** FILES_CHANGED, TASK_DESCRIPTION, DECISIONS_CONTEXT, FEATURE_KNOWLEDGE, HEAD_BEFORE
17
+ **Produces:** FILES_CHANGED, TASK_DESCRIPTION, DECISIONS_CONTEXT, FEATURE_KNOWLEDGE, FEATURE_KNOWLEDGE_RULES, HEAD_BEFORE
18
18
 
19
19
  Detect changed files and build context:
20
20
 
@@ -23,8 +23,21 @@ Detect changed files and build context:
23
23
  3. If no changes found, report "No changes to review" and exit
24
24
  4. Build TASK_DESCRIPTION from recent commit messages or branch name
25
25
  5. Record `HEAD_BEFORE` (`git rev-parse HEAD`) now, before Simplify runs — Phase 3 compares against it
26
+
27
+ **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:
28
+
29
+ ```bash
30
+ node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
31
+ ```
32
+
33
+ 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.
34
+
35
+ 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.
36
+
26
37
  ### Load DECISIONS_CONTEXT
27
38
 
39
+ When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
40
+
28
41
  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`):
29
42
 
30
43
  ```bash
@@ -48,9 +61,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
48
61
 
49
62
  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.
50
63
 
51
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
64
+ **Step 2 — Agents apply the decisions:**
52
65
 
53
- 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.
66
+ 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.
54
67
 
55
68
  Pass `DECISIONS_CONTEXT` to Scrutinize agent — the compact index lists active ADR/PF entries; Scrutinize agent uses `devflow:apply-decisions` to Read full entry bodies on demand. Known pitfalls help identify reintroduced issues, prior decisions help validate architectural consistency. (Simplify agent does not consume decisions — it operates at code-shape level and Scrutinize agent runs after to catch any architectural drift.)
56
69
 
@@ -82,26 +95,36 @@ Glob `{worktree}/.devflow/features/*/KNOWLEDGE.md`. For each file found, read on
82
95
 
83
96
  Match the current task area and description against each index line (or frontmatter `description` + `directories` on fallback). Select entries whose documented area overlaps the current task. This is a relevance judgment — prefer specificity over breadth.
84
97
 
85
- **Step 4 — Read selected KBs:**
98
+ **Step 4 — Read each selected KB's Rules:**
86
99
 
87
- For each selected entry, read `{worktree}/.devflow/features/{slug}/KNOWLEDGE.md` in full. When the KB content contradicts the current code you observe, **trust the code** — the code is the freshness mechanism; the KB may lag behind.
100
+ For each selected entry, `{kb}` is `{worktree}/.devflow/features/{slug}/KNOWLEDGE.md`:
88
101
 
89
- **Step 5 — Set FEATURE_KNOWLEDGE:**
102
+ 1. List its `##` headings with line numbers through Bash: `command grep -n '^## ' "{kb}"`. This only locates sections; the text of a KB comes from the Read view alone.
103
+ 2. Read the `## Rules` range (its line to the next heading) with the Read tool, using `offset` and `limit`, and choose the one to three bullets most relevant to the current task. The choice is yours, made per KB.
104
+ 3. If the KB has no `## Rules` section, choose one to three entries from its `## Anti-Patterns` or `## Gotchas` range the same way, and label them by that section's name instead of an ID.
90
105
 
91
- Concatenate the selected KNOWLEDGE.md files under slug headers:
106
+ When a KB contradicts the code you observe, **trust the code** — the code is the freshness mechanism; the KB may lag behind. A missing Rules section, a missing heading list and `(none)` are legitimate states, not errors.
107
+
108
+ **Step 5 — Set FEATURE_KNOWLEDGE and FEATURE_KNOWLEDGE_RULES:**
109
+
110
+ Write one block per selected KB. Paste each bullet verbatim from the Read view, never from a shell view. The path is relative to the checkout root; an agent resolves it under `WORKTREE_PATH` when one is provided.
92
111
 
93
112
  ```
94
113
  --- Feature knowledge: {slug} ---
95
- {full KNOWLEDGE.md content}
114
+ KB: .devflow/features/{slug}/KNOWLEDGE.md
115
+ Rules:
116
+ - **KB-AP-2** {bullet text, verbatim}
117
+ - **KB-INV-1** {bullet text, verbatim}
118
+ Headings: L5 Rules · L40 Overview · L62 Anti-Patterns · L118 Key Files
96
119
  ```
97
120
 
98
- If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set `FEATURE_KNOWLEDGE` to `(none)`.
121
+ A KB with no Rules section labels its entries `Rules ({section name}):` and gives them no ID. `FEATURE_KNOWLEDGE` is these blocks; `FEATURE_KNOWLEDGE_RULES` is the same blocks without the `Headings:` line. Both come from this one selection, and each spawn names the variable its recipient takes. If no KBs exist, no KBs are relevant, or `.devflow/features/` is absent, set both to `(none)`.
99
122
 
100
- **One git call, then direct file reads — no `.cjs` script.** After resolving `{worktree}`, this step is 1 index read (or N frontmatter reads on fallback), bounded by KB count.
123
+ **One git call, then direct reads — no `.cjs` script.** After resolving `{worktree}`, this step is 1 index read (or N frontmatter reads on fallback), plus one heading listing and one Rules read per selected KB, bounded by KB count.
101
124
 
102
- Pass `FEATURE_KNOWLEDGE` to Scrutinize agent.
125
+ Pass `FEATURE_KNOWLEDGE_RULES` to Scrutinize agent.
103
126
 
104
- **Extract:** FILES_CHANGED (list), TASK_DESCRIPTION (string), DECISIONS_CONTEXT (string, optional), FEATURE_KNOWLEDGE (string, optional), HEAD_BEFORE (40-hex SHA)
127
+ **Extract:** FILES_CHANGED (list), TASK_DESCRIPTION (string), DECISIONS_CONTEXT (string, optional), FEATURE_KNOWLEDGE (string, optional), FEATURE_KNOWLEDGE_RULES (string, optional), HEAD_BEFORE (40-hex SHA)
105
128
 
106
129
  ### Phase 1: Simplify agent (Code Refinement)
107
130
 
@@ -129,7 +152,7 @@ Agent(subagent_type="Scrutinize", run_in_background=false):
129
152
  "TASK_DESCRIPTION: {task_description}
130
153
  FILES_CHANGED: {files_changed}
131
154
  DECISIONS_CONTEXT: {decisions_context}
132
- FEATURE_KNOWLEDGE: {feature_knowledge}
155
+ FEATURE_KNOWLEDGE: {feature_knowledge_rules}
133
156
  Evaluate against 9-pillar framework. Fix P0/P1 issues. Return structured report.
134
157
  Follow devflow:apply-decisions to scan DECISIONS_CONTEXT and Read full ADR/PF bodies on demand. Skip if (none).
135
158
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE. Skip if (none)."
@@ -195,17 +218,7 @@ git -C "{start}" rev-parse --show-toplevel
195
218
 
196
219
  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}`.
197
220
 
198
- **Step 1 — Check the opt-out gate, with `{root}` = `{worktree}`:**
199
-
200
- **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:
201
-
202
- ```bash
203
- node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
204
- ```
205
-
206
- 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.
207
-
208
- 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.
221
+ **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.
209
222
 
210
223
  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.
211
224
 
@@ -235,7 +248,7 @@ Write the knowledge base to:
235
248
  Then update the index cache by performing a read-modify-write on:
236
249
  {worktree}/.devflow/features/index.md
237
250
 
238
- Index line format: `- **{slug}** — {areas} — {Use-when description}`
251
+ Index line format: `- **{slug}** — {areas} — {Use-when description}` — at most 300 characters, the description at most 220; reword a longer one, never cut it.
239
252
 
240
253
  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.
241
254
 
@@ -231,4 +231,105 @@ export function rewriteAgentFrontmatter(content, opts) {
231
231
  const changed = newContent !== content;
232
232
  return Ok({ content: newContent, changed });
233
233
  }
234
+ const WHOLE_SERVER_PREFIX = 'mcp__';
235
+ /** A server name as it appears in a whole-server entry: the charset Claude Code keeps, no `__`. */
236
+ const WHOLE_SERVER_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
237
+ /**
238
+ * True when `tool` is a whole-server allowlist entry: `mcp__` and a server name of
239
+ * 1-64 characters from `[A-Za-z0-9_-]` with no `__` (D-WHOLE-SERVER-ENTRY).
240
+ */
241
+ export function isWholeServerTool(tool) {
242
+ if (!tool.startsWith(WHOLE_SERVER_PREFIX))
243
+ return false;
244
+ const name = tool.slice(WHOLE_SERVER_PREFIX.length);
245
+ return WHOLE_SERVER_NAME_RE.test(name) && !name.includes('__');
246
+ }
247
+ /** The single-line inline form `tools: ["A", "B"]`, within a frontmatter body. */
248
+ const INLINE_TOOLS_RE = /^tools:[ \t]*\[([^\r\n]*)\][ \t]*(?=\r?$)/m;
249
+ function toolItems(inner) {
250
+ return inner.split(',').filter(item => item.trim() !== '');
251
+ }
252
+ function unquoteTool(item) {
253
+ return item.trim().replace(/^(["'])(.*)\1$/, '$2');
254
+ }
255
+ /**
256
+ * Replace the whole-server entries of the first frontmatter block's inline `tools:` line
257
+ * with `extraTools`, and touch nothing else.
258
+ *
259
+ * Idempotent: stamping the same list twice yields the same bytes, and stamping a new list
260
+ * replaces the previous one rather than adding to it. Every other entry of the line keeps
261
+ * its original text, and every other byte of the file (other keys, the body, CRLF or LF)
262
+ * is untouched. An empty `extraTools` removes the stamp.
263
+ *
264
+ * Rejects, rather than repairs: an entry that is not a whole-server entry (`invalid-tool`;
265
+ * it would otherwise reach the frontmatter as free text), a file with no inline
266
+ * `tools: [...]` line (`no-tools-line`; a block-list `tools:` is not edited) and a file
267
+ * with no well-formed frontmatter.
268
+ *
269
+ * Pure function — no I/O.
270
+ */
271
+ export function stampAgentTools(content, extraTools) {
272
+ for (const tool of extraTools) {
273
+ if (!isWholeServerTool(tool))
274
+ return Err('invalid-tool');
275
+ }
276
+ const parts = parseFrontmatter(content);
277
+ if (!parts.ok)
278
+ return Err(parts.error);
279
+ const { fmBody, openDelim, closeDelim, afterClose, eol } = parts.value;
280
+ const match = INLINE_TOOLS_RE.exec(fmBody);
281
+ if (!match)
282
+ return Err('no-tools-line');
283
+ const rawItems = toolItems(match[1]);
284
+ const kept = rawItems.filter(item => !isWholeServerTool(unquoteTool(item)));
285
+ const stamped = [...new Set(extraTools)].map(tool => `"${tool}"`);
286
+ // Nothing to remove and nothing to add: the line, whatever its spacing, is already right.
287
+ if (kept.length === rawItems.length && stamped.length === 0)
288
+ return Ok({ content, changed: false });
289
+ // The kept entries keep their own text; only the edges lose the padding that no longer
290
+ // borders a neighbour, so the appended entries land after `, ` and the line stays one shape.
291
+ const keptText = kept
292
+ .map((item, i) => {
293
+ const lead = i === 0 ? item.trimStart() : item;
294
+ return i === kept.length - 1 ? lead.trimEnd() : lead;
295
+ })
296
+ .join(',');
297
+ const inner = [keptText, stamped.join(', ')].filter(text => text !== '').join(', ');
298
+ const line = `tools: [${inner}]`;
299
+ const newBody = fmBody.slice(0, match.index) + line + fmBody.slice(match.index + match[0].length);
300
+ const newContent = openDelim + newBody + eol + closeDelim + afterClose;
301
+ return Ok({ content: newContent, changed: newContent !== content });
302
+ }
303
+ /**
304
+ * The whole-server entries of the first frontmatter block's inline `tools:` line, in
305
+ * order. Ok([]) when the line carries none; an error when there is no line to read.
306
+ * Pure function — no I/O.
307
+ */
308
+ export function readStampedTools(content) {
309
+ const parts = parseFrontmatter(content);
310
+ if (!parts.ok)
311
+ return Err(parts.error);
312
+ const match = INLINE_TOOLS_RE.exec(parts.value.fmBody);
313
+ if (!match)
314
+ return Err('no-tools-line');
315
+ return Ok(toolItems(match[1]).map(unquoteTool).filter(isWholeServerTool));
316
+ }
317
+ /**
318
+ * The text a converge installs for an agent: the variant `source` wearing the whole-server
319
+ * entries the installed copy carries. The stamp is applied at install and by
320
+ * `devflow tracker --servers`, never by a converge, so a converge only CARRIES it. Without
321
+ * the carry, switching an agent between prompt variants would drop its stamp and compare
322
+ * unequal to the installed copy on every run.
323
+ *
324
+ * `source` is returned unchanged when there is nothing safe to carry: either side has no
325
+ * well-formed inline `tools:` line, or the installed copy carries no entries.
326
+ * Pure function — no I/O.
327
+ */
328
+ export function carryStampedTools(source, installed) {
329
+ const carried = readStampedTools(installed);
330
+ if (!carried.ok || carried.value.length === 0)
331
+ return source;
332
+ const stamped = stampAgentTools(source, carried.value);
333
+ return stamped.ok ? stamped.value.content : source;
334
+ }
234
335
  //# sourceMappingURL=agent-frontmatter.js.map
@@ -599,6 +599,49 @@ export async function loadShippedAgentDefaults(dirs = agentSourceDirs(), opts) {
599
599
  }
600
600
  return defaults;
601
601
  }
602
+ // ---------------------------------------------------------------------------
603
+ // carryAgentOverrides — the frontmatter a converge keeps
604
+ // ---------------------------------------------------------------------------
605
+ /**
606
+ * D-AGENT-OVERRIDE-CARRY: the text a prompt converge installs for an agent is the
607
+ * variant `source` wearing the installed copy's `model:` and `effort:`.
608
+ *
609
+ * Those two lines are the only frontmatter keys reapplyAgentMapping manages, and
610
+ * `devflow agents` and the proxy put their values there. A converge that installed
611
+ * the bare variant would write every overridden agent back to its shipped model
612
+ * and effort and leave reapplyAgentMapping to restore it: a window with default
613
+ * models, and a no-op converge that rewrites everything. Carrying the two lines
614
+ * keeps that window shut, so a copy already holding the right variant and its
615
+ * overrides compares byte-equal and is not touched.
616
+ *
617
+ * The carry is an optimisation, never the authority: reapplyAgentMapping reads
618
+ * agent-models.json and decides what the installed frontmatter must say. It
619
+ * agrees with the carry whenever the installed copy was last written by it, and
620
+ * where they disagree the later reapply wins. Both go through
621
+ * rewriteAgentFrontmatter, so the model-name charset guard, the first-block scope
622
+ * and the EOL handling are the ones the reapply uses.
623
+ *
624
+ * `source` is returned unchanged when there is nothing safe to carry: either side
625
+ * has no well-formed frontmatter, the installed copy has no model, its model
626
+ * fails the model-name charset or its effort is not an EFFORT_LEVELS member. An
627
+ * installed copy with no `effort:` line carries no effort (a mapping effort of
628
+ * `inherit` yields exactly that).
629
+ *
630
+ * Pure function — no I/O.
631
+ */
632
+ export function carryAgentOverrides(source, installed) {
633
+ const model = readFrontmatterModel(installed);
634
+ const effort = readFrontmatterEffort(installed);
635
+ if (!model.ok || !effort.ok || model.value === '')
636
+ return source;
637
+ if (effort.value !== '' && !isEffortLevel(effort.value))
638
+ return source;
639
+ const carried = rewriteAgentFrontmatter(source, {
640
+ model: model.value,
641
+ effort: effort.value === '' ? null : effort.value,
642
+ });
643
+ return carried.ok ? carried.value.content : source;
644
+ }
602
645
  /**
603
646
  * Idempotent convergence function: walk every installed agent file and
604
647
  * rewrite frontmatter model/effort to match the effective mapping.
@@ -1,16 +1,21 @@
1
1
  import { join } from 'path';
2
2
  import { getPackageRoot } from './paths.js';
3
3
  import { SKILL_REFS_OUTPUT_DIR } from './mds-variants.js';
4
+ import { LEARNING_OFF_OUTPUT_DIR } from './learning-variants.js';
4
5
  /**
5
6
  * Flat skills source directory: src/assets/skills/{name}/
6
7
  * All plugins' skills live here directly (no per-plugin subdirectory).
8
+ *
9
+ * @param root - Package root to resolve against. Injectable so a caller working
10
+ * on a temp tree (the learning converge) reads the skill source from the same
11
+ * root as the rest of its sources.
7
12
  */
8
- export function skillsDir() {
9
- return join(getPackageRoot(), 'src', 'assets', 'skills');
13
+ export function skillsDir(root = getPackageRoot()) {
14
+ return join(root, 'src', 'assets', 'skills');
10
15
  }
11
16
  /**
12
- * Flat agents source directory: src/assets/agents/{name}.md
13
- * All plugins' agents live here directly.
17
+ * Flat agents source directory: src/assets/agents/{name}.md, or {name}.mds for a
18
+ * generator host. All plugins' agents live here directly.
14
19
  *
15
20
  * @param root - Package root to resolve against. Injectable so a caller working
16
21
  * on a temp tree (the test harness) reads the layout from here rather than
@@ -35,17 +40,49 @@ export function scriptsDir() {
35
40
  }
36
41
  /**
37
42
  * Compiled commands directory: dist/commands/
38
- * Single lookup directory for installed commands — .mds compile output
39
- * plus verbatim hand-authored .md copies.
43
+ * Single lookup directory for installed commands: one compiled file per `.mds`
44
+ * command host in src/assets/commands/. A host that carries a learning arm also
45
+ * has a learning-off variant (see learningOffDir).
40
46
  */
41
47
  export function commandsDir() {
42
48
  return join(getPackageRoot(), 'dist', 'commands');
43
49
  }
50
+ /**
51
+ * Learning-off variant directory: dist/learning-off/{commands,agents}/
52
+ *
53
+ * Holds the learning-off variant of each prompt that carries a learning arm
54
+ * (D-LEARNING-VARIANTS), and only those. Absent until the build has run, so every
55
+ * reader tolerates its absence.
56
+ *
57
+ * The spelling comes from LEARNING_OFF_OUTPUT_DIR in learning-variants.ts, the
58
+ * build's own destination, rather than being retyped here.
59
+ *
60
+ * @param kind - Which prompt kind's variants.
61
+ * @param root - Package root to resolve against (see agentsDir).
62
+ */
63
+ export function learningOffDir(kind, root = getPackageRoot()) {
64
+ return join(root, ...LEARNING_OFF_OUTPUT_DIR.split('/'), kind);
65
+ }
66
+ /**
67
+ * Command source directories, MOST-PREFERRED FIRST.
68
+ *
69
+ * D-LEARNING-VARIANT-INSTALL: with learning off the learning-off variant comes
70
+ * first, so a host with an arm installs its off variant and a host with none falls
71
+ * through to the one compiled file; with learning on the order is the normal
72
+ * single-directory lookup.
73
+ *
74
+ * @param learning - The machine's settled learning switch.
75
+ * @param root - Package root to resolve against (see agentsDir).
76
+ */
77
+ export function commandSourceDirs(learning, root = getPackageRoot()) {
78
+ const normal = join(root, 'dist', 'commands');
79
+ return learning ? [normal] : [learningOffDir('commands', root), normal];
80
+ }
44
81
  /**
45
82
  * Compiled agents directory: dist/agents/{name}.md
46
83
  *
47
- * Output of the .mds generator hosts. The directory is absent until at least
48
- * one generator host exists, so every reader must tolerate its absence.
84
+ * Output of the .mds generator hosts. The directory is absent until the build has
85
+ * run, so every reader must tolerate its absence.
49
86
  *
50
87
  * @param root - Package root to resolve against (see agentsDir).
51
88
  */
@@ -87,9 +124,17 @@ export function compiledSkillRefsDir(root = getPackageRoot()) {
87
124
  * The non-empty tuple makes an empty list a compile error at every call site:
88
125
  * an empty list would survive a `??` default and resolve to nothing.
89
126
  *
127
+ * D-LEARNING-VARIANT-INSTALL: `learning: false` puts dist/learning-off/agents first,
128
+ * for the installer and the converge only. The default (learning on) is the order
129
+ * every other reader wants: the shipped defaults (model, effort) are the same in
130
+ * both variants, so loadShippedAgentDefaults and the test harness keep reading the
131
+ * learning-on tree.
132
+ *
90
133
  * @param root - Package root to resolve against (see agentsDir).
134
+ * @param learning - The machine's settled learning switch; on by default.
91
135
  */
92
- export function agentSourceDirs(root = getPackageRoot()) {
93
- return [compiledAgentsDir(root), agentsDir(root)];
136
+ export function agentSourceDirs(root = getPackageRoot(), learning = true) {
137
+ const normal = [compiledAgentsDir(root), agentsDir(root)];
138
+ return learning ? normal : [learningOffDir('agents', root), ...normal];
94
139
  }
95
140
  //# sourceMappingURL=assets.js.map