devflow-kit 3.4.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 (186) hide show
  1. package/CHANGELOG.md +29 -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 +39 -182
  6. package/dist/agents/design.md +0 -1
  7. package/dist/agents/git.md +31 -693
  8. package/dist/agents/knowledge.md +8 -2
  9. package/dist/agents/research.md +6 -0
  10. package/dist/agents/review.md +0 -4
  11. package/dist/agents/scrutinize.md +3 -0
  12. package/dist/agents/skim.md +4 -0
  13. package/dist/agents/triage.md +8 -1
  14. package/dist/cli/commands/behavior-prompts.js +77 -0
  15. package/dist/cli/commands/behavior.js +123 -0
  16. package/dist/cli/commands/flags.js +8 -2
  17. package/dist/cli/commands/init-seed.js +7 -3
  18. package/dist/cli/commands/init.js +72 -5
  19. package/dist/cli/commands/rules.js +16 -0
  20. package/dist/cli/commands/tracker.js +112 -15
  21. package/dist/cli.js +2 -0
  22. package/dist/commands/bug-analysis.md +5 -5
  23. package/dist/commands/code-review.md +14 -9
  24. package/dist/commands/debug.md +25 -4
  25. package/dist/commands/dynamic-build.md +28 -38
  26. package/dist/commands/dynamic-plan.md +11 -9
  27. package/dist/commands/dynamic-profile.md +3 -3
  28. package/dist/commands/dynamic-tickets.md +12 -10
  29. package/dist/commands/explore.md +3 -3
  30. package/dist/commands/implement.md +91 -125
  31. package/dist/commands/plan.md +15 -8
  32. package/dist/commands/release.md +3 -2
  33. package/dist/commands/research.md +16 -9
  34. package/dist/commands/resolve.md +20 -15
  35. package/dist/commands/self-review.md +2 -2
  36. package/dist/core/agent-frontmatter.js +101 -0
  37. package/dist/core/compliance-compose.js +6 -4
  38. package/dist/core/feature-switch.js +23 -2
  39. package/dist/core/flags.js +111 -0
  40. package/dist/core/manifest.js +11 -3
  41. package/dist/core/mds-variants.js +68 -26
  42. package/dist/core/plugins.js +42 -65
  43. package/dist/core/project-paths.js +0 -4
  44. package/dist/core/proxy-log.js +1 -1
  45. package/dist/core/proxy-state.js +1 -1
  46. package/dist/core/tracker.js +120 -2
  47. package/dist/learning-off/agents/code-edit.md +129 -0
  48. package/dist/learning-off/agents/code-fix.md +161 -0
  49. package/dist/learning-off/agents/code.md +37 -180
  50. package/dist/learning-off/agents/design.md +0 -1
  51. package/dist/learning-off/agents/knowledge.md +8 -2
  52. package/dist/learning-off/agents/research.md +6 -0
  53. package/dist/learning-off/agents/review.md +0 -4
  54. package/dist/learning-off/agents/scrutinize.md +3 -0
  55. package/dist/learning-off/agents/skim.md +4 -0
  56. package/dist/learning-off/agents/triage.md +8 -1
  57. package/dist/learning-off/commands/bug-analysis.md +3 -3
  58. package/dist/learning-off/commands/code-review.md +12 -7
  59. package/dist/learning-off/commands/debug.md +22 -2
  60. package/dist/learning-off/commands/dynamic-build.md +27 -37
  61. package/dist/learning-off/commands/dynamic-plan.md +10 -8
  62. package/dist/learning-off/commands/dynamic-profile.md +2 -2
  63. package/dist/learning-off/commands/dynamic-tickets.md +11 -9
  64. package/dist/learning-off/commands/explore.md +1 -1
  65. package/dist/learning-off/commands/implement.md +89 -123
  66. package/dist/learning-off/commands/plan.md +13 -6
  67. package/dist/learning-off/commands/release.md +3 -2
  68. package/dist/learning-off/commands/research.md +14 -7
  69. package/dist/learning-off/commands/resolve.md +18 -13
  70. package/dist/skills/git/references/learn-conventions.md +27 -3
  71. package/dist/skills/git/references/pr/check-ci-status.md +19 -0
  72. package/dist/skills/git/references/pr/check-merge-readiness.md +19 -0
  73. package/dist/skills/git/references/pr/fetch-review-threads.md +30 -0
  74. package/dist/skills/git/references/pr/post-resolution-summary.md +18 -0
  75. package/dist/skills/git/references/pr/post-review-summary.md +20 -0
  76. package/dist/skills/git/references/pr/resolve-review-threads.md +28 -4
  77. package/dist/skills/git/references/pr/update-pr-evidence.md +13 -0
  78. package/dist/skills/git/references/pr/validate-branch.md +26 -0
  79. package/dist/skills/git/references/tracker/github/associate-release.md +24 -0
  80. package/dist/skills/git/references/tracker/github/backlink-shipped-issues.md +31 -1
  81. package/dist/skills/git/references/tracker/github/create-release.md +30 -1
  82. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +89 -2
  83. package/dist/skills/git/references/tracker/github/ensure-traceable-issue.md +16 -1
  84. package/dist/skills/git/references/tracker/github/fetch-issue.md +36 -1
  85. package/dist/skills/git/references/tracker/github/fetch-issues-batch.md +47 -1
  86. package/dist/skills/git/references/tracker/github/gather-release-evidence.md +25 -0
  87. package/dist/skills/git/references/tracker/github/manage-debt.md +21 -1
  88. package/dist/skills/git/references/tracker/github/post-wave-report.md +15 -1
  89. package/dist/skills/git/references/tracker/github/setup-task.md +41 -1
  90. package/dist/skills/git/references/tracker/jira/associate-release.md +24 -0
  91. package/dist/skills/git/references/tracker/jira/backlink-shipped-issues.md +31 -1
  92. package/dist/skills/git/references/tracker/jira/create-release.md +30 -1
  93. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +89 -2
  94. package/dist/skills/git/references/tracker/jira/ensure-traceable-issue.md +16 -1
  95. package/dist/skills/git/references/tracker/jira/fetch-issue.md +36 -1
  96. package/dist/skills/git/references/tracker/jira/fetch-issues-batch.md +47 -1
  97. package/dist/skills/git/references/tracker/jira/gather-release-evidence.md +25 -0
  98. package/dist/skills/git/references/tracker/jira/manage-debt.md +21 -1
  99. package/dist/skills/git/references/tracker/jira/post-wave-report.md +15 -1
  100. package/dist/skills/git/references/tracker/jira/setup-task.md +41 -1
  101. package/dist/skills/git/references/tracker/linear/associate-release.md +24 -0
  102. package/dist/skills/git/references/tracker/linear/backlink-shipped-issues.md +31 -1
  103. package/dist/skills/git/references/tracker/linear/create-release.md +30 -1
  104. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +89 -2
  105. package/dist/skills/git/references/tracker/linear/ensure-traceable-issue.md +16 -1
  106. package/dist/skills/git/references/tracker/linear/fetch-issue.md +36 -1
  107. package/dist/skills/git/references/tracker/linear/fetch-issues-batch.md +47 -1
  108. package/dist/skills/git/references/tracker/linear/gather-release-evidence.md +25 -0
  109. package/dist/skills/git/references/tracker/linear/manage-debt.md +21 -1
  110. package/dist/skills/git/references/tracker/linear/post-wave-report.md +15 -1
  111. package/dist/skills/git/references/tracker/linear/setup-task.md +41 -1
  112. package/dist/targets/claude-code/agent-type-owner.js +164 -0
  113. package/dist/targets/claude-code/behavior-install.js +111 -0
  114. package/dist/targets/claude-code/installer.js +69 -2
  115. package/dist/targets/claude-code/learning-install.js +9 -1
  116. package/dist/targets/claude-code/tracker-install.js +33 -16
  117. package/dist/targets/claude-code/tracker-servers.js +107 -0
  118. package/package.json +4 -3
  119. package/src/assets/agents/_partials/_code.mds +109 -0
  120. package/src/assets/agents/code-edit.mds +81 -0
  121. package/src/assets/agents/code-fix.mds +113 -0
  122. package/src/assets/agents/code.mds +18 -222
  123. package/src/assets/agents/design.mds +0 -1
  124. package/src/assets/agents/evaluate.md +7 -1
  125. package/src/assets/agents/explore.md +58 -0
  126. package/src/assets/agents/git.mds +31 -693
  127. package/src/assets/agents/knowledge.mds +8 -2
  128. package/src/assets/agents/learning.md +19 -69
  129. package/src/assets/agents/research.mds +6 -0
  130. package/src/assets/agents/review.mds +0 -4
  131. package/src/assets/agents/scrutinize.mds +3 -0
  132. package/src/assets/agents/simplify.md +3 -0
  133. package/src/assets/agents/skim.mds +4 -0
  134. package/src/assets/agents/synthesize.md +26 -352
  135. package/src/assets/agents/test.md +8 -0
  136. package/src/assets/agents/tracker.md +20 -7
  137. package/src/assets/agents/triage.mds +8 -1
  138. package/src/assets/agents/validate.md +5 -0
  139. package/src/assets/commands/_partials/_compliance.mds +36 -0
  140. package/src/assets/commands/_partials/_decisions.mds +2 -2
  141. package/src/assets/commands/_partials/_engine.mds +8 -8
  142. package/src/assets/commands/_partials/_preamble.mds +3 -3
  143. package/src/assets/commands/_partials/_roster.mds +4 -3
  144. package/src/assets/commands/_partials/_wave.mds +2 -2
  145. package/src/assets/commands/bug-analysis.mds +3 -3
  146. package/src/assets/commands/code-review.mds +11 -8
  147. package/src/assets/commands/debug.mds +24 -2
  148. package/src/assets/commands/dynamic-build.mds +14 -25
  149. package/src/assets/commands/dynamic-plan.mds +4 -3
  150. package/src/assets/commands/dynamic-tickets.mds +5 -4
  151. package/src/assets/commands/explore.mds +1 -1
  152. package/src/assets/commands/implement.mds +89 -125
  153. package/src/assets/commands/plan.mds +12 -7
  154. package/src/assets/commands/release.mds +3 -2
  155. package/src/assets/commands/research.mds +14 -7
  156. package/src/assets/commands/resolve.mds +17 -14
  157. package/src/assets/mds/.mdsroot +4 -0
  158. package/src/assets/mds/git/_ops.mds +383 -0
  159. package/src/assets/mds/git/_ops_pr.mds +201 -0
  160. package/src/assets/mds/git/_ops_steps.mds +147 -0
  161. package/src/assets/mds/git/_pr.mds +39 -44
  162. package/src/assets/mds/git/_references.mds +6 -3
  163. package/src/assets/mds/tracker/_common.mds +5 -5
  164. package/src/assets/mds/tracker/_contract.mds +4 -3
  165. package/src/assets/mds/tracker/_github.mds +60 -14
  166. package/src/assets/mds/tracker/_jira.mds +60 -14
  167. package/src/assets/mds/tracker/_linear.mds +60 -14
  168. package/src/assets/mds/tracker/_steps.mds +10 -9
  169. package/src/assets/rules/behavior.md +6 -0
  170. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +2 -1
  171. package/src/assets/scripts/hooks/lib/project-paths.cjs +0 -6
  172. package/src/assets/skills/agent-references/SKILL.md +33 -0
  173. package/src/assets/skills/agent-references/references/learning/maintain.md +71 -0
  174. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +71 -0
  175. package/src/assets/skills/agent-references/references/synthesize/design.md +36 -0
  176. package/src/assets/skills/agent-references/references/synthesize/exploration.md +38 -0
  177. package/src/assets/skills/agent-references/references/synthesize/planning.md +60 -0
  178. package/src/assets/skills/agent-references/references/synthesize/research.md +61 -0
  179. package/src/assets/skills/agent-references/references/synthesize/review.md +76 -0
  180. package/src/assets/skills/compliance/SKILL.md +1 -1
  181. package/src/assets/skills/docs-framework/SKILL.md +80 -83
  182. package/src/assets/skills/docs-framework/references/patterns.md +14 -25
  183. package/src/assets/skills/docs-framework/references/violations.md +7 -7
  184. package/src/assets/skills/gap-analysis/SKILL.md +0 -16
  185. package/src/assets/skills/gap-analysis/references/compliance.md +19 -0
  186. package/dist/skills/git/references/pr/ensure-pr-ready.md +0 -24
