karajan-code 4.70.3 → 5.0.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 (388) 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/ai-surface.js +9 -7
  61. package/src/checks/config-files.js +8 -7
  62. package/src/checks/dir-setup.js +34 -1
  63. package/src/checks/native-build.js +21 -39
  64. package/src/checks/runner.js +128 -142
  65. package/src/cli/advanced-commands.js +6 -6
  66. package/src/cli/register-meta.js +42 -82
  67. package/src/cli/register-pipeline.js +8 -140
  68. package/src/cli/register-plan.js +9 -100
  69. package/src/cli/retired-commands.js +49 -0
  70. package/src/cli.js +18 -5
  71. package/src/commands/agent-run.js +40 -26
  72. package/src/commands/audit.js +127 -124
  73. package/src/commands/board.js +5 -9
  74. package/src/commands/check.js +29 -16
  75. package/src/commands/doctor.js +2 -0
  76. package/src/commands/env.js +176 -148
  77. package/src/commands/go.js +76 -55
  78. package/src/commands/harden.js +105 -86
  79. package/src/commands/hu-start.js +52 -0
  80. package/src/commands/hu.js +288 -221
  81. package/src/commands/identity.js +111 -78
  82. package/src/commands/init-atril.js +47 -0
  83. package/src/commands/init.js +11 -40
  84. package/src/commands/install-tools.js +70 -85
  85. package/src/commands/main-guard.js +78 -0
  86. package/src/commands/policy.js +226 -184
  87. package/src/commands/qmd.js +27 -21
  88. package/src/commands/rag.js +75 -48
  89. package/src/commands/review-gate.js +422 -339
  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 +94 -25
  94. package/src/config/loader.js +34 -17
  95. package/src/config/overrides.js +2 -1
  96. package/src/config/secrets.js +36 -0
  97. package/src/config.js +1 -0
  98. package/src/environment/adr.js +16 -2
  99. package/src/environment/board-access.js +11 -9
  100. package/src/git/karajan-label.js +20 -8
  101. package/src/guards/main-guard.js +54 -0
  102. package/src/harden/branch-protection.js +36 -3
  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 +81 -35
  106. package/src/harden/sentinel/sentinel-lib.mjs +149 -23
  107. package/src/harden/sentinel/sentinel-shell.mjs +64 -0
  108. package/src/harden/sentinel/sessionstart.mjs +3 -2
  109. package/src/harden/sentinel/stop.mjs +5 -4
  110. package/src/harden/workflow-engine.js +12 -1
  111. package/src/harden/workflow-templates.js +1 -1
  112. package/src/hu/board-lookup.js +29 -0
  113. package/src/hu/gh-identity.js +23 -0
  114. package/src/hu/github-board.js +4 -2
  115. package/src/hu/lane-prune.js +15 -7
  116. package/src/hu/lane-setup.js +65 -0
  117. package/src/hu/lanes.js +66 -0
  118. package/src/hu/team-board-mirror.js +3 -2
  119. package/src/infrastructure/command-runner.js +1 -1
  120. package/src/infrastructure/file-system-service.js +1 -1
  121. package/src/mcp/handlers/direct-handlers.js +2 -292
  122. package/src/mcp/handlers/hu-handlers.js +44 -55
  123. package/src/mcp/handlers/management-handlers.js +2 -42
  124. package/src/mcp/server-handlers.js +16 -22
  125. package/src/mcp/tools.js +5 -200
  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 +56 -43
  134. package/src/rag/bootstrap-index.js +28 -54
  135. package/src/rag/migrate.js +1 -1
  136. package/src/rag/onnx-fallback.js +2 -2
  137. package/src/rag/pid-lease.js +70 -0
  138. package/src/rag/project-store.js +2 -12
  139. package/src/review/card-first.js +57 -46
  140. package/src/review/dual-review.js +5 -4
  141. package/src/review/gate-gitignore.js +23 -4
  142. package/src/review/gate-hook.js +47 -0
  143. package/src/review/one-shot-review.js +51 -37
  144. package/src/review/publish-pr.js +18 -3
  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 +34 -3
  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 +2 -26
  154. package/src/roles/audit-role.js +106 -125
  155. package/src/skills/atril-skills.js +5 -4
  156. package/src/sonar/scanner.js +116 -101
  157. package/src/steward/invariants.js +5 -4
  158. package/src/steward/phantom-sweep.js +66 -0
  159. package/src/tournament/judge.js +60 -53
  160. package/src/utils/agent-home.js +33 -0
  161. package/src/utils/gh-account.js +37 -0
  162. package/src/utils/git.js +0 -13
  163. package/src/utils/home-migration.js +7 -1
  164. package/src/utils/main-checkout.js +53 -0
  165. package/src/utils/qmd-collection.js +3 -1
  166. package/src/utils/welcome.js +3 -3
  167. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  168. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  169. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  172. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  173. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  174. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  175. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  176. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  177. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  178. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  179. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  180. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  181. package/packages/hu-board/src/run-tracker.js +0 -121
  182. package/src/activity-log.js +0 -59
  183. package/src/agents/host-agent.js +0 -38
  184. package/src/agents/model-registry.js +0 -142
  185. package/src/audit/plan-adherence.js +0 -224
  186. package/src/autonomy/arbiter-role.js +0 -71
  187. package/src/autonomy/arbiter.js +0 -22
  188. package/src/autonomy/decider.js +0 -18
  189. package/src/autonomy/decision-resolver.js +0 -58
  190. package/src/autonomy/outcome-report.js +0 -55
  191. package/src/autonomy/stage-ask.js +0 -30
  192. package/src/brain/decision-tracker.js +0 -125
  193. package/src/brain/decisor.js +0 -243
  194. package/src/brain/solomon-consult.js +0 -171
  195. package/src/brain/standby-scheduler.js +0 -8
  196. package/src/budget/comparison.js +0 -96
  197. package/src/canvas/parse-md.js +0 -238
  198. package/src/canvas/schema.js +0 -101
  199. package/src/canvas/to-flat.js +0 -85
  200. package/src/canvas/validate.js +0 -163
  201. package/src/ci/dispatch.js +0 -99
  202. package/src/ci/index.js +0 -3
  203. package/src/ci/pr-diff.js +0 -26
  204. package/src/cli/register-standby.js +0 -26
  205. package/src/commands/architect.js +0 -111
  206. package/src/commands/autorun.js +0 -49
  207. package/src/commands/researcher.js +0 -54
  208. package/src/commands/resume.js +0 -72
  209. package/src/commands/review.js +0 -89
  210. package/src/commands/run.js +0 -185
  211. package/src/commands/standby.js +0 -117
  212. package/src/commands/status.js +0 -36
  213. package/src/commands/triage.js +0 -78
  214. package/src/commands/undo.js +0 -62
  215. package/src/domains/domain-loader.js +0 -245
  216. package/src/domains/domain-registry.js +0 -140
  217. package/src/domains/domain-synthesizer.js +0 -129
  218. package/src/git/automation.js +0 -433
  219. package/src/git/coder-commit-guard.js +0 -40
  220. package/src/git/hu-automation.js +0 -243
  221. package/src/git/hu-snapshot.js +0 -10
  222. package/src/git/symlink-targets.js +0 -57
  223. package/src/golden/asserter.js +0 -92
  224. package/src/golden/loader.js +0 -52
  225. package/src/golden/runner.js +0 -92
  226. package/src/golden/schema.js +0 -41
  227. package/src/guards/duplicate-members.js +0 -86
  228. package/src/guards/intent-guard.js +0 -161
  229. package/src/guards/perf-guard.js +0 -126
  230. package/src/guards/policy-guard.js +0 -90
  231. package/src/guards/policy-resolver.js +0 -85
  232. package/src/guards/secret-redactor.js +0 -71
  233. package/src/hu/acceptance-runner.js +0 -138
  234. package/src/hu/lazy-planner.js +0 -109
  235. package/src/hu/parallel-executor.js +0 -94
  236. package/src/hu/routing-metrics.js +0 -17
  237. package/src/hu/split-validator.js +0 -143
  238. package/src/hu/splitting-detector.js +0 -158
  239. package/src/hu/splitting-generator.js +0 -175
  240. package/src/hu/store.js +0 -394
  241. package/src/infrastructure/mocks.js +0 -182
  242. package/src/mcp/handlers/run-handler.js +0 -318
  243. package/src/mcp/sovereignty-guard.js +0 -113
  244. package/src/mcp/tool-arg-normalizers.js +0 -16
  245. package/src/mutate/reviewer-signal.js +0 -76
  246. package/src/orchestrator/agent-fallback.js +0 -81
  247. package/src/orchestrator/brain-coordinator.js +0 -186
  248. package/src/orchestrator/ci-integration.js +0 -139
  249. package/src/orchestrator/config-init.js +0 -426
  250. package/src/orchestrator/direct-actions.js +0 -217
  251. package/src/orchestrator/drivers/error-recovery.js +0 -287
  252. package/src/orchestrator/drivers/init-context.js +0 -238
  253. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  254. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  255. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  256. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  257. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  258. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  259. package/src/orchestrator/drivers/post-loop.js +0 -404
  260. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  261. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  262. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  263. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  264. package/src/orchestrator/drivers/pre-loop.js +0 -524
  265. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  266. package/src/orchestrator/feedback-enrichment.js +0 -140
  267. package/src/orchestrator/feedback-queue.js +0 -151
  268. package/src/orchestrator/flow-control.js +0 -170
  269. package/src/orchestrator/flow-runner.js +0 -365
  270. package/src/orchestrator/fs-leak-detector.js +0 -228
  271. package/src/orchestrator/hu-scheduler.js +0 -114
  272. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  273. package/src/orchestrator/iteration-gate.js +0 -73
  274. package/src/orchestrator/iteration-stages.js +0 -9
  275. package/src/orchestrator/parallel-limiter.js +0 -74
  276. package/src/orchestrator/pipeline-context.js +0 -72
  277. package/src/orchestrator/post-loop-stages.js +0 -468
  278. package/src/orchestrator/pre-loop-stages.js +0 -11
  279. package/src/orchestrator/preflight-checks.js +0 -487
  280. package/src/orchestrator/repair/repair-detector.js +0 -117
  281. package/src/orchestrator/repair/repair-runner.js +0 -142
  282. package/src/orchestrator/reviewer-fallback.js +0 -45
  283. package/src/orchestrator/role-output-compressor.js +0 -189
  284. package/src/orchestrator/session-journal.js +0 -308
  285. package/src/orchestrator/solomon-escalation.js +0 -331
  286. package/src/orchestrator/solomon-rules.js +0 -212
  287. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  288. package/src/orchestrator/stages/architect-stage.js +0 -172
  289. package/src/orchestrator/stages/coder-stage.js +0 -506
  290. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  291. package/src/orchestrator/stages/format-stage.js +0 -61
  292. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  293. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  294. package/src/orchestrator/stages/perf-stage.js +0 -140
  295. package/src/orchestrator/stages/planner-stage.js +0 -85
  296. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  297. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  298. package/src/orchestrator/stages/research-stage.js +0 -69
  299. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  300. package/src/orchestrator/stages/sonar-stage.js +0 -439
  301. package/src/orchestrator/stages/stage-classes.js +0 -84
  302. package/src/orchestrator/stages/stage-executor.js +0 -155
  303. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  304. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  305. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  306. package/src/orchestrator/stages/triage-stage.js +0 -172
  307. package/src/orchestrator/standby.js +0 -71
  308. package/src/orchestrator/verification-gate.js +0 -183
  309. package/src/orchestrator.js +0 -22
  310. package/src/plan/adr-generator.js +0 -170
  311. package/src/plan/adr-loader.js +0 -60
  312. package/src/plan/plan-executor.js +0 -100
  313. package/src/plan/plan-validation.js +0 -4
  314. package/src/prompts/acceptance-coverage.js +0 -32
  315. package/src/prompts/arbiter.js +0 -38
  316. package/src/prompts/architect.js +0 -103
  317. package/src/prompts/hu-reviewer.js +0 -244
  318. package/src/prompts/tool-correctness-judge.js +0 -31
  319. package/src/prompts/triage.js +0 -57
  320. package/src/rag/embedder.js +0 -2
  321. package/src/rag/embedders/_cloud-base.js +0 -2
  322. package/src/rag/embedders/cohere.js +0 -2
  323. package/src/rag/embedders/mistral.js +0 -2
  324. package/src/rag/embedders/onnx.js +0 -2
  325. package/src/rag/embedders/openai.js +0 -2
  326. package/src/rag/embedders/voyage.js +0 -2
  327. package/src/rag/rerank.js +0 -4
  328. package/src/rag/where-parser.js +0 -4
  329. package/src/repeat-detector.js +0 -88
  330. package/src/review/diff-generator.js +0 -112
  331. package/src/review/git-stash-helper.js +0 -104
  332. package/src/review/schema.js +0 -31
  333. package/src/review/scope-filter.js +0 -152
  334. package/src/review/snapshot-diff.js +0 -122
  335. package/src/review/tdd-discipline.js +0 -90
  336. package/src/review/tdd-policy.js +0 -69
  337. package/src/roles/acceptance-coverage-role.js +0 -78
  338. package/src/roles/architect-role.js +0 -77
  339. package/src/roles/coder-role.js +0 -113
  340. package/src/roles/commiter-role.js +0 -113
  341. package/src/roles/domain-curator-role.js +0 -134
  342. package/src/roles/hu-reviewer-role.js +0 -64
  343. package/src/roles/impeccable-role.js +0 -85
  344. package/src/roles/index.js +0 -18
  345. package/src/roles/karajan-brain-role.js +0 -134
  346. package/src/roles/perf-role.js +0 -122
  347. package/src/roles/planner-role.js +0 -151
  348. package/src/roles/refactorer-role.js +0 -30
  349. package/src/roles/repairer-role.js +0 -181
  350. package/src/roles/researcher-role.js +0 -70
  351. package/src/roles/reviewer-role.js +0 -115
  352. package/src/roles/security-role.js +0 -73
  353. package/src/roles/solomon-role.js +0 -271
  354. package/src/roles/sonar-role.js +0 -72
  355. package/src/roles/tester-role.js +0 -235
  356. package/src/roles/tool-correctness-judge-role.js +0 -78
  357. package/src/roles/triage-role.js +0 -99
  358. package/src/session/journal/decisions-writer.js +0 -200
  359. package/src/session/journal/iteration-logger.js +0 -194
  360. package/src/session/journal/summary-writer.js +0 -320
  361. package/src/session/journal/tree-writer.js +0 -164
  362. package/src/session/journal/write-all.js +0 -89
  363. package/src/session-cleanup.js +0 -116
  364. package/src/skills/semantic-detector.js +0 -167
  365. package/src/skills/skill-detector.js +0 -385
  366. package/src/sonar/cloud-scanner.js +0 -78
  367. package/src/tracker-bootstrap.js +0 -44
  368. package/src/types/index.js +0 -38
  369. package/src/types/policy.js +0 -43
  370. package/src/types/solomon.js +0 -87
  371. package/src/utils/async-lock.js +0 -31
  372. package/src/utils/budget.js +0 -229
  373. package/src/utils/cwd-confirm.js +0 -39
  374. package/src/utils/display/event-handlers.js +0 -403
  375. package/src/utils/display/formatters.js +0 -132
  376. package/src/utils/display/header.js +0 -47
  377. package/src/utils/display/session-summary.js +0 -118
  378. package/src/utils/display/solomon.js +0 -42
  379. package/src/utils/pricing.js +0 -29
  380. package/src/utils/quota-simulator.js +0 -56
  381. package/src/utils/role-assigner.js +0 -137
  382. package/src/utils/rtk-wrapper.js +0 -87
  383. package/src/utils/run-registry.js +0 -9
  384. package/src/utils/status-dashboard.js +0 -254
  385. package/src/utils/tool-call-extractor.js +0 -56
  386. package/src/webperf/devtools-detect.js +0 -112
  387. package/src/webperf/webperf-role-integration.js +0 -66
  388. 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
- }