devflow-kit 3.7.0 → 3.8.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 (184) hide show
  1. package/CHANGELOG.md +359 -1
  2. package/README.md +11 -11
  3. package/dist/agents/code.md +2 -4
  4. package/dist/agents/design.md +1 -1
  5. package/dist/agents/fix.md +6 -7
  6. package/dist/agents/git.md +1 -1
  7. package/dist/agents/harden.md +1 -1
  8. package/dist/agents/refactor.md +2 -4
  9. package/dist/agents/review.md +4 -4
  10. package/dist/agents/skim.md +2 -2
  11. package/dist/agents/triage.md +2 -2
  12. package/dist/cli/commands/capture.js +1 -1
  13. package/dist/cli/commands/curation-move-io.js +102 -0
  14. package/dist/cli/commands/curation-move-step.js +374 -0
  15. package/dist/cli/commands/{learning.js → curation.js} +302 -99
  16. package/dist/cli/commands/gate-level-prompts.js +426 -0
  17. package/dist/cli/commands/gates.js +34 -11
  18. package/dist/cli/commands/init-seed.js +11 -8
  19. package/dist/cli/commands/init.js +240 -54
  20. package/dist/cli/commands/memory.js +2 -2
  21. package/dist/cli/commands/prompt-io.js +37 -0
  22. package/dist/cli/commands/uninstall.js +81 -9
  23. package/dist/cli.js +2 -2
  24. package/dist/commands/bug-analysis.md +24 -35
  25. package/dist/commands/code-review.md +32 -36
  26. package/dist/commands/debug.md +4 -4
  27. package/dist/commands/dynamic-build.md +25 -24
  28. package/dist/commands/dynamic-plan.md +4 -4
  29. package/dist/commands/dynamic-profile.md +4 -4
  30. package/dist/commands/dynamic-tickets.md +4 -4
  31. package/dist/commands/explore.md +5 -5
  32. package/dist/commands/implement.md +52 -81
  33. package/dist/commands/plan.md +5 -5
  34. package/dist/commands/release.md +4 -4
  35. package/dist/commands/research.md +5 -5
  36. package/dist/commands/resolve.md +28 -30
  37. package/dist/commands/self-review.md +4 -12
  38. package/dist/core/assets.js +20 -20
  39. package/dist/core/curate-tuning-config.js +5 -6
  40. package/dist/core/curation-move-notice.js +84 -0
  41. package/dist/core/curation-move-skip.js +36 -0
  42. package/dist/core/curation-move.js +35 -0
  43. package/dist/core/curation-queue-cleanup.js +134 -0
  44. package/dist/core/curation-store.js +98 -0
  45. package/dist/core/{learning-variants.js → curation-variants.js} +65 -41
  46. package/dist/core/echo-value.js +11 -5
  47. package/dist/core/evidence-policy.js +1 -1
  48. package/dist/core/feature-switch.js +95 -19
  49. package/dist/core/fs-atomic.js +1 -1
  50. package/dist/core/gate-level.js +10 -2
  51. package/dist/core/git.js +16 -14
  52. package/dist/core/ledger-root.js +82 -22
  53. package/dist/core/linked-path.js +4 -4
  54. package/dist/core/manifest-lock.js +238 -0
  55. package/dist/core/manifest.js +58 -14
  56. package/dist/core/migrations.js +1 -13
  57. package/dist/core/observations.js +2 -2
  58. package/dist/core/plugins.js +15 -15
  59. package/dist/core/pool.js +26 -0
  60. package/dist/core/project-paths.js +103 -34
  61. package/dist/core/queue-drain.js +2 -2
  62. package/dist/core/tracked-areas.js +35 -0
  63. package/dist/core/tracker.js +1 -1
  64. package/dist/{learning-off → curation-off}/agents/code.md +2 -4
  65. package/dist/{learning-off → curation-off}/agents/fix.md +6 -7
  66. package/dist/{learning-off → curation-off}/agents/refactor.md +2 -4
  67. package/dist/{learning-off → curation-off}/agents/review.md +3 -3
  68. package/dist/{learning-off → curation-off}/agents/triage.md +1 -1
  69. package/dist/{learning-off → curation-off}/commands/bug-analysis.md +20 -31
  70. package/dist/{learning-off → curation-off}/commands/code-review.md +29 -33
  71. package/dist/{learning-off → curation-off}/commands/debug.md +1 -1
  72. package/dist/{learning-off → curation-off}/commands/dynamic-build.md +22 -21
  73. package/dist/{learning-off → curation-off}/commands/explore.md +1 -1
  74. package/dist/{learning-off → curation-off}/commands/implement.md +49 -78
  75. package/dist/{learning-off → curation-off}/commands/plan.md +1 -1
  76. package/dist/{learning-off → curation-off}/commands/resolve.md +25 -27
  77. package/dist/{learning-off → curation-off}/commands/self-review.md +1 -9
  78. package/dist/hud/components/{learning-counts.js → curation-counts.js} +36 -17
  79. package/dist/hud/config.js +1 -1
  80. package/dist/hud/index.js +6 -6
  81. package/dist/hud/render.js +3 -3
  82. package/dist/skills/git/references/pr/validate-branch.md +8 -5
  83. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +13 -5
  84. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +13 -5
  85. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +13 -5
  86. package/dist/targets/claude-code/{learning-install.js → curation-install.js} +85 -65
  87. package/dist/targets/claude-code/installer.js +15 -15
  88. package/dist/targets/claude-code/language-stamp.js +5 -5
  89. package/dist/targets/claude-code/post-install.js +2 -2
  90. package/dist/targets/claude-code/tracker-servers.js +1 -1
  91. package/package.json +3 -2
  92. package/src/assets/agents/_partials/_code.mds +11 -11
  93. package/src/assets/agents/code.mds +2 -2
  94. package/src/assets/agents/curate.md +8 -8
  95. package/src/assets/agents/debug.mds +6 -6
  96. package/src/assets/agents/design.mds +18 -18
  97. package/src/assets/agents/fix.mds +6 -5
  98. package/src/assets/agents/git.mds +1 -1
  99. package/src/assets/agents/harden.mds +7 -7
  100. package/src/assets/agents/knowledge.mds +23 -23
  101. package/src/assets/agents/refactor.mds +2 -2
  102. package/src/assets/agents/research.mds +15 -15
  103. package/src/assets/agents/review.mds +43 -43
  104. package/src/assets/agents/skim.mds +11 -11
  105. package/src/assets/agents/triage.mds +22 -22
  106. package/src/assets/agents/validate.md +13 -26
  107. package/src/assets/commands/_partials/_decisions.mds +13 -13
  108. package/src/assets/commands/_partials/_engine.mds +8 -8
  109. package/src/assets/commands/_partials/_knowledge.mds +29 -15
  110. package/src/assets/commands/_partials/_preamble.mds +4 -4
  111. package/src/assets/commands/_partials/_settings.mds +2 -2
  112. package/src/assets/commands/_partials/_wave.mds +7 -7
  113. package/src/assets/commands/bug-analysis.mds +37 -48
  114. package/src/assets/commands/code-review.mds +46 -50
  115. package/src/assets/commands/debug.mds +14 -14
  116. package/src/assets/commands/dynamic-build.mds +38 -37
  117. package/src/assets/commands/dynamic-plan.mds +22 -22
  118. package/src/assets/commands/dynamic-profile.mds +2 -2
  119. package/src/assets/commands/dynamic-tickets.mds +8 -8
  120. package/src/assets/commands/explore.mds +20 -20
  121. package/src/assets/commands/implement.mds +77 -98
  122. package/src/assets/commands/plan.mds +27 -27
  123. package/src/assets/commands/release.mds +12 -12
  124. package/src/assets/commands/research.mds +19 -19
  125. package/src/assets/commands/resolve.mds +56 -50
  126. package/src/assets/commands/self-review.mds +20 -20
  127. package/src/assets/mds/git/_ops.mds +3 -1
  128. package/src/assets/mds/git/_ops_pr.mds +1 -1
  129. package/src/assets/mds/git/_ops_steps.mds +10 -4
  130. package/src/assets/mds/git/_pr.mds +7 -4
  131. package/src/assets/rules/quality.md +1 -0
  132. package/src/assets/scripts/hooks/background-memory-update +2 -2
  133. package/src/assets/scripts/hooks/capture-prompt +12 -12
  134. package/src/assets/scripts/hooks/capture-question +11 -11
  135. package/src/assets/scripts/hooks/capture-turn +12 -12
  136. package/src/assets/scripts/hooks/{learning-lock → curation-lock} +2 -2
  137. package/src/assets/scripts/hooks/curation-move +293 -0
  138. package/src/assets/scripts/hooks/ensure-devflow-init +45 -13
  139. package/src/assets/scripts/hooks/ensure-proxy +4 -4
  140. package/src/assets/scripts/hooks/ensure-root-gitignore +4 -4
  141. package/src/assets/scripts/hooks/git-marker +81 -0
  142. package/src/assets/scripts/hooks/json-helper.cjs +134 -51
  143. package/src/assets/scripts/hooks/json-parse +1 -1
  144. package/src/assets/scripts/hooks/lib/curation-move.cjs +1582 -0
  145. package/src/assets/scripts/hooks/lib/{learning-store.cjs → curation-store.cjs} +242 -118
  146. package/src/assets/scripts/hooks/lib/decisions-format.cjs +3 -3
  147. package/src/assets/scripts/hooks/lib/mkdir-lock.cjs +24 -7
  148. package/src/assets/scripts/hooks/lib/project-paths.cjs +132 -39
  149. package/src/assets/scripts/hooks/lib/render-decisions.cjs +34 -22
  150. package/src/assets/scripts/hooks/lib/tracked-areas.cjs +259 -0
  151. package/src/assets/scripts/hooks/memory-worker +1 -1
  152. package/src/assets/scripts/hooks/pre-compact-memory +6 -16
  153. package/src/assets/scripts/hooks/queue-append +228 -103
  154. package/src/assets/scripts/hooks/resolve-project-root +2 -2
  155. package/src/assets/scripts/hooks/session-start-context +143 -69
  156. package/src/assets/scripts/hooks/session-start-memory +3 -43
  157. package/src/assets/scripts/lib/project-config.cjs +12 -5
  158. package/src/assets/scripts/resolve-base.cjs +1043 -0
  159. package/src/assets/scripts/resolve-settings.cjs +42 -18
  160. package/src/assets/skills/agent-references/SKILL.md +5 -2
  161. package/src/assets/skills/agent-references/references/commands/implement-levels.md +27 -0
  162. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +6 -4
  163. package/src/assets/skills/agent-references/references/commands/resolve-levels.md +24 -0
  164. package/src/assets/skills/apply-decisions/SKILL.md +1 -1
  165. package/src/assets/skills/docs-framework/SKILL.md +7 -7
  166. package/src/assets/skills/docs-framework/references/patterns.md +3 -4
  167. package/src/assets/skills/quality-gates/SKILL.md +1 -1
  168. package/src/assets/skills/worktree-support/references/roots.md +2 -2
  169. package/templates/lint-pack/.lint-pack.conf +86 -0
  170. package/templates/lint-pack/README.md +190 -0
  171. package/templates/lint-pack/lint-pack.sh +905 -0
  172. package/dist/core/learning-queue-cleanup.js +0 -27
  173. package/dist/core/learning-store.js +0 -61
  174. /package/dist/{learning-off → curation-off}/agents/debug.md +0 -0
  175. /package/dist/{learning-off → curation-off}/agents/design.md +0 -0
  176. /package/dist/{learning-off → curation-off}/agents/harden.md +0 -0
  177. /package/dist/{learning-off → curation-off}/agents/knowledge.md +0 -0
  178. /package/dist/{learning-off → curation-off}/agents/research.md +0 -0
  179. /package/dist/{learning-off → curation-off}/agents/skim.md +0 -0
  180. /package/dist/{learning-off → curation-off}/commands/dynamic-plan.md +0 -0
  181. /package/dist/{learning-off → curation-off}/commands/dynamic-profile.md +0 -0
  182. /package/dist/{learning-off → curation-off}/commands/dynamic-tickets.md +0 -0
  183. /package/dist/{learning-off → curation-off}/commands/release.md +0 -0
  184. /package/dist/{learning-off → curation-off}/commands/research.md +0 -0