@@ -16,18 +16,22 @@
16
16
  * --set converges the sentinel in BOTH directions, so flipping
17
17
  * back to github removes what flipping away wrote.
18
18
  * A failed re-arm or sentinel step warns, it never aborts.
19
+ * --servers sets the machine's tracker tool servers (D-TRACKER-SERVER-ALLOWLIST) and
20
+ * re-stamps the installed Git and Tracker agents at once; it is orthogonal to --set and
21
+ * neither flag touches the other's manifest field.
19
22
  */
20
23
  import { Command } from 'commander';
21
24
  import { promises as fs } from 'fs';
22
25
  import * as path from 'path';
23
26
  import * as p from '@clack/prompts';
24
27
  import color from 'picocolors';
25
- import { DEFAULT_TRACKER_PROVIDER, TRACKER_ATTEMPTS_MAX, TRACKER_CONVENTIONS_READ_BYTES, TRACKER_PROVIDERS, applyTrackerSentinel, describeTrackerValue, parseTrackerFrontmatter, parseTrackerId, readBoundedHead, rearmTrackerInference, trackerConventionsPath, } from '../../core/tracker.js';
28
+ import { DEFAULT_TRACKER_PROVIDER, TRACKER_ATTEMPTS_MAX, TRACKER_CONVENTIONS_READ_BYTES, TRACKER_PROVIDERS, applyTrackerSentinel, describeTrackerValue, effectiveTrackerServers, parseTrackerFrontmatter, parseTrackerId, parseTrackerServers, readBoundedHead, rearmTrackerInference, trackerConventionsPath, } from '../../core/tracker.js';
26
29
  import { readManifest, syncManifestFeature } from '../../core/manifest.js';
