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
@@ -31,6 +31,8 @@ import { stripFlags } from '../../core/flags.js';
31
31
  import { stripDevflowTeammateModeFromJson } from '../../core/teammate-mode-cleanup.js';
32
32
  import { getPackageRoot, isContainedIn } from '../../core/paths.js';
33
33
  import { firstSymbolicLink } from '../../core/linked-path.js';
34
+ import { MANIFEST_LOCK_NAME, MANIFEST_LOCK_STALE_MARK } from '../../core/manifest-lock.js';
35
+ import { CURATION_TUNING_FILE_NAME, LEGACY_NAMES, MOVE_JOURNAL_SUFFIX, MOVE_LOCK_PREFIX, MOVE_LOCK_SUFFIX } from '../../core/project-paths.js';
34
36
  import { restampInstalledCommands } from '../../targets/claude-code/language-stamp.js';
35
37
  import { installCommandsAndAgents } from '../../targets/claude-code/installer.js';
36
38
  /**
@@ -489,8 +491,10 @@ export function userContentPaths(devflowDir) {
489
491
  const name = `${TRACKER_LEGACY_CONVENTIONS_FILE}.${id}.bak`;
490
492
  return { relPath: name, label: `${name} (previous issue tracker conventions)` };
491
493
  }),
492
- // learning.json — global curate agent tuning config
493
- { relPath: 'learning.json', label: 'learning.json' },
494
+ // The global curate agent tuning config, under both its names: the new name on an
495
+ // upgraded machine, and the old name, which a downgraded machine can still hold.
496
+ { relPath: CURATION_TUNING_FILE_NAME, label: CURATION_TUNING_FILE_NAME },
497
+ { relPath: LEGACY_NAMES.tuningFile, label: LEGACY_NAMES.tuningFile },
494
498
  // hud.json — user HUD enable/disable preference and display config
495
499
  { relPath: 'hud.json', label: 'hud.json (HUD configuration)' },
496
500
  ];
@@ -527,6 +531,54 @@ export async function enumerateUserDevFlowContent(devflowDir) {
527
531
  }
528
532
  return items;
529
533
  }
534
+ /** `text` as a regular-expression source, every character literal. */
535
+ function escapeRegExp(text) {
536
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
537
+ }
538
+ /** The name of a move lock or journal: the shared prefix, a 16-digit lowercase hex key, then `suffix`. */
539
+ function moveFileShape(suffix) {
540
+ return new RegExp(`^${escapeRegExp(MOVE_LOCK_PREFIX)}[0-9a-f]{16}${escapeRegExp(suffix)}$`);
541
+ }
542
+ /**
543
+ * The curation move's lock, one directory per ledger root, taken directly in the machine
544
+ * directory: `.curation-move-<16 lowercase hex>.lock`. The store builds the name from a
545
+ * hash of the real ledger root, so the names exist only on disk. The prefix and the
546
+ * suffix are the ones the path helpers hold (project-paths.ts).
547
+ */
548
+ const CURATION_MOVE_LOCK_SHAPE = moveFileShape(MOVE_LOCK_SUFFIX);
549
+ /**
550
+ * The curation move's journal, one regular file per ledger root, beside the lock:
551
+ * `.curation-move-<16 lowercase hex>.journal`, with the same key as the lock. It exists
552
+ * only while a set move is unfinished, and a crash leaves it behind. A file named
553
+ * `.curation-move.journal` inside a project's old ledger folder is no journal of the
554
+ * move and is not matched here.
555
+ */
556
+ const CURATION_MOVE_JOURNAL_SHAPE = moveFileShape(MOVE_JOURNAL_SUFFIX);
557
+ /**
558
+ * The manifest lock, a directory in the machine directory (manifest-lock.ts), and the
559
+ * directory a stale break moves aside: `manifest.json.lock` and
560
+ * `manifest.json.lock.stale-<digits>-<12 lowercase hex>`. A holder that dies in its
561
+ * critical section leaves the first, a break whose removal failed leaves the second.
562
+ */
563
+ const MANIFEST_LOCK_SHAPE = new RegExp(`^${escapeRegExp(MANIFEST_LOCK_NAME)}(?:${escapeRegExp(MANIFEST_LOCK_STALE_MARK)}[0-9]+-[0-9a-f]{12})?$`);
564
+ /** Whether `child` of `devflowDir` is a real directory (a link is not followed). */
565
+ async function isRealDirectory(devflowDir, child) {
566
+ try {
567
+ return (await fs.lstat(path.join(devflowDir, child))).isDirectory();
568
+ }
569
+ catch {
570
+ return false;
571
+ }
572
+ }
573
+ /** Whether `child` of `devflowDir` is a regular file (a link is not followed). */
574
+ async function isRegularFile(devflowDir, child) {
575
+ try {
576
+ return (await fs.lstat(path.join(devflowDir, child))).isFile();
577
+ }
578
+ catch {
579
+ return false;
580
+ }
581
+ }
530
582
  /**
531
583
  * Expand `installArtifactPaths` against real disk.
532
584
  *
@@ -553,8 +605,15 @@ export async function resolveInstallArtifactPaths(devflowDir) {
553
605
  }
554
606
  }
555
607
  for (const child of children) {
556
- if (child.startsWith(entry.relPath))
557
- resolved.push({ relPath: child, isDir: entry.isDir });
608
+ if (!child.startsWith(entry.relPath))
609
+ continue;
610
+ if (entry.shape !== undefined && !entry.shape.test(child))
611
+ continue;
612
+ if (entry.realDirOnly === true && !(await isRealDirectory(devflowDir, child)))
613
+ continue;
614
+ if (entry.realFileOnly === true && !(await isRegularFile(devflowDir, child)))
615
+ continue;
616
+ resolved.push({ relPath: child, isDir: entry.isDir });
558
617
  }
559
618
  }
560
619
  return resolved;
@@ -621,6 +680,18 @@ export function installArtifactPaths(devflowDir) {
621
680
  // A prefix, because the names exist only on disk. Content is a scrubbed copy
622
681
  // that was never placed, so it is machine state like the three above.
623
682
  { relPath: TRACKER_STAGED_PREFIX, isPrefix: true },
683
+ // The curation move's per-root lock directory. A crash between taking the lock and
684
+ // releasing it leaves one behind, and its name carries a hash that exists only on disk.
685
+ // Only a real directory of exactly that shape goes; the move's skip markers sit under
686
+ // logs/ and go with it.
687
+ { relPath: MOVE_LOCK_PREFIX, isPrefix: true, isDir: true, shape: CURATION_MOVE_LOCK_SHAPE, realDirOnly: true },
688
+ // The move's journal beside it. A crash between two renames of the ledger set leaves
689
+ // one behind. Only a regular file of exactly that shape goes.
690
+ { relPath: MOVE_LOCK_PREFIX, isPrefix: true, shape: CURATION_MOVE_JOURNAL_SHAPE, realFileOnly: true },
691
+ // The manifest lock, and the directory a stale break moved aside. A crash inside the
692
+ // critical section leaves the first; a failed cleanup leaves the second, whose name
693
+ // carries a pid and a random suffix. Only a real directory of exactly that shape goes.
694
+ { relPath: MANIFEST_LOCK_NAME, isPrefix: true, isDir: true, shape: MANIFEST_LOCK_SHAPE, realDirOnly: true },
624
695
  // per-project hook logs (logs/{project-slug}/) AND global logs — remove the
625
696
  // whole logs/ tree; covers proxy.log, debug logs, and any project-slug dirs.
626
697
  { relPath: 'logs', isDir: true },
@@ -644,7 +715,8 @@ export function installArtifactPaths(devflowDir) {
644
715
  * so an entry here is deleted even when the user answers "no" to the full wipe.
645
716
  * User-authored state (everything in `userContentPaths`: skill/rule shadows,
646
717
  * preference-profile.md, the tracker conventions with the legacy tracker.md and
647
- * its backups, learning.json, hud.json) is removed only by the confirmed full-dir rm.
718
+ * its backups, the global tuning file under both names, hud.json) is removed only by
719
+ * the confirmed full-dir rm.
648
720
  * agent-models.json is an INSTALL ARTIFACT (stale per-agent overrides silently
649
721
  * re-apply to renamed/deleted agents on reinstall — AC-P1-F4) and therefore
650
722
  * belongs in this list, not in enumerateUserDevFlowContent.
@@ -953,7 +1025,7 @@ export async function refreshRetainedCommandsAndAgents(opts) {
953
1025
  plugins: retained,
954
1026
  agentsMap: buildAssetMaps(retained).agentsMap,
955
1027
  claudeDir,
956
- learning: manifest.features.learning,
1028
+ curation: manifest.features.curation,
957
1029
  trackerServers: effectiveTrackerServers(manifest.features.tracker),
958
1030
  warn: (msg) => p.log.warn(msg),
959
1031
  });
@@ -1073,7 +1145,7 @@ export async function runSelectivePhaseForScope(opts) {
1073
1145
  * FULL PHASE (per scope): revert agent model overrides, remove all Devflow
1074
1146
  * assets from the scope, then perform scope-appropriate devflowDir cleanup.
1075
1147
  *
1076
- * Local scope: remove only install artifacts — project data (memory, learning,
1148
+ * Local scope: remove only install artifacts — project data (memory, curation,
1077
1149
  * docs, features) is always preserved under the git root.
1078
1150
  * User scope: interactive TTY with user-authored content → confirm before wiping
1079
1151
  * ~/.devflow/; non-interactive or no user content → artifacts-only.
@@ -1115,11 +1187,11 @@ export async function runFullPhaseForScope(opts) {
1115
1187
  await removeAllDevFlow(claudeDir, devflowScriptsDir, verbose, mayChange);
1116
1188
  if (scope === 'local') {
1117
1189
  // Local scope: devflowDir is gitRoot/.devflow/ which holds project data
1118
- // (memory, learning, features, docs, config.json). Never remove those —
1190
+ // (memory, curation, features, docs, config.json). Never remove those —
1119
1191
  // only remove install artifacts: scripts/ (done above) + manifest.json and
1120
1192
  // the other Devflow-generated artifacts (see removeDevFlowInstallArtifacts).
1121
1193
  await removeDevFlowInstallArtifacts(devflowDir, verbose, mayChange);
1122
- p.log.info('Local project data (memory, learning, features, docs) preserved');
1194
+ p.log.info('Local project data (memory, curation, features, docs) preserved');
1123
1195
  }
1124
1196
  else {
1125
1197
  // User scope (devflowDir = ~/.devflow/): offer full cleanup behind a confirm gate
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import { skillsCommand } from './cli/commands/skills.js';
11
11
  import { hudCommand } from './cli/commands/hud.js';
12
12
  import { flagsCommand } from './cli/commands/flags.js';
13
13
  import { knowledgeCommand } from './cli/commands/knowledge/index.js';
14
- import { learningCommand } from './cli/commands/learning.js';
14
+ import { curationCommand } from './cli/commands/curation.js';
15
15
  import { rulesCommand } from './cli/commands/rules.js';
16
16
  import { debugCommand } from './cli/commands/debug.js';
17
17
  import { securityCommand } from './cli/commands/security.js';
@@ -42,7 +42,7 @@ program.addCommand(skillsCommand);
42
42
  program.addCommand(hudCommand);
43
43
  program.addCommand(flagsCommand);
44
44
  program.addCommand(knowledgeCommand);
45
- program.addCommand(learningCommand);
45
+ program.addCommand(curationCommand);
46
46
  program.addCommand(rulesCommand);
47
47
  program.addCommand(debugCommand);
48
48
  program.addCommand(securityCommand);
@@ -3,7 +3,7 @@ description: Proactive bug finding with static and semantic analysis — hunts r
3
3
  ---
4
4
  # Bug Analysis Command
5
5
 
6
- Run a proactive bug analysis on the current branch by combining static analysis tools with parallel semantic analyzers, then synthesizing results into an actionable bug report. Supports incremental analysis, timestamped report directories, and `/resolve` compatibility.
6
+ Run a proactive bug analysis on the current branch by combining static analysis tools with parallel semantic analyzers, then synthesizing results into an actionable bug report.
7
7
 
8
8
  ## Usage
9
9
 
@@ -55,16 +55,16 @@ Agent(subagent_type="Git", run_in_background=false):
55
55
  PR_TEST_PLAN_BLOCK: {PR_TEST_PLAN_BLOCK verbatim, or (none)}
56
56
  APPLY_CONVENTIONS: {APPLY_CONVENTIONS}
57
57
  Validate branch, commit if needed, push, create PR if needed.
58
- Return: branch, base_branch, branch-slug, PR#"
58
+ Return: branch, base_branch, base_ref, branch-slug, PR#"
59
59
  ```
60
60
 
61
- **Extract from response:** `branch`, `base_branch`, `branch_slug`, `pr_number`.
61
+ **Extract from response:** `branch`, `base_branch`, `base_ref`, `branch_slug`, `pr_number`. Stop with cause and fix when the Base line reads (rejected) or `branch_slug` does not match the slug pattern above; name the Git agent's fix, never your own, and for the slug the allowed characters, never its value.
62
62
 
63
63
  **Fetch PR body** (after extracting `pr_number`):
64
64
  ```bash
65
65
  PR_DESCRIPTION=$(gh pr view {pr_number} --json body --jq '.body' 2>/dev/null || echo "(none)")
66
66
  ```
67
- If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
67
+ If `pr_number` is absent, set `PR_DESCRIPTION` to `(none)`.
68
68
 
69
69
  ### Phase 2: Static Analysis
70
70
 
@@ -75,14 +75,15 @@ If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
75
75
  **Produces:** DIFF_RANGE, ANALYSIS_DIR
76
76
  **Requires:** BRANCH_INFO
77
77
 
78
- 1. Check `{worktree}/.devflow/docs/bug-analysis/{branch-slug}/.last-analysis-head`:
78
+ 1. Check `{gd}/devflow/bug-analysis/{branch-slug}/.last-analysis-head`, `{gd}` being what `git -C "{worktree}" rev-parse --absolute-git-dir` prints:
79
79
  - **If exists AND `--full` NOT set:**
80
- - Read the SHA from the file
81
- - Verify reachable: `git cat-file -t {sha}` — if exit code non-zero (rebase invalidated SHA), fall through to full
80
+ - The stored analysis head is checked as a commit ID: use it only when it is exactly 40 or 64 lowercase hex, else take the full-range path
81
+ - Verify reachable: `git -C "{worktree}" cat-file -t {sha}` must print `commit`; if not (rebase invalidated SHA), take the full-range path
82
82
  - If SHA == current HEAD → "No new commits since last analysis. Use --full for a full re-analysis." Stop.
83
83
  - Set `DIFF_RANGE` to `{sha}...HEAD`
84
- - **If not exists, unreachable SHA, or `--full`:**
85
- - Set `DIFF_RANGE` to `{base_branch}...HEAD`
84
+ - **Full-range path (no marker, or `--full`):**
85
+ - Stop as for a (rejected) Base line unless `base_ref` is 40 or 64 hex.
86
+ - Set `DIFF_RANGE` to `{base_ref}...HEAD`
86
87
  2. Generate timestamp: `YYYY-MM-DD_HHMM`. If directory already exists (same-minute collision), append seconds (`YYYY-MM-DD_HHMMSS`).
87
88
  3. Create timestamped analysis directory: `mkdir -p "{worktree}/.devflow/docs/bug-analysis/{branch-slug}/{timestamp}/"`
88
89
  4. Set `ANALYSIS_DIR` to that path.
@@ -95,7 +96,7 @@ If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
95
96
  CHANGED_FILES=$(git diff --name-only {DIFF_RANGE})
96
97
  ```
97
98
 
98
- Store result as `CHANGED_FILES` — used throughout Steps 2d and Phase 4 to avoid repeated git invocations and ensure consistency.
99
+ Store as `CHANGED_FILES`; Steps 2d and Phase 4 read it. A non-zero diff exit stops.
99
100
 
100
101
  If output is empty → "No changes to analyze." Stop.
101
102
 
@@ -124,7 +125,7 @@ Skip if `--no-static` flag provided or all tools unavailable.
124
125
 
125
126
  Run available tools on the changed files (from `CHANGED_FILES` computed in Step 2b).
126
127
 
127
- **Semgrep and Snyk run in parallel** — launch both in the background, then wait for both before proceeding to CodeQL. CodeQL is conditional and sequential (it needs Semgrep/Snyk results to decide whether to run).
128
+ **Semgrep and Snyk run in parallel** — launch both in the background, then wait for both before proceeding to CodeQL. CodeQL is conditional and sequential.
128
129
 
129
130
  **Semgrep** (if available):
130
131
  ```bash
@@ -135,11 +136,9 @@ Parse SARIF output → extract findings.
135
136
 
136
137
  **Snyk Code** (if available):
137
138
  ```bash
138
- # Run a single project-level scan; filter SARIF results programmatically to CHANGED_FILES before LLM processing.
139
- # Per-file invocation via xargs would invoke snyk O(n) times and --file is for dependency scanning, not source code.
139
+ # One project-level scan (a per-file xargs run would invoke snyk O(n) times; --file is for dependency scans).
140
140
  SNYK_SARIF=$(timeout 300 snyk code test --sarif 2>/dev/null)
141
- # Programmatic filter: keep only results whose file path appears in CHANGED_FILES.
142
- # This is defense-in-depth — filtering at the data layer before the LLM agent sees any output.
141
+ # Programmatic filter: keep only results whose file path is in CHANGED_FILES — defense-in-depth, before the LLM agent sees any output.
143
142
  SNYK_FILTERED=$(echo "$SNYK_SARIF" | jq --argjson files "$(echo "$CHANGED_FILES" | jq -R . | jq -s .)" \
144
143
  '.runs[].results |= map(select(.locations[].physicalLocation.artifactLocation.uri as $uri | $files | index($uri) != null))' \
145
144
  2>/dev/null || echo "$SNYK_SARIF")
@@ -165,7 +164,7 @@ trap - EXIT INT TERM
165
164
  ```
166
165
  Parse `CODEQL_SARIF` → extract findings. If database creation fails, `CODEQL_EXIT` is non-zero — skip CodeQL findings and note it. The `trap` guarantees cleanup on SIGTERM/SIGINT/EXIT so orphaned temp directories do not accumulate across interrupted sessions.
167
166
 
168
- **Normalize** all findings to unified table, cap at top 50 by severity. Truncate each Description entry to 200 characters maximum to bound the serialized size of `STATIC_FINDINGS`:
167
+ **Normalize** all findings to unified table, cap at top 50 by severity. Truncate each Description entry to 200 characters maximum:
169
168
 
170
169
  | Tool | File:Line | CWE | Severity | Title | Description |
171
170
  |------|-----------|-----|----------|-------|-------------|
@@ -187,13 +186,13 @@ If no tool produced findings: set `STATIC_FINDINGS` to `(none)`.
187
186
  node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
188
187
  ```
189
188
 
190
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off GATE_LEVEL=full` instead.
189
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on CURATION=on KNOWLEDGE=off GATE_LEVEL=full` instead.
191
190
 
192
191
  The accepted line is the only source of these values.
193
192
 
194
193
  ### Load DECISIONS_CONTEXT
195
194
 
196
- When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
195
+ When the settings line says `CURATION=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
197
196
 
198
197
  The decisions ledger belongs to the repository, not to one checkout: a linked worktree reads the main worktree's copy, a subdirectory session the repository root's. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
199
198
 
@@ -207,11 +206,11 @@ Line 1 is the checkout's toplevel, line 2 the common git directory. A git older
207
206
  2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
208
207
  3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
209
208
 
210
- This is the rule the learning hooks apply, so you read the index the Curate agent writes.
209
+ This is the rule the curation hooks apply, so you read the index the Curate agent writes.
211
210
 
212
211
  **Step 1 — Read the pre-rendered index:**
213
212
 
214
- Attempt to read `{ledger}/.devflow/learning/index.md`.
213
+ Attempt to read `{ledger}/.devflow/curation/index.md`.
215
214
 
216
215
  - If the file exists and contains non-empty content: use that content as `DECISIONS_CONTEXT`.
217
216
  - If the file is absent or empty: set `DECISIONS_CONTEXT` to `(none)`.
@@ -355,13 +354,14 @@ Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
355
354
 
356
355
  **Requires:** BRANCH_INFO, ANALYSIS_DIR
357
356
 
358
- 1. Write current HEAD SHA to `{worktree}/.devflow/docs/bug-analysis/{branch-slug}/.last-analysis-head`
357
+ 1. In one Bash call run `M="$(git -C "{worktree}" rev-parse --absolute-git-dir)/devflow/bug-analysis/{branch-slug}" && mkdir -p "$M" && git -C "{worktree}" rev-parse HEAD > "$M/.last-analysis-head"`. A failed or refused write never stops the run and is not retried another way.
359
358
  2. Report to user:
360
359
 
361
360
  ```
362
361
  ## Bug Analysis Complete
363
362
 
364
363
  **Branch**: {branch} -> {base_branch}
364
+ {any base `TRACEABILITY: DEGRADED` line from Phase 1, as returned}
365
365
  **Analysis**: {ANALYSIS_DIR}
366
366
 
367
367
  ### Risk Assessment: {risk_level}
@@ -397,11 +397,7 @@ Run `/resolve` to process and fix these findings.
397
397
  │ └─ Git agent (ensure-pr-ready)
398
398
  │
399
399
  ├─ Phase 2: Static Analysis
400
- │ ├─ Step 2a: Incremental detection + timestamp setup
401
- │ ├─ Step 2b: Check changed files (stop if none)
402
- │ ├─ Step 2c: Tool availability check
403
- │ └─ Step 2d: Tiered static analysis (semgrep → snyk → codeql)
404
- │ Write static-findings.md
400
+ │ └─ 2a incremental range, 2b changed files (stop if none), 2c tools, 2d semgrep → snyk → codeql
405
401
  │
406
402
  ├─ Phase 3: Context Loading
407
403
  │ ├─ index.md (pre-rendered) → DECISIONS_CONTEXT
@@ -412,10 +408,7 @@ Run `/resolve` to process and fix these findings.
412
408
  │ └─ Detect active focuses (security + functional always; integration + usability conditional)
413
409
  │
414
410
  ├─ Phase 5: Bug Analysis (PARALLEL)
415
- │ ├─ Debug agent: security (+ STATIC_FINDINGS)
416
- │ ├─ Debug agent: functional
417
- │ ├─ Debug agent: integration (conditional)
418
- │ └─ Debug agent: usability (conditional)
411
+ │ └─ Debug agent per active focus (security also reads STATIC_FINDINGS)
419
412
  │
420
413
  ├─ Phase 6: Synthesis
421
414
  │ └─ Synthesize agent (mode: bug-analysis)
@@ -431,16 +424,12 @@ Run `/resolve` to process and fix these findings.
431
424
  |------|----------|
432
425
  | No new commits since last analysis | Stop: "No new commits since last analysis. Use --full for a full re-analysis." |
433
426
  | Rebase invalidates `.last-analysis-head` SHA | `git cat-file -t` check fails → fallback to full diff |
434
- | Zero changed files in DIFF_RANGE | Stop: "No changes to analyze." |
427
+ | Zero changed files in DIFF_RANGE with exit 0 | Stop: "No changes to analyze." |
435
428
  | Same-minute analysis collision | `mkdir` with seconds suffix (`YYYY-MM-DD_HHMMSS`) |
436
429
  | All static tools unavailable | Warn, proceed with semantic-only analysis |
437
430
  | `--no-static` flag | Skip Phase 2c and 2d entirely; `STATIC_FINDINGS=(none)` |
438
431
  | `--full` flag | Bypass incremental detection (Step 2a), run full diff from base |
439
432
  | CodeQL database creation fails | Skip CodeQL, note in output, continue with other tools |
440
- | Static tool produces no findings | `STATIC_FINDINGS=(none)` — normal, proceed with semantic analysis |
441
- | No plan artifact found | `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)` — proceed without acceptance criteria |
442
- | `integration` focus skipped | Not spawned when only 1 directory changed |
443
- | `usability` focus skipped | Not spawned when no UI files changed |
444
433
 
445
434
  ## Resolve Compatibility
446
435
 
@@ -3,7 +3,7 @@ description: Comprehensive branch review using specialized sub-agents for PR rea
3
3
  ---
4
4
  # Code Review Command
5
5
 
6
- Run a comprehensive code review of the current branch by spawning parallel review agents, then synthesizing results into PR comments. Supports incremental reviews, timestamped report directories, and multi-worktree auto-discovery.
6
+ Run a comprehensive code review of the current branch by spawning parallel review agents, then synthesizing results into PR comments.
7
7
 
8
8
  ## Usage
9
9
 
@@ -27,8 +27,8 @@ Run a comprehensive code review of the current branch by spawning parallel revie
27
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
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
29
29
  2. **If `--path` flag provided:** use only that worktree, skip discovery
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.
31
- 3. **If only 1 reviewable worktree**: proceed as single-worktree flow — zero behavior change
30
+ **`--path` validation**: the path must be a directory in `git worktree list` output; if not, report an error and stop.
31
+ 3. **If only 1 reviewable worktree**: proceed as single-worktree flow
32
32
  4. **If multiple reviewable worktrees:** report "Found N worktrees with reviewable branches: {list with paths and branches}" and proceed with multi-worktree flow
33
33
 
34
34
  #### Step 0b: Resolve the compliance lens and gate level
@@ -41,7 +41,7 @@ Run a comprehensive code review of the current branch by spawning parallel revie
41
41
  node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
42
42
  ```
43
43
 
44
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off GATE_LEVEL=full` instead.
44
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on CURATION=on KNOWLEDGE=off GATE_LEVEL=full` instead.
45
45
 
46
46
  The accepted line is the only source of these values.
47
47
 
@@ -97,20 +97,20 @@ PR_DESCRIPTION_GUIDANCE: {pr_description_guidance}
97
97
  PR_TEST_PLAN_BLOCK: {PR_TEST_PLAN_BLOCK verbatim, or (none)}
98
98
  APPLY_CONVENTIONS: {APPLY_CONVENTIONS}
99
99
  Validate branch, commit if needed, push, create PR if needed.
100
- Return: branch, base_branch, branch-slug, PR#"
100
+ Return: branch, base_branch, base_ref, branch-slug, PR#"
101
101
  ```
102
102
 
103
103
  In multi-worktree mode, spawn all pre-flight agents **in a single message**.
104
104
 
105
105
  **If BLOCKED:** In single-worktree mode, stop and report. In multi-worktree mode, report the failure but continue with other worktrees.
106
106
 
107
- **Extract from response:** `branch`, `base_branch`, `branch_slug`, `pr_number` per worktree.
107
+ **Extract from response:** `branch`, `base_branch`, `base_ref`, `branch_slug`, `pr_number` per worktree. Stop with cause and fix when the Base line reads (rejected) or `branch_slug` does not match the slug pattern above; name the Git agent's fix, never your own, and for the slug the allowed characters, never its value. In multi-worktree mode this blocks only that worktree.
108
108
 
109
109
  **Fetch PR body** (after extracting `pr_number`):
110
110
  ```bash
111
111
  PR_DESCRIPTION=$(gh pr view {pr_number} --json body --jq '.body' 2>/dev/null || echo "(none)")
112
112
  ```
113
- If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
113
+ If `pr_number` is absent, set `PR_DESCRIPTION` to `(none)`.
114
114
 
115
115
  #### Step 0d: Incremental Detection & Timestamp Setup
116
116
 
@@ -120,15 +120,16 @@ If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
120
120
  For each worktree:
121
121
 
122
122
  1. Generate timestamp: `YYYY-MM-DD_HHMM`. If directory already exists (same-minute collision), append seconds (`YYYY-MM-DD_HHMMSS`).
123
- 2. Create timestamped review directory: `mkdir -p {worktree}/.devflow/docs/reviews/{branch-slug}/{timestamp}/`
124
- 3. Check if `{worktree}/.devflow/docs/reviews/{branch-slug}/.last-review-head` exists:
123
+ 2. Check if `{gd}/devflow/reviews/{branch-slug}/.last-review-head` exists, `{gd}` being what `git -C "{worktree}" rev-parse --absolute-git-dir` prints:
125
124
  - **If yes AND `--full` NOT set:**
126
- - Read the SHA from the file
127
- - Verify reachable: `git -C {worktree} cat-file -t {sha}` (handles rebases — if unreachable, fallback to full)
125
+ - The stored review head is checked as a commit ID: use it only when it is exactly 40 or 64 lowercase hex, else take the full-range path
126
+ - Verify reachable: `git -C "{worktree}" cat-file -t {sha}` must print `commit` (else take the full-range path)
128
127
  - Check if SHA == current HEAD → if so, skip review: "No new commits since last review. Use --full for a full re-review."
129
128
  - Set `DIFF_RANGE` to `{last-review-sha}...HEAD`
130
- - **If no (first review), or `--full`:**
131
- - Set `DIFF_RANGE` to `{base_branch}...HEAD`
129
+ - **Full-range path (no marker, or `--full`):**
130
+ - Stop as for a (rejected) Base line unless `base_ref` is 40 or 64 hex.
131
+ - Set `DIFF_RANGE` to `{base_ref}...HEAD`
132
+ 3. Create timestamped review directory: `mkdir -p {worktree}/.devflow/docs/reviews/{branch-slug}/{timestamp}/`
132
133
 
133
134
  #### Step 0e-i: Load Prior Resolution and Count Cycles
134
135
 
@@ -199,10 +200,11 @@ List the changed paths into `{REVIEW_DIR}/paths.txt` and print only the line cou
199
200
 
200
201
  ```bash
201
202
  git -C "{WORKTREE_PATH}" diff --name-only --no-renames {DIFF_RANGE} > "{REVIEW_DIR}/paths.txt"
203
+ echo "exit=$?"
202
204
  wc -l < "{REVIEW_DIR}/paths.txt"
203
205
  ```
204
206
 
205
- Read `paths.txt` once when the count is about 40 or fewer, and in ranges of about 40 lines (Read with offset and limit) when it is more.
207
+ The `exit=` line is the diff's. A non-zero diff exit stops. Read `paths.txt` once when the count is about 40 or fewer, and in ranges of about 40 lines (Read with offset and limit) when it is more.
206
208
 
207
209
  Set `DIFF_CLASS` by these rules, in order; the first that decides wins:
208
210
 
@@ -276,7 +278,7 @@ Write `diff.patch` once per worktree, by redirect:
276
278
  git -C "{WORKTREE_PATH}" diff --no-color --no-ext-diff --no-textconv {DIFF_RANGE} > "{REVIEW_DIR}/diff.patch"
277
279
  ```
278
280
 
279
- It holds the whole diff of `DIFF_RANGE`. On a `code` diff, add one patch for each language focus in REVIEW_FOCUS_LIST, by the same command with quoted pathspecs from this table:
281
+ It holds the whole diff of `DIFF_RANGE`. A non-zero exit of any patch write stops. On a `code` diff, add one patch for each language focus in REVIEW_FOCUS_LIST, by the same command with quoted pathspecs from this table:
280
282
 
281
283
  ```bash
282
284
  git -C "{WORKTREE_PATH}" diff --no-color --no-ext-diff --no-textconv {DIFF_RANGE} -- '<pathspec>' ... > "{REVIEW_DIR}/diff-{focus}.patch"
@@ -300,7 +302,7 @@ The core, database, dependencies, documentation and compliance focuses, and ever
300
302
 
301
303
  ### Load DECISIONS_CONTEXT
302
304
 
303
- When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
305
+ When the settings line says `CURATION=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
304
306
 
305
307
  The decisions ledger belongs to the repository, not to one checkout: a linked worktree reads the main worktree's copy, a subdirectory session the repository root's. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
306
308
 
@@ -314,11 +316,11 @@ Line 1 is the checkout's toplevel, line 2 the common git directory. A git older
314
316
  2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
315
317
  3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
316
318
 
317
- This is the rule the learning hooks apply, so you read the index the Curate agent writes.
319
+ This is the rule the curation hooks apply, so you read the index the Curate agent writes.
318
320
 
319
321
  **Step 1 — Read the pre-rendered index:**
320
322
 
321
- Attempt to read `{ledger}/.devflow/learning/index.md`.
323
+ Attempt to read `{ledger}/.devflow/curation/index.md`.
322
324
 
323
325
  - If the file exists and contains non-empty content: use that content as `DECISIONS_CONTEXT`.
324
326
  - If the file is absent or empty: set `DECISIONS_CONTEXT` to `(none)`.
@@ -425,7 +427,7 @@ Spawn Review agents **in a single message**, one per focus in REVIEW_FOCUS_LIST
425
427
  | documentation | conditional | devflow:documentation | diff.patch |
426
428
  | compliance | diff-driven | devflow:compliance | diff.patch |
427
429
 
428
- Review agent count: at `full`, `code` runs 8 core +1-12; `standard` runs up to 2 fewer; lean `code` runs 3-4; a reduced class or a mix runs 2-6.
430
+ Review agent count: `code` runs 8 core +1-12 at `full`, up to 2 fewer at `standard`, 3-4 at lean; a reduced class or a mix runs 2-6.
429
431
 
430
432
  **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:
431
433
 
@@ -451,7 +453,7 @@ Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE — feature-specifi
451
453
  IMPORTANT: Write report to {worktree_path}/.devflow/docs/reviews/{branch-slug}/{timestamp}/{focus}.md using Write tool"
452
454
  ```
453
455
 
454
- In multi-worktree mode, process worktrees **sequentially**: complete Phases 1-4 for one worktree before starting the next, so Review spawns never pile up.
456
+ In multi-worktree mode, process worktrees **sequentially**: Phases 1-4 for one worktree, then the next.
455
457
 
456
458
  ### Phase 3: Synthesis → Review Comment (Sequential)
457
459
 
@@ -494,34 +496,29 @@ Posts the consolidated review-summary comment (marker-deduped by the operation o
494
496
  **Requires:** BRANCH_INFO, REVIEW_DIR
495
497
 
496
498
  Per worktree, after successful completion:
497
- 1. Write current HEAD SHA to `{worktree_path}/.devflow/docs/reviews/{branch-slug}/.last-review-head`
499
+ 1. In one Bash call run `M="$(git -C "{worktree_path}" rev-parse --absolute-git-dir)/devflow/reviews/{branch-slug}" && mkdir -p "$M" && git -C "{worktree_path}" rev-parse HEAD > "$M/.last-review-head"`. A failed or refused write never stops the run and is not retried another way.
498
500
  2. Display results from all agents:
499
501
  - Merge recommendation (from Synthesize agent)
500
502
  - Issue counts by category (🔴 blocking / ⚠️ should-fix / ℹ️ pre-existing)
501
503
  - Review comment status: POSTED / POSTED+TRUNCATED (body exceeded 60k after redaction) / SKIPPED (D7 dedup) / DEGRADED (from Git)
502
504
  - Publication status: FULL (private repo) | FULL (config override) | STUB (public repository) | OFF (publication disabled by config) | STUB (visibility undeterminable) | STUB (evidence policy) (from Git agent)
505
+ - Any base `TRACEABILITY: DEGRADED` line from Step 0c, as returned
503
506
  - Artifact paths
504
507
 
505
508
  ## Architecture
506
509
 
507
510
  ```
508
- /code-review (orchestrator - spawns agents only)
511
+ /code-review
509
512
  │
510
- ├─ Phase 0: Worktree Discovery & Pre-flight
511
- │ ├─ Step 0a: git worktree list → filter reviewable
513
+ ├─ Phase 0: Worktree Discovery & Pre-flight, Steps 0a-0f (Git agent per worktree, parallel)
512
514
  │ ├─ Step 0b: Resolve the compliance lens and gate level
513
- │ ├─ Step 0c: Git agent (ensure-pr-ready) per worktree [parallel]
514
- │ ├─ Step 0d: Incremental detection + timestamp setup per worktree
515
- │ ├─ Step 0e-i: Load prior resolution-summary.md
516
- │ ├─ Step 0e-ii: Convergence assessment (warn only)
517
- │ └─ Step 0f: Resolve REVIEW_PUBLICATION per worktree (publication gate)
518
515
  │
519
- ├─ Per worktree (SEQUENTIAL — one worktree at a time):
516
+ ├─ Per worktree (SEQUENTIAL):
520
517
  │ │
521
518
  │ ├─ Phase 1: Analyze changed files
522
519
  │ │ ├─ Classify the diff (paths.txt → DIFF_CLASS)
523
- │ │ ├─ Choose the focuses (code: file types + the language stamp; all: the gate level)
524
- │ │ └─ Write diff.patch and diff-{focus}.patch, by redirect
520
+ │ │ ├─ Choose the focuses
521
+ │ │ └─ Write diff.patch and diff-{focus}.patch
525
522
  │ │
526
523
  │ ├─ Phase 2: Reviews (PARALLEL within worktree), one per REVIEW_FOCUS_LIST focus
527
524
  │ │ ├─ code: core (diff.patch), language (diff-{focus}.patch), conditional
@@ -539,7 +536,7 @@ Per worktree, after successful completion:
539
536
  | Case | Handling |
540
537
  |------|----------|
541
538
  | `--full` in multi-worktree mode | Applies to all worktrees (global modifier). It sets the diff range, never the gate level |
542
- | Review comment already posted | Git agent matches its own marker on the `{cycle, timestamp}` pair → skip silently (D7); a re-review in the same cycle (different timestamp) posts its own comment. The marker's format belongs to the operation; this caller passes the pair and never restates the literal |
539
+ | Review comment already posted | Git agent matches its own marker on the `{cycle, timestamp}` pair → skip silently (D7); a re-review in the same cycle (different timestamp) posts its own comment. The marker's format belongs to the operation |
543
540
  | Docs-only diff | Its Step 1.1 set, narrowed by level |
544
541
  | Tests-only diff | Its Step 1.1 set, narrowed by level |
545
542
  | Lockfile-only diff | Its Step 1.1 set, narrowed by level |
@@ -548,7 +545,6 @@ Per worktree, after successful completion:
548
545
  | A file moved across classes | `--no-renames` lists both paths, so a code path makes the diff `code` |
549
546
  | Path listing above about 40 lines | Read in ranges (Step 1.1) |
550
547
  | Language files changed, plugin not installed | The focus is absent from the stamped line: not spawned |
551
- | Public repo (`auto` mode) | Git agent probes visibility, posts counts-only STUB comment; full report stays local |
552
548
  | `reviewPublication: off` | Git agent skips posting the review comment (OFF) — except that, only when `EVIDENCE_POLICY` is `required`, Step 0f passes `stub` and a counts-only STUB comment posts |
553
549
 
554
550
  ## Backwards Compatibility
@@ -557,8 +553,8 @@ Per worktree, after successful completion:
557
553
 
558
554
  ## Principles
559
555
 
560
- 1. **Orchestration only** - Command spawns agents, doesn't do git/review work itself. The exceptions are the read-only Phase 0 probes (`git worktree list`, `git -C ... branch --show-current`, `git -C ... cat-file -t`), the bounded Phase 1 path listing (a redirect into `paths.txt`, a `wc -l` count and ranged reads) and the redirect-only patch writes (`diff.patch`, `diff-{focus}.patch`). Redirect-only writes print nothing, so no diff content reaches this thread
556
+ 1. **Orchestration only** - Command spawns agents, doesn't do git/review work itself. The exceptions are the read-only Phase 0 probes (`git worktree list`, `git -C ... branch --show-current`, `git -C ... cat-file -t`, `git -C ... rev-parse`), the bounded Phase 1 path listing and the writes (`diff.patch`, `diff-{focus}.patch`, the marker and its `mkdir -p`). These writes print nothing
561
557
  2. **Parallel, not background** - Multiple agents in one message, but `run_in_background=false` so phases complete before proceeding
562
- 3. **Git agent for git work** - All git operations go through Git agent, except the exceptions named in Principle 1; redirect-only writes print nothing
558
+ 3. **Git agent for git work** - All git operations go through Git agent, except the exceptions named in Principle 1; its writes print nothing
563
559
  4. **Honest reporting** - Display agent outputs directly
564
560
  5. **Incremental by default** - Only review new changes unless `--full` specified
@@ -38,7 +38,7 @@ $ARGUMENTS
38
38
  node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
39
39
  ```
40
40
 
41
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> LEARNING=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on LEARNING=on KNOWLEDGE=off GATE_LEVEL=full` instead.
41
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on CURATION=on KNOWLEDGE=off GATE_LEVEL=full` instead.
42
42
 
43
43
  The accepted line is the only source of these values.
44
44
 
@@ -52,7 +52,7 @@ Phase 7 passes `COMPLIANCE_DIRECTIVE` to the Code agent that implements the fix,
52
52
 
53
53
  ### Load DECISIONS_CONTEXT
54
54
 
55
- When the settings line says `LEARNING=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
55
+ When the settings line says `CURATION=off`, set `DECISIONS_CONTEXT` to `(none)` and skip this step, locating no ledger and reading no index.
56
56
 
57
57
  The decisions ledger belongs to the repository, not to one checkout: a linked worktree reads the main worktree's copy, a subdirectory session the repository root's. Locate it with ONE git call, run from the start directory — `WORKTREE_PATH` if provided, otherwise cwd (`devflow:worktree-support`):
58
58
 
@@ -66,11 +66,11 @@ Line 1 is the checkout's toplevel, line 2 the common git directory. A git older
66
66
  2. **The toplevel** — line 1, or on an older git the line after the echoed flag.
67
67
  3. **The start directory itself** — when the command failed or printed no absolute toplevel (outside a git repository).
68
68
 
69
- This is the rule the learning hooks apply, so you read the index the Curate agent writes.
69
+ This is the rule the curation hooks apply, so you read the index the Curate agent writes.
70
70
 
71
71
  **Step 1 — Read the pre-rendered index:**
72
72
 
73
- Attempt to read `{ledger}/.devflow/learning/index.md`.
73
+ Attempt to read `{ledger}/.devflow/curation/index.md`.
74
74
 
75
75
  - If the file exists and contains non-empty content: use that content as `DECISIONS_CONTEXT`.
76
76
  - If the file is absent or empty: set `DECISIONS_CONTEXT` to `(none)`.