@@ -75,12 +75,12 @@ Pass it as `complianceFrameworks` when invoking the workflow; the engine builds
75
75
 
76
76
  `{integration worktree root}` is the toplevel of the checkout the integration branch is checked out in: `{worktree}` unless the wave runs in a linked worktree.
77
77
 
78
- <!-- learning:on -->
78
+ <!-- curation:on -->
79
79
  **1. Apply decisions context**
80
80
 
81
81
  Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Code agent prompts.
82
82
 
83
- <!-- learning:end -->
83
+ <!-- curation:end -->
84
84
  **2. Read budget**
85
85
 
86
86
  Note the `budget` value from the Workflow tool context (or default to "medium" if not provided). This governs Review agent roster size and verification vote count.
@@ -158,9 +158,9 @@ const TICKET = args.ticket || args[0] || "see task description";
158
158
  const PLAN = args.plan || null;
159
159
  const CRITERIA = args.criteria || null;
160
160
  const TEST_PLAN = args.testPlan || null; // the plan's checked TP lines, one string (Pre-authoring step 4)
161
- <!-- learning:on -->
161
+ <!-- curation:on -->
162
162
  const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
163
- <!-- learning:end -->
163
+ <!-- curation:end -->
164
164
  const ISSUE_INPUT = args.issueInput || args.issueNumber || "(none)"; // this ticket's OWN raw reference (Pre-authoring step 5; a wave passes issueInput) — setup-task's input, never a Code agent's