27
30
  import { loadSettingsModule, personalConfigTrackedWarning, repoTrackerSelection } from '../../core/evidence-policy.js';
28
31
  import { getClaudeDirectory, getDevFlowDirectory } from '../../targets/claude-code/claude-paths.js';
29
32
  import { SKILL_REFS_SKILL_NAME, installedReferenceManifest } from '../../core/mds-variants.js';
30
33
  import { prefixSkillName } from '../../core/plugins.js';
34
+ import { TRACKER_STAMPED_AGENTS, restampTrackerAgents, } from '../../targets/claude-code/tracker-servers.js';
31
35
  /**
32
36
  * Read the provenance header of `~/.devflow/tracker/{provider}.md`.
33
37
  *
@@ -112,6 +116,10 @@ export async function readTrackerStatusConventions(devflowDir, provider) {
112
116
  provenance: await readTrackerProvenance(devflowDir, provider),
113
117
  };
114
118
  }
119
+ /** Render the tracker servers for the `--status` note. Pure; every name was validated at its boundary. */
120
+ export function formatTrackerServers(list, isDefault) {
121
+ return `${list.join(', ')}${isDefault ? ` ${color.dim('(default)')}` : ''}`;
122
+ }
115
123
  /**
116
124
  * The `--status` note's lines. Pure.
117
125
  *
@@ -131,7 +139,7 @@ export function formatTrackerStatus(input) {
131
139
  }
132
140
  lines.push(...(input.conventions.kind === 'none'
133
141
  ? ['Conventions: none (GitHub needs no learned conventions)', 'File: none']
134
- : [`Conventions: ${formatTrackerProvenance(input.conventions.provenance)}`, `File: ${input.conventions.file}`]), `Mechanics: ${formatTrackerMechanics(input.mechanics)}`, `Inference: ${input.inference}`);
142
+ : [`Conventions: ${formatTrackerProvenance(input.conventions.provenance)}`, `File: ${input.conventions.file}`]), `Mechanics: ${formatTrackerMechanics(input.mechanics)}`, `Servers: ${formatTrackerServers(input.servers.list, input.servers.isDefault)}`, `Inference: ${input.inference}`);
135
143
  return lines.join('\n');
136
144
  }
137
145
  /** The real adapter — the ONE binding of each operation into the `--set` path. */
@@ -164,7 +172,8 @@ export function buildTrackerSetIO() {
164
172
  export async function runTrackerSet(opts) {
165
173
  const { devflowDir, current, requested, io } = opts;
166
174
  const messages = [];
167
- await io.syncManifest(devflowDir, { provider: requested });
175
+ // The provider changes; the tracker servers are a different setting and ride along.
176
+ await io.syncManifest(devflowDir, { ...current, provider: requested });
168
177
  // [DR-22] a selection change re-arms the attempt counters; [DR-10] the
169
178
  // sentinel converges in both directions.
170
179
  const rearm = await io.rearmInference(devflowDir);
@@ -180,18 +189,72 @@ export async function runTrackerSet(opts) {
180
189
  });
181
190
  return { provider: requested, messages };
182
191
  }
192
+ /** The real adapter — the ONE binding of each operation into the `--servers` path. */
193
+ export function buildTrackerServersIO() {
194
+ return {
195
+ syncManifest: (devflowDir, state) => syncManifestFeature(devflowDir, 'tracker', state),
196
+ restamp: (claudeDir, servers, warn) => restampTrackerAgents({ claudeDir, servers, warn }),
197
+ };
198
+ }
199
+ /**
200
+ * Set the machine's tracker tool servers.
201
+ *
202
+ * Follows `--set`'s convergence order (D-TRACKER-CONVERGE-SET): the manifest first, then
203
+ * the installed agents, which are re-stamped ONLY when the manifest write landed, so an
204
+ * installed agent never names a list the manifest does not record. Unlike `--set`, a failed
205
+ * manifest write is the whole command failing (there is nothing else for it to do), so it is
206
+ * returned as `ok: false` for the caller to exit on; a failed re-stamp warns, it never aborts.
207
+ *
208
+ * `default` removes the key, so an absent `servers` stays the one spelling of the default.
209
+ * Never throws.
210
+ */
211
+ export async function runTrackerServers(opts) {
212
+ const { devflowDir, claudeDir, current, requested, io } = opts;
213
+ const messages = [];
214
+ const next = requested.kind === 'default'
215
+ ? { provider: current.provider }
216
+ : { provider: current.provider, servers: requested.servers };
217
+ try {
218
+ await io.syncManifest(devflowDir, next);
219
+ }
220
+ catch (err) {
221
+ messages.push({
222
+ level: 'error',
223
+ text: `Could not write the manifest — the tracker servers were not changed (${err instanceof Error ? err.message : String(err)})`,
224
+ });
225
+ return { ok: false, state: current, messages };
226
+ }
227
+ const servers = effectiveTrackerServers(next);
228
+ const unchanged = servers.join('\n') === effectiveTrackerServers(current).join('\n');
229
+ const label = `${servers.join(', ')}${requested.kind === 'default' ? ' (default)' : ''}`;
230
+ messages.push({ level: unchanged ? 'info' : 'success', text: `Tracker servers: ${label}${unchanged ? ' (unchanged)' : ''}` });
231
+ const restamp = await io.restamp(claudeDir, servers, (message) => messages.push({ level: 'warn', text: message }));
232
+ const done = restamp.rewritten.length + restamp.unchanged.length;
233
+ if (restamp.absent.length === TRACKER_STAMPED_AGENTS.length && restamp.failed.length === 0) {
234
+ messages.push({ level: 'info', text: 'No installed Git or Tracker agent to update — run devflow init' });
235
+ }
236
+ else if (restamp.rewritten.length > 0) {
237
+ messages.push({ level: 'info', text: `Updated the installed ${restamp.rewritten.join(' and ')} agent${restamp.rewritten.length > 1 ? 's' : ''}` });
238
+ }
239
+ else if (done > 0 && restamp.failed.length === 0) {
240
+ messages.push({ level: 'info', text: 'The installed agents already carry this list' });
241
+ }
242
+ return { ok: true, state: next, messages };
243
+ }
183
244
  export const trackerCommand = new Command('tracker')
184
- .description('Show or set the machine\'s issue tracker provider')
245
+ .description('Show or set the machine\'s issue tracker provider and tracker servers')
185
246
  .option('--status', 'Show the provider in effect and its learned conventions file, and re-arm background inference')
186
247
  .option('--set <id>', 'Set the machine\'s default issue tracker provider: github, jira, or linear')
248
+ .option('--servers <names>', 'Set the tracker servers the Git and Tracker agents may call: comma-separated names, or "default"')
187
249
  .action(async (options) => {
188
250
  const devflowDir = getDevFlowDirectory();
189
- const hasFlag = options.status || options.set !== undefined;
251
+ const hasFlag = options.status || options.set !== undefined || options.servers !== undefined;
190
252
  if (!hasFlag) {
191
253
  p.intro(color.bgCyan(color.white(' Tracker ')));
192
254
  const validIds = TRACKER_PROVIDERS.map(t => `${t.id} — ${t.hint}`).join('\n ');
193
255
  p.note(`${color.cyan('devflow tracker --status')} Show the provider and learned conventions\n` +
194
- `${color.cyan('devflow tracker --set <id>')} Set the machine's default provider\n\n` +
256
+ `${color.cyan('devflow tracker --set <id>')} Set the machine's default provider\n` +
257
+ `${color.cyan('devflow tracker --servers <names>')} Set the tracker servers the agents may call\n\n` +
195
258
  `Valid provider IDs:\n ${validIds}`, 'Usage');
196
259
  p.outro(color.dim('github is the default — devflow tracker --set github returns to it'));
197
260
  return;
@@ -206,7 +269,17 @@ export const trackerCommand = new Command('tracker')
206
269
  }
207
270
  setProvider = parsed.value;
208
271
  }
209
- const manifest = await readManifest(devflowDir);
272
+ // Validate --servers the same way: a bad name exits before any I/O, never repaired.
273
+ let setServers;
274
+ if (options.servers !== undefined) {
275
+ const parsed = parseTrackerServers(options.servers);
276
+ if (!parsed.ok) {
277
+ p.log.error(parsed.error);
278
+ process.exit(1);
279
+ }
280
+ setServers = parsed.value;
281
+ }
282
+ const manifest = await readManifest(devflowDir, { onWarning: (message) => p.log.warn(message) });
210
283
  if (!manifest) {
211
284
  p.log.error('No manifest found. Run devflow init first.');
212
285
  process.exit(1);
@@ -238,6 +311,7 @@ export const trackerCommand = new Command('tracker')
238
311
  selection,
239
312
  conventions,
240
313
  mechanics,
314
+ servers: { list: effectiveTrackerServers(current), isDefault: current.servers === undefined },
241
315
  inference,
242
316
  }), 'Tracker Status');
243
317
  if (!statusRearm.ok)
@@ -248,13 +322,34 @@ export const trackerCommand = new Command('tracker')
248
322
  return;
249
323
  }
250
324
  // ── Set ────────────────────────────────────────────────────────────────────
251
- const outcome = await runTrackerSet({
252
- devflowDir,
253
- current,
254
- requested: setProvider ?? current.provider,
255
- io: buildTrackerSetIO(),
256
- });
257
- for (const msg of outcome.messages) {
325
+ // `--set` and `--servers` are independent settings and may be given together: the provider
326
+ // first, then the servers against the state it left.
327
+ const messages = [];
328
+ let state = current;
329
+ if (setProvider !== undefined) {
330
+ const outcome = await runTrackerSet({
331
+ devflowDir,
332
+ current,
333
+ requested: setProvider,
334
+ io: buildTrackerSetIO(),
335
+ });
336
+ messages.push(...outcome.messages);
337
+ state = { ...current, provider: outcome.provider };
338
+ }
339
+ let failed = false;
340
+ if (setServers !== undefined) {
341
+ const outcome = await runTrackerServers({
342
+ devflowDir,
343
+ claudeDir: getClaudeDirectory(),
344
+ current: state,
345
+ requested: setServers,
346
+ io: buildTrackerServersIO(),
347
+ });
348
+ messages.push(...outcome.messages);
349
+ state = outcome.state;
350
+ failed = !outcome.ok;
351
+ }
352
+ for (const msg of messages) {
258
353
  switch (msg.level) {
259
354
  case 'success':
260
355
  p.log.success(msg.text);
@@ -270,7 +365,9 @@ export const trackerCommand = new Command('tracker')
270
365
  break;
271
366
  }
272
367
  }
273
- if (outcome.provider !== DEFAULT_TRACKER_PROVIDER) {
368
+ if (failed)
369
+ process.exit(1);
370
+ if (setProvider !== undefined && state.provider !== DEFAULT_TRACKER_PROVIDER) {
274
371
  p.log.info(color.dim('Your issue conventions are learned in the background at the next session start'));
275
372
  }
276
373
  });
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@ import { safeDeleteCommand } from './cli/commands/safe-delete.js';
19
19
  import { proxyCommand } from './cli/commands/proxy.js';
20
20
  import { agentsCommand } from './cli/commands/agents.js';
21
21
  import { complianceCommand } from './cli/commands/compliance.js';
22
+ import { behaviorCommand } from './cli/commands/behavior.js';
22
23
  import { trackerCommand } from './cli/commands/tracker.js';
23
24
  const __filename = fileURLToPath(import.meta.url);
24
25
  const __dirname = dirname(__filename);
@@ -48,6 +49,7 @@ program.addCommand(safeDeleteCommand);
48
49
  program.addCommand(proxyCommand);
49
50
  program.addCommand(agentsCommand);
50
51
  program.addCommand(complianceCommand);
52
+ program.addCommand(behaviorCommand);
51
53
  program.addCommand(trackerCommand);
52
54
  // Handle no command (bare `devflow`) or unknown subcommand.
53
55
  // When Commander sees an unrecognised first argument it does not route to any
@@ -38,7 +38,7 @@ git -C "{start}" rev-parse --show-toplevel
38
38
  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.
39
39
 
40
40
  Render the test-plan block from `/implement`'s evidence file, and from nothing else:
41
- 1. `branch_slug` is `git branch --show-current` with every `/` replaced by `-`.
41
+ 1. `branch_slug` is `git -C "{worktree}" branch --show-current` with every `/` replaced by `-`.
42
42
  2. Only when `branch_slug` matches `^[A-Za-z0-9._-]{1,200}$` and the file exists, run (the path double-quoted):
43
43
 
44
44
  ```bash
@@ -218,9 +218,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
218
218
 
219
219
  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.
220
220
 
221
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
221
+ **Step 2 — Agents apply the decisions:**
222
222
 
223
- 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.
223
+ 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.
224
224
 
225
225
  #### Feature Knowledge
226
226
 
@@ -281,8 +281,8 @@ A KB with no Rules section labels its entries `Rules ({section name}):` and give
281
281
 
282
282
  #### Plan Artifact
283
283
 
284
- 1. List `{worktree}/.devflow/docs/design/*.md` — sort descending by filename (timestamps are naturally sortable), scan the 10 most recent
285
- 2. Read the most recent file if it exists
284
+ 1. List the design plans: `{worktree}/.devflow/docs/design/*.md` (a `/plan` artifact) and `{worktree}/.devflow/docs/design/*/*/*-plan.md` (a `/dynamic-plan` per-ticket plan). Sort by timestamp, newest first, and scan the 10 most recent. A `/plan` artifact's timestamp is the `YYYY-MM-DD_HHMM` before its `.md`; a per-ticket plan's is the name of its parent directory. Never sort by file name, which starts with an issue ID or a ticket slug. A per-ticket plan counts only when its `{ticket-slug}` (its file name without `-plan.md`) appears in the worktree's branch name
285
+ 2. Read the most recent file that carries an `## Acceptance Criteria` section
286
286
  3. Extract `## Acceptance Criteria` section → parse into table: `| ID | Criterion | Type | Testable Condition |`
287
287
  4. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
288
288
  5. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
@@ -22,9 +22,10 @@ Run a comprehensive code review of the current branch by spawning parallel revie
22
22
 
23
23
  **Produces:** WORKTREES
24
24
 
25
- 1. **Discover reviewable 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 reviewable 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 0c's Git agent validates its branch
28
29
  2. **If `--path` flag provided:** use only that worktree, skip discovery
29
30
  **`--path` validation**: Before proceeding, verify the path exists as a directory and appears in `git worktree list` output. If not: report error and stop.
30
31
  3. **If only 1 reviewable worktree** (the common case): proceed as single-worktree flow — zero behavior change
@@ -71,9 +72,9 @@ Set `EVIDENCE_POLICY`, `ISSUE_REQUIRED`, `APPLY_CONVENTIONS` and `REQUIRE_NON_AU
71
72
  **Requires:** WORKTREES
72
73
 
73
74
  Discover PR description guidance from plan artifact (per worktree):
74
- 1. List `{worktree}/.devflow/docs/design/*.md` files
75
- 2. Sort by timestamp in filename (descending -- timestamps are YYYY-MM-DD_HHMM, naturally sortable)
76
- 3. Read the most recent file, extract `## PR Description Guidance` section
75
+ 1. List the design plans: `{worktree}/.devflow/docs/design/*.md` (a `/plan` artifact) and `{worktree}/.devflow/docs/design/*/*/*-plan.md` (a `/dynamic-plan` per-ticket plan)
76
+ 2. Sort by timestamp, newest first, and scan the 10 most recent. A `/plan` artifact's timestamp is the `YYYY-MM-DD_HHMM` before its `.md`; a per-ticket plan's is the name of its parent directory. Never sort by file name, which starts with an issue ID or a ticket slug. A per-ticket plan counts only when its `{ticket-slug}` (its file name without `-plan.md`) appears in the worktree's branch name
77
+ 3. Read the most recent file that carries the section, extract `## PR Description Guidance` section
77
78
  4. If no plan files exist or section not found, set `PR_DESCRIPTION_GUIDANCE` to `(none)`
78
79
 
79
80
  Render the test-plan block (per worktree) from `/implement`'s evidence file, and from nothing else:
@@ -310,9 +311,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
310
311
 
311
312
  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.
312
313
 
313
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
314
+ **Step 2 — Agents apply the decisions:**
314
315
 
315
- 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.
316
+ 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.
316
317
 
317
318
  This produces a compact index of active ADR/PF entries. Pass `DECISIONS_CONTEXT` to all Review agents. Review agents use `devflow:apply-decisions` to Read full entry bodies on demand.
318
319
 
@@ -408,6 +409,10 @@ Spawn Review agents **in a single message**, for the focus set `DIFF_CLASS` sele
408
409
 
409
410
  Review agent count: `code` runs 8 core, +1-12 added by Phase 1 (max 20 total per worktree); a reduced class or a mix runs 2-6.
410
411
 
412
+ **Set the compliance focus directive** from `COMPLIANCE_FRAMEWORKS`. `COMPLIANCE_FOCUS_DIRECTIVE` is this one line, with `<ids>` replaced by the value of `COMPLIANCE_FRAMEWORKS`; only the compliance focus's spawn carries it:
413
+
414
+ > For this compliance focus, 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.
415
+
411
416
  Each Review agent invocation (all in one message, **NOT background**):
412
417
  ```
413
418
  Agent(subagent_type="Review", run_in_background=false):
@@ -421,7 +426,7 @@ DECISIONS_CONTEXT: {decisions_context}
421
426
  FEATURE_KNOWLEDGE: {feature_knowledge}
422
427
  PR_DESCRIPTION: <pr-description>{pr_description}</pr-description>
423
428
  PRIOR_RESOLUTIONS: <prior-resolution-summary>{prior_resolutions}</prior-resolution-summary>
424
- COMPLIANCE_FRAMEWORKS: {COMPLIANCE_FRAMEWORKS} (compliance focus only)
429
+ {COMPLIANCE_FOCUS_DIRECTIVE} (compliance focus only)
425
430
  If PRIOR_RESOLUTIONS is not (none), follow Cross-Cycle Awareness in review.md.
426
431
  Follow devflow:apply-decisions to scan the index and Read full ADR/PF bodies on demand.
427
432
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE — feature-specific patterns and anti-patterns inform findings.
@@ -42,6 +42,14 @@ Accept the output only when it is exactly two lines: `exit=0` last and, before i
42
42
 
43
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
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
+
45
53
  ### Load DECISIONS_CONTEXT
46
54
 
47
55
  When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
@@ -69,9 +77,9 @@ Attempt to read `{ledger}/.devflow/learning/index.md`.
69
77
 
70
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.
71
79
 
72
- **Step 2 — Apply decisions using `devflow:apply-decisions`:**
80
+ **Step 2 — Agents apply the decisions:**
73
81
 
74
- 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.
75
83
 
76
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.
77
85
 
@@ -226,7 +234,20 @@ Produce the final report:
226
234
 
227
235
  Ask user via AskUserQuestion: "Want me to implement this fix?"
228
236
 
229
- - **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
+
230
251
  - **NO** → Done. Report stands as documentation.
231
252
 
232
253
  ### Feature Knowledge Write-Back (Conditional)
@@ -307,7 +328,7 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
307
328
  ├─ Phase 6: Root cause report with confidence level
308
329
  │
309
330
  ├─ Phase 7: Offer Fix
310
- │ └─ AskUserQuestion → implement fix + Simplify agent, or done
331
+ │ └─ AskUserQuestion → Code agent (fix) + Simplify agent, or done
311
332
  │
312
333
  └─ Feature Knowledge Write-Back (Conditional)
313
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
 
@@ -65,7 +65,7 @@ The `budget` global governs depth. Scale Review agent roster and verification vo
65
65
 
66
66
  ### Handoff convention for sequential Code agents within a ticket
67
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), `{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.
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
69
 
70
70
  ### IRON RULE (LLM-vs-plumbing)
71
71
 
@@ -107,7 +107,7 @@ This is the rule the learning hooks apply (D-LEDGER-MAIN-WORKTREE, D-PROMPT-ROOT
107
107
 
108
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`.
109
109
 
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, 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.
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.
111
111
 
112
112
  ---
113
113
 
@@ -121,11 +121,12 @@ The following agentType values are valid. Model tiers are shown for reference
121
121
 
122
122
  | agentType | Model tier | Role |
123
123
  |-----------|-----------|------|
124
- | 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 |
125
126
  | Validate | haiku | Build / typecheck / lint / test — fast correctness gate |
126
127
  | Simplify | sonnet | Reduces complexity, removes duplication, improves readability |
127
128
  | Scrutinize | opus | 9-pillar self-review — deep structural analysis |
128
- | Evaluate | opus | Plan-fidelity / alignment — "was the plan implemented correctly?" |
129
+ | Evaluate | sonnet | Plan-fidelity / alignment — "was the plan implemented correctly?" |
129
130
  | Test | sonnet | Scenario-based acceptance tests against acceptance criteria |
130
131
  | Review | opus | Focus-parameterized code review — spawn ONE agent() per focus area |
131
132
  | Git | haiku | Git operations — branches, commits, merges, worktree management |
@@ -135,7 +136,7 @@ The following agentType values are valid. Model tiers are shown for reference
135
136
 
136
137
  ### Agent caveats
137
138
 
138
- - **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.
139
140
  - **Always omit `opts.model` with `agentType`.** Each agent honors its own frontmatter model tier; overriding it defeats the per-agent specialization.
140
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.
141
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.
@@ -186,7 +187,7 @@ Author the resolved `ISSUE_REQUIRED` and `APPLY_CONVENTIONS` into the workflow s
186
187
 
187
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.
188
189
 
189
- 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.
190
191
 
191
192
  **0c. Resolve the docs root**
192
193
 
@@ -268,7 +269,7 @@ If a number is found, record it as the command-level `ISSUE_NUMBER`:
268
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.
269
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`.
270
271
 
271
- 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.
272
273
 
273
274
  ---
274
275
 
@@ -294,6 +295,7 @@ const ISSUE_INPUT = args.issueInput || args.issueNumber || "(none)"; // this ti
294
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
295
296
  const APPLY_CONVENTIONS = String(args.applyConventions) === "false" ? "false" : "true";
296
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>
297
299
 
298
300
  // Phase 1: Git setup — declare the operation; the agent owns the process, the branch name included
299
301
  const setup = await phase("setup", () =>
@@ -310,7 +312,7 @@ Return: {"branch": "<the - **Branch name**: value under your ### Branch, or (non
310
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.
311
313
  const ISSUE_ID_SHAPE = /^(?:[1-9][0-9]{0,8}|[A-Z][A-Z0-9_]{0,9}-[1-9][0-9]{0,8})$/;
312
314
  const ISSUE_NUMBER = ISSUE_ID_SHAPE.test(String(setup?.issueId ?? "")) ? setup.issueId : "(none)"; // the captured Issue ID; "(none)" ⇒ none was captured
313
- 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
314
316
  // The branch setup-task created, as it reported it: every later phase and the merge use it. The engine never names one.
315
317
  const BRANCH = typeof setup?.branch === "string" && setup.branch.trim() !== "" && setup.branch.trim() !== "(none)" ? setup.branch : "(none)";
316
318
 
@@ -336,9 +338,7 @@ ${PLAN ? `Implementation plan:\n${PLAN}` : "No plan provided — use best judgme
336
338
  Relevant architectural decisions (apply devflow:apply-decisions algorithm):
337
339
  ${DECISIONS_CONTEXT}
338
340
 
339
- ISSUE_NUMBER: ${ISSUE_NUMBER}
340
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
341
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
341
+ ${COMPLIANCE_DIRECTIVE}
342
342
 
343
343
  When you build or run tests to verify your work, follow your Running commands block.
344
344
 
@@ -373,10 +373,8 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
373
373
  Fix the validation failures on branch ${BRANCH}:
374
374
  ${failureDetails}
375
375
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
376
- ISSUE_NUMBER: ${ISSUE_NUMBER}
377
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
378
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
379
- Commit fixes with conventional-commit message.`, { agentType: "Code" });
376
+ ${COMPLIANCE_DIRECTIVE}
377
+ Commit fixes with conventional-commit message.`, { agentType: "Code-fix" });
380
378
  const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} after fix attempt ${attempt} (follow your Running commands block).
381
379
  Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
382
380
  if (recheck?.verdict === "PASS") break;
@@ -416,10 +414,8 @@ Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answ
416
414
  Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
417
415
  ${evaluation?.rationale || "see the Evaluate agent's report"}
418
416
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
419
- ISSUE_NUMBER: ${ISSUE_NUMBER}
420
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
421
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
422
- Self-verify your fix compiles, following your Running commands block. Commit fixes.`, { agentType: "Code" });
417
+ ${COMPLIANCE_DIRECTIVE}
418
+ Self-verify your fix compiles, following your Running commands block. Commit fixes.`, { agentType: "Code-fix" });
423
419
  evalVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
424
420
  }
425
421
  }
@@ -438,10 +434,8 @@ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scen
438
434
  Fix the failing acceptance test scenarios on branch ${BRANCH}:
439
435
  ${testResult.failures}
440
436
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
441
- ISSUE_NUMBER: ${ISSUE_NUMBER}
442
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
443
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
444
- Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Code" });
437
+ ${COMPLIANCE_DIRECTIVE}
438
+ Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Code-fix" });
445
439
  testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
446
440
  }
447
441
  }
@@ -558,9 +552,7 @@ ${chunk.map((f, n) => `${n + 1}. ${f.file ? `${f.file}${f.line ? `:${f.line}` :
558
552
  SCOPE: ${chunk.map((f, n) => `${n + 1}=${CAREFUL_SEVERITIES.has(String(f.severity).toLowerCase()) ? "Careful" : "Standard"}`).join(", ")}
559
553
  PUSH: false
560
554
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
561
- ISSUE_NUMBER: ${ISSUE_NUMBER}
562
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
563
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
555
+ ${COMPLIANCE_DIRECTIVE}
564
556
  Fix all findings in this batch. Self-verify your fix compiles, following your Running commands block. Commit with conventional-commit message.
565
557
  Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<description of any finding that could not be fixed>"]}`, { agentType: "Code" });
566
558
  chunkResults.push({ chunk, result: r });
@@ -619,10 +611,8 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
619
611
  Fix the final validation failures on branch ${BRANCH}:
620
612
  ${failureDetails}
621
613
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
622
- ISSUE_NUMBER: ${ISSUE_NUMBER}
623
- ISSUE_PR_LINK: ${ISSUE_PR_LINK}
624
- COMPLIANCE_FRAMEWORKS: ${COMPLIANCE_FRAMEWORKS}
625
- Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code" });
614
+ ${COMPLIANCE_DIRECTIVE}
615
+ Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Code-fix" });
626
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).
627
617
  Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
628
618
  if (recheck?.verdict === "PASS") break;
@@ -707,12 +697,12 @@ Run builds, typechecks, lints and tests in the foreground, each with an explicit
707
697
  The standard implementation unit for one ticket. Run in order:
708
698
 
709
699
  ```
710
- Code(agentType:"Code", prompt: "OPERATION: implement" first line, then full task + plan + DECISIONS_CONTEXT + COMPLIANCE_FRAMEWORKS + handoff if sequential)
700
+ Code(agentType:"Code", prompt: "OPERATION: implement" first line, then full task + plan + DECISIONS_CONTEXT + COMPLIANCE_DIRECTIVE + handoff if sequential)
711
701
  → gate1_postcode()
712
702
  → gate2_acceptance() ← Gate 2 runs HERE — before the review pass, not after
713
703
  ```
714
704
 
715
- 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.
716
706
 
717
707
  Gate 2 runs at implementation acceptance — this matches devflow's deliberate placement: "evaluation is part of implementation acceptance, not post-review" (§6.1).
718
708
 
@@ -725,7 +715,7 @@ ORDER IS LOAD-BEARING. Run exactly in this sequence:
725
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
726
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
727
717
  - Build/test commands follow `build_execution_doctrine()`: the Validate agent's Running commands block, foreground under an explicit Bash timeout.
728
- - 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
729
719
  - If still FAIL after 2 retries → escalate as `validation-exhausted` (do not loop endlessly)
730
720
 
731
721
  **Gate 1 contains NO Evaluate agent and NO Test agent.** Those are Gate 2 only.
@@ -746,11 +736,11 @@ Gate 2 inputs are produced by `/devflow:dynamic-plan`'s plan-challenge step —
746
736
 
747
737
  **Evaluate agent** (only if a plan exists):
748
738
  - Run `evaluator_panel()` — see that block for the one spawn and the two lenses it names
749
- - 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.
750
740
 
751
741
  **Test agent** (only if acceptance criteria or a test plan exist):
752
742
  - Scenario-based acceptance tests covering functionality, API contracts, performance
753
- - 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.
754
744
 
755
745
  **When Gate 2 inputs are absent:**
756
746
  - No plan → skip the Evaluate agent silently (note in output: "Gate 2 Evaluate agent skipped — no plan available")
@@ -869,7 +859,7 @@ If survivors remain: batch the confirmed findings for fixing: group findings by
869
859
 
870
860
  ### Engine invariants (non-negotiable)
871
861
 
872
- 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.
873
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.
874
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).
875
865
  4. **Gate 2 runs once, at implementation acceptance.** It does not re-run after review-fixes.
@@ -1013,7 +1003,7 @@ Two parallel sibling tickets can produce real git conflicts. The resolution is *
1013
1003
  **Resolution procedure:**
1014
1004
 
1015
1005
  1. Git agent detects the conflict and reports the conflicting files + sections
1016
- 2. Spawn a Code agent whose prompt opens with `OPERATION: implement` and carries `COMPLIANCE_FRAMEWORKS` and `DECISIONS_CONTEXT`, 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:
1017
1007
  - Both ticket descriptions and plans
1018
1008
  - The conflicting diff sections (both sides)
1019
1009
  - Relevant ADRs from DECISIONS_CONTEXT (loaded by main model before authoring)