@karajan-family/code 4.21.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 (746) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +98 -0
  3. package/bin/karajan-mcp.js +2 -0
  4. package/bin/kj-rag-mcp.js +75 -0
  5. package/bin/kj-tail +361 -0
  6. package/bin/kj.js +2 -0
  7. package/docs/README.es.md +80 -0
  8. package/docs/karajan-code-logo-small.png +0 -0
  9. package/library/logical-clocks-lamport.md +40 -0
  10. package/package.json +140 -0
  11. package/packages/ai-trash/README.md +37 -0
  12. package/packages/ai-trash/bin/kj-trash.js +10 -0
  13. package/packages/ai-trash/package.json +43 -0
  14. package/packages/ai-trash/src/cli.js +200 -0
  15. package/packages/ai-trash/src/destructive-parser.js +74 -0
  16. package/packages/ai-trash/src/git-snapshot.js +128 -0
  17. package/packages/ai-trash/src/hook.js +137 -0
  18. package/packages/ai-trash/src/index.js +14 -0
  19. package/packages/ai-trash/src/install.js +73 -0
  20. package/packages/ai-trash/src/logger.js +60 -0
  21. package/packages/ai-trash/src/manifest.js +91 -0
  22. package/packages/ai-trash/src/permissions.js +26 -0
  23. package/packages/ai-trash/src/snapshot.js +66 -0
  24. package/packages/ai-trash/src/ulid.js +66 -0
  25. package/packages/hu-board/README.md +69 -0
  26. package/packages/hu-board/package.json +28 -0
  27. package/packages/hu-board/public/app.js +259 -0
  28. package/packages/hu-board/public/index.html +77 -0
  29. package/packages/hu-board/public/pipeline.css +132 -0
  30. package/packages/hu-board/public/pipeline.html +48 -0
  31. package/packages/hu-board/public/pipeline.js +200 -0
  32. package/packages/hu-board/public/rag.html +21 -0
  33. package/packages/hu-board/public/rag.js +39 -0
  34. package/packages/hu-board/public/styles.css +973 -0
  35. package/packages/hu-board/public/utils/api.js +139 -0
  36. package/packages/hu-board/public/utils/board-view.js +473 -0
  37. package/packages/hu-board/public/utils/command-launcher.js +213 -0
  38. package/packages/hu-board/public/utils/config-editor.js +181 -0
  39. package/packages/hu-board/public/utils/dashboard-view.js +103 -0
  40. package/packages/hu-board/public/utils/formatters.js +300 -0
  41. package/packages/hu-board/public/utils/graph-view.js +216 -0
  42. package/packages/hu-board/public/utils/hu-actions.js +224 -0
  43. package/packages/hu-board/public/utils/init-listeners.js +180 -0
  44. package/packages/hu-board/public/utils/log-panel.js +222 -0
  45. package/packages/hu-board/public/utils/modals.js +309 -0
  46. package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
  47. package/packages/hu-board/public/utils/preflight-view.js +89 -0
  48. package/packages/hu-board/public/utils/project-actions.js +143 -0
  49. package/packages/hu-board/public/utils/project-picker-view.js +139 -0
  50. package/packages/hu-board/public/utils/run-launcher.js +169 -0
  51. package/packages/hu-board/public/utils/server-push.js +252 -0
  52. package/packages/hu-board/public/utils/sessions-view.js +199 -0
  53. package/packages/hu-board/public/utils/story-detail-view.js +324 -0
  54. package/packages/hu-board/public/utils/story-edit-form.js +271 -0
  55. package/packages/hu-board/public/wiki.html +32 -0
  56. package/packages/hu-board/public/wiki.js +53 -0
  57. package/packages/hu-board/src/auth.js +92 -0
  58. package/packages/hu-board/src/boot-info.js +16 -0
  59. package/packages/hu-board/src/cleanup-zombies.js +145 -0
  60. package/packages/hu-board/src/command-runner.js +133 -0
  61. package/packages/hu-board/src/config-yaml.js +531 -0
  62. package/packages/hu-board/src/db.js +963 -0
  63. package/packages/hu-board/src/ephemeral-cleaner.js +196 -0
  64. package/packages/hu-board/src/event-bus.js +58 -0
  65. package/packages/hu-board/src/format.js +207 -0
  66. package/packages/hu-board/src/hu-zombie-reaper.js +103 -0
  67. package/packages/hu-board/src/journal-parser.js +180 -0
  68. package/packages/hu-board/src/plan-mutations.js +424 -0
  69. package/packages/hu-board/src/preflight.js +286 -0
  70. package/packages/hu-board/src/routes/api.js +1374 -0
  71. package/packages/hu-board/src/routes/pipeline.js +45 -0
  72. package/packages/hu-board/src/routes/rag.js +42 -0
  73. package/packages/hu-board/src/routes/wiki.js +43 -0
  74. package/packages/hu-board/src/run-tracker.js +121 -0
  75. package/packages/hu-board/src/server.js +438 -0
  76. package/packages/hu-board/src/sync.js +732 -0
  77. package/packages/hu-board/src/token-store.js +56 -0
  78. package/packages/hu-board/src/zombie-reaper.js +206 -0
  79. package/scripts/ai-attribution-guard.yml +84 -0
  80. package/scripts/audit-test-diet.mjs +151 -0
  81. package/scripts/build-sea.mjs +150 -0
  82. package/scripts/diet-baseline.mjs +170 -0
  83. package/scripts/dual-publish.mjs +67 -0
  84. package/scripts/esbuild-sea.config.mjs +273 -0
  85. package/scripts/git-hooks/post-merge +18 -0
  86. package/scripts/install-binary.ps1 +174 -0
  87. package/scripts/install-binary.sh +159 -0
  88. package/scripts/install-guard-all-repos.sh +85 -0
  89. package/scripts/install-kj.sh +193 -0
  90. package/scripts/install.js +901 -0
  91. package/scripts/install.sh +7 -0
  92. package/scripts/postinstall.js +117 -0
  93. package/scripts/protect-all-repos.sh +88 -0
  94. package/scripts/quickstart-gate.mjs +97 -0
  95. package/scripts/regen-arch-stats.sh +71 -0
  96. package/scripts/setup-multi-instance.sh +150 -0
  97. package/scripts/verify-pack.mjs +278 -0
  98. package/src/activity-log.js +59 -0
  99. package/src/agents/agy-agent.js +62 -0
  100. package/src/agents/aider-agent.js +78 -0
  101. package/src/agents/availability.js +52 -0
  102. package/src/agents/base-agent.js +134 -0
  103. package/src/agents/claude-agent.js +413 -0
  104. package/src/agents/codex-agent.js +112 -0
  105. package/src/agents/copilot-agent.js +65 -0
  106. package/src/agents/gemini-agent.js +85 -0
  107. package/src/agents/host-agent.js +38 -0
  108. package/src/agents/index.js +59 -0
  109. package/src/agents/kimi-agent.js +45 -0
  110. package/src/agents/model-registry.js +137 -0
  111. package/src/agents/opencode-agent.js +72 -0
  112. package/src/agents/qwen-agent.js +20 -0
  113. package/src/agents/resolve-bin.js +73 -0
  114. package/src/audit/agent-readiness.js +259 -0
  115. package/src/audit/ai-slop-findings.js +102 -0
  116. package/src/audit/audit-fallback.js +66 -0
  117. package/src/audit/audit-history-display.js +61 -0
  118. package/src/audit/audit-history.js +102 -0
  119. package/src/audit/basal-cost.js +281 -0
  120. package/src/audit/circular-deps.js +144 -0
  121. package/src/audit/dead-exports.js +186 -0
  122. package/src/audit/deterministic-summary.js +327 -0
  123. package/src/audit/harness-scorecard.js +100 -0
  124. package/src/audit/harness-section.js +81 -0
  125. package/src/audit/infra-findings.js +100 -0
  126. package/src/audit/injection-findings.js +114 -0
  127. package/src/audit/issue-filter.js +133 -0
  128. package/src/audit/osv-findings.js +167 -0
  129. package/src/audit/plan-adherence.js +224 -0
  130. package/src/audit/semgrep-findings.js +129 -0
  131. package/src/audit/sonar-findings.js +93 -0
  132. package/src/audit/webperf-input.js +133 -0
  133. package/src/autonomy/arbiter-role.js +71 -0
  134. package/src/autonomy/arbiter.js +22 -0
  135. package/src/autonomy/decider.js +18 -0
  136. package/src/autonomy/decision-resolver.js +58 -0
  137. package/src/autonomy/outcome-report.js +55 -0
  138. package/src/autonomy/policy.js +41 -0
  139. package/src/autonomy/stage-ask.js +30 -0
  140. package/src/bootstrap.js +276 -0
  141. package/src/brain/agent-error-classifier.js +116 -0
  142. package/src/brain/decision-tracker.js +125 -0
  143. package/src/brain/decisor.js +243 -0
  144. package/src/brain/solomon-consult.js +171 -0
  145. package/src/brain/standby-scheduler.js +8 -0
  146. package/src/brain/standby-store.js +12 -0
  147. package/src/brain/with-brain-recovery.js +235 -0
  148. package/src/budget/comparison.js +96 -0
  149. package/src/canvas/parse-md.js +238 -0
  150. package/src/canvas/schema.js +101 -0
  151. package/src/canvas/to-flat.js +85 -0
  152. package/src/canvas/validate.js +163 -0
  153. package/src/checks/ai-surface.js +79 -0
  154. package/src/checks/ai-trash.js +34 -0
  155. package/src/checks/binaries.js +207 -0
  156. package/src/checks/ci.js +105 -0
  157. package/src/checks/config-files.js +217 -0
  158. package/src/checks/dir-setup.js +103 -0
  159. package/src/checks/guarantee-level.js +38 -0
  160. package/src/checks/hardware.js +150 -0
  161. package/src/checks/harness-scorecard.js +41 -0
  162. package/src/checks/mcp-health.js +169 -0
  163. package/src/checks/method.js +73 -0
  164. package/src/checks/native-build.js +67 -0
  165. package/src/checks/node.js +70 -0
  166. package/src/checks/ollama.js +41 -0
  167. package/src/checks/orphans.js +77 -0
  168. package/src/checks/ports.js +188 -0
  169. package/src/checks/project-checks.js +245 -0
  170. package/src/checks/qmd.js +41 -0
  171. package/src/checks/rag-hooks.js +54 -0
  172. package/src/checks/release-check.js +102 -0
  173. package/src/checks/rtk.js +36 -0
  174. package/src/checks/runner.js +326 -0
  175. package/src/checks/skills.js +53 -0
  176. package/src/checks/sonar.js +67 -0
  177. package/src/checks/squeezr.js +36 -0
  178. package/src/checks/system.js +53 -0
  179. package/src/checks/tokens.js +172 -0
  180. package/src/checks/types.js +103 -0
  181. package/src/ci/dispatch.js +99 -0
  182. package/src/ci/index.js +3 -0
  183. package/src/ci/pr-diff.js +26 -0
  184. package/src/ci/repo.js +45 -0
  185. package/src/cli/_shared.js +30 -0
  186. package/src/cli/advanced-commands.js +61 -0
  187. package/src/cli/register-meta.js +772 -0
  188. package/src/cli/register-pipeline.js +335 -0
  189. package/src/cli/register-plan.js +224 -0
  190. package/src/cli/register-roles-skills.js +54 -0
  191. package/src/cli/register-sonar.js +28 -0
  192. package/src/cli/register-standby.js +26 -0
  193. package/src/cli.js +155 -0
  194. package/src/commands/advanced.js +31 -0
  195. package/src/commands/agent-run.js +63 -0
  196. package/src/commands/agents.js +105 -0
  197. package/src/commands/architect.js +123 -0
  198. package/src/commands/audit.js +430 -0
  199. package/src/commands/autorun.js +49 -0
  200. package/src/commands/board.js +458 -0
  201. package/src/commands/check.js +40 -0
  202. package/src/commands/clean.js +145 -0
  203. package/src/commands/code.js +45 -0
  204. package/src/commands/config.js +74 -0
  205. package/src/commands/discover.js +105 -0
  206. package/src/commands/doctor.js +251 -0
  207. package/src/commands/env.js +207 -0
  208. package/src/commands/harden.js +203 -0
  209. package/src/commands/hu.js +210 -0
  210. package/src/commands/identity.js +60 -0
  211. package/src/commands/init.js +950 -0
  212. package/src/commands/install-tools.js +432 -0
  213. package/src/commands/mutate.js +94 -0
  214. package/src/commands/ollama.js +44 -0
  215. package/src/commands/onboard.js +45 -0
  216. package/src/commands/plan/_shared.js +55 -0
  217. package/src/commands/plan/add-hu.js +23 -0
  218. package/src/commands/plan/delete.js +9 -0
  219. package/src/commands/plan/fix.js +99 -0
  220. package/src/commands/plan/generate.js +532 -0
  221. package/src/commands/plan/index.js +13 -0
  222. package/src/commands/plan/list.js +21 -0
  223. package/src/commands/plan/migrate-result.js +41 -0
  224. package/src/commands/plan/ready.js +46 -0
  225. package/src/commands/plan/remove-hu.js +17 -0
  226. package/src/commands/plan/share.js +85 -0
  227. package/src/commands/plan/show.js +81 -0
  228. package/src/commands/plan/unshare.js +41 -0
  229. package/src/commands/plan/validate.js +20 -0
  230. package/src/commands/plan.js +17 -0
  231. package/src/commands/policy.js +164 -0
  232. package/src/commands/privacy.js +45 -0
  233. package/src/commands/qmd.js +86 -0
  234. package/src/commands/rag-mcp.js +18 -0
  235. package/src/commands/rag.js +173 -0
  236. package/src/commands/report-issue.js +77 -0
  237. package/src/commands/report.js +469 -0
  238. package/src/commands/researcher.js +54 -0
  239. package/src/commands/resume.js +68 -0
  240. package/src/commands/review-gate.js +355 -0
  241. package/src/commands/review.js +89 -0
  242. package/src/commands/roles.js +116 -0
  243. package/src/commands/run.js +185 -0
  244. package/src/commands/scan.js +18 -0
  245. package/src/commands/skills.js +86 -0
  246. package/src/commands/sonar.js +53 -0
  247. package/src/commands/standby.js +117 -0
  248. package/src/commands/start.js +121 -0
  249. package/src/commands/status.js +36 -0
  250. package/src/commands/sync.js +103 -0
  251. package/src/commands/telemetry.js +70 -0
  252. package/src/commands/tournament.js +85 -0
  253. package/src/commands/triage.js +78 -0
  254. package/src/commands/undo.js +62 -0
  255. package/src/commands/watch.js +46 -0
  256. package/src/commands/webperf.js +162 -0
  257. package/src/commands/worktree.js +76 -0
  258. package/src/config/defaults.js +270 -0
  259. package/src/config/loader.js +204 -0
  260. package/src/config/overrides.js +220 -0
  261. package/src/config/role-resolver.js +171 -0
  262. package/src/config/schema.js +299 -0
  263. package/src/config/test-harness.js +78 -0
  264. package/src/config.js +34 -0
  265. package/src/domains/domain-loader.js +245 -0
  266. package/src/domains/domain-registry.js +140 -0
  267. package/src/domains/domain-synthesizer.js +129 -0
  268. package/src/environment/adr.js +45 -0
  269. package/src/environment/board-access.js +97 -0
  270. package/src/environment/board-select.js +59 -0
  271. package/src/environment/briefs.js +129 -0
  272. package/src/environment/issue-report.js +72 -0
  273. package/src/environment/mcp-wiring.js +44 -0
  274. package/src/environment/playbook.js +127 -0
  275. package/src/git/automation.js +334 -0
  276. package/src/git/hu-automation.js +171 -0
  277. package/src/git/hu-snapshot.js +10 -0
  278. package/src/golden/asserter.js +92 -0
  279. package/src/golden/loader.js +52 -0
  280. package/src/golden/runner.js +92 -0
  281. package/src/golden/schema.js +41 -0
  282. package/src/guards/intent-guard.js +161 -0
  283. package/src/guards/output-guard.js +149 -0
  284. package/src/guards/perf-guard.js +126 -0
  285. package/src/guards/policy-guard.js +90 -0
  286. package/src/guards/policy-resolver.js +85 -0
  287. package/src/guards/secret-redactor.js +71 -0
  288. package/src/harden/advisory.js +203 -0
  289. package/src/harden/alternatives.js +50 -0
  290. package/src/harden/check.js +81 -0
  291. package/src/harden/config-engine.js +128 -0
  292. package/src/harden/config-templates.js +122 -0
  293. package/src/harden/guidelines-engine.js +48 -0
  294. package/src/harden/guidelines-templates.js +36 -0
  295. package/src/harden/harden-engine.js +80 -0
  296. package/src/harden/harness-hooks.js +107 -0
  297. package/src/harden/hook-commands.js +25 -0
  298. package/src/harden/hook-templates.js +183 -0
  299. package/src/harden/interactive.js +76 -0
  300. package/src/harden/sentinel-hooks.js +721 -0
  301. package/src/harden/stack-roots.js +105 -0
  302. package/src/harden/test-on-push.js +38 -0
  303. package/src/harden/workflow-engine.js +116 -0
  304. package/src/harden/workflow-templates.js +285 -0
  305. package/src/hu/acceptance-runner.js +138 -0
  306. package/src/hu/auto-generator.js +358 -0
  307. package/src/hu/graph.js +71 -0
  308. package/src/hu/lazy-planner.js +109 -0
  309. package/src/hu/model-router.js +108 -0
  310. package/src/hu/parallel-executor.js +94 -0
  311. package/src/hu/split-validator.js +143 -0
  312. package/src/hu/splitting-detector.js +158 -0
  313. package/src/hu/splitting-generator.js +175 -0
  314. package/src/hu/store.js +392 -0
  315. package/src/hu/worktree-bootstrap.js +59 -0
  316. package/src/identity/bootstrap.js +48 -0
  317. package/src/identity/compare.js +38 -0
  318. package/src/identity/detect.js +51 -0
  319. package/src/identity/store.js +66 -0
  320. package/src/infrastructure/command-runner.js +44 -0
  321. package/src/infrastructure/environment.js +38 -0
  322. package/src/infrastructure/file-system-service.js +63 -0
  323. package/src/infrastructure/mocks.js +182 -0
  324. package/src/lang/chunk-go.js +94 -0
  325. package/src/lang/chunk-java.js +90 -0
  326. package/src/lang/chunk-python.js +88 -0
  327. package/src/lang/chunk-rust.js +102 -0
  328. package/src/lang/go.js +14 -0
  329. package/src/lang/java.js +16 -0
  330. package/src/lang/js.js +8 -0
  331. package/src/lang/python.js +14 -0
  332. package/src/lang/registry.js +79 -0
  333. package/src/lang/rust.js +14 -0
  334. package/src/lang/tree-sitter-loader.js +45 -0
  335. package/src/mcp/direct-role-runner.js +112 -0
  336. package/src/mcp/error-classifiers.js +66 -0
  337. package/src/mcp/handlers/direct-handlers.js +486 -0
  338. package/src/mcp/handlers/hu-handlers.js +93 -0
  339. package/src/mcp/handlers/management-handlers.js +265 -0
  340. package/src/mcp/handlers/rag-handler.js +52 -0
  341. package/src/mcp/handlers/run-handler.js +313 -0
  342. package/src/mcp/orphan-guard.js +131 -0
  343. package/src/mcp/preflight.js +47 -0
  344. package/src/mcp/progress.js +149 -0
  345. package/src/mcp/response-compressor.js +81 -0
  346. package/src/mcp/run-kj.js +135 -0
  347. package/src/mcp/server-handlers.js +79 -0
  348. package/src/mcp/server.js +66 -0
  349. package/src/mcp/shared-helpers.js +297 -0
  350. package/src/mcp/sovereignty-guard.js +112 -0
  351. package/src/mcp/suggest-handler.js +92 -0
  352. package/src/mcp/tool-arg-normalizers.js +16 -0
  353. package/src/mcp/tools.js +432 -0
  354. package/src/mutate/diff-scope.js +105 -0
  355. package/src/mutate/reviewer-signal.js +76 -0
  356. package/src/mutate/runner.js +72 -0
  357. package/src/mutate/tool-registry.js +90 -0
  358. package/src/onboarder/cache.js +24 -0
  359. package/src/onboarder/collectors/framework-multilang.js +64 -0
  360. package/src/onboarder/collectors/index.js +136 -0
  361. package/src/orchestrator/agent-fallback.js +81 -0
  362. package/src/orchestrator/brain-coordinator.js +186 -0
  363. package/src/orchestrator/ci-integration.js +139 -0
  364. package/src/orchestrator/config-init.js +510 -0
  365. package/src/orchestrator/direct-actions.js +217 -0
  366. package/src/orchestrator/drivers/error-recovery.js +287 -0
  367. package/src/orchestrator/drivers/init-context.js +227 -0
  368. package/src/orchestrator/drivers/iteration-loop.js +380 -0
  369. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +69 -0
  370. package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
  371. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
  372. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +114 -0
  373. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
  374. package/src/orchestrator/drivers/post-loop.js +383 -0
  375. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +136 -0
  376. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
  377. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
  378. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +391 -0
  379. package/src/orchestrator/drivers/pre-loop.js +507 -0
  380. package/src/orchestrator/drivers/run-hu-batch.js +427 -0
  381. package/src/orchestrator/feedback-enrichment.js +140 -0
  382. package/src/orchestrator/feedback-queue.js +151 -0
  383. package/src/orchestrator/flow-control.js +170 -0
  384. package/src/orchestrator/flow-runner.js +364 -0
  385. package/src/orchestrator/fs-leak-detector.js +210 -0
  386. package/src/orchestrator/hu-scheduler.js +114 -0
  387. package/src/orchestrator/hu-sub-pipeline.js +616 -0
  388. package/src/orchestrator/integrations.js +81 -0
  389. package/src/orchestrator/iteration-gate.js +73 -0
  390. package/src/orchestrator/iteration-stages.js +9 -0
  391. package/src/orchestrator/parallel-limiter.js +74 -0
  392. package/src/orchestrator/pipeline-context.js +72 -0
  393. package/src/orchestrator/post-loop-stages.js +468 -0
  394. package/src/orchestrator/pre-loop-stages.js +11 -0
  395. package/src/orchestrator/preflight-checks.js +462 -0
  396. package/src/orchestrator/repair/repair-detector.js +117 -0
  397. package/src/orchestrator/repair/repair-runner.js +142 -0
  398. package/src/orchestrator/reviewer-fallback.js +45 -0
  399. package/src/orchestrator/role-output-compressor.js +189 -0
  400. package/src/orchestrator/session-journal.js +308 -0
  401. package/src/orchestrator/solomon-escalation.js +305 -0
  402. package/src/orchestrator/solomon-rules.js +212 -0
  403. package/src/orchestrator/stages/architect-stage.js +172 -0
  404. package/src/orchestrator/stages/coder-stage.js +480 -0
  405. package/src/orchestrator/stages/domain-curator-stage.js +82 -0
  406. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
  407. package/src/orchestrator/stages/hu-reviewer-stage.js +394 -0
  408. package/src/orchestrator/stages/perf-stage.js +140 -0
  409. package/src/orchestrator/stages/planner-stage.js +85 -0
  410. package/src/orchestrator/stages/rag-context-stage.js +77 -0
  411. package/src/orchestrator/stages/rag-preload-decisor.js +58 -0
  412. package/src/orchestrator/stages/research-stage.js +69 -0
  413. package/src/orchestrator/stages/reviewer-stage.js +422 -0
  414. package/src/orchestrator/stages/sonar-stage.js +421 -0
  415. package/src/orchestrator/stages/stage-classes.js +84 -0
  416. package/src/orchestrator/stages/stage-executor.js +155 -0
  417. package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
  418. package/src/orchestrator/stages/tool-judge-stage.js +53 -0
  419. package/src/orchestrator/stages/triage-stage.js +172 -0
  420. package/src/orchestrator/standby.js +71 -0
  421. package/src/orchestrator/verification-gate.js +183 -0
  422. package/src/orchestrator.js +22 -0
  423. package/src/plan/adr-generator.js +170 -0
  424. package/src/plan/adr-loader.js +60 -0
  425. package/src/plan/plan-executor.js +100 -0
  426. package/src/plan/plan-fixer.js +192 -0
  427. package/src/plan/plan-hu-ops.js +16 -0
  428. package/src/plan/plan-id.js +4 -0
  429. package/src/plan/plan-migrate-result.js +24 -0
  430. package/src/plan/plan-reviewer.js +236 -0
  431. package/src/plan/plan-schema.js +334 -0
  432. package/src/plan/plan-store.js +260 -0
  433. package/src/plan/plan-structural-pass.js +122 -0
  434. package/src/plan/plan-validation.js +4 -0
  435. package/src/plan/preflight-hu.js +105 -0
  436. package/src/plan/spec-detector.js +61 -0
  437. package/src/plan/tests-synthesizer.js +131 -0
  438. package/src/planning-game/adapter.js +108 -0
  439. package/src/planning-game/architect-adrs.js +45 -0
  440. package/src/planning-game/client.js +108 -0
  441. package/src/planning-game/decomposition.js +87 -0
  442. package/src/planning-game/pipeline-adapter.js +251 -0
  443. package/src/plugins/loader.js +67 -0
  444. package/src/policy/add.js +111 -0
  445. package/src/policy/decisions.js +42 -0
  446. package/src/policy/engine.js +82 -0
  447. package/src/policy/exceptions.js +60 -0
  448. package/src/privacy/onboarding.js +35 -0
  449. package/src/privacy/scan.js +120 -0
  450. package/src/prompts/arbiter.js +38 -0
  451. package/src/prompts/architect.js +103 -0
  452. package/src/prompts/audit.js +474 -0
  453. package/src/prompts/coder.js +287 -0
  454. package/src/prompts/diff-clip.js +27 -0
  455. package/src/prompts/discover.js +222 -0
  456. package/src/prompts/hu-reviewer.js +219 -0
  457. package/src/prompts/planner.js +312 -0
  458. package/src/prompts/prompt-layout.js +80 -0
  459. package/src/prompts/prompt-resolver.js +120 -0
  460. package/src/prompts/reviewer.js +75 -0
  461. package/src/prompts/rtk-snippet.js +25 -0
  462. package/src/prompts/spec-reviewer.js +66 -0
  463. package/src/prompts/split-signal.js +82 -0
  464. package/src/prompts/start-decision.js +52 -0
  465. package/src/prompts/tool-correctness-judge.js +31 -0
  466. package/src/prompts/triage.js +57 -0
  467. package/src/rag/auto-update.js +72 -0
  468. package/src/rag/chunker-ast.js +56 -0
  469. package/src/rag/chunker.js +132 -0
  470. package/src/rag/embedder.js +2 -0
  471. package/src/rag/embedders/_cloud-base.js +2 -0
  472. package/src/rag/embedders/cohere.js +2 -0
  473. package/src/rag/embedders/factory.js +2 -0
  474. package/src/rag/embedders/mistral.js +2 -0
  475. package/src/rag/embedders/onnx.js +2 -0
  476. package/src/rag/embedders/openai.js +2 -0
  477. package/src/rag/embedders/voyage.js +2 -0
  478. package/src/rag/eval.js +61 -0
  479. package/src/rag/governed-embedder.js +39 -0
  480. package/src/rag/indexer.js +199 -0
  481. package/src/rag/library.js +62 -0
  482. package/src/rag/ollama-capability.js +59 -0
  483. package/src/rag/ollama-manager.js +98 -0
  484. package/src/rag/onnx-fallback.js +69 -0
  485. package/src/rag/rerank.js +4 -0
  486. package/src/rag/retriever.js +2 -0
  487. package/src/rag/vec-store.js +17 -0
  488. package/src/rag/watcher.js +62 -0
  489. package/src/rag/where-parser.js +4 -0
  490. package/src/repeat-detector.js +88 -0
  491. package/src/review/card-first.js +99 -0
  492. package/src/review/card-verify.js +83 -0
  493. package/src/review/diff-generator.js +112 -0
  494. package/src/review/gate-gitignore.js +77 -0
  495. package/src/review/git-stash-helper.js +104 -0
  496. package/src/review/mutation-pregate.js +48 -0
  497. package/src/review/one-shot-review.js +122 -0
  498. package/src/review/parser.js +80 -0
  499. package/src/review/policy-gate.js +26 -0
  500. package/src/review/profiles.js +66 -0
  501. package/src/review/reviewer-fallback.js +125 -0
  502. package/src/review/schema.js +31 -0
  503. package/src/review/scope-filter.js +152 -0
  504. package/src/review/snapshot-diff.js +122 -0
  505. package/src/review/solomon-arbitration.js +113 -0
  506. package/src/review/sonar-pregate.js +63 -0
  507. package/src/review/tdd-discipline.js +90 -0
  508. package/src/review/tdd-policy.js +69 -0
  509. package/src/review/tests-with-code.js +33 -0
  510. package/src/review/verdict-store.js +87 -0
  511. package/src/review/workspace.js +21 -0
  512. package/src/roles/agent-role.js +194 -0
  513. package/src/roles/architect-role.js +77 -0
  514. package/src/roles/audit-role.js +258 -0
  515. package/src/roles/base-role.js +165 -0
  516. package/src/roles/coder-role.js +107 -0
  517. package/src/roles/commiter-role.js +110 -0
  518. package/src/roles/discover-role.js +81 -0
  519. package/src/roles/domain-curator-role.js +141 -0
  520. package/src/roles/hu-reviewer-role.js +64 -0
  521. package/src/roles/impeccable-role.js +85 -0
  522. package/src/roles/index.js +18 -0
  523. package/src/roles/karajan-brain-role.js +134 -0
  524. package/src/roles/onboarder-role.js +61 -0
  525. package/src/roles/perf-role.js +122 -0
  526. package/src/roles/planner-role.js +151 -0
  527. package/src/roles/refactorer-role.js +30 -0
  528. package/src/roles/repairer-role.js +181 -0
  529. package/src/roles/researcher-role.js +70 -0
  530. package/src/roles/reviewer-role.js +104 -0
  531. package/src/roles/security-role.js +73 -0
  532. package/src/roles/solomon-role.js +265 -0
  533. package/src/roles/sonar-role.js +70 -0
  534. package/src/roles/spec-reviewer-role.js +106 -0
  535. package/src/roles/tester-role.js +235 -0
  536. package/src/roles/tool-correctness-judge-role.js +78 -0
  537. package/src/roles/triage-role.js +99 -0
  538. package/src/session/journal/decisions-writer.js +200 -0
  539. package/src/session/journal/iteration-logger.js +194 -0
  540. package/src/session/journal/summary-writer.js +320 -0
  541. package/src/session/journal/tree-writer.js +164 -0
  542. package/src/session/journal/write-all.js +89 -0
  543. package/src/session/mutators.js +339 -0
  544. package/src/session/runtime-overrides.js +59 -0
  545. package/src/session/store.js +106 -0
  546. package/src/session-cleanup.js +116 -0
  547. package/src/skills/addyosmani-catalog.js +315 -0
  548. package/src/skills/addyosmani-role-map.js +139 -0
  549. package/src/skills/openskills-client.js +157 -0
  550. package/src/skills/semantic-detector.js +167 -0
  551. package/src/skills/skill-detector.js +385 -0
  552. package/src/skills/skill-loader.js +219 -0
  553. package/src/skills/skills-cache.js +128 -0
  554. package/src/sonar/api.js +128 -0
  555. package/src/sonar/cloud-scanner.js +78 -0
  556. package/src/sonar/config-resolver.js +88 -0
  557. package/src/sonar/credentials.js +60 -0
  558. package/src/sonar/discovery.js +124 -0
  559. package/src/sonar/enforcer.js +19 -0
  560. package/src/sonar/issue-filter.js +12 -0
  561. package/src/sonar/manager.js +181 -0
  562. package/src/sonar/project-key.js +120 -0
  563. package/src/sonar/scanner.js +307 -0
  564. package/src/sonar/token-bootstrap.js +205 -0
  565. package/src/sonar/token-recovery.js +67 -0
  566. package/src/spec-review/refine-loop.js +58 -0
  567. package/src/spec-review/run-spec-review.js +119 -0
  568. package/src/start/assessment.js +74 -0
  569. package/src/start/maturity.js +73 -0
  570. package/src/start/start-decider-role.js +79 -0
  571. package/src/start/sweep.js +157 -0
  572. package/src/sync/apply-canvas-patch.js +55 -0
  573. package/src/sync/detect-drift.js +113 -0
  574. package/src/tournament/crown.js +88 -0
  575. package/src/tournament/judge.js +150 -0
  576. package/src/tournament/run.js +142 -0
  577. package/src/tournament/scoreboard.js +124 -0
  578. package/src/tracker-bootstrap.js +44 -0
  579. package/src/types/agent.js +55 -0
  580. package/src/types/config.js +114 -0
  581. package/src/types/hu.js +47 -0
  582. package/src/types/index.js +38 -0
  583. package/src/types/policy.js +43 -0
  584. package/src/types/session.js +75 -0
  585. package/src/types/solomon.js +87 -0
  586. package/src/types/stage.js +68 -0
  587. package/src/utils/agent-detect.js +93 -0
  588. package/src/utils/ai-trash.js +48 -0
  589. package/src/utils/async-lock.js +31 -0
  590. package/src/utils/atomic-write.js +6 -0
  591. package/src/utils/banner.js +66 -0
  592. package/src/utils/binary-sources.js +70 -0
  593. package/src/utils/board-prompt-bridge.js +108 -0
  594. package/src/utils/budget.js +229 -0
  595. package/src/utils/cli-ask-question.js +93 -0
  596. package/src/utils/cli-progress.js +220 -0
  597. package/src/utils/cli-run-log.js +83 -0
  598. package/src/utils/cwd-confirm.js +39 -0
  599. package/src/utils/derive-project-name-from-cwd.js +119 -0
  600. package/src/utils/display/event-handlers.js +403 -0
  601. package/src/utils/display/formatters.js +132 -0
  602. package/src/utils/display/header.js +47 -0
  603. package/src/utils/display/resume-hint.js +41 -0
  604. package/src/utils/display/session-summary.js +118 -0
  605. package/src/utils/display/solomon.js +42 -0
  606. package/src/utils/display/spec-findings.js +44 -0
  607. package/src/utils/doc-links.js +29 -0
  608. package/src/utils/docker-install.js +34 -0
  609. package/src/utils/escape-regexp.js +15 -0
  610. package/src/utils/events.js +35 -0
  611. package/src/utils/fs.js +19 -0
  612. package/src/utils/garbage-collector.js +481 -0
  613. package/src/utils/git.js +279 -0
  614. package/src/utils/home-migration.js +87 -0
  615. package/src/utils/injection-guard.js +186 -0
  616. package/src/utils/install-hints.js +180 -0
  617. package/src/utils/json-extract.js +64 -0
  618. package/src/utils/locale.js +59 -0
  619. package/src/utils/logger.js +96 -0
  620. package/src/utils/managed-markers.js +93 -0
  621. package/src/utils/manifest-sniff.js +82 -0
  622. package/src/utils/messages.js +61 -0
  623. package/src/utils/model-selector.js +107 -0
  624. package/src/utils/os-detect.js +79 -0
  625. package/src/utils/paths.js +13 -0
  626. package/src/utils/pending-user-action.js +95 -0
  627. package/src/utils/port-check.js +6 -0
  628. package/src/utils/port-occupant.js +96 -0
  629. package/src/utils/pricing.js +29 -0
  630. package/src/utils/process.js +4 -0
  631. package/src/utils/project-detect.js +160 -0
  632. package/src/utils/prompt-project-name.js +35 -0
  633. package/src/utils/provider-env.js +116 -0
  634. package/src/utils/qmd-collection.js +72 -0
  635. package/src/utils/qmd-detect.js +18 -0
  636. package/src/utils/qmd-install.js +37 -0
  637. package/src/utils/quota-simulator.js +56 -0
  638. package/src/utils/rate-limit-detector.js +181 -0
  639. package/src/utils/repo-cleaner.js +79 -0
  640. package/src/utils/retry.js +88 -0
  641. package/src/utils/role-assigner.js +137 -0
  642. package/src/utils/role-env.js +58 -0
  643. package/src/utils/rtk-detect.js +18 -0
  644. package/src/utils/rtk-install.js +38 -0
  645. package/src/utils/rtk-wrapper.js +87 -0
  646. package/src/utils/run-context.js +75 -0
  647. package/src/utils/run-log.js +217 -0
  648. package/src/utils/run-registry.js +9 -0
  649. package/src/utils/shared-paths.js +12 -0
  650. package/src/utils/squeezr-detect.js +21 -0
  651. package/src/utils/squeezr-install.js +37 -0
  652. package/src/utils/stack-detect.js +135 -0
  653. package/src/utils/stall-detector.js +159 -0
  654. package/src/utils/status-dashboard.js +254 -0
  655. package/src/utils/task-file.js +109 -0
  656. package/src/utils/telemetry.js +124 -0
  657. package/src/utils/templates-root.js +61 -0
  658. package/src/utils/tool-call-extractor.js +56 -0
  659. package/src/utils/tool-governor.js +116 -0
  660. package/src/utils/tool-installer.js +93 -0
  661. package/src/utils/update-check.js +280 -0
  662. package/src/utils/vector-store-orphans.js +89 -0
  663. package/src/utils/welcome.js +47 -0
  664. package/src/utils/with-timeout.js +42 -0
  665. package/src/utils/wizard.js +47 -0
  666. package/src/webperf/cwv-gate.js +94 -0
  667. package/src/webperf/devtools-detect.js +112 -0
  668. package/src/webperf/scanner.js +209 -0
  669. package/src/webperf/webperf-role-integration.js +66 -0
  670. package/templates/coder-rules.md +45 -0
  671. package/templates/docker-compose.sonar.yml +60 -0
  672. package/templates/domains/example-dental.md +57 -0
  673. package/templates/kj.config.yml +128 -0
  674. package/templates/review-rules.md +17 -0
  675. package/templates/roles/architect/anthropic.md +74 -0
  676. package/templates/roles/architect/default.md +72 -0
  677. package/templates/roles/architect/google.md +61 -0
  678. package/templates/roles/architect/openai.md +60 -0
  679. package/templates/roles/architect/opencode.md +59 -0
  680. package/templates/roles/architect.md +76 -0
  681. package/templates/roles/audit.md +68 -0
  682. package/templates/roles/coder/anthropic.md +81 -0
  683. package/templates/roles/coder/default.md +78 -0
  684. package/templates/roles/coder/google.md +87 -0
  685. package/templates/roles/coder/openai.md +68 -0
  686. package/templates/roles/coder/opencode.md +58 -0
  687. package/templates/roles/coder.md +152 -0
  688. package/templates/roles/commiter.md +44 -0
  689. package/templates/roles/discover.md +167 -0
  690. package/templates/roles/domain-curator.md +52 -0
  691. package/templates/roles/hu-reviewer.md +236 -0
  692. package/templates/roles/impeccable-design.md +87 -0
  693. package/templates/roles/impeccable.md +125 -0
  694. package/templates/roles/karajan-brain.md +95 -0
  695. package/templates/roles/onboarder.md +45 -0
  696. package/templates/roles/planner/anthropic.md +59 -0
  697. package/templates/roles/planner/default.md +57 -0
  698. package/templates/roles/planner/google.md +75 -0
  699. package/templates/roles/planner/openai.md +55 -0
  700. package/templates/roles/planner/opencode.md +62 -0
  701. package/templates/roles/planner.md +61 -0
  702. package/templates/roles/refactorer.md +48 -0
  703. package/templates/roles/repairer.md +103 -0
  704. package/templates/roles/researcher.md +41 -0
  705. package/templates/roles/reviewer/anthropic.md +61 -0
  706. package/templates/roles/reviewer/default.md +62 -0
  707. package/templates/roles/reviewer/google.md +63 -0
  708. package/templates/roles/reviewer/openai.md +57 -0
  709. package/templates/roles/reviewer/opencode.md +42 -0
  710. package/templates/roles/reviewer-paranoid.md +38 -0
  711. package/templates/roles/reviewer-relaxed.md +34 -0
  712. package/templates/roles/reviewer-strict.md +37 -0
  713. package/templates/roles/reviewer.md +62 -0
  714. package/templates/roles/security.md +54 -0
  715. package/templates/roles/solomon/anthropic.md +71 -0
  716. package/templates/roles/solomon/default.md +83 -0
  717. package/templates/roles/solomon/google.md +61 -0
  718. package/templates/roles/solomon/openai.md +53 -0
  719. package/templates/roles/solomon/opencode.md +55 -0
  720. package/templates/roles/solomon.md +83 -0
  721. package/templates/roles/sonar.md +49 -0
  722. package/templates/roles/spec-reviewer.md +47 -0
  723. package/templates/roles/tester.md +50 -0
  724. package/templates/roles/triage.md +69 -0
  725. package/templates/skills/kj-architect.md +45 -0
  726. package/templates/skills/kj-audit.md +63 -0
  727. package/templates/skills/kj-board.md +16 -0
  728. package/templates/skills/kj-code.md +51 -0
  729. package/templates/skills/kj-csv-transform.md +40 -0
  730. package/templates/skills/kj-data-report.md +44 -0
  731. package/templates/skills/kj-discover.md +24 -0
  732. package/templates/skills/kj-rag-query.md +50 -0
  733. package/templates/skills/kj-review.md +47 -0
  734. package/templates/skills/kj-run.md +73 -0
  735. package/templates/skills/kj-security.md +49 -0
  736. package/templates/skills/kj-sonar.md +41 -0
  737. package/templates/skills/kj-sql-analysis.md +40 -0
  738. package/templates/skills/kj-test.md +40 -0
  739. package/templates/workflows/automerge.yml +30 -0
  740. package/templates/workflows/houston-override.yml +46 -0
  741. package/templates/workflows/kj-ci-gateway.yml +58 -0
  742. package/vendor/tree-sitter-grammars/README.md +21 -0
  743. package/vendor/tree-sitter-grammars/go.wasm +0 -0
  744. package/vendor/tree-sitter-grammars/java.wasm +0 -0
  745. package/vendor/tree-sitter-grammars/python.wasm +0 -0
  746. package/vendor/tree-sitter-grammars/rust.wasm +0 -0
@@ -0,0 +1,77 @@
1
+ // KJC-PCS-0049 Camino C — pre-loop RAG retrieval stage.
2
+ // Runs between triage and domainCurator. When `config.rag.preload.enabled`
3
+ // is true and the corpus has chunks, queries the retriever with the
4
+ // task text and returns a markdown block ready to prepend to the task.
5
+ // Failures degrade gracefully (warn + continue); the rest of the pipeline
6
+ // never sees an exception from this stage.
7
+ import { emitProgress, makeEvent } from "../../utils/events.js";
8
+ import { openVecStore, countChunks } from "../../rag/vec-store.js";
9
+ import { makeGovernedEmbedder } from "../../rag/governed-embedder.js";
10
+ import { query } from "../../rag/retriever.js";
11
+
12
+ const DEFAULT_TOP_K = 5;
13
+ const DEFAULT_SCOPE = "all";
14
+ const DEFAULT_SEARCH_MODE = "hybrid";
15
+ const DEFAULT_SEARCH_ALPHA = 0.6;
16
+ const MAX_CHUNK_CHARS = 600;
17
+
18
+ // KJC-TSK-0463 — pull search-time knobs from config so the HU Board
19
+ // modal (rag.search.{mode,alpha,rerank}) actually steers retrieval at
20
+ // pipeline time. Returns the args object query() expects; unset fields
21
+ // fall back to the retriever's own defaults.
22
+ export function resolveSearchOpts(config) {
23
+ const s = config?.rag?.search ?? {};
24
+ const out = {
25
+ mode: typeof s.mode === "string" ? s.mode : DEFAULT_SEARCH_MODE,
26
+ alpha: Number.isFinite(s.alpha) ? s.alpha : DEFAULT_SEARCH_ALPHA,
27
+ };
28
+ if (s.rerank && typeof s.rerank === "object") out.rerankOpts = s.rerank;
29
+ else if (s.rerank === true) out.rerankOpts = {};
30
+ return out;
31
+ }
32
+
33
+ /**
34
+ * Runs the RAG preload stage. Returns:
35
+ * { skipped: true, reason } — when disabled / empty / error
36
+ * { skipped: false, contextBlock, hits } — markdown block + raw hits
37
+ *
38
+ * The caller decides whether to mutate `task` with the contextBlock. We
39
+ * keep that responsibility at the call site so this module stays pure.
40
+ */
41
+ export async function runRagContextStage({ config, logger, emitter, eventBase, task }) {
42
+ const preload = config?.rag?.preload;
43
+ if (!preload?.enabled) return { skipped: true, reason: "disabled" };
44
+ if (typeof task !== "string" || task.length === 0) return { skipped: true, reason: "no-task" };
45
+ try {
46
+ const dim = config?.rag?.embedder?.dim || 768;
47
+ const db = openVecStore({ dim });
48
+ try {
49
+ if (countChunks(db) === 0) {
50
+ logger?.info?.("[rag-preload] corpus is empty, skipping (run `kj rag index` to seed)");
51
+ return { skipped: true, reason: "empty" };
52
+ }
53
+ const topK = preload.topK || DEFAULT_TOP_K;
54
+ const scope = preload.scope || DEFAULT_SCOPE;
55
+ const embedder = makeGovernedEmbedder(config);
56
+ const searchOpts = resolveSearchOpts(config);
57
+ const hits = await query(db, embedder, task, { topK, scope, ...searchOpts });
58
+ if (hits.length === 0) return { skipped: true, reason: "no-hits" };
59
+ const lines = hits.map((h) => {
60
+ const label = h.metadata?.hu_id || h.metadata?.symbol || h.metadata?.headingPath?.join(" > ") || "block";
61
+ const text = h.text.length > MAX_CHUNK_CHARS ? `${h.text.slice(0, MAX_CHUNK_CHARS)}…` : h.text;
62
+ return `### [${h.kind} · ${label}] ${h.source}\n\n${text}`;
63
+ });
64
+ const contextBlock = `## Prior context from RAG (auto-retrieved, top ${hits.length})\n\n${lines.join("\n\n")}`;
65
+ emitProgress(emitter, makeEvent("rag-preload:hits", { ...eventBase, stage: "rag-preload" }, {
66
+ message: `RAG preload: injected ${hits.length} chunk(s) into the task context`,
67
+ detail: { topK, scope, hits: hits.length, mode: searchOpts.mode, alpha: searchOpts.alpha, rerank: Boolean(searchOpts.rerankOpts) },
68
+ }));
69
+ return { skipped: false, contextBlock, hits };
70
+ } finally {
71
+ db.close();
72
+ }
73
+ } catch (err) {
74
+ logger?.warn?.(`[rag-preload] failed (non-blocking): ${err.message}`);
75
+ return { skipped: true, reason: "error", error: err.message };
76
+ }
77
+ }
@@ -0,0 +1,58 @@
1
+ // KJC-TSK-0434 — RAG Camino D: Brain decisor heuristic for retrieval.
2
+ // Decides WHETHER to pay the cost of running the RAG preload stage based
3
+ // on signals from triage + task shape, instead of pre-fetching every run.
4
+ //
5
+ // Returns `{ pull: boolean, reason: string }`. The pre-loop driver
6
+ // skips `runRagContextStage` when `pull === false`.
7
+ //
8
+ // Policies (config.rag.preload.policy):
9
+ // - "always" : pull regardless (back-compat with v2.24.0 behaviour)
10
+ // - "never" : never pull (useful for benchmarking, debugging)
11
+ // - "auto" : heuristic — pull only when triage signals complexity,
12
+ // decomposition, or when the task body is long enough
13
+ // to suggest non-trivial scope. Default.
14
+
15
+ const DEFAULT_POLICY = "auto";
16
+ const COMPLEX_LEVELS = new Set(["complex", "high", "epic"]);
17
+ const LONG_TASK_CHARS = 200;
18
+
19
+ /**
20
+ * @param {object} params
21
+ * @param {object} [params.triage] - triage stage result (may be null)
22
+ * @param {string} [params.task] - the user task text
23
+ * @param {object} [params.config] - full karajan config
24
+ * @returns {{ pull: boolean, reason: string }}
25
+ */
26
+ export function shouldPreloadRag({ triage, task, config } = {}) {
27
+ const policy = config?.rag?.preload?.policy || DEFAULT_POLICY;
28
+
29
+ if (policy === "always") return { pull: true, reason: "policy:always" };
30
+ if (policy === "never") return { pull: false, reason: "policy:never" };
31
+
32
+ // policy === "auto" — apply heuristic.
33
+ // Brownfield is an explicit user signal — if onboarding produced a
34
+ // brief and config marks the project as brownfield, prior context is
35
+ // almost always worth pulling.
36
+ if (config?.rag?.preload?.brownfield === true) {
37
+ return { pull: true, reason: "auto:brownfield" };
38
+ }
39
+
40
+ // Triage said this is big enough to decompose → multi-HU run benefits
41
+ // from prior context fed to researcher/architect/planner.
42
+ if (triage?.shouldDecompose === true) {
43
+ return { pull: true, reason: "auto:decompose" };
44
+ }
45
+
46
+ // Triage classified the task as complex / high-effort.
47
+ if (triage?.level && COMPLEX_LEVELS.has(String(triage.level).toLowerCase())) {
48
+ return { pull: true, reason: "auto:complex" };
49
+ }
50
+
51
+ // Long task body usually means a brief with context, motivation and
52
+ // non-trivial scope — worth checking the RAG.
53
+ if (typeof task === "string" && task.length >= LONG_TASK_CHARS) {
54
+ return { pull: true, reason: "auto:long-task" };
55
+ }
56
+
57
+ return { pull: false, reason: "auto:low-value" };
58
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Researcher stage logic.
3
+ * Extracted from pre-loop-stages.js for maintainability.
4
+ */
5
+
6
+ import { ResearcherRole } from "../../roles/researcher-role.js";
7
+ import { addCheckpoint } from "../../session/store.js";
8
+ import { emitProgress, makeEvent, emitAgentOutput } from "../../utils/events.js";
9
+ import { createStallDetector } from "../../utils/stall-detector.js";
10
+
11
+ export async function runResearcherStage({ config, logger, emitter, eventBase, session, coderRole, trackBudget }) {
12
+ logger.setContext({ iteration: 0, stage: "researcher" });
13
+
14
+ const researcherProvider = config?.roles?.researcher?.provider || coderRole.provider;
15
+ emitProgress(
16
+ emitter,
17
+ makeEvent("researcher:start", { ...eventBase, stage: "researcher" }, {
18
+ message: "Researcher investigating codebase",
19
+ detail: { researcher: researcherProvider, provider: researcherProvider, executorType: "agent" }
20
+ })
21
+ );
22
+
23
+ const researcherOnOutput = (payload) => emitAgentOutput(emitter, eventBase, "researcher", researcherProvider, payload);
24
+ const researcherStall = createStallDetector({
25
+ onOutput: researcherOnOutput, emitter, eventBase, stage: "researcher", provider: researcherProvider
26
+ });
27
+
28
+ const researcher = new ResearcherRole({ config, logger, emitter });
29
+ await researcher.init({ task: session.task });
30
+ const researchStart = Date.now();
31
+ let researchOutput;
32
+ try {
33
+ researchOutput = await researcher.run({ task: session.task, onOutput: researcherStall.onOutput });
34
+ } catch (err) {
35
+ logger.warn(`Researcher threw: ${err.message}`);
36
+ researchOutput = { ok: false, summary: `Researcher error: ${err.message}`, result: { error: err.message } };
37
+ } finally {
38
+ researcherStall.stop();
39
+ }
40
+ trackBudget({
41
+ role: "researcher",
42
+ provider: config?.roles?.researcher?.provider || coderRole.provider,
43
+ model: config?.roles?.researcher?.model || coderRole.model,
44
+ result: researchOutput,
45
+ duration_ms: Date.now() - researchStart
46
+ });
47
+
48
+ await addCheckpoint(session, {
49
+ stage: "researcher",
50
+ iteration: 0,
51
+ ok: researchOutput.ok,
52
+ provider: config?.roles?.researcher?.provider || coderRole.provider,
53
+ model: config?.roles?.researcher?.model || coderRole.model || null
54
+ });
55
+
56
+ emitProgress(
57
+ emitter,
58
+ makeEvent("researcher:end", { ...eventBase, stage: "researcher" }, {
59
+ status: researchOutput.ok ? "ok" : "fail",
60
+ message: researchOutput.ok ? "Research completed" : `Research failed: ${researchOutput.summary}`,
61
+ detail: { provider: researcherProvider, executorType: "agent" }
62
+ })
63
+ );
64
+
65
+ const stageResult = { ok: researchOutput.ok, summary: researchOutput.summary || null };
66
+ const researchContext = researchOutput.ok ? researchOutput.result : null;
67
+
68
+ return { researchContext, stageResult };
69
+ }
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Reviewer stage logic + fetchReviewDiff.
3
+ * Extracted from iteration-stages.js for maintainability.
4
+ */
5
+
6
+ import { addCheckpoint, markSessionStatus, saveSession } from "../../session/store.js";
7
+ import { setReviewerFeedback, setDeferredIssues } from "../../session/mutators.js";
8
+ import { generateDiff } from "../../review/diff-generator.js";
9
+ import { redactSecrets } from "../../guards/secret-redactor.js";
10
+ import { validateReviewResult } from "../../review/schema.js";
11
+ import { filterReviewScope } from "../../review/scope-filter.js";
12
+ import { emitProgress, makeEvent, emitAgentOutput } from "../../utils/events.js";
13
+ import { runReviewerWithFallback } from "../reviewer-fallback.js";
14
+ import { invokeSolomon } from "../solomon-escalation.js";
15
+ import { detectRateLimit } from "../../utils/rate-limit-detector.js";
16
+ import { createStallDetector } from "../../utils/stall-detector.js";
17
+ import { applyQuotaSimulation } from "../../utils/quota-simulator.js";
18
+
19
+ function categorizeIssues(issues) {
20
+ const categories = { security: 0, correctness: 0, tests: 0, style: 0, other: 0 };
21
+ const securityKw = /inject(?:ion)?|xss|csrf|secret.?leak|credential.?expos|auth(?:entication|orization).?(?:bypass|fail|miss|broken|weak)|vulnerab|exploit|httponly|token.?expos/i;
22
+ const styleKw = /naming|name|rename|style|format|indent|spacing|convention|cosmetic|readability|comment|jsdoc|whitespace/i;
23
+ const testKw = /test|coverage|assert|spec|mock/i;
24
+
25
+ for (const issue of issues || []) {
26
+ const desc = issue.description || "";
27
+ const sev = (issue.severity || "").toLowerCase();
28
+ if (sev === "critical" || securityKw.test(desc)) categories.security++;
29
+ else if (testKw.test(desc)) categories.tests++;
30
+ else if (sev === "low" || sev === "minor" || styleKw.test(desc)) categories.style++;
31
+ else categories.correctness++;
32
+ }
33
+ return categories;
34
+ }
35
+
36
+ function buildReviewHistory(session) {
37
+ return (session.checkpoints || [])
38
+ .filter(cp => cp.stage === "reviewer")
39
+ .map(cp => ({ iteration: cp.iteration, note: cp.note || "" }));
40
+ }
41
+
42
+ async function handleReviewerStalledSolomon({ review, repeatCounts, repeatState, config, logger, emitter, eventBase, session, iteration, task, askQuestion, budgetSummary, repeatDetector, brainCtx }) {
43
+ // DETERMINISTIC GUARD: security issues NEVER go to Solomon — always return to coder
44
+ const categories = categorizeIssues(review.blocking_issues);
45
+ if (categories.security > 0) {
46
+ logger.info(`Reviewer found ${categories.security} security issue(s) — returning to coder (Solomon bypassed)`);
47
+ emitProgress(emitter, makeEvent("reviewer:security-block", { ...eventBase, stage: "reviewer" }, {
48
+ message: `${categories.security} security issue(s) detected — must be fixed before approval`,
49
+ detail: { securityCount: categories.security, blockingIssues: review.blocking_issues }
50
+ }));
51
+ return { review, solomonApproved: false };
52
+ }
53
+
54
+ // Brain: when enabled, ALL paths go through Brain, not Solomon
55
+ if (brainCtx?.enabled) {
56
+ const logPrefix = repeatState.stalled
57
+ ? `Reviewer stalled (${repeatCounts.reviewer} repeats)`
58
+ : `Reviewer rejected (first rejection)`;
59
+ logger.info(`Brain: ${logPrefix} — Brain will handle (Solomon bypassed)`);
60
+ emitProgress(emitter, makeEvent("brain:escalate", { ...eventBase, stage: "reviewer" }, {
61
+ message: `${logPrefix} — Brain handling`,
62
+ detail: { repeats: repeatCounts.reviewer || 1, reason: repeatState.reason || "first_rejection" }
63
+ }));
64
+ // Push reviewer feedback into Brain queue
65
+ const { processRoleOutput } = await import("../brain-coordinator.js");
66
+ processRoleOutput(brainCtx, { roleName: "reviewer", output: review, iteration });
67
+ return { review, solomonApproved: false };
68
+ }
69
+
70
+ const logPrefix = repeatState.stalled
71
+ ? `Reviewer stalled (${repeatCounts.reviewer} repeats)`
72
+ : `Reviewer rejected (first rejection)`;
73
+ logger.warn(`${logPrefix}. Invoking Solomon mediation.`);
74
+ emitProgress(
75
+ emitter,
76
+ makeEvent("solomon:escalate", { ...eventBase, stage: "reviewer" }, {
77
+ message: `${logPrefix} — Solomon mediating`,
78
+ detail: { repeats: repeatCounts.reviewer || 1, reason: repeatState.reason || "first_rejection" }
79
+ })
80
+ );
81
+
82
+ const solomonResult = await invokeSolomon({
83
+ config, logger, emitter, eventBase, stage: "reviewer", askQuestion, session, iteration,
84
+ conflict: {
85
+ stage: "reviewer",
86
+ task,
87
+ iterationCount: repeatCounts.reviewer || 1,
88
+ maxIterations: config.session?.fail_fast_repeats ?? 2,
89
+ isFirstRejection: !repeatState.stalled,
90
+ isRepeat: repeatState.stalled,
91
+ stalledReason: repeatState.reason || "first_rejection",
92
+ blockingIssues: review.blocking_issues,
93
+ issueCategories: categorizeIssues(review.blocking_issues),
94
+ history: [
95
+ ...buildReviewHistory(session),
96
+ { agent: "reviewer", feedback: review.blocking_issues.map(x => x.description).join("; ") }
97
+ ]
98
+ }
99
+ });
100
+
101
+ if (solomonResult.action === "approve") {
102
+ logger.info("Solomon overrode reviewer — approving code");
103
+ emitProgress(emitter, makeEvent("solomon:override", { ...eventBase, stage: "solomon" }, {
104
+ message: "Solomon overrode reviewer rejection — code approved",
105
+ detail: { ruling: solomonResult.ruling?.result?.ruling }
106
+ }));
107
+ return { review: { ...review, approved: true, solomon_override: true }, solomonApproved: true };
108
+ }
109
+ if (solomonResult.action === "pause") {
110
+ await markSessionStatus(session, "stalled");
111
+ return { review, stalled: true, stalledResult: { paused: true, sessionId: session.id, question: solomonResult.question, context: "reviewer_stalled" } };
112
+ }
113
+ if (solomonResult.action === "continue") {
114
+ repeatDetector.reviewer = { lastHash: null, repeatCount: 0 };
115
+ if (solomonResult.humanGuidance) {
116
+ setReviewerFeedback(session, `Solomon/user guidance: ${solomonResult.humanGuidance}`);
117
+ await saveSession(session);
118
+ }
119
+ return { review };
120
+ }
121
+ if (solomonResult.action === "subtask") {
122
+ return { review, stalled: true, stalledResult: { paused: true, sessionId: session.id, subtask: solomonResult.subtask, context: "reviewer_subtask" } };
123
+ }
124
+
125
+ // Fallback
126
+ const message = `Manual intervention required: reviewer issues repeated ${repeatCounts.reviewer || 1} times.`;
127
+ await markSessionStatus(session, "stalled");
128
+ emitProgress(
129
+ emitter,
130
+ makeEvent("session:end", { ...eventBase, stage: "reviewer" }, {
131
+ status: "stalled",
132
+ message,
133
+ detail: { reason: repeatState.reason, repeats: repeatCounts.reviewer, budget: budgetSummary() }
134
+ })
135
+ );
136
+ return { review, stalled: true, stalledResult: { approved: false, sessionId: session.id, reason: "stalled" } };
137
+ }
138
+
139
+ async function handleReviewerRejection({ review, repeatDetector, config, logger, emitter, eventBase, session, iteration, task, askQuestion, budgetSummary, brainCtx }) {
140
+ repeatDetector.addIteration([], review.blocking_issues);
141
+ const repeatState = repeatDetector.isStalled();
142
+
143
+ const solomonEnabled = Boolean(config.pipeline?.solomon?.enabled);
144
+
145
+ // Brain: if enabled and bypass_solomon_on_correctness, push issues to queue and skip Solomon
146
+ // for non-style rejections. Solomon is still consulted on genuine dilemmas (style-only, deadlock).
147
+ if (brainCtx?.enabled && config.brain?.bypass_solomon_on_correctness !== false) {
148
+ const cats = categorizeIssues(review.blocking_issues);
149
+ const nonStyleIssues = cats.security + cats.correctness + cats.tests;
150
+ // Only bypass if there ARE non-style issues (correctness, tests, security, or "other" is correctness)
151
+ // If it's style-only, let Solomon evaluate whether to override
152
+ if (nonStyleIssues > 0 || cats.other > 0) {
153
+ const { processRoleOutput } = await import("../brain-coordinator.js");
154
+ processRoleOutput(brainCtx, { roleName: "reviewer", output: review, iteration });
155
+ logger.info(`Brain: reviewer rejected with ${nonStyleIssues} non-style issue(s) — bypassing Solomon, returning to coder`);
156
+ emitProgress(emitter, makeEvent("brain:bypass-solomon", { ...eventBase, stage: "reviewer" }, {
157
+ message: `Brain: ${nonStyleIssues} non-style issue(s) — sending to coder without Solomon`,
158
+ detail: { categories: cats, queueSize: brainCtx.feedbackQueue.entries.length }
159
+ }));
160
+ // Persist flat feedback for compat with non-Brain flow
161
+ setReviewerFeedback(session, review.blocking_issues
162
+ .map((x, idx) => `R-${idx + 1} [${x.severity || "medium"}]: ${x.description}`)
163
+ .join("\n"));
164
+ await saveSession(session);
165
+ return null; // null = continue to next iteration (coder fixes)
166
+ }
167
+ logger.info("Brain: reviewer rejected with style-only issues — consulting Solomon for dilemma");
168
+ }
169
+
170
+ // When Solomon is disabled, only act on stalls (legacy behavior)
171
+ if (!solomonEnabled) {
172
+ if (!repeatState.stalled) return null;
173
+ const repeatCounts = repeatDetector.getRepeatCounts();
174
+ return handleReviewerStalledSolomon({
175
+ review, repeatCounts, repeatState, config, logger, emitter,
176
+ eventBase, session, iteration, task, askQuestion,
177
+ budgetSummary, repeatDetector, brainCtx
178
+ });
179
+ }
180
+
181
+ // Solomon evaluates EVERY rejection
182
+ const repeatCounts = repeatDetector.getRepeatCounts();
183
+ const evaluateEventName = brainCtx?.enabled ? "brain:evaluate" : "solomon:evaluate";
184
+ logger.info(`Reviewer rejected — ${brainCtx?.enabled ? "Brain" : "Solomon"} evaluating ${review.blocking_issues.length} blocking issue(s)`);
185
+ emitProgress(emitter, makeEvent(evaluateEventName, { ...eventBase, stage: brainCtx?.enabled ? "brain" : "solomon" }, {
186
+ message: `${brainCtx?.enabled ? "Brain" : "Solomon"} evaluating reviewer rejection`,
187
+ detail: { blockingCount: review.blocking_issues.length, isRepeat: repeatState.stalled }
188
+ }));
189
+
190
+ return handleReviewerStalledSolomon({
191
+ review, repeatCounts, repeatState, config, logger, emitter,
192
+ eventBase, session, iteration, task, askQuestion,
193
+ budgetSummary, repeatDetector, brainCtx
194
+ });
195
+ }
196
+
197
+ export async function fetchReviewDiff(session, logger, projectDir = null) {
198
+ let diff;
199
+ if (session.ci_pr_number) {
200
+ const { getPrDiff } = await import("../../ci/pr-diff.js");
201
+ diff = await getPrDiff(session.ci_pr_number);
202
+ logger.info(`Reviewer reading PR diff #${session.ci_pr_number}`);
203
+ } else {
204
+ diff = await generateDiff({ baseRef: session.session_start_sha, stageNewFiles: true, projectDir });
205
+ }
206
+ // Inbound boundary: mask hardcoded secrets before the diff reaches the
207
+ // (possibly cloud) reviewer model. Runs BEFORE the injection guard below so
208
+ // that guard still sees the real prompt-injection phrasing (KJC-TSK-0583).
209
+ return redactSecrets(diff);
210
+ }
211
+
212
+ export async function runReviewerStage({ reviewerRole, config, logger, emitter, eventBase, session, trackBudget, iteration, reviewRules, task, repeatDetector, budgetSummary, askQuestion, brainCtx }) {
213
+ logger.setContext({ iteration, stage: "reviewer" });
214
+ emitProgress(
215
+ emitter,
216
+ makeEvent("reviewer:start", { ...eventBase, stage: "reviewer" }, {
217
+ message: `Reviewer (${reviewerRole.provider}) running`,
218
+ detail: { reviewer: reviewerRole.provider, provider: reviewerRole.provider, executorType: "agent" }
219
+ })
220
+ );
221
+
222
+ let diff;
223
+ try {
224
+ diff = await fetchReviewDiff(session, logger, config?.projectDir || null);
225
+ } catch (err) {
226
+ logger.warn(`Review diff generation failed: ${err.message}`);
227
+ return { approved: false, blocking_issues: [{ description: `Diff generation failed: ${err.message}` }], non_blocking_suggestions: [], summary: `Reviewer failed: cannot generate diff — ${err.message}`, confidence: 0 };
228
+ }
229
+
230
+ // Injection guard: scan diff before sending to AI reviewer
231
+ const { scanDiff } = await import("../../utils/injection-guard.js");
232
+ const guardResult = scanDiff(diff);
233
+ if (!guardResult.clean) {
234
+ logger.warn(`Injection guard: ${guardResult.summary}`);
235
+ emitProgress(emitter, makeEvent("guard:injection", { ...eventBase, stage: "reviewer" }, {
236
+ message: `Injection guard blocked review: ${guardResult.summary}`,
237
+ detail: { findings: guardResult.findings, summary: guardResult.summary }
238
+ }));
239
+ return {
240
+ approved: false,
241
+ blocking_issues: guardResult.findings.map((f) => ({
242
+ id: `INJECTION_${f.type.toUpperCase()}`,
243
+ severity: "critical",
244
+ description: `Potential prompt injection (${f.type}): ${f.snippet}`,
245
+ line: f.line,
246
+ })),
247
+ non_blocking_suggestions: [],
248
+ summary: `Review blocked by injection guard: ${guardResult.summary}`,
249
+ confidence: 1,
250
+ };
251
+ }
252
+
253
+ const reviewerOnOutput = (payload) => emitAgentOutput(emitter, eventBase, "reviewer", reviewerRole.provider, payload);
254
+ const reviewerStall = createStallDetector({
255
+ onOutput: reviewerOnOutput, emitter, eventBase, stage: "reviewer", provider: reviewerRole.provider
256
+ });
257
+ const reviewerStart = Date.now();
258
+ let reviewerExec;
259
+ try {
260
+ reviewerExec = await runReviewerWithFallback({
261
+ reviewerName: reviewerRole.provider,
262
+ config,
263
+ logger,
264
+ emitter,
265
+ reviewInput: { task, diff, reviewRules, onOutput: reviewerStall.onOutput },
266
+ session,
267
+ iteration,
268
+ onAttemptResult: ({ reviewer, result }) => {
269
+ trackBudget({ role: "reviewer", provider: reviewer, model: reviewerRole.model, result, duration_ms: Date.now() - reviewerStart });
270
+ }
271
+ });
272
+ } catch (err) {
273
+ logger.warn(`Reviewer threw: ${err.message}`);
274
+ reviewerExec = { execResult: { ok: false, error: err.message }, attempts: [{ reviewer: reviewerRole.provider, result: { ok: false, error: err.message } }] };
275
+ } finally {
276
+ reviewerStall.stop();
277
+ }
278
+
279
+ if (applyQuotaSimulation(reviewerExec.execResult, { iteration, agent: reviewerRole.provider })) {
280
+ // Reviewer wraps the agent call: detectRateLimit reads `lastAttempt.result.error`,
281
+ // so propagate the synthetic line into the most recent attempt too.
282
+ const last = reviewerExec.attempts?.at(-1);
283
+ if (last) {
284
+ if (!last.result || typeof last.result !== "object") last.result = {};
285
+ last.result.error = reviewerExec.execResult.result.error;
286
+ }
287
+ }
288
+
289
+ if (!reviewerExec.execResult?.ok) {
290
+ const lastAttempt = reviewerExec.attempts.at(-1);
291
+ const details =
292
+ lastAttempt?.result?.error ||
293
+ lastAttempt?.execResult?.summary ||
294
+ `reviewer=${lastAttempt?.reviewer || "unknown"}`;
295
+
296
+ const rateLimitCheck = detectRateLimit({
297
+ stderr: lastAttempt?.result?.error || "",
298
+ stdout: lastAttempt?.result?.output || ""
299
+ });
300
+
301
+ if (rateLimitCheck.isRateLimit) {
302
+ // Enter standby instead of pausing
303
+ return {
304
+ action: "standby",
305
+ standbyInfo: {
306
+ agent: reviewerRole.provider,
307
+ cooldownMs: rateLimitCheck.cooldownMs || (rateLimitCheck.isProviderOutage ? 30000 : null),
308
+ cooldownUntil: rateLimitCheck.cooldownUntil,
309
+ message: rateLimitCheck.message,
310
+ isProviderOutage: rateLimitCheck.isProviderOutage || false
311
+ }
312
+ };
313
+ }
314
+
315
+ await markSessionStatus(session, "failed");
316
+ emitProgress(
317
+ emitter,
318
+ makeEvent("reviewer:end", { ...eventBase, stage: "reviewer" }, {
319
+ status: "fail",
320
+ message: `Reviewer failed: ${details}`,
321
+ detail: { provider: reviewerRole.provider, executorType: "agent" }
322
+ })
323
+ );
324
+ throw new Error(`Reviewer failed: ${details}`);
325
+ }
326
+
327
+ const reviewResult = reviewerExec.execResult.result;
328
+ let review;
329
+ try {
330
+ review = validateReviewResult({
331
+ approved: reviewResult.approved,
332
+ blocking_issues: reviewResult.blocking_issues || [],
333
+ non_blocking_suggestions: reviewResult.non_blocking_suggestions || [],
334
+ summary: reviewResult.raw_summary || "",
335
+ confidence: reviewResult.confidence ?? 0
336
+ });
337
+ } catch (error_) {
338
+ logger.warn(`Reviewer output validation failed: ${error_.message}`);
339
+ review = {
340
+ approved: false,
341
+ blocking_issues: [{
342
+ id: "PARSE_ERROR",
343
+ severity: "high",
344
+ description: `Reviewer output could not be parsed: ${error_.message}`
345
+ }],
346
+ non_blocking_suggestions: [],
347
+ summary: `Parse error: ${error_.message}`,
348
+ confidence: 0
349
+ };
350
+ }
351
+ // --- Scope filter: auto-defer out-of-scope blocking issues ---
352
+ const { review: filteredReview, demoted, deferred, allDemoted } = filterReviewScope(review, diff);
353
+ review = filteredReview;
354
+
355
+ if (demoted.length > 0) {
356
+ logger.info(`Scope filter: deferred ${demoted.length} out-of-scope issue(s)${allDemoted ? " — auto-approved" : ""}`);
357
+
358
+ // Accumulate deferred issues in session for tracking
359
+ if (!session.deferred_issues) setDeferredIssues(session, []);
360
+ session.deferred_issues.push(...deferred);
361
+ await saveSession(session);
362
+
363
+ emitProgress(
364
+ emitter,
365
+ makeEvent("reviewer:scope_filter", { ...eventBase, stage: "reviewer" }, {
366
+ message: `Scope filter deferred ${demoted.length} out-of-scope issue(s)`,
367
+ detail: {
368
+ demotedCount: demoted.length,
369
+ autoApproved: allDemoted,
370
+ totalDeferred: session.deferred_issues.length,
371
+ deferred: deferred.map(d => ({ file: d.file, id: d.id, description: d.description }))
372
+ }
373
+ })
374
+ );
375
+ await addCheckpoint(session, {
376
+ stage: "reviewer-scope-filter",
377
+ iteration,
378
+ demoted_count: demoted.length,
379
+ auto_approved: allDemoted,
380
+ total_deferred: session.deferred_issues.length
381
+ });
382
+ }
383
+
384
+ // Feedback signature for stale detection: concatenated issue descriptions
385
+ const feedbackSignature = review.blocking_issues
386
+ .map(x => (x.description || "").slice(0, 120))
387
+ .join("|") || "approved";
388
+
389
+ await addCheckpoint(session, {
390
+ stage: "reviewer",
391
+ iteration,
392
+ approved: review.approved,
393
+ blocking_issues: review.blocking_issues.length,
394
+ provider: reviewerRole.provider,
395
+ model: reviewerRole.model || null,
396
+ note: feedbackSignature
397
+ });
398
+
399
+ emitProgress(
400
+ emitter,
401
+ makeEvent("reviewer:end", { ...eventBase, stage: "reviewer" }, {
402
+ status: review.approved ? "ok" : "fail",
403
+ message: review.approved ? "Review approved" : `Review rejected (${review.blocking_issues.length} blocking)`,
404
+ detail: {
405
+ approved: review.approved,
406
+ blockingCount: review.blocking_issues.length,
407
+ issues: review.blocking_issues.map(
408
+ (x) => `${x.id || "ISSUE"}: ${x.description || "Missing description"}`
409
+ ),
410
+ provider: reviewerRole.provider,
411
+ executorType: "agent"
412
+ }
413
+ })
414
+ );
415
+
416
+ if (!review.approved) {
417
+ const rejectionResult = await handleReviewerRejection({ review, repeatDetector, config, logger, emitter, eventBase, session, iteration, task, askQuestion, budgetSummary, brainCtx });
418
+ if (rejectionResult) return rejectionResult;
419
+ }
420
+
421
+ return { review };
422
+ }