165
165
  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
166
166
  const APPLY_CONVENTIONS = String(args.applyConventions) === "false" ? "false" : "true";
@@ -175,7 +175,7 @@ TASK_DESCRIPTION: ${TICKET}
175
175
  ISSUE_REQUIRED: ${ISSUE_REQUIRED}
176
176
  APPLY_CONVENTIONS: ${APPLY_CONVENTIONS}
177
177
  ${ISSUE_INPUT !== "(none)" ? "ISSUE_INPUT: " + ISSUE_INPUT : ""}
178
- Return: {"branch": "<the - **Branch name**: value under your ### Branch, or (none)>", "issueId": "<the - **Issue ID**: value under your ### Handoff Values, or (none)>", "prLinkLine": "<the - **PR link line**: value, or (none)>"}`, { agentType: "Git" })
178
+ Return: {"branch": "<the - **Branch name**: value under your ### Branch, or (none)>", "issueId": "<the - **Issue ID**: value under your ### Handoff Values, or (none)>", "prLinkLine": "<the - **PR link line**: value, or (none)>", "baseSha": "<the 40 or 64 hex git rev-parse HEAD prints before step 4>"}`, { agentType: "Git" })
179
179
  );
180
180
 
181
181
  // The ticket's own Handoff Values, read from setup-task's Output — never derived from ISSUE_INPUT.
@@ -185,6 +185,7 @@ const ISSUE_NUMBER = ISSUE_ID_SHAPE.test(String(setup?.issueId ?? "")) ? setup.i
185
185
  const ISSUE_PR_LINK = setup?.prLinkLine || "(none)"; // "(none)" ⇒ the wave PR carries no closing reference for this ticket
186
186
  // The branch setup-task created, as it reported it: every later phase and the merge use it. The engine never names one.
187
187
  const BRANCH = typeof setup?.branch === "string" && setup.branch.trim() !== "" && setup.branch.trim() !== "(none)" ? setup.branch : "(none)";
188
+ const BASE_SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(String(setup?.baseSha ?? "")) ? setup.baseSha : "(none)"; // the commit the ticket branch was forked from
188
189
 
189
190
  // Ticket-link stop, before implement. The workflow cannot ask, so it never records an exception: it stops.
190
191
  if (ISSUE_REQUIRED === "true" && ISSUE_NUMBER === "(none)") {
@@ -196,6 +197,11 @@ if (BRANCH === "(none)") {
196
197
  return { ticket: TICKET, branch: BRANCH, verdict: "ESCALATED", issueId: ISSUE_NUMBER, issuePrLink: ISSUE_PR_LINK, escalations: [{ type: "branch-missing", description: "setup-task reported no branch name — check its Output, then re-run" }] };
197
198
  }
198
199
 
200
+ // Base stop: every review range starts at the commit the ticket branch was forked from.
201
+ if (BASE_SHA === "(none)") {
202
+ return { ticket: TICKET, branch: BRANCH, verdict: "ESCALATED", issueId: ISSUE_NUMBER, issuePrLink: ISSUE_PR_LINK, escalations: [{ type: "base-missing", description: "setup-task reported no base commit — check its Output, then re-run" }] };
203
+ }
204
+
199
205
  // Phase 2: Implement
200
206
  await phase("implement", () =>
201
207
  agent(`OPERATION: implement
@@ -205,11 +211,11 @@ ${TICKET}
205
211
 
206
212
  ${PLAN ? `Implementation plan:\n${PLAN}` : "No plan provided — use best judgment."}
207
213
 
208
- <!-- learning:on -->
214
+ <!-- curation:on -->
209
215
  Relevant architectural decisions (apply devflow:apply-decisions algorithm):
210
216
  ${DECISIONS_CONTEXT}
211
217
 
212
- <!-- learning:end -->
218
+ <!-- curation:end -->
213
219
  ${COMPLIANCE_DIRECTIVE}
214
220
 
215
221
  When you build or run tests to verify your work, follow your Running commands block.
@@ -244,9 +250,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
244
250
  await agent(`OPERATION: validation-fix
245
251
  Fix the validation failures on branch ${BRANCH}:
246
252
  ${failureDetails}
247
- <!-- learning:on -->
253
+ <!-- curation:on -->
248
254
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
249
- <!-- learning:end -->
255
+ <!-- curation:end -->
250
256
  ${COMPLIANCE_DIRECTIVE}
251
257
  Commit fixes with conventional-commit message.`, { agentType: "Fix" });
252
258
  const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} after fix attempt ${attempt} (follow your Running commands block).
@@ -287,9 +293,9 @@ Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answ
287
293
  await agent(`OPERATION: alignment-fix
288
294
  Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
289
295
  ${evaluation?.rationale || "see the Evaluate agent's report"}
290
- <!-- learning:on -->
296
+ <!-- curation:on -->
291
297
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
292
- <!-- learning:end -->
298
+ <!-- curation:end -->
293
299
  ${COMPLIANCE_DIRECTIVE}
294
300
  Self-verify your fix compiles, following your Running commands block. Commit fixes.`, { agentType: "Fix" });
295
301
  evalVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
@@ -309,9 +315,9 @@ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scen
309
315
  await agent(`OPERATION: qa-fix
310
316
  Fix the failing acceptance test scenarios on branch ${BRANCH}:
311
317
  ${testResult.failures}
312
- <!-- learning:on -->
318
+ <!-- curation:on -->
313
319
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
314
- <!-- learning:end -->
320
+ <!-- curation:end -->
315
321
  ${COMPLIANCE_DIRECTIVE}
316
322
  Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Fix" });
317
323
  testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
@@ -323,10 +329,9 @@ Self-verify your fix compiles and the scenarios pass, following your Running com
323
329
 
324
330
  // Phase 5: Review → verify → fix (single pass)
325
331
  const reviewResult = await phase("review", async () => {
326
- // Review scope: the entire branch diff from the base branch to HEAD. No base SHA is tracked —
327
- // Review agents diff the branch against its merge-base with the default branch.
332
+ // Review scope: the entire branch diff, from the commit the ticket branch was forked from to HEAD.
328
333
  // The pass runs exactly ONCE.
329
- const reviewScope = `Review the full branch diff on branch ${BRANCH}. Diff the branch against its merge-base with the default branch (do not guess a base SHA — compute the merge-base from git).`;
334
+ const reviewScope = `Review the full branch diff on branch ${BRANCH}.\nDIFF_RANGE: ${BASE_SHA}...HEAD\n`;
330
335
 
331
336
  // Review agent focus list (order = index mapping for dead-reviewer detection)
332
337
  const reviewFocuses = ["security", "architecture", "performance", "complexity", "consistency", "regression", "testing", "reliability"];
@@ -370,13 +375,12 @@ Focus discipline: ${focus}. Return: {"focus": "${focus}", "reviewed": true, "fil
370
375
  return { survivingFindings: [], fixedFindings: [], coverageGaps };
371
376
  }
372
377
 
373
- // Adversarial verification — bounded 2-3 agent panel, majority-survives
374
- // Panel asks perspective-diverse questions over the WHOLE finding set (not one agent per finding).
375
- // A finding survives if >50% of panel agents CONFIRM it.
378
+ // Adversarial panel over the WHOLE finding set (not one agent per finding): >50% CONFIRMED survives
376
379
  const VERIFY_PANEL = 3;
377
380
  const panels = await parallel(
378
381
  Array.from({ length: VERIFY_PANEL }, (_, lens) => () =>
379
382
  agent(`Adversarially verify all ${allFindings.length} findings on branch ${BRANCH}.
383
+ DIFF_RANGE: ${BASE_SHA}...HEAD
380
384
  Lens ${lens + 1} of ${VERIFY_PANEL}:
381
385
  ${["Does each finding actually reproduce in the current code? Hunt for findings that describe patterns not present in the diff.",
382
386
  "Is each finding a real issue or a false positive in context? Consider intent, surrounding code, and project conventions.",
@@ -394,16 +398,13 @@ ${JSON.stringify(allFindings.map((f, i) => ({ index: i, description: f.descripti
394
398
  panels.filter(p => (p.verdicts || []).find(v => v.index === i)?.verdict === "CONFIRMED").length > VERIFY_PANEL / 2
395
399
  );
396
400
 
397
- // No confirmed findings — coverageGaps carry forward and block PASS downstream; also eliminates the empty-batch parallel() path
401
+ // No confirmed findings: coverageGaps carry forward and block PASS downstream
398
402
  if (confirmedFindings.length === 0) {
399
403
  return { survivingFindings: [], fixedFindings: [], coverageGaps };
400
404
  }
401
405
 
402
- // Batched fix Code agents — one file per set of sub-batches; max 5 findings per sub-batch.
403
- // Sub-batches for the SAME file run sequentially (never two Code agents editing the same file concurrently —
404
- // concurrent same-file edits cause index contention and lost fixes).
405
- // Sub-batches for DISTINCT files run in parallel (different code areas — safe per concurrency doctrine).
406
- // A finding with no file field gets its own singleton group (unknown scope — never mix with known files).
406
+ // Sub-batches (max 5 findings): same file in sequence, distinct files in parallel.
407
+ // A finding with no file is a singleton group.
407
408
  const MAX_BATCH = 5; // max 5 findings per sub-batch
408
409
  const fileGroups = {};
409
410
  for (let fi = 0; fi < confirmedFindings.length; fi++) {
@@ -413,9 +414,7 @@ ${JSON.stringify(allFindings.map((f, i) => ({ index: i, description: f.descripti
413
414
  fileGroups[key].push(f);
414
415
  }
415
416
 
416
- // One entry per distinct file; each entry runs its sub-batches sequentially.
417
- // Distinct-file groups are paced in chunks of FIX_CHUNK — same pacing bar as the Review spawn
418
- // path — to avoid launching all Code agents at once on a large branch (reliability: explicit bound).
417
+ // One entry per file; FIX_CHUNK chunks bound concurrent Code agents
419
418
  const FIX_CHUNK = 5;
420
419
  // issue-fix SCOPE per finding: CRITICAL or HIGH takes the Careful protocol (failing regression test first); anything else is Standard.
421
420
  const CAREFUL_SEVERITIES = new Set(["critical", "high"]);
@@ -429,9 +428,9 @@ ISSUES:
429
428
  ${chunk.map((f, n) => `${n + 1}. ${f.file ? `${f.file}${f.line ? `:${f.line}` : ""} — ` : ""}${f.description} (${f.severity})`).join("\n")}
430
429
  SCOPE: ${chunk.map((f, n) => `${n + 1}=${CAREFUL_SEVERITIES.has(String(f.severity).toLowerCase()) ? "Careful" : "Standard"}`).join(", ")}
431
430
  PUSH: false
432
- <!-- learning:on -->
431
+ <!-- curation:on -->
433
432
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
434
- <!-- learning:end -->
433
+ <!-- curation:end -->
435
434
  ${COMPLIANCE_DIRECTIVE}
436
435
  Fix all findings in this batch. Self-verify your fix compiles, following your Running commands block. Commit with conventional-commit message.
437
436
  Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<description of any finding that could not be fixed>"]}`, { agentType: "Code" });
@@ -490,9 +489,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
490
489
  await agent(`OPERATION: validation-fix
491
490
  Fix the final validation failures on branch ${BRANCH}:
492
491
  ${failureDetails}
493
- <!-- learning:on -->
492
+ <!-- curation:on -->
494
493
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
495
- <!-- learning:end -->
494
+ <!-- curation:end -->
496
495
  ${COMPLIANCE_DIRECTIVE}
497
496
  Self-verify your fix compiles. Commit fixes with conventional-commit message.`, { agentType: "Fix" });
498
497
  const recheck = await agent(`Re-run build, typecheck, lint, tests on branch ${BRANCH} (final gate, after fix attempt ${attempt}; follow your Running commands block).
@@ -588,7 +587,7 @@ const results = {}; // ticketId → its row of the workflow's return
588
587
  const rowOf = (ticketId, r, merged, treeEqual = null) => ({ ticket: ticketId, ran: true, verdict: r?.verdict || r?.overallVerdict || null, merged, treeEqual, issuePrLink: r?.issuePrLink || "(none)", evaluateVerdict: r?.gate2?.evaluateVerdict, testVerdict: r?.gate2?.testVerdict, surviving: r?.survivingFindings?.length, coverageComplete: r?.reviewCoverage?.complete });
589
588
  const MAX_ROUNDS = Math.max(10, remainingTickets.length * 2 + 5); // heuristic; always finite
590
589
  const MERGE_SHA = /^[0-9a-f]{40}$/; // a SHA as git prints it; with the checked branch, the only Git-reported values that reach a merge, Validate or undo prompt
591
- const BRANCH_NAME = /^(?![-#]|(?:@|(?:FETCH_|ORIG_|MERGE_)?HEAD)$)(?!.*\.\.)[A-Za-z0-9._/+@,#-]{1,200}$/; // implement's ref allowlist; no leading - or #, no revision name, no ..: the name enters git lines
590
+ const BRANCH_NAME = /^(?![-#]|(?:@|(?:FETCH_|ORIG_|MERGE_)?HEAD)$)(?!.*\.\.)[A-Za-z0-9._/+@,#-]{1,200}$/; // STRICT_NAME_RE's deliberate variant, less its origin/ refusal; the name enters git lines
592
591
  // One ticket's merge, post-merge Validate and undo (D-WAVE-MERGE-VALIDATE: Git validates nothing).
593
592
  const mergeValidateUndo = async (ticketId, engineResult) => {
594
593
  if (typeof engineResult.branch !== "string" || !BRANCH_NAME.test(engineResult.branch)) return { kept: false, reason: "ticket branch name refused: not a plain git ref name", halted: null, treeEqual: null };
@@ -646,11 +645,11 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
646
645
  for (const ticketId of ready) {
647
646
  try {
648
647
  // ticketId is this ticket's OWN ISSUE_REF (pre-fetch heading): the engine's `ticket` and setup-task ISSUE_INPUT; the engine returns the branch setup-task created, merged below; plans[ticketId] is its own plan (Pre-authoring step 4)
649
- <!-- learning:on -->
648
+ <!-- curation:on -->
650
649
  const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), decisionsContext: DECISIONS_CONTEXT, issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
651
- <!-- learning:off -->
650
+ <!-- curation:off -->
652
651
  const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
653
- <!-- learning:end -->
652
+ <!-- curation:end -->
654
653
  // PASS and UNVERIFIED merge (verdict, or overallVerdict as the SINGLE alias); PARTIAL, FAIL, ESCALATED (the ticket-link and branch stops included) or no verdict quarantine.
655
654
  if (["PASS", "UNVERIFIED"].includes(engineResult.verdict || engineResult.overallVerdict)) {
656
655
  const { kept, reason, halted, treeEqual } = await mergeValidateUndo(ticketId, engineResult);
@@ -761,12 +760,14 @@ Do NOT ask questions mid-workflow — that is impossible (F4). The workflow only
761
760
 
762
761
  7. **Wave PR evidence** — only when step 6 reported the wave PR, after it; it never blocks, and every outcome below goes into the run summary. Take `{n}` from step 6's `- **PR**: #{n}` line when `{n}` matches `^[1-9][0-9]{0,9}$` as a whole; no such line ⇒ record `TRACEABILITY: DEGRADED (wave PR number not captured)` and skip this step. `PR_TEST_PLAN_BLOCK` `(none)` ⇒ record `Wave evidence: skipped (no wave test plan)` and skip it.
763
762
 
763
+ **Changed files.** Take `{base_ref}` from step 6's `- **Base ref**:` line; only when it is 40 or 64 lowercase hex, run `git -C "{integration worktree root}" diff --name-only --diff-filter=d {base_ref}...HEAD; echo "exit=$?"`. Unless it ran and printed `exit=0`, record `TRACEABILITY: DEGRADED (wave base unusable)` and skip this step.
764
+
764
765
  **(a) Test the wave.** Spawn one Test agent on the integration worktree with the wave test plan — the TP lines of the `## Test Plan` section step 3(b) wrote to `{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md`:
765
766
 
766
767
  ```
767
768
  Agent(subagent_type="Test"):
768
769
  "ORIGINAL_REQUEST: the merged tickets of wave/{slug}, as the wave test plan names them
769
- FILES_CHANGED: {the files wave/{slug} changes against the - **Base**: branch step 6 reported}
770
+ FILES_CHANGED: {the file list that diff printed}
770
771
  TEST_PLAN: {the TP lines of the wave evidence file's ## Test Plan section}
771
772
  WORKTREE_PATH: {integration worktree root}
772
773
  Cover every TEST_PLAN line on the integration branch as it stands. Report PASS or FAIL with evidence."
@@ -11,12 +11,12 @@ output-dir: dist/commands
11
11
  @import "./_partials/_settings.mds" as settings
12
12
  {{authoring_preamble()}}
13
13
 
14
- <!-- learning:on -->
14
+ <!-- curation:on -->
15
15
  ### Settings line
16
16
 
17
17
  {{settings.settings_resolve()}}
18
18
 
19
- <!-- learning:end -->
19
+ <!-- curation:end -->
20
20
  {{authoring_decisions()}}
21
21
  ---
22
22
 
@@ -52,12 +52,12 @@ Before authoring, verify:
52
52
 
53
53
  Pass `{worktree}` to the workflow as its `root` argument, and its `slug` argument: the `{slug}` of the ticket directory (`tickets/{slug}/{ts}/`) when that is the ticket source, otherwise `wave`.
54
54
 
55
- <!-- learning:on -->
55
+ <!-- curation:on -->
56
56
  **1. Apply decisions context**
57
57
 
58
58
  Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note verbatim ADR/PF IDs to inject into Design agent prompts.
59
59
 
60
- <!-- learning:end -->
60
+ <!-- curation:end -->
61
61
  **2. Read the preference profile**
62
62
 
63
63
  Check whether `~/.devflow/preference-profile.md` exists:
@@ -122,9 +122,9 @@ export const meta = {
122
122
  // AskUserQuestion happens at the command boundary after this workflow returns — NOT here.
123
123
 
124
124
  const ticketSource = args.ticketSource || args[0] || "see task description";
125
- <!-- learning:on -->
125
+ <!-- curation:on -->
126
126
  const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
127
- <!-- learning:end -->
127
+ <!-- curation:end -->
128
128
  const PREFERENCE_PROFILE = args.preferenceProfile || ""; // injected before authoring
129
129
  const TP_CONTRACT = args.tpContract || ""; // the "Test-plan line (TP)" contract below — its paragraph and four bullets — passed verbatim as tpContract when invoking the workflow, never pasted into this script (it holds backticks)
130
130
  const slug = args.slug || "wave";
@@ -146,9 +146,9 @@ const plans = await phase("plan-parallel", () =>
146
146
  parallel((tickets || []).map(ticket => () =>
147
147
  agent(`Write an implementation plan for this ticket.
148
148
  Ticket: ${JSON.stringify(ticket)}
149
- <!-- learning:on -->
149
+ <!-- curation:on -->
150
150
  Decisions context (apply devflow:apply-decisions; a plan file can be posted to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
151
- <!-- learning:end -->
151
+ <!-- curation:end -->
152
152
  The plan must cover: approach overview, affected files and modules, key design decisions, implementation sequence (what to build first), risks and mitigations, and any open questions you cannot resolve from the ticket alone.
153
153
  Write a thorough but tight plan — every section must earn its place for a Code agent who has no other context.
154
154
  Return: { ticketTitle, planMarkdown, openDecisions (array of genuine unknowns requiring user input) }.`, { agentType: "Design" })
@@ -192,9 +192,9 @@ Return: { ticketTitle, improvements (array), acceptanceCriteria (array of number
192
192
  const crossCritic = await phase("cross-plan-critic", () =>
193
193
  agent(`Cross-plan conflict audit.
194
194
  All ticket plans + challenged criteria: ${JSON.stringify(challenged)}
195
- <!-- learning:on -->
195
+ <!-- curation:on -->
196
196
  Decisions context: ${DECISIONS_CONTEXT}
197
- <!-- learning:end -->
197
+ <!-- curation:end -->
198
198
 
199
199
  Audit:
200
200
  1. API conflicts: two tickets defining the same API differently (different signatures, return types, error codes).
@@ -202,11 +202,11 @@ Audit:
202
202
  3. Undeclared dependencies: ticket A's plan implicitly requires something ticket B provides, but no dependency is declared.
203
203
  4. Scope overlap: two tickets both claim ownership of the same module or behavior.
204
204
 
205
- <!-- learning:on -->
205
+ <!-- curation:on -->
206
206
  For each conflict found: state which two tickets are involved, describe the conflict precisely, and propose a resolution aligned with the decisions ledger.
207
- <!-- learning:off -->
207
+ <!-- curation:off -->
208
208
  For each conflict found: state which two tickets are involved, describe the conflict precisely, and propose a resolution.
209
- <!-- learning:end -->
209
+ <!-- curation:end -->
210
210
 
211
211
  Return: { conflicts (array of {tickets, conflictDescription, proposedResolution}), planAmendments (array of {ticketTitle, amendment}) }.`, { agentType: "Design" })
212
212
  );
@@ -243,26 +243,26 @@ For each ticket, write ${OUTDIR}/{ticket-slug}-plan.md, {ticket-slug} being the
243
243
  - ## Acceptance Criteria (numbered, positive + negative)
244
244
  - ## Test Plan — the challenger's testPlan lines, verbatim, one per line, and nothing else: no prose, no blank line between them, no setup or outcome
245
245
  - ## Test Scenarios — one line per TP, in TP order: TP-n: its setup, then its expected outcome, from testScenarios
246
- <!-- learning:on -->
246
+ <!-- curation:on -->
247
247
  - ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source, naming a recorded decision in words, never by ID)
248
- <!-- learning:off -->
248
+ <!-- curation:off -->
249
249
  - ## Auto-Resolved Decisions (if any — list each as: decision → resolution → source)
250
- <!-- learning:end -->
250
+ <!-- curation:end -->
251
251
 
252
252
  Then write ${OUTDIR}/DECISIONS-NEEDED.md:
253
- <!-- learning:on -->
253
+ <!-- curation:on -->
254
254
  - ## Auto-Resolved Decisions — list each silently-resolved decision as: decision → resolution → source (preference profile / ADR-NNN), so auto-resolution is auditable and reversible. If none, write "None."
255
- <!-- learning:off -->
255
+ <!-- curation:off -->
256
256
  - ## Auto-Resolved Decisions — list each silently-resolved decision as: decision → resolution → source (preference profile), so auto-resolution is auditable and reversible. If none, write "None."
257
- <!-- learning:end -->
257
+ <!-- curation:end -->
258
258
  - ## Decisions Needed
259
259
  - One section per open decision: what the decision is, why it matters, what options exist.
260
260
  - Include cross-plan conflicts that were not auto-resolved.
261
- <!-- learning:on -->
261
+ <!-- curation:on -->
262
262
  - If no decisions needed, write: "No open decisions — all settled by preference profile or existing ADRs."
263
- <!-- learning:off -->
263
+ <!-- curation:off -->
264
264
  - If no decisions needed, write: "No open decisions — all settled by the preference profile."
265
- <!-- learning:end -->
265
+ <!-- curation:end -->
266
266
 
267
267
  Return: { planPaths (array), decisionsNeededPath (string), decisionsNeededCount (number) }.`, { agentType: "Synthesize" })
268
268
  );
@@ -7,12 +7,12 @@ output-dir: dist/commands
7
7
  @import "./_partials/_settings.mds" as settings
8
8
  {{authoring_preamble()}}
9
9
 
10
- <!-- learning:on -->
10
+ <!-- curation:on -->
11
11
  ### Settings line
12
12
 
13
13
  {{settings.settings_resolve()}}
14
14
 
15
- <!-- learning:end -->
15
+ <!-- curation:end -->
16
16
  {{authoring_decisions()}}
17
17
  ---
18
18
 
@@ -12,12 +12,12 @@ output-dir: dist/commands
12
12
  @import "./_partials/_settings.mds" as settings
13
13
  {{authoring_preamble()}}
14
14
 
15
- <!-- learning:on -->
15
+ <!-- curation:on -->
16
16
  ### Settings line
17
17
 
18
18
  {{settings.settings_resolve()}}
19
19
 
20
- <!-- learning:end -->
20
+ <!-- curation:end -->
21
21
  {{authoring_decisions()}}
22
22
  ---
23
23
 
@@ -61,12 +61,12 @@ Before you write the workflow script:
61
61
 
62
62
  Pass `{worktree}` to the workflow as its `root` argument.
63
63
 
64
- <!-- learning:on -->
64
+ <!-- curation:on -->
65
65
  **1. Apply decisions context**
66
66
 
67
67
  Apply the `devflow:apply-decisions` algorithm to the DECISIONS_CONTEXT loaded by the decisions step above: scan the index, Read relevant entries, note the verbatim ADR/PF IDs you will inject into Design agent prompts.
68
68
 
69
- <!-- learning:end -->
69
+ <!-- curation:end -->
70
70
  **2. Read and distill the initiative**
71
71
 
72
72
  Read or note the user's input:
@@ -103,9 +103,9 @@ export const meta = {
103
103
  const candidates = args.candidates || []; // [{title, summary, wave, dependsOn}]
104
104
  const initiative = args.initiative || args[0] || "see task description";
105
105
  const constraints = args.constraints || "";
106
- <!-- learning:on -->
106
+ <!-- curation:on -->
107
107
  const DECISIONS_CONTEXT = args.decisionsContext || ""; // injected before authoring
108
- <!-- learning:end -->
108
+ <!-- curation:end -->
109
109
  const slug = args.slug || initiative.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+/, '').slice(0, 50).replace(/-+$/, '') || 'untitled';
110
110
  const now = new Date(), two = (n) => String(n).padStart(2, '0');
111
111
  const ts = `${now.getFullYear()}-${two(now.getMonth() + 1)}-${two(now.getDate())}_${two(now.getHours())}${two(now.getMinutes())}`;
@@ -118,9 +118,9 @@ const drafts = await phase("draft", () =>
118
118
  agent(`Draft ticket for initiative: "${initiative}"
119
119
  Ticket: ${JSON.stringify(c)}
120
120
  Constraints: ${constraints}
121
- <!-- learning:on -->
121
+ <!-- curation:on -->
122
122
  Decisions context (apply devflow:apply-decisions; the ticket is filed to the tracker, so state each decision in words, never by ID): ${DECISIONS_CONTEXT}
123
- <!-- learning:end -->
123
+ <!-- curation:end -->
124
124
  Write the ticket body following the ticket_body_template structure (Wave/Depends-on header, Summary, Scope with In/Out + anti-features, Invariants, numbered Acceptance Criteria with at least one negative criterion, Open Questions).
125
125
  Return a JSON object with: title (string), summary (string), wave (number), dependsOn (array), bodyMarkdown (string), openQuestions (array).`, { agentType: "Design" })
126
126
  ))
@@ -32,34 +32,34 @@ $ARGUMENTS
32
32
 
33
33
  ## Phases
34
34
 
35
- <!-- learning:on -->
35
+ <!-- curation:on -->
36
36
  ### Phase 1: Load Decisions (Orchestrator-Local)
37
- <!-- learning:off -->
37
+ <!-- curation:off -->
38
38
  ### Phase 1: Resolve Settings
39
- <!-- learning:end -->
39
+ <!-- curation:end -->
40
40
 
41
- <!-- learning:on -->
41
+ <!-- curation:on -->
42
42
  **Produces:** DECISIONS_CONTEXT
43
43
 
44
- <!-- learning:end -->
44
+ <!-- curation:end -->
45
45
  {{settings.settings_resolve()}}
46
46
 
47
47
  {{decisions_load()}}
48
- <!-- learning:on -->
48
+ <!-- curation:on -->
49
49
  The orchestrator uses `DECISIONS_CONTEXT` locally when framing exploration — prior decisions and pitfalls suggest specific areas to investigate. Follow `devflow:apply-decisions` to Read full entry bodies on demand. **Do NOT pass `DECISIONS_CONTEXT` to Explore sub-agents** — decisions context stays in the orchestrator, not in the investigation workers.
50
50
 
51
- <!-- learning:end -->
51
+ <!-- curation:end -->
52
52
  **No up-front feature knowledge load** — explore investigation workers read code directly to avoid confirmation bias. Feature knowledge is only created as a write-back at the end, after exploration is complete.
53
53
 
54
54
  ### Phase 2: Orient
55
55
 
56
56
  **Produces:** ORIENT_OUTPUT
57
57
 
58
- <!-- learning:on -->
59
- Spawn `Agent(subagent_type="Skim")` with `LEARNING` from the settings line, to get codebase overview relevant to the exploration question:
60
- <!-- learning:off -->
58
+ <!-- curation:on -->
59
+ Spawn `Agent(subagent_type="Skim")` with `CURATION` from the settings line, to get codebase overview relevant to the exploration question:
60
+ <!-- curation:off -->
61
61
  Spawn `Agent(subagent_type="Skim")` to get codebase overview relevant to the exploration question:
62
- <!-- learning:end -->
62
+ <!-- curation:end -->
63
63
 
64
64
  - File structure and module boundaries in the target area
65
65
  - Entry points and key abstractions
@@ -105,11 +105,11 @@ Present findings to user. Use AskUserQuestion to offer focused follow-up explora
105
105
 
106
106
  ### Phase 6: Suggest Feature Knowledge Creation (Conditional)
107
107
 
108
- <!-- learning:on -->
108
+ <!-- curation:on -->
109
109
  **Requires:** MERGED_FINDINGS, DECISIONS_CONTEXT
110
- <!-- learning:off -->
110
+ <!-- curation:off -->
111
111
  **Requires:** MERGED_FINDINGS
112
- <!-- learning:end -->
112
+ <!-- curation:end -->
113
113
  **Produces:** FEATURE_KNOWLEDGE_STATUS (created | skipped)
114
114
 
115
115
  1. Check if matching feature knowledge already exists by reading `{worktree}/.devflow/features/index.md` (or globbing frontmatter if absent). If covered → skip
@@ -143,11 +143,11 @@ Structured exploration findings with concrete code references:
143
143
  ```
144
144
  /explore (orchestrator)
145
145
  │
146
- <!-- learning:on -->
146
+ <!-- curation:on -->
147
147
  ├─ Phase 1: Load Decisions (Orchestrator-Local)
148
- <!-- learning:off -->
148
+ <!-- curation:off -->
149
149
  ├─ Phase 1: Resolve Settings
150
- <!-- learning:end -->
150
+ <!-- curation:end -->
151
151
  │
152
152
  ├─ Phase 2: Orient
153
153
  │ └─ Skim agent (codebase overview)
@@ -168,11 +168,11 @@ Structured exploration findings with concrete code references:
168
168
 
169
169
  1. **Structure over browsing** - Every claim must cite file:line references
170
170
  2. **Parallel execution** - All explorers run simultaneously for speed
171
- <!-- learning:on -->
171
+ <!-- curation:on -->
172
172
  3. **Knowledge-informed** - Prior decisions guide where to look; NO pre-loaded feature knowledge in sub-agents (avoids confirmation bias)
173
- <!-- learning:off -->
173
+ <!-- curation:off -->
174
174
  3. **Knowledge-informed** - NO pre-loaded feature knowledge in sub-agents (avoids confirmation bias)
175
- <!-- learning:end -->
175
+ <!-- curation:end -->
176
176
  4. **User-driven depth** - Present findings, then offer drill-down into specific areas
177
177
 
178
178
  ## Error Handling