karajan-code 4.70.4 → 5.0.1

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 (390) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -14
  3. package/packages/hu-board/package.json +1 -1
  4. package/packages/hu-board/public/app.js +4 -18
  5. package/packages/hu-board/public/index.html +5 -2
  6. package/packages/hu-board/public/styles.css +26 -0
  7. package/packages/hu-board/public/utils/api.js +0 -74
  8. package/packages/hu-board/public/utils/board-tour.js +62 -0
  9. package/packages/hu-board/public/utils/board-view.js +49 -144
  10. package/packages/hu-board/public/utils/command-launcher.js +8 -70
  11. package/packages/hu-board/public/utils/formatters.js +10 -0
  12. package/packages/hu-board/public/utils/governance-view.js +13 -1
  13. package/packages/hu-board/public/utils/hu-actions.js +17 -68
  14. package/packages/hu-board/public/utils/init-listeners.js +7 -8
  15. package/packages/hu-board/public/utils/log-panel.js +3 -16
  16. package/packages/hu-board/public/utils/maggle-mode.js +2 -12
  17. package/packages/hu-board/public/utils/modals.js +7 -0
  18. package/packages/hu-board/public/utils/project-picker-view.js +1 -1
  19. package/packages/hu-board/public/utils/server-push.js +3 -22
  20. package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
  21. package/packages/hu-board/public/utils/story-detail-view.js +5 -19
  22. package/packages/hu-board/src/cleanup-zombies.js +0 -1
  23. package/packages/hu-board/src/command-runner.js +36 -25
  24. package/packages/hu-board/src/db-connection.js +282 -0
  25. package/packages/hu-board/src/db-paths.js +113 -0
  26. package/packages/hu-board/src/db-queries.js +188 -0
  27. package/packages/hu-board/src/db-tombstones.js +51 -0
  28. package/packages/hu-board/src/db.js +28 -619
  29. package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
  30. package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
  31. package/packages/hu-board/src/plan-mutations.js +3 -153
  32. package/packages/hu-board/src/plans-service.js +55 -0
  33. package/packages/hu-board/src/routes/api/commands.js +122 -0
  34. package/packages/hu-board/src/routes/api/plans.js +55 -0
  35. package/packages/hu-board/src/routes/api/projects.js +267 -0
  36. package/packages/hu-board/src/routes/api/prompts.js +135 -0
  37. package/packages/hu-board/src/routes/api/sessions.js +95 -0
  38. package/packages/hu-board/src/routes/api/stories.js +183 -0
  39. package/packages/hu-board/src/routes/api/system.js +241 -0
  40. package/packages/hu-board/src/routes/api/tombstones.js +38 -0
  41. package/packages/hu-board/src/routes/api.js +24 -1371
  42. package/packages/hu-board/src/routes/governance.js +23 -1
  43. package/packages/hu-board/src/routes/lanes.js +40 -0
  44. package/packages/hu-board/src/routes/rag.js +4 -3
  45. package/packages/hu-board/src/server.js +20 -34
  46. package/packages/hu-board/src/sync-files.js +318 -0
  47. package/packages/hu-board/src/sync-project.js +99 -0
  48. package/packages/hu-board/src/sync.js +22 -566
  49. package/packages/hu-board/src/zombie-reaper.js +1 -1
  50. package/scripts/dual-publish.mjs +19 -3
  51. package/scripts/postinstall.js +15 -2
  52. package/scripts/verify-pack-mcp.mjs +11 -7
  53. package/scripts/verify-pack.mjs +4 -4
  54. package/src/audit/audit-history.js +3 -2
  55. package/src/audit/dead-exports.js +68 -59
  56. package/src/audit/deterministic-summary.js +33 -13
  57. package/src/audit/npm-signatures.js +41 -0
  58. package/src/audit/sbom.js +35 -0
  59. package/src/checks/agent-token.js +112 -0
  60. package/src/checks/dir-setup.js +34 -1
  61. package/src/checks/native-build.js +21 -39
  62. package/src/checks/runner.js +128 -142
  63. package/src/cli/advanced-commands.js +6 -6
  64. package/src/cli/register-meta.js +42 -82
  65. package/src/cli/register-pipeline.js +8 -143
  66. package/src/cli/register-plan.js +9 -100
  67. package/src/cli/register-roles-skills.js +1 -1
  68. package/src/cli/retired-commands.js +49 -0
  69. package/src/cli.js +18 -5
  70. package/src/commands/agent-run.js +40 -26
  71. package/src/commands/audit.js +127 -124
  72. package/src/commands/board.js +5 -9
  73. package/src/commands/check.js +29 -16
  74. package/src/commands/doctor.js +2 -0
  75. package/src/commands/env.js +176 -150
  76. package/src/commands/go.js +71 -53
  77. package/src/commands/harden.js +90 -81
  78. package/src/commands/hu-start.js +52 -0
  79. package/src/commands/hu.js +288 -221
  80. package/src/commands/identity.js +111 -78
  81. package/src/commands/init-atril.js +47 -0
  82. package/src/commands/init.js +11 -40
  83. package/src/commands/install-tools.js +70 -85
  84. package/src/commands/main-guard.js +78 -0
  85. package/src/commands/policy.js +226 -184
  86. package/src/commands/qmd.js +27 -21
  87. package/src/commands/rag.js +121 -55
  88. package/src/commands/review-gate.js +421 -341
  89. package/src/commands/skills.js +4 -1
  90. package/src/commands/start.js +4 -5
  91. package/src/commands/steward.js +96 -47
  92. package/src/commands/tournament.js +46 -37
  93. package/src/commands/worktree.js +107 -26
  94. package/src/config/defaults.js +1 -0
  95. package/src/config/loader.js +34 -17
  96. package/src/config/role-resolver.js +1 -0
  97. package/src/config/schema.js +1 -0
  98. package/src/config/secrets.js +36 -0
  99. package/src/config.js +1 -0
  100. package/src/environment/adr.js +16 -2
  101. package/src/git/karajan-label.js +20 -8
  102. package/src/guards/main-guard.js +54 -0
  103. package/src/harden/hook-templates.js +16 -1
  104. package/src/harden/sentinel/posttooluse.mjs +18 -6
  105. package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
  106. package/src/harden/sentinel/sentinel-lib.mjs +129 -26
  107. package/src/harden/sentinel/sentinel-shell.mjs +5 -16
  108. package/src/harden/sentinel/sessionstart.mjs +3 -2
  109. package/src/harden/sentinel/stop.mjs +5 -4
  110. package/src/harden/workflow-templates.js +1 -1
  111. package/src/hu/board-lookup.js +29 -0
  112. package/src/hu/gh-identity.js +23 -0
  113. package/src/hu/github-board.js +4 -2
  114. package/src/hu/lane-prune.js +83 -13
  115. package/src/hu/lane-setup.js +65 -0
  116. package/src/hu/lanes.js +66 -0
  117. package/src/hu/team-board-mirror.js +3 -2
  118. package/src/infrastructure/command-runner.js +1 -1
  119. package/src/infrastructure/file-system-service.js +1 -1
  120. package/src/mcp/handlers/direct-handlers.js +2 -292
  121. package/src/mcp/handlers/hu-handlers.js +1 -35
  122. package/src/mcp/handlers/management-handlers.js +2 -42
  123. package/src/mcp/handlers/rag-handler.js +9 -1
  124. package/src/mcp/server-handlers.js +16 -22
  125. package/src/mcp/tools.js +1 -196
  126. package/src/plan/lane-plans.js +54 -0
  127. package/src/plan/plan-store.js +7 -4
  128. package/src/policy/add.js +26 -12
  129. package/src/policy/adr-number-check.js +31 -0
  130. package/src/prompts/audit.js +299 -339
  131. package/src/prompts/card-context.js +10 -5
  132. package/src/prompts/reviewer.js +5 -2
  133. package/src/rag/auto-update.js +71 -44
  134. package/src/rag/bootstrap-index.js +46 -54
  135. package/src/rag/indexer.js +9 -4
  136. package/src/rag/migrate.js +1 -1
  137. package/src/rag/onnx-fallback.js +2 -2
  138. package/src/rag/pid-lease.js +181 -0
  139. package/src/rag/project-store.js +2 -12
  140. package/src/review/card-first.js +57 -46
  141. package/src/review/gate-gitignore.js +23 -4
  142. package/src/review/gate-hook.js +12 -4
  143. package/src/review/one-shot-review.js +45 -35
  144. package/src/review/publish-pr.js +14 -2
  145. package/src/review/repo-docs.js +73 -0
  146. package/src/review/repo-stack.js +59 -0
  147. package/src/review/reviewer-fallback.js +19 -4
  148. package/src/review/seal-commit.js +29 -0
  149. package/src/review/sonar-pregate.js +59 -44
  150. package/src/review/third-party-review.js +90 -16
  151. package/src/review/third-party-size.js +43 -0
  152. package/src/review/third-party-skills.js +54 -0
  153. package/src/review/verify-pr.js +1 -24
  154. package/src/roles/audit-role.js +106 -125
  155. package/src/skills/atril-cli.js +19 -1
  156. package/src/skills/atril-skills.js +5 -4
  157. package/src/skills/skills-search.js +72 -0
  158. package/src/skills/translate.js +62 -0
  159. package/src/sonar/scanner.js +116 -101
  160. package/src/steward/invariants.js +5 -4
  161. package/src/steward/phantom-sweep.js +66 -0
  162. package/src/tournament/judge.js +60 -53
  163. package/src/utils/gh-account.js +37 -0
  164. package/src/utils/git.js +0 -13
  165. package/src/utils/home-migration.js +7 -1
  166. package/src/utils/main-checkout.js +53 -0
  167. package/src/utils/qmd-collection.js +3 -1
  168. package/src/utils/welcome.js +3 -3
  169. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  172. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  173. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  174. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  175. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  176. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  177. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  178. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  179. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  180. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  181. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  182. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  183. package/packages/hu-board/src/run-tracker.js +0 -121
  184. package/src/activity-log.js +0 -59
  185. package/src/agents/host-agent.js +0 -38
  186. package/src/agents/model-registry.js +0 -142
  187. package/src/audit/plan-adherence.js +0 -224
  188. package/src/autonomy/arbiter-role.js +0 -71
  189. package/src/autonomy/arbiter.js +0 -22
  190. package/src/autonomy/decider.js +0 -18
  191. package/src/autonomy/decision-resolver.js +0 -58
  192. package/src/autonomy/outcome-report.js +0 -55
  193. package/src/autonomy/stage-ask.js +0 -30
  194. package/src/brain/decision-tracker.js +0 -125
  195. package/src/brain/decisor.js +0 -243
  196. package/src/brain/solomon-consult.js +0 -171
  197. package/src/brain/standby-scheduler.js +0 -8
  198. package/src/budget/comparison.js +0 -96
  199. package/src/canvas/parse-md.js +0 -238
  200. package/src/canvas/schema.js +0 -101
  201. package/src/canvas/to-flat.js +0 -85
  202. package/src/canvas/validate.js +0 -163
  203. package/src/ci/dispatch.js +0 -99
  204. package/src/ci/index.js +0 -3
  205. package/src/ci/pr-diff.js +0 -26
  206. package/src/cli/register-standby.js +0 -26
  207. package/src/commands/architect.js +0 -111
  208. package/src/commands/autorun.js +0 -49
  209. package/src/commands/researcher.js +0 -54
  210. package/src/commands/resume.js +0 -72
  211. package/src/commands/review.js +0 -89
  212. package/src/commands/run.js +0 -185
  213. package/src/commands/standby.js +0 -117
  214. package/src/commands/status.js +0 -36
  215. package/src/commands/triage.js +0 -78
  216. package/src/commands/undo.js +0 -62
  217. package/src/domains/domain-loader.js +0 -245
  218. package/src/domains/domain-registry.js +0 -140
  219. package/src/domains/domain-synthesizer.js +0 -129
  220. package/src/git/automation.js +0 -433
  221. package/src/git/coder-commit-guard.js +0 -40
  222. package/src/git/hu-automation.js +0 -243
  223. package/src/git/hu-snapshot.js +0 -10
  224. package/src/git/symlink-targets.js +0 -57
  225. package/src/golden/asserter.js +0 -92
  226. package/src/golden/loader.js +0 -52
  227. package/src/golden/runner.js +0 -92
  228. package/src/golden/schema.js +0 -41
  229. package/src/guards/duplicate-members.js +0 -86
  230. package/src/guards/intent-guard.js +0 -161
  231. package/src/guards/perf-guard.js +0 -126
  232. package/src/guards/policy-guard.js +0 -90
  233. package/src/guards/policy-resolver.js +0 -85
  234. package/src/guards/secret-redactor.js +0 -71
  235. package/src/hu/acceptance-runner.js +0 -138
  236. package/src/hu/lazy-planner.js +0 -109
  237. package/src/hu/parallel-executor.js +0 -94
  238. package/src/hu/routing-metrics.js +0 -17
  239. package/src/hu/split-validator.js +0 -143
  240. package/src/hu/splitting-detector.js +0 -158
  241. package/src/hu/splitting-generator.js +0 -175
  242. package/src/hu/store.js +0 -394
  243. package/src/infrastructure/mocks.js +0 -182
  244. package/src/mcp/handlers/run-handler.js +0 -318
  245. package/src/mcp/sovereignty-guard.js +0 -113
  246. package/src/mcp/tool-arg-normalizers.js +0 -16
  247. package/src/mutate/reviewer-signal.js +0 -76
  248. package/src/orchestrator/agent-fallback.js +0 -81
  249. package/src/orchestrator/brain-coordinator.js +0 -186
  250. package/src/orchestrator/ci-integration.js +0 -139
  251. package/src/orchestrator/config-init.js +0 -426
  252. package/src/orchestrator/direct-actions.js +0 -217
  253. package/src/orchestrator/drivers/error-recovery.js +0 -287
  254. package/src/orchestrator/drivers/init-context.js +0 -238
  255. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  256. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  257. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  258. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  259. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  260. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  261. package/src/orchestrator/drivers/post-loop.js +0 -404
  262. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  263. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  264. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  265. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  266. package/src/orchestrator/drivers/pre-loop.js +0 -524
  267. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  268. package/src/orchestrator/feedback-enrichment.js +0 -140
  269. package/src/orchestrator/feedback-queue.js +0 -151
  270. package/src/orchestrator/flow-control.js +0 -170
  271. package/src/orchestrator/flow-runner.js +0 -365
  272. package/src/orchestrator/fs-leak-detector.js +0 -228
  273. package/src/orchestrator/hu-scheduler.js +0 -114
  274. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  275. package/src/orchestrator/iteration-gate.js +0 -73
  276. package/src/orchestrator/iteration-stages.js +0 -9
  277. package/src/orchestrator/parallel-limiter.js +0 -74
  278. package/src/orchestrator/pipeline-context.js +0 -72
  279. package/src/orchestrator/post-loop-stages.js +0 -468
  280. package/src/orchestrator/pre-loop-stages.js +0 -11
  281. package/src/orchestrator/preflight-checks.js +0 -487
  282. package/src/orchestrator/repair/repair-detector.js +0 -117
  283. package/src/orchestrator/repair/repair-runner.js +0 -142
  284. package/src/orchestrator/reviewer-fallback.js +0 -45
  285. package/src/orchestrator/role-output-compressor.js +0 -189
  286. package/src/orchestrator/session-journal.js +0 -308
  287. package/src/orchestrator/solomon-escalation.js +0 -331
  288. package/src/orchestrator/solomon-rules.js +0 -212
  289. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  290. package/src/orchestrator/stages/architect-stage.js +0 -172
  291. package/src/orchestrator/stages/coder-stage.js +0 -506
  292. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  293. package/src/orchestrator/stages/format-stage.js +0 -61
  294. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  295. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  296. package/src/orchestrator/stages/perf-stage.js +0 -140
  297. package/src/orchestrator/stages/planner-stage.js +0 -85
  298. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  299. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  300. package/src/orchestrator/stages/research-stage.js +0 -69
  301. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  302. package/src/orchestrator/stages/sonar-stage.js +0 -439
  303. package/src/orchestrator/stages/stage-classes.js +0 -84
  304. package/src/orchestrator/stages/stage-executor.js +0 -155
  305. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  306. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  307. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  308. package/src/orchestrator/stages/triage-stage.js +0 -172
  309. package/src/orchestrator/standby.js +0 -71
  310. package/src/orchestrator/verification-gate.js +0 -183
  311. package/src/orchestrator.js +0 -22
  312. package/src/plan/adr-generator.js +0 -170
  313. package/src/plan/adr-loader.js +0 -60
  314. package/src/plan/plan-executor.js +0 -100
  315. package/src/plan/plan-validation.js +0 -4
  316. package/src/prompts/acceptance-coverage.js +0 -32
  317. package/src/prompts/arbiter.js +0 -38
  318. package/src/prompts/architect.js +0 -103
  319. package/src/prompts/hu-reviewer.js +0 -244
  320. package/src/prompts/tool-correctness-judge.js +0 -31
  321. package/src/prompts/triage.js +0 -57
  322. package/src/rag/embedder.js +0 -2
  323. package/src/rag/embedders/_cloud-base.js +0 -2
  324. package/src/rag/embedders/cohere.js +0 -2
  325. package/src/rag/embedders/mistral.js +0 -2
  326. package/src/rag/embedders/onnx.js +0 -2
  327. package/src/rag/embedders/openai.js +0 -2
  328. package/src/rag/embedders/voyage.js +0 -2
  329. package/src/rag/rerank.js +0 -4
  330. package/src/rag/where-parser.js +0 -4
  331. package/src/repeat-detector.js +0 -88
  332. package/src/review/diff-generator.js +0 -112
  333. package/src/review/git-stash-helper.js +0 -104
  334. package/src/review/schema.js +0 -31
  335. package/src/review/scope-filter.js +0 -152
  336. package/src/review/snapshot-diff.js +0 -122
  337. package/src/review/tdd-discipline.js +0 -90
  338. package/src/review/tdd-policy.js +0 -69
  339. package/src/roles/acceptance-coverage-role.js +0 -78
  340. package/src/roles/architect-role.js +0 -77
  341. package/src/roles/coder-role.js +0 -113
  342. package/src/roles/commiter-role.js +0 -113
  343. package/src/roles/domain-curator-role.js +0 -134
  344. package/src/roles/hu-reviewer-role.js +0 -64
  345. package/src/roles/impeccable-role.js +0 -85
  346. package/src/roles/index.js +0 -18
  347. package/src/roles/karajan-brain-role.js +0 -134
  348. package/src/roles/perf-role.js +0 -122
  349. package/src/roles/planner-role.js +0 -151
  350. package/src/roles/refactorer-role.js +0 -30
  351. package/src/roles/repairer-role.js +0 -181
  352. package/src/roles/researcher-role.js +0 -70
  353. package/src/roles/reviewer-role.js +0 -115
  354. package/src/roles/security-role.js +0 -73
  355. package/src/roles/solomon-role.js +0 -271
  356. package/src/roles/sonar-role.js +0 -72
  357. package/src/roles/tester-role.js +0 -235
  358. package/src/roles/tool-correctness-judge-role.js +0 -78
  359. package/src/roles/triage-role.js +0 -99
  360. package/src/session/journal/decisions-writer.js +0 -200
  361. package/src/session/journal/iteration-logger.js +0 -194
  362. package/src/session/journal/summary-writer.js +0 -320
  363. package/src/session/journal/tree-writer.js +0 -164
  364. package/src/session/journal/write-all.js +0 -89
  365. package/src/session-cleanup.js +0 -116
  366. package/src/skills/semantic-detector.js +0 -167
  367. package/src/skills/skill-detector.js +0 -385
  368. package/src/sonar/cloud-scanner.js +0 -78
  369. package/src/tracker-bootstrap.js +0 -44
  370. package/src/types/index.js +0 -38
  371. package/src/types/policy.js +0 -43
  372. package/src/types/solomon.js +0 -87
  373. package/src/utils/async-lock.js +0 -31
  374. package/src/utils/budget.js +0 -229
  375. package/src/utils/cwd-confirm.js +0 -39
  376. package/src/utils/display/event-handlers.js +0 -403
  377. package/src/utils/display/formatters.js +0 -132
  378. package/src/utils/display/header.js +0 -47
  379. package/src/utils/display/session-summary.js +0 -118
  380. package/src/utils/display/solomon.js +0 -42
  381. package/src/utils/pricing.js +0 -29
  382. package/src/utils/quota-simulator.js +0 -56
  383. package/src/utils/role-assigner.js +0 -137
  384. package/src/utils/rtk-wrapper.js +0 -87
  385. package/src/utils/run-registry.js +0 -9
  386. package/src/utils/status-dashboard.js +0 -254
  387. package/src/utils/tool-call-extractor.js +0 -56
  388. package/src/webperf/devtools-detect.js +0 -112
  389. package/src/webperf/webperf-role-integration.js +0 -66
  390. package/templates/skills/kj-run.md +0 -73
@@ -1,125 +0,0 @@
1
- /**
2
- * Iteration phase: quality gates (TDD check, SonarQube local + cloud,
3
- * Impeccable design audit).
4
- *
5
- * Extracted verbatim from `src/orchestrator/drivers/iteration-loop.js` in
6
- * the v2.7.x audit follow-up to keep the iteration-loop driver under the
7
- * 600-LOC ceiling. Behaviour is byte-for-byte identical to the inlined
8
- * version.
9
- *
10
- * Returns { action: "ok" | "return" | "continue", result? }.
11
- * - "ok" → caller continues to the next phase.
12
- * - "return" → caller returns `result` (pause / stalled / blocked).
13
- * - "continue" → caller continues the iteration loop (next iteration).
14
- */
15
-
16
- import { generateDiff } from "../../../review/diff-generator.js";
17
- import { withLock } from "../../../utils/async-lock.js";
18
- import {
19
- runTddCheckStage, runSonarStage, runSonarCloudStage,
20
- } from "../../iteration-stages.js";
21
- import { runImpeccableStage } from "../../post-loop-stages.js";
22
- import { runPerfStage } from "../../stages/perf-stage.js";
23
- import { runTddDisciplineStage } from "../../stages/tdd-discipline-stage.js";
24
- import { runSuiteGreenStage, suiteGreenRequired } from "../../stages/suite-green-stage.js";
25
- import { runToolJudgeStage } from "../../stages/tool-judge-stage.js";
26
- import { runFormatStage } from "../../stages/format-stage.js";
27
- import { tryCiComment } from "../../ci-integration.js";
28
-
29
- export async function runQualityGateStages({ config, logger, emitter, eventBase, session, trackBudget, i, askQuestion, repeatDetector, budgetSummary, sonarState, task, stageResults, coderRole, pipelineFlags, brainCtx }) {
30
- // KJC-TSK-1237 (#2565): the coder's files reach every gate formatted.
31
- await runFormatStage({ config, logger, emitter, eventBase, session });
32
-
33
- const tddResult = await runTddCheckStage({ config, logger, emitter, eventBase, session, trackBudget, iteration: i, askQuestion, task, brainCtx });
34
- if (tddResult.action === "pause") return { action: "return", result: tddResult.result };
35
- if (tddResult.action === "continue") return { action: "continue" };
36
-
37
- // KJC-BUG-0298 (#1993): with TDD, a red suite never reaches the reviewer.
38
- if (config.development?.methodology === "tdd" && suiteGreenRequired(config)) {
39
- const green = await runSuiteGreenStage({ config, logger, emitter, eventBase, session, iteration: i });
40
- if (green.action === "continue") return { action: "continue" };
41
- }
42
-
43
- // KJC-TSK-0398 PR3: opt-in red-then-green check.
44
- if (config.development?.require_red_then_green) {
45
- const disc = await runTddDisciplineStage({
46
- config, logger, emitter, eventBase,
47
- sourceFiles: tddResult.sourceFiles || [],
48
- testFiles: tddResult.testFiles || [],
49
- });
50
- if (disc.action === "continue") return { action: "continue" };
51
- }
52
-
53
- // Sonar runs for code tasks per policy. Since v2.7.4 it is NOT
54
- // toggleable via config — that's intrinsic to Karajan. The taskType
55
- // policy (resolved_policies.sonar) is the single source of truth.
56
- // Solomon may skip a single iteration via rule alerts; that's a
57
- // runtime decision, not a config option.
58
- //
59
- // Test-harness escape hatch via config.testHarness.disableSonarStage
60
- // — production code reads `config?.testHarness?.disableSonarStage`,
61
- // never `globalThis.*`. The legacy override surface is documented
62
- // (and exclusively read) in src/config/test-harness.js. ESLint rule
63
- // (#557) blocks any re-introduction of `globalThis.__KJ_*` outside
64
- // that one file.
65
- const sonarStageDisabledForTest = config?.testHarness?.disableSonarStage === true;
66
- if (!sonarStageDisabledForTest && session.resolved_policies?.sonar !== false) {
67
- // Serialize across parallel HU lanes (KJC-TSK-0625): the SonarQube
68
- // server scans one project key at a time. No-op on sequential runs.
69
- const sonarResult = await withLock("sonar-scan", () => runSonarStage({
70
- config, logger, emitter, eventBase, session, trackBudget, iteration: i,
71
- repeatDetector, budgetSummary, sonarState, askQuestion, task, brainCtx
72
- }));
73
- if (sonarResult.action === "stalled" || sonarResult.action === "pause") return { action: "return", result: sonarResult.result };
74
- if (sonarResult.action === "continue") return { action: "continue" };
75
- if (sonarResult.stageResult) {
76
- stageResults.sonar = sonarResult.stageResult;
77
- await tryCiComment({ config, session, logger, agent: "Sonar", body: `SonarQube scan: ${sonarResult.stageResult.summary || "completed"}` });
78
- }
79
- }
80
-
81
- if (config.sonarcloud?.enabled) {
82
- const cloudResult = await runSonarCloudStage({
83
- config, logger, emitter, eventBase, session, trackBudget, iteration: i
84
- });
85
- if (cloudResult.stageResult) {
86
- stageResults.sonarcloud = cloudResult.stageResult;
87
- }
88
- }
89
-
90
- if (pipelineFlags?.impeccableEnabled) {
91
- const diff = await generateDiff({ baseRef: session.session_start_sha, projectDir: config?.projectDir || null });
92
- const impeccableMode = pipelineFlags?.impeccableMode || "audit";
93
- const impeccableResult = await runImpeccableStage({
94
- config, logger, emitter, eventBase, session, coderRole, trackBudget,
95
- iteration: i, task, diff, mode: impeccableMode
96
- });
97
- if (impeccableResult.stageResult) {
98
- stageResults.impeccable = impeccableResult.stageResult;
99
- }
100
- }
101
-
102
- if (pipelineFlags?.perfEnabled) {
103
- const perfResult = await runPerfStage({
104
- config, logger, emitter, eventBase, session, trackBudget,
105
- iteration: i, task, brainCtx
106
- });
107
- if (perfResult.stageResult) {
108
- stageResults.perf = perfResult.stageResult;
109
- }
110
- if (perfResult.action === "continue") return { action: "continue" };
111
- }
112
-
113
- // KJC-TSK-0375 PR3: opt-in tool-call quality judge. Non-blocking.
114
- if (pipelineFlags?.toolJudgeEnabled) {
115
- const judgeResult = await runToolJudgeStage({
116
- config, logger, emitter, eventBase, session, coderRole, trackBudget,
117
- iteration: i, task,
118
- });
119
- if (judgeResult.stageResult) {
120
- stageResults.toolJudge = judgeResult.stageResult;
121
- }
122
- }
123
-
124
- return { action: "ok" };
125
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * Iteration phase: reviewer stage with standby handling.
3
- *
4
- * Extracted verbatim from `src/orchestrator/drivers/iteration-loop.js` in
5
- * the v2.7.x audit follow-up to keep the iteration-loop driver under the
6
- * 600-LOC ceiling. Behaviour is byte-for-byte identical to the inlined
7
- * version.
8
- *
9
- * Returns { action: "ok" | "return" | "retry", review?, result? }.
10
- * - "ok" → caller proceeds with `review` (approved or with blocking
11
- * issues).
12
- * - "return" → caller returns `result` (pause / stalled).
13
- * - "retry" → caller decrements its iteration counter and retries.
14
- */
15
-
16
- import { stageRegistry } from "../../stages/stage-classes.js";
17
- import { runStage } from "../../stages/stage-executor.js";
18
- import { handleStandbyResult } from "../error-recovery.js";
19
-
20
- export async function runReviewerGateStage({ pipelineFlags, reviewerRole, config, logger, emitter, eventBase, session, trackBudget, i, reviewRules, task, repeatDetector, budgetSummary, askQuestion, brainCtx }) {
21
- // Reviewer via StageRegistry (TSK-0336). ReviewerStage.canRun returns
22
- // `reviewerEnabled !== false`; when false, runStage returns null and we
23
- // synthesize the "disabled-by-pipeline" stub (same shape as the previous
24
- // early-return). Otherwise execute() runs runReviewerStage.
25
- const reviewerCtx = {
26
- reviewerRole, config, logger, emitter, eventBase, session, trackBudget,
27
- iteration: i, reviewRules, task, repeatDetector, budgetSummary, askQuestion,
28
- brainCtx, pipelineFlags,
29
- };
30
- const reviewerResult = await runStage(stageRegistry.get("reviewer"), reviewerCtx);
31
- if (reviewerResult === null) {
32
- // KJC-BUG-0297 (#1993): `reviewed: false` so the pipeline never stamps a
33
- // verdict nobody gave nor commits on it (finalizeGitAutomation).
34
- return {
35
- action: "ok",
36
- review: { approved: true, reviewed: false, blocking_issues: [], non_blocking_suggestions: [], summary: "Reviewer disabled by pipeline: nothing was reviewed", confidence: 0 }
37
- };
38
- }
39
- if (reviewerResult.action === "pause") return { action: "return", result: reviewerResult.result };
40
- const revStandby = await handleStandbyResult({ stageResult: reviewerResult, session, emitter, eventBase, i, stage: "reviewer", logger, config, askQuestion });
41
- if (revStandby.handled) {
42
- if (revStandby.action === "return") return { action: "return", result: revStandby.result };
43
- if (revStandby.action === "skip") {
44
- // Solomon said skip review — treat as approved
45
- return { action: "ok", review: { approved: true, blocking_issues: [], non_blocking_suggestions: [], summary: "Review skipped (agent rate-limited, Solomon approved)", confidence: 0.7 } };
46
- }
47
- if (revStandby.action === "retry_reviewer_only") {
48
- // Retry just the reviewer — use alternative agent if Solomon recommended one
49
- let retryReviewerRole = reviewerRole;
50
- const alt = session._alternative_agent;
51
- if (alt?.stage === "reviewer" && alt?.provider) {
52
- retryReviewerRole = { provider: alt.provider, model: null };
53
- logger.info(`Retrying reviewer with alternative agent: ${alt.provider}`);
54
- delete session._alternative_agent;
55
- }
56
- return runReviewerGateStage({ pipelineFlags: { reviewerEnabled: true }, reviewerRole: retryReviewerRole, config, logger, emitter, eventBase, session, trackBudget, i, reviewRules, task, repeatDetector, budgetSummary, askQuestion });
57
- }
58
- return { action: "retry" };
59
- }
60
- if (reviewerResult.stalled) return { action: "return", result: reviewerResult.stalledResult };
61
- return { action: "ok", review: reviewerResult.review };
62
- }
@@ -1,404 +0,0 @@
1
- /**
2
- * Post-loop driver — extracted from src/orchestrator/flow-runner.js in
3
- * TSK-0335 (Oleada 3 of the v2.7.4 audit refactor).
4
- *
5
- * Handlers for everything that happens AFTER the iteration loop decides
6
- * an outcome (approved, max-iterations, or simple finalization):
7
- *
8
- * - handlePostLoopStages: run tester → security → final audit
9
- * after the reviewer has approved; any of
10
- * them can demand one more coder pass.
11
- * - finalizeApprovedSession: commit/push/PR + PG card transition +
12
- * full session journal + session:end event.
13
- * - handleMaxIterationsReached: Brain-owned decision at the iteration
14
- * cap (extend vs finalize vs escalate).
15
- * Legacy path delegates to Solomon.
16
- * - tryAutoStartBoard: auto-launch HU Board when config opts in.
17
- * - writeHistoryRecord: non-blocking HU history write on session end.
18
- *
19
- * Extracted verbatim; no behavior change. flow-runner.js now imports these
20
- * instead of defining them. Part of breaking up the 2254-LOC god-module —
21
- * see docs/ARCHITECTURE.md and the TSK-0335 acceptance criteria.
22
- */
23
-
24
- import { generateDiff } from "../../review/diff-generator.js";
25
- import { finalizeGitAutomation } from "../../git/automation.js";
26
- import { listCommitsBetween, listFilesChangedSince } from "../../utils/git.js";
27
- import { loadPlan } from "../../plan/plan-store.js";
28
- import { computePlanAdherenceScore } from "../../audit/plan-adherence.js";
29
- import { saveSession, markSessionStatus } from "../../session/store.js";
30
- import {
31
- setReviewerFeedback, setBudget, setRtkSavings, resetRetryCount,
32
- } from "../../session/mutators.js";
33
- import { emitProgress, makeEvent } from "../../utils/events.js";
34
- import { runTesterStage, runSecurityStage, runFinalAuditStage } from "../post-loop-stages.js";
35
- import { runAcceptanceCoverageStage } from "../stages/acceptance-coverage-stage.js";
36
- import { criteriaOfCard } from "../../roles/acceptance-coverage-role.js";
37
- import { invokeSolomon } from "../solomon-escalation.js";
38
- import { tryCiComment } from "../ci-integration.js";
39
- import { getIntegration } from "../integrations.js";
40
-
41
- export async function handlePostLoopStages({ config, session, emitter, eventBase, coderRole, trackBudget, i, task, stageResults, ciEnabled: _ciEnabled, testerEnabled, securityEnabled, askQuestion, logger, brainCtx }) {
42
- const postLoopDiff = await generateDiff({ baseRef: session.session_start_sha });
43
-
44
- if (testerEnabled) {
45
- const testerResult = await runTesterStage({
46
- config, logger, emitter, eventBase, session, coderRole, trackBudget,
47
- iteration: i, task, diff: postLoopDiff, askQuestion
48
- });
49
- if (testerResult.action === "pause") return { action: "return", result: testerResult.result };
50
- if (testerResult.action === "continue") {
51
- const summary = testerResult.stageResult?.summary || "Tester found issues";
52
- setReviewerFeedback(session, `Tester FAILED — fix these issues:\n${summary}`);
53
- await saveSession(session);
54
- if (testerResult.stageResult) stageResults.tester = testerResult.stageResult;
55
- // Brain: push tester failure into feedback queue + compress for next coder iteration
56
- if (brainCtx?.enabled) {
57
- const { processRoleOutput } = await import("../brain-coordinator.js");
58
- processRoleOutput(brainCtx, { roleName: "tester", output: testerResult.stageResult, iteration: i });
59
- }
60
- return { action: "continue" };
61
- }
62
- if (testerResult.stageResult) {
63
- stageResults.tester = testerResult.stageResult;
64
- await tryCiComment({ config, session, logger, agent: "Tester", body: `Tests: ${testerResult.stageResult.summary || "completed"}` });
65
- }
66
- }
67
-
68
- if (securityEnabled) {
69
- const securityResult = await runSecurityStage({
70
- config, logger, emitter, eventBase, session, coderRole, trackBudget,
71
- iteration: i, task, diff: postLoopDiff, askQuestion, brainCtx
72
- });
73
- if (securityResult.action === "pause") return { action: "return", result: securityResult.result };
74
- if (securityResult.action === "continue") {
75
- const summary = securityResult.stageResult?.summary || "Security found issues";
76
- setReviewerFeedback(session, `Security FAILED — fix these issues:\n${summary}`);
77
- await saveSession(session);
78
- if (securityResult.stageResult) stageResults.security = securityResult.stageResult;
79
- // Brain: push security findings into feedback queue + compress for next coder iteration
80
- if (brainCtx?.enabled) {
81
- const { processRoleOutput } = await import("../brain-coordinator.js");
82
- processRoleOutput(brainCtx, { roleName: "security", output: securityResult.stageResult, iteration: i });
83
- }
84
- return { action: "continue" };
85
- }
86
- if (securityResult.stageResult) {
87
- stageResults.security = securityResult.stageResult;
88
- await tryCiComment({ config, session, logger, agent: "Security", body: `Security scan: ${securityResult.stageResult.summary || "completed"}` });
89
- }
90
- }
91
-
92
- // Final audit — last quality gate before declaring success
93
- const auditResult = await runFinalAuditStage({
94
- config, logger, emitter, eventBase, session, coderRole, trackBudget,
95
- iteration: i, task, diff: postLoopDiff
96
- });
97
- if (auditResult.stageResult) {
98
- stageResults.audit = auditResult.stageResult;
99
- await tryCiComment({ config, session, logger, agent: "Audit", body: `Final audit: ${auditResult.stageResult.summary || "completed"}` });
100
- }
101
- if (auditResult.action === "retry") {
102
- // Audit found actionable issues — loop back to coder
103
- setReviewerFeedback(session, auditResult.feedback);
104
- await saveSession(session);
105
- return { action: "continue" };
106
- }
107
-
108
- // KJC-TSK-0987 (#1894): with a card, each acceptance criterion is read against
109
- // the diff by a different AI before the run is approved. An uncovered one goes
110
- // back to the coder; a judge with no verdict stops the approval, never grants it.
111
- const coverage = await runAcceptanceCoverageStage({
112
- config, logger, emitter, eventBase, trackBudget, iteration: i, task,
113
- criteria: criteriaOfCard(session.pg_card), diff: postLoopDiff,
114
- });
115
- if (coverage.stageResult) stageResults.acceptanceCoverage = coverage.stageResult;
116
- if (coverage.action === "retry") {
117
- setReviewerFeedback(session, coverage.feedback);
118
- await saveSession(session);
119
- return { action: "continue" };
120
- }
121
- if (coverage.action === "fail") {
122
- return { action: "return", result: { approved: false, sessionId: session.id, reason: "acceptance_coverage_unavailable", error: coverage.stageResult.summary } };
123
- }
124
-
125
- return { action: "proceed" };
126
- }
127
-
128
- export async function finalizeApprovedSession({ config, gitCtx, task, logger, session, stageResults, emitter, eventBase, budgetSummary, pgCard, pgProject, review, i, rtkTracker }) {
129
- const gitResult = await finalizeGitAutomation({ config, gitCtx, task, logger, session, stageResults, review });
130
-
131
- // Accumulate final commits for PG card lifecycle tracking
132
- if (gitResult?.commits?.length) {
133
- const tracker = getIntegration("tracker");
134
- for (const c of gitResult.commits) tracker?.onCommit?.(session, c);
135
- }
136
-
137
- if (stageResults.planner?.ok) {
138
- stageResults.planner.completedSteps = [...(stageResults.planner.steps ?? [])];
139
- }
140
- setBudget(session, budgetSummary());
141
- await markSessionStatus(session, "approved");
142
-
143
- await getIntegration("tracker")?.onSessionApproved?.({ pgCard, pgProject, config, session, gitResult, logger });
144
-
145
- const deferredIssues = session.deferred_issues || [];
146
- const rtkSavings = rtkTracker?.hasData() ? rtkTracker.summary() : undefined;
147
- if (rtkSavings) setRtkSavings(session, rtkSavings);
148
- await saveSession(session);
149
-
150
- // --- Journal: write final files (shared writer, KJC-BUG-0084) ---
151
- const journalDir = session._journalDir;
152
- if (journalDir) {
153
- // 2026-05-07 dogfooding: gitResult.commits only carries the post-loop
154
- // commit (scaffold). Compute the full range from session_start_sha so
155
- // every commit the run produced shows up in execution order.
156
- let allCommits;
157
- try { allCommits = (await listCommitsBetween(session.head_at_start || session.session_start_sha)) || []; }
158
- catch { allCommits = gitResult?.commits || []; }
159
- const summaryCommits = allCommits.length > 0 ? allCommits : (gitResult?.commits || []);
160
-
161
- // KJC-TSK-0376 — plan adherence metric (deepeval-inspired). When the
162
- // run executed against a known plan (`kj run --plan <id>`), score how
163
- // faithfully the coder followed it. Pure offline; safe-by-default
164
- // (try/catch swallows missing helper or unreadable plan).
165
- let planAdherence = null;
166
- try {
167
- const planId = session._planRef?.planId
168
- || stageResults?.huReviewer?.planId
169
- || null;
170
- if (planId) {
171
- const projectDir = config?.projectDir
172
- || session.config_snapshot?.projectDir
173
- || process.cwd();
174
- const plan = await loadPlan(projectDir, planId).catch(() => null);
175
- if (plan && Array.isArray(plan.hus) && plan.hus.length > 0) {
176
- const filesChanged = await listFilesChangedSince(
177
- session.head_at_start || session.session_start_sha,
178
- ).catch(() => []);
179
- planAdherence = computePlanAdherenceScore({
180
- commits: summaryCommits,
181
- plan,
182
- filesChanged,
183
- });
184
- }
185
- }
186
- } catch (err) {
187
- logger?.warn?.(`Plan adherence calculation skipped: ${err.message}`);
188
- }
189
-
190
- const { writeSessionJournal } = await import("../../session/journal/write-all.js");
191
- await writeSessionJournal({
192
- session, logger,
193
- result: "APPROVED",
194
- iterations: i,
195
- budget: budgetSummary(),
196
- stages: stageResults,
197
- commits: summaryCommits,
198
- planAdherence,
199
- });
200
- }
201
-
202
- // Mirror the same enriched commit list into the session:end event so the
203
- // terminal `🏁 Result` banner (printSessionGit) shows every commit the
204
- // run produced, not just the post-loop scaffold one. We compute again
205
- // (cheap: one git log call) rather than threading a variable through
206
- // because the journal block is wrapped in try/catch and may have set
207
- // `summaryCommits` to a fallback we don't want for the event detail.
208
- // try/catch tolerates both promise rejection and sync undefined returns
209
- // (see the journal block above for the detailed rationale).
210
- let eventCommits = null;
211
- try { eventCommits = await listCommitsBetween(session.head_at_start || session.session_start_sha); }
212
- catch { /* leave null — we'll use gitResult unchanged */ }
213
- const gitForEvent = (Array.isArray(eventCommits) && eventCommits.length > 0)
214
- ? { ...gitResult, commits: eventCommits }
215
- : gitResult;
216
- const endDetail = { approved: true, iterations: i, stages: stageResults, git: gitForEvent, budget: budgetSummary(), deferredIssues };
217
- if (rtkSavings) endDetail.rtk_savings = rtkSavings;
218
- emitProgress(
219
- emitter,
220
- makeEvent("session:end", { ...eventBase, stage: "done" }, {
221
- message: deferredIssues.length > 0
222
- ? `Session approved (${deferredIssues.length} deferred issue(s) tracked as tech debt)`
223
- : "Session approved",
224
- detail: endDetail
225
- })
226
- );
227
- const result = { approved: true, sessionId: session.id, review, git: gitResult, deferredIssues };
228
- if (rtkSavings) result.rtk_savings = rtkSavings;
229
- return result;
230
- }
231
-
232
- export async function handleMaxIterationsReached({ session, budgetSummary, emitter, eventBase, config, stageResults, logger, askQuestion, task, rtkTracker, brainCtx }) {
233
- const budget = budgetSummary();
234
-
235
- // Brain-owned decision: max_iterations is guidance, not a hard rule.
236
- // Brain evaluates the feedback queue state to decide extend / finalize / escalate.
237
- // Solomon is only consulted if Brain cannot decide on its own.
238
- if (brainCtx?.enabled) {
239
- const entries = brainCtx.feedbackQueue?.entries || [];
240
- const pending = entries.map(e => ({ source: e.source, category: e.category, severity: e.severity, description: e.description }));
241
- const hasSecurity = entries.some(e => e.category === "security" || e.source === "security");
242
- const hasCorrectness = entries.some(e => ["correctness", "tests"].includes(e.category));
243
-
244
- if (hasSecurity) {
245
- // Brain: security issues unresolved → cannot finalize, escalate
246
- logger.warn(`Brain: max_iterations reached with ${entries.filter(e => e.category === "security" || e.source === "security").length} unresolved security issue(s) — cannot finalize`);
247
- return { paused: true, sessionId: session.id, question: "Brain: unresolved security issues at max_iterations. Review manually or extend pipeline.", context: "brain_security_block", pending };
248
- }
249
-
250
- if (hasCorrectness) {
251
- // Brain: correctness/test issues pending. Cap at MAX_EXTENSIONS to avoid infinite extensions.
252
- const MAX_EXTENSIONS = 2;
253
- if (brainCtx.extensionCount >= MAX_EXTENSIONS) {
254
- logger.warn(`Brain: ${brainCtx.extensionCount} extensions exhausted with correctness issues still pending — escalating to human`);
255
- return { paused: true, sessionId: session.id, question: `Brain exhausted ${MAX_EXTENSIONS} extensions with correctness/tests still pending. Manual intervention needed.`, context: "brain_extension_cap", pending };
256
- }
257
- brainCtx.extensionCount += 1;
258
- logger.info(`Brain: max_iterations reached with ${entries.filter(e => ["correctness", "tests"].includes(e.category)).length} correctness issue(s) pending — extending iterations (extension ${brainCtx.extensionCount}/${MAX_EXTENSIONS})`);
259
- resetRetryCount(session, "reviewer");
260
- await saveSession(session);
261
- return { approved: false, sessionId: session.id, reason: "max_iterations_extended", extraIterations: Math.ceil(config.max_iterations / 2) };
262
- }
263
-
264
- if (entries.length === 0) {
265
- // Brain: no pending feedback → last reviewer approved, finalize
266
- logger.info("Brain: max_iterations reached with clean feedback queue — finalizing as approved");
267
- return { approved: true, sessionId: session.id, reason: "brain_approved" };
268
- }
269
-
270
- // hasStyleOnly: genuine dilemma → Brain consults Solomon
271
- logger.info(`Brain: max_iterations with ${entries.length} style-only issue(s) — consulting Solomon on dilemma`);
272
- const solomonResult = await invokeSolomon({
273
- config, logger, emitter, eventBase, stage: "max_iterations", askQuestion, session,
274
- iteration: config.max_iterations,
275
- conflict: {
276
- stage: "brain-max-iterations",
277
- task,
278
- iterationCount: config.max_iterations,
279
- maxIterations: config.max_iterations,
280
- budget_usd: budget?.total_cost_usd || 0,
281
- dilemma: `Max iterations reached with ${entries.length} style-only issue(s) pending. Accept as-is or request more work?`,
282
- pendingIssues: pending,
283
- history: [{ agent: "pipeline", feedback: session.last_reviewer_feedback || "Max iterations reached" }]
284
- }
285
- });
286
- // Brain applies Solomon's decision
287
- if (solomonResult.action === "approve") {
288
- logger.info("Brain: Solomon advised approve for style-only pending — finalizing");
289
- return { approved: true, sessionId: session.id, reason: "brain_solomon_approved" };
290
- }
291
- if (solomonResult.action === "continue") {
292
- return { approved: false, sessionId: session.id, reason: "max_iterations_extended", extraIterations: solomonResult.extraIterations || Math.ceil(config.max_iterations / 2) };
293
- }
294
- if (solomonResult.action === "pause") {
295
- return { paused: true, sessionId: session.id, question: solomonResult.question, context: "brain_solomon_dilemma" };
296
- }
297
- // Fallback: escalate to human
298
- return { paused: true, sessionId: session.id, question: `Brain+Solomon cannot resolve: ${entries.length} pending issue(s) at max_iterations`, context: "max_iterations" };
299
- }
300
-
301
- // Legacy path (Brain disabled): original Solomon-driven flow
302
- const solomonResult = await invokeSolomon({
303
- config, logger, emitter, eventBase, stage: "max_iterations", askQuestion, session,
304
- iteration: config.max_iterations,
305
- conflict: {
306
- stage: "max_iterations",
307
- task,
308
- iterationCount: config.max_iterations,
309
- maxIterations: config.max_iterations,
310
- budget_usd: budget?.total_cost_usd || 0,
311
- history: [{ agent: "pipeline", feedback: session.last_reviewer_feedback || "Max iterations reached without reviewer approval" }]
312
- }
313
- });
314
-
315
- if (solomonResult.action === "approve") {
316
- logger.info("Solomon approved coder's work at max_iterations checkpoint");
317
- return { approved: true, sessionId: session.id, reason: "solomon_approved" };
318
- }
319
-
320
- if (solomonResult.action === "continue") {
321
- if (solomonResult.humanGuidance) {
322
- setReviewerFeedback(session, `Solomon guidance: ${solomonResult.humanGuidance}`);
323
- }
324
- resetRetryCount(session, "reviewer");
325
- await saveSession(session);
326
- const extraIterations = solomonResult.extraIterations || config.max_iterations;
327
- return { approved: false, sessionId: session.id, reason: "max_iterations_extended", humanGuidance: solomonResult.humanGuidance, extraIterations };
328
- }
329
-
330
- if (solomonResult.action === "pause") {
331
- return { paused: true, sessionId: session.id, question: solomonResult.question, context: "max_iterations" };
332
- }
333
-
334
- if (solomonResult.action === "subtask") {
335
- return { paused: true, sessionId: session.id, subtask: solomonResult.subtask, context: "max_iterations_subtask" };
336
- }
337
-
338
- // Solomon couldn't resolve — fail
339
- setBudget(session, budgetSummary());
340
- const rtkSavings = rtkTracker?.hasData() ? rtkTracker.summary() : undefined;
341
- if (rtkSavings) setRtkSavings(session, rtkSavings);
342
- await markSessionStatus(session, "failed");
343
- const failDetail = { approved: false, reason: "max_iterations", iterations: config.max_iterations, stages: stageResults, budget: budgetSummary() };
344
- if (rtkSavings) failDetail.rtk_savings = rtkSavings;
345
- emitProgress(
346
- emitter,
347
- makeEvent("session:end", { ...eventBase, stage: "done" }, {
348
- status: "fail",
349
- message: "Max iterations reached (Solomon could not resolve)",
350
- detail: failDetail
351
- })
352
- );
353
- return { approved: false, sessionId: session.id, reason: "max_iterations" };
354
- }
355
-
356
- /**
357
- * KJC-TSK-0884 (ADR 0011): the board starts by itself, like Sonar. Only a
358
- * declared false on either switch opts out; nothing declared means on.
359
- */
360
- export const boardAutoStartWanted = (config) => config?.hu_board?.auto_start !== false && config?.hu_board?.enabled !== false;
361
-
362
- export async function tryAutoStartBoard(config, logger, emitter, eventBase) {
363
- if (!boardAutoStartWanted(config)) return;
364
- // Never auto-start during vitest: races the PID file and starts a detached
365
- // process that outlives the test run.
366
- if (process.env.VITEST || process.env.NODE_ENV === "test") return;
367
-
368
- try {
369
- const { startBoard, renderBoardBanner, boardSlug } = await import("../../commands/board.js");
370
- const boardPort = config.hu_board?.port || 4000;
371
- // KJC-TSK-0884: the project's scoped view (/p/<slug>), the parent repo's
372
- // even from a worktree lane.
373
- const slug = config.projectDir ? boardSlug(config.projectDir) : null;
374
- const boardResult = await startBoard(boardPort, { projectSlug: slug });
375
- const status = boardResult.alreadyRunning ? "already running" : "started";
376
- // Highlighted URL box — visible regardless of log level (matches the
377
- // post-planner auto-HU banner so the user gets the same signal either
378
- // way the board got started).
379
- console.log(renderBoardBanner({ url: boardResult.url, status }));
380
- logger.info(`HU Board ${status} at ${boardResult.url}`);
381
- emitProgress(emitter, makeEvent("board:started", eventBase, {
382
- message: `HU Board running at ${boardResult.url}`,
383
- detail: { pid: boardResult.pid, port: boardPort, url: boardResult.url }
384
- }));
385
- } catch (err) {
386
- logger.warn(`HU Board auto-start failed (non-blocking): ${err.message}`);
387
- }
388
- }
389
-
390
- export async function writeHistoryRecord({ sessionId, task, result, logger }) {
391
- try {
392
- const { createHistoryRecord } = await import("../../hu/store.js");
393
- const approved = Boolean(result?.approved);
394
- const summary = result?.review?.summary || result?.reason || null;
395
- await createHistoryRecord(sessionId, {
396
- task,
397
- result: JSON.stringify(result),
398
- approved,
399
- summary
400
- });
401
- } catch (err) {
402
- logger.warn(`HU history record failed (non-blocking): ${err.message}`);
403
- }
404
- }