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,468 +0,0 @@
1
- /**
2
- * HU sub-pipeline driver — extracted from flow-runner.js in PR-L
3
- * (audit follow-up). Was 244 LOC inlined inside _runFlowInner, the
4
- * second-worst long-function per `kj audit`.
5
- *
6
- * Behaviour identical to the inlined version — the closure passed to
7
- * runHuSubPipeline is moved verbatim, only its surrounding context
8
- * (logger, emitter, ctx, askQuestion) becomes function parameters.
9
- *
10
- * The extraction also fixes a latent bug: flow-runner.js called
11
- * `runCoderStage`, `runSonarStage`, `generateDiff` and
12
- * `runSecurityStage` without static OR dynamic imports. Those
13
- * references would ReferenceError at runtime on the HU sub-pipeline
14
- * path; the fact that no test caught it suggests the path was rarely
15
- * exercised end-to-end (preflight failures often abort earlier).
16
- * This module imports them explicitly.
17
- *
18
- * Returns:
19
- * - { handled: true, result } — needsSubPipeline matched; caller
20
- * must short-circuit and return `result`.
21
- * - { handled: false } — caller falls through to the standard
22
- * single-task pipeline (runIterationLoop).
23
- */
24
-
25
- import { emitProgress, makeEvent } from "../../utils/events.js";
26
- import { needsSubPipeline, runHuSubPipeline } from "../hu-sub-pipeline.js";
27
- import { runCoderStage } from "../stages/coder-stage.js";
28
- import { runSonarStage } from "../stages/sonar-stage.js";
29
- import { runSuiteGreenStage, suiteGreenRequired } from "../stages/suite-green-stage.js";
30
- import { runIterationLoop } from "./iteration-loop.js";
31
- import { classifyFailure, createFailureTracker } from "../repair/repair-detector.js";
32
- import { runRepair } from "../repair/repair-runner.js";
33
- import { writeHistoryRecord } from "./post-loop.js";
34
- import { setReviewerFeedback } from "../../session/mutators.js";
35
- import { createHuSnapshot } from "../../git/hu-snapshot.js";
36
- import { generateDiff } from "../../review/diff-generator.js";
37
- import { runAcceptanceCoverageStage } from "../stages/acceptance-coverage-stage.js";
38
- import { criteriaOfCard } from "../../roles/acceptance-coverage-role.js";
39
- import { applyCoderRoute } from "../../hu/coder-routing.js";
40
-
41
- /**
42
- * @param {object} args
43
- * @param {object} args.ctx — PipelineContext from initFlowContext
44
- * @param {string} args.task — original task description (for writeHistoryRecord)
45
- * @param {object} args.askQuestion
46
- * @param {object} args.emitter
47
- * @param {object} args.logger
48
- * @returns {Promise<{handled: boolean, result?: object}>}
49
- */
50
- export async function runHuBatch({ ctx, task, askQuestion, emitter, logger }) {
51
- if (!needsSubPipeline(ctx.stageResults.huReviewer)) {
52
- return { handled: false };
53
- }
54
-
55
- // --- HU Sub-Pipeline: run each certified HU as an independent iteration loop ---
56
- logger.info(`HU sub-pipeline: ${ctx.stageResults.huReviewer.certified} certified stories — running each as a sub-pipeline`);
57
- emitProgress(emitter, makeEvent("hu:sub-pipeline:start", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
58
- message: `Running ${ctx.stageResults.huReviewer.certified} HUs as sub-pipelines`,
59
- detail: { total: ctx.stageResults.huReviewer.total, certified: ctx.stageResults.huReviewer.certified }
60
- }));
61
-
62
- // Per-HU pipeline: focused max_iterations, fresh Brain state, own git branch.
63
- const huMaxIterations = ctx.config.hu_max_iterations ?? 3;
64
- const huBranches = new Map();
65
- const { prepareHuBranch, finalizeHuCommit, collectGitWarnings } = await import("../../git/hu-automation.js");
66
-
67
- // PR F (v2.7.5): the per-HU loop needs three pieces of plan context:
68
- // • ADRs (loaded once from disk, applies to every HU on this plan)
69
- // • the plan-reviewer findings (rides on the huBatch)
70
- // • each HU's spec_section (rides on the story)
71
- // Loading ADRs once outside the loop avoids hitting the filesystem
72
- // per-HU. When the plan never went through `kj plan` (legacy
73
- // auto-generated batch), planId is null → loadActiveAdrs falls back
74
- // to the "_loose" bucket and returns [] silently.
75
- const planId = ctx.stageResults.huReviewer.planId || null;
76
- const reviewerFindings = ctx.stageResults.huReviewer.review || null;
77
- let planAdrs = [];
78
- try {
79
- const { loadActiveAdrs } = await import("../../plan/adr-loader.js");
80
- planAdrs = await loadActiveAdrs(ctx.config.projectDir || process.cwd(), planId);
81
- if (planAdrs.length > 0) {
82
- logger.info(`Plan ${planId || "(loose)"}: loaded ${planAdrs.length} ADR(s) for coder context`);
83
- }
84
- } catch (err) {
85
- logger.warn(`ADR loader failed (non-blocking): ${err.message}`);
86
- }
87
-
88
- const subPipelineResult = await runHuSubPipeline({
89
- huReviewerResult: ctx.stageResults.huReviewer,
90
- runIterationFn: async (huTask, story, laneOpts = {}) => {
91
- // Apply per-HU policies based on task_type (infra skips reviewer/sonar/tdd).
92
- // KJC-TSK-0400: `effectiveTaskType` mira primero story.task_type y, si
93
- // no es válido, infiere del prefijo del title ([SPIKE], [DOC]…). Así
94
- // un HU con title "[SPIKE] Decidir auth" entra automáticamente a las
95
- // policies de spike (sin Sonar/TDD/tests).
96
- const { applyPolicies, effectiveTaskType } = await import("../../guards/policy-resolver.js");
97
- const resolvedTaskType = effectiveTaskType(story);
98
- if (resolvedTaskType !== story.task_type) {
99
- logger.info(`HU ${story.id}: task_type inferido del title → ${resolvedTaskType} (era ${story.task_type || "null"})`);
100
- }
101
- const huPolicies = applyPolicies({ taskType: resolvedTaskType, policies: ctx.config.policies });
102
- logger.info(`HU ${story.id} (${resolvedTaskType}): policies → reviewer=${huPolicies.reviewer}, tdd=${huPolicies.tdd}, sonar=${huPolicies.sonar}, tests=${huPolicies.testsRequired}`);
103
-
104
- // PAR-E2 PR2 (KJC-TSK-0629): every per-HU adjustment lands on LANE
105
- // copies. ctx.config/pipelineFlags/session/brainCtx are shared by all
106
- // lanes, so concurrent lanes must never mutate them. This also fixes a
107
- // latent leak: max_iterations was mutated on ctx.config and only the
108
- // fallback path restored it — the acceptance path returned early and
109
- // left it clamped to hu_max_iterations for the rest of the run.
110
- const worktreePath = laneOpts?.worktreePath || null;
111
- const projectDir = worktreePath || ctx.config.projectDir || process.cwd();
112
- // PAR-H (KJC-TSK-0631): the lane's slot travels as env vars so any
113
- // service the coder or the acceptance tests start can offset its
114
- // ports (offset = slot × 100; the project applies its own base).
115
- const laneEnv = Number.isInteger(laneOpts?.laneSlot)
116
- ? { KJ_LANE_SLOT: String(laneOpts.laneSlot), KJ_PORT_OFFSET: String(laneOpts.laneSlot * 100) }
117
- : null;
118
- const laneConfig = { ...ctx.config, projectDir, max_iterations: huMaxIterations, lane_env: laneEnv };
119
- if (!huPolicies.tdd) laneConfig.development = { ...laneConfig.development, methodology: "standard", require_test_changes: false };
120
- if (!huPolicies.sonar) laneConfig.sonarqube = { ...laneConfig.sonarqube, enabled: false };
121
- const laneFlags = { ...ctx.pipelineFlags };
122
- if (!huPolicies.reviewer) laneFlags.reviewerEnabled = false;
123
- if (!huPolicies.testsRequired) laneFlags.testerEnabled = false;
124
- // Worktree lanes clone the session so reviewer feedback never leaks
125
- // across concurrent HUs. Disk saves still share the session id (last
126
- // writer wins) — journal noise at most; in-memory isolation is what
127
- // guards correctness.
128
- const laneSession = worktreePath ? { ...ctx.session } : ctx.session;
129
- let laneBrain = ctx.brainCtx;
130
- if (ctx.brainCtx?.enabled) {
131
- const { createBrainContext } = await import("../brain-coordinator.js");
132
- const fresh = createBrainContext({ enabled: true });
133
- if (worktreePath) {
134
- laneBrain = { ...ctx.brainCtx, extensionCount: 0, feedbackQueue: fresh.feedbackQueue, verificationTracker: fresh.verificationTracker };
135
- } else {
136
- ctx.brainCtx.extensionCount = 0;
137
- ctx.brainCtx.feedbackQueue = fresh.feedbackQueue;
138
- ctx.brainCtx.verificationTracker = fresh.verificationTracker;
139
- }
140
- }
141
- let lanePlannedTask = ctx.plannedTask;
142
-
143
- // A worktree lane skips the main-tree checkout: `git worktree add -b`
144
- // already left the worktree checked out on kj-hu-<id>.
145
- let branchName;
146
- if (worktreePath) {
147
- branchName = `kj-hu-${story.id}`;
148
- huBranches.set(story.id, branchName);
149
- } else {
150
- branchName = await prepareHuBranch({ story, huBranches, config: laneConfig, logger, cardId: ctx.session?.pg_task_id || null });
151
- }
152
-
153
- // KJC-TSK-0408 step 2: snapshot del workspace ANTES de invocar al
154
- // coder. Si el usuario hace Undo después, restaura este SHA. El
155
- // ref se crea sobre el HEAD ya checkouteado a la rama de la HU,
156
- // así que apunta al estado limpio sobre el que el coder empieza.
157
- // Idempotente — si la HU ya tenía snapshot, se sobreescribe.
158
- try {
159
- const snap = await createHuSnapshot({ projectDir, huId: story.id });
160
- if (snap.ok) {
161
- story.outcome = { ...(story.outcome || {}), snapshot_sha: snap.sha, snapshot_ref: snap.ref };
162
- logger.info(`HU ${story.id}: snapshot ${snap.sha.slice(0, 7)} (ref ${snap.ref})`);
163
- } else {
164
- logger.warn(`HU ${story.id}: snapshot falló (non-blocking): ${snap.error}`);
165
- }
166
- } catch (err) {
167
- logger.warn(`HU ${story.id}: snapshot threw (non-blocking): ${err.message}`);
168
- }
169
-
170
- // If HU has acceptance_tests, Brain runs them as the gate instead of
171
- // the standard reviewer/tester pipeline. This is the radical fix:
172
- // concrete executable tests replace subjective reviewer opinions.
173
- if (story.acceptance_tests?.length > 0) {
174
- const { runAcceptanceTests, buildDiagnosticPrompt } = await import("../../hu/acceptance-runner.js");
175
- // Issue #538 — Repairer runtime safety net. The tracker remembers
176
- // each test's last failure signature; classifyFailure flags
177
- // structural bugs (broken jq, shell parse error, missing tool)
178
- // after 2 consecutive identical failures so we don't burn 5
179
- // iterations against an irreparable test (the 2026-04-29 bug).
180
- const failureTracker = createFailureTracker();
181
-
182
- for (let attempt = 1; attempt <= laneConfig.max_iterations; attempt++) {
183
- logger.info(`HU ${story.id}: coder iteration ${attempt}/${laneConfig.max_iterations}`);
184
- applyCoderRoute({ laneConfig, story, attempt, config: ctx.config, logger });
185
- emitProgress(emitter, makeEvent("iteration:start", { ...ctx.eventBase, stage: "iteration" }, {
186
- message: `Iteration ${attempt}/${laneConfig.max_iterations}`,
187
- detail: { iteration: attempt, maxIterations: laneConfig.max_iterations }
188
- }));
189
-
190
- // Coder runs with the HU task + any diagnostic feedback from previous attempt.
191
- // Phase 2 of tests-first (v2.7.5): also hand over the declared
192
- // acceptance_tests so the coder has the contract from turn 1,
193
- // not only after the first failed run.
194
- const coderResult = await runCoderStage({
195
- coderRoleInstance: ctx.coderRoleInstance, coderRole: ctx.coderRole,
196
- config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
197
- session: laneSession, plannedTask: lanePlannedTask,
198
- trackBudget: ctx.trackBudget, iteration: attempt, brainCtx: laneBrain,
199
- acceptanceTests: story.acceptance_tests,
200
- // PR F (v2.7.5): plan-aware coder context. ADRs are
201
- // shared across the plan, the rest are scoped to this
202
- // HU. buildCoderPrompt skips any section whose data is
203
- // null/empty so legacy paths render the same prompt.
204
- adrs: planAdrs,
205
- specSection: story.spec_section || null,
206
- reviewerFindings,
207
- huId: story.id
208
- });
209
- if (coderResult?.action === "standby" || coderResult?.action === "pause") {
210
- return coderResult?.result || { approved: false, reason: "coder_failed" };
211
- }
212
-
213
- // Sonar quality gate (sw task_type only, when policies say sonar=true)
214
- if (huPolicies.sonar && laneConfig.sonarqube?.enabled) {
215
- try {
216
- const sonarResult = await runSonarStage({
217
- config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
218
- session: laneSession, trackBudget: ctx.trackBudget, iteration: attempt,
219
- // sonarState is required: runSonarStage reads/writes
220
- // .issuesInitial / .issuesFinal on it. Forgetting this
221
- // surfaces as `Cannot read properties of undefined
222
- // (reading 'issuesInitial')` and the catch below swallows
223
- // it as "sonar stage failed (non-blocking)" — the gate
224
- // never actually runs for the HU, every iteration burns
225
- // budget for nothing. Mirrors iteration-loop.js.
226
- sonarState: ctx.sonarState,
227
- repeatDetector: ctx.repeatDetector,
228
- budgetSummary: ctx.budgetSummary,
229
- askQuestion, brainCtx: laneBrain
230
- });
231
- if (sonarResult?.action === "continue") {
232
- // Sonar failed — add to feedback for next coder attempt
233
- lanePlannedTask = `${huTask}\n\n--- SONAR FAILURE ---\n${laneSession.last_reviewer_feedback}`;
234
- continue;
235
- }
236
- } catch (err) {
237
- logger.warn(`HU ${story.id}: sonar stage failed (non-blocking): ${err.message}`);
238
- }
239
- }
240
-
241
- // KJC-BUG-0328 (#2088): a code HU is never approved over a red project
242
- // suite; it goes back to the coder, as in the iteration loop.
243
- if ((huPolicies.tdd || huPolicies.testsRequired) && suiteGreenRequired(laneConfig)) {
244
- const green = await runSuiteGreenStage({ config: laneConfig, logger, emitter, eventBase: ctx.eventBase, session: laneSession, iteration: attempt });
245
- if (green.action === "continue") {
246
- lanePlannedTask = `${huTask}\n\n--- TEST SUITE RED ---\n${laneSession.last_reviewer_feedback}`;
247
- continue;
248
- }
249
- }
250
-
251
- // Brain runs acceptance tests
252
- logger.info(`HU ${story.id}: running ${story.acceptance_tests.length} acceptance tests`);
253
- emitProgress(emitter, makeEvent("hu:acceptance-start", { ...ctx.eventBase, stage: "acceptance" }, {
254
- message: `Running ${story.acceptance_tests.length} acceptance tests`,
255
- detail: { huId: story.id, testCount: story.acceptance_tests.length }
256
- }));
257
-
258
- const testResult = await runAcceptanceTests(story.acceptance_tests, projectDir, { env: laneEnv });
259
- emitProgress(emitter, makeEvent("hu:acceptance-end", { ...ctx.eventBase, stage: "acceptance" }, {
260
- status: testResult.allPassed ? "ok" : "fail",
261
- message: testResult.summary,
262
- detail: { allPassed: testResult.allPassed, results: testResult.results.map(r => ({ cmd: r.cmd, passed: r.passed })) }
263
- }));
264
-
265
- if (testResult.allPassed) {
266
- // Phase 3 of tests-first (v2.7.5): the shell gate is green,
267
- // but any Gherkin scenarios are still "pending" — they need
268
- // the tester to translate them into real test files before
269
- // we can call the HU done. If none are pending, approve
270
- // immediately like before.
271
- const pendingGherkinTests = (story.acceptance_tests || []).filter(
272
- (t) => t && typeof t === "object" && t.type === "gherkin"
273
- );
274
- if (pendingGherkinTests.length === 0) {
275
- logger.info(`HU ${story.id}: all shell acceptance tests PASSED, no Gherkin to translate — approved`);
276
- const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
277
- return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed", git };
278
- }
279
-
280
- // Shell tests passed; Gherkin needs translation. Invoke the
281
- // tester with both so it writes code tests for the Gherkin,
282
- // runs the suite, and returns verdict.
283
- logger.info(`HU ${story.id}: shell gate passed, asking tester to translate ${pendingGherkinTests.length} Gherkin scenario(s)`);
284
- const { runTesterStage } = await import("../post-loop-stages.js");
285
- const shellResults = testResult.results.filter((r) => r.type !== "gherkin");
286
- const testerOutcome = await runTesterStage({
287
- config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
288
- session: laneSession, coderRole: ctx.coderRole, trackBudget: ctx.trackBudget,
289
- iteration: attempt, task: huTask, diff: null, askQuestion,
290
- pendingGherkinTests, shellTestResults: shellResults,
291
- });
292
- const testerStage = testerOutcome?.stageResult;
293
- if (testerOutcome?.action !== "continue" && testerStage?.verdict === "pass") {
294
- logger.info(`HU ${story.id}: tester translated Gherkin and verdict=pass — approved`);
295
- const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
296
- return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed", git };
297
- }
298
-
299
- // Tester rejected (translated tests failed or coverage
300
- // inadequate). Feed its diagnostic back to the coder for
301
- // another iteration.
302
- const failing = testerStage?.failing_scenarios?.join("\n- ") || testerStage?.summary || "tester rejected";
303
- const diagnostic = [
304
- "Tester translated the Gherkin acceptance tests and some FAILED:",
305
- failing ? `- ${failing}` : "",
306
- "",
307
- "Fix the implementation so every scenario passes. Do NOT soften the scenarios.",
308
- ].filter(Boolean).join("\n");
309
- logger.warn(`HU ${story.id}: tester verdict=fail after Gherkin translation — sending back to coder`);
310
- setReviewerFeedback(laneSession, diagnostic);
311
- lanePlannedTask = `${huTask}\n\n--- GHERKIN TRANSLATION FAILURES ---\n${diagnostic}`;
312
- continue;
313
- }
314
-
315
- // Brain diagnoses failures and sends concrete fix to coder
316
- const failed = testResult.results.filter(r => !r.passed);
317
-
318
- // Repairer (#538): for each failing shell test, see if it's been
319
- // failing the same way 2× in a row AND looks structurally broken
320
- // (jq syntax, shell parse error, missing tool, etc.). If yes,
321
- // invoke RepairerRole to rewrite the test in the plan + batch
322
- // before iterating again. If unfixable, escalate (fail the HU
323
- // immediately rather than burn the rest of max_iterations).
324
- let repairedAny = false;
325
- let repairEscalate = null;
326
- for (const f of failed) {
327
- if (f.type !== "shell") continue;
328
- const consecutive = failureTracker.record(f.cmd, f.output);
329
- const cls = classifyFailure({
330
- cmd: f.cmd, errorOutput: f.output, consecutive,
331
- });
332
- if (!cls.infeasible) continue;
333
- const testIndex = story.acceptance_tests.findIndex((t) => {
334
- const c = typeof t === "object" ? t.content : t;
335
- return c === f.cmd;
336
- });
337
- if (testIndex < 0) continue;
338
- // Mutate `story.acceptance_tests` in place. The same story
339
- // reference lives inside runHuSubPipeline's batch (passed
340
- // by ref), so the next iteration's acceptance runner sees
341
- // the fixed test without us needing batch+batchSessionId
342
- // in this scope. The reconcile from PR #544 will sync the
343
- // plan JSON on the next run.
344
- const repair = await runRepair({
345
- story, testIndex, errorOutput: f.output,
346
- batchSessionId: ctx.stageResults.huReviewer.batchSessionId,
347
- // batch unavailable in this closure scope — runRepair
348
- // gates saveHuBatch on its presence and skips when
349
- // missing; the in-memory mutation alone is sufficient
350
- // for the next acceptance run within this same HU loop.
351
- batch: null,
352
- config: ctx.config, logger, emitter, eventBase: ctx.eventBase,
353
- failureKind: cls.kind,
354
- savePlanPatch: null,
355
- });
356
- if (repair.escalate) { repairEscalate = repair.reason; break; }
357
- if (repair.repaired) {
358
- repairedAny = true;
359
- failureTracker.reset(f.cmd);
360
- }
361
- }
362
-
363
- if (repairEscalate) {
364
- logger.warn(`HU ${story.id}: Repairer escalated — ${repairEscalate}`);
365
- return { approved: false, sessionId: laneSession.id, reason: "repair_escalated" };
366
- }
367
- if (repairedAny) {
368
- // Tests were rewritten — restart iteration with the fixed
369
- // tests in place; do NOT increment attempt against the user
370
- // because the failure was the planner's bug, not the coder's.
371
- logger.info(`HU ${story.id}: Repairer fixed test(s); restarting acceptance gate without consuming iteration`);
372
- attempt -= 1;
373
- continue;
374
- }
375
-
376
- // No repair happened — normal coder feedback path.
377
- const diagnostic = buildDiagnosticPrompt(failed);
378
- logger.warn(`HU ${story.id}: ${failed.length} acceptance test(s) FAILED — sending diagnostic to coder`);
379
- setReviewerFeedback(laneSession, diagnostic);
380
- lanePlannedTask = `${huTask}\n\n--- ACCEPTANCE TEST FAILURES ---\n${diagnostic}`;
381
- }
382
-
383
- // All iterations exhausted
384
- logger.warn(`HU ${story.id}: max iterations reached with acceptance tests still failing`);
385
- return { approved: false, sessionId: laneSession.id, reason: "acceptance_tests_failed" };
386
- }
387
-
388
- // Fallback: no acceptance_tests → standard pipeline (reviewer/tester).
389
- // The loop reads everything through ctx.* — handing it a lane view
390
- // keeps the shared ctx untouched (no more mutate-and-restore dance).
391
- const laneCtx = { ...ctx, config: laneConfig, pipelineFlags: laneFlags, session: laneSession, brainCtx: laneBrain, plannedTask: lanePlannedTask,
392
- beforeIteration: (attempt) => applyCoderRoute({ laneConfig, story, attempt, config: ctx.config, logger }) };
393
- const result = await runIterationLoop(laneCtx, { task: huTask, askQuestion, emitter, logger });
394
- if (result?.approved) {
395
- // KJC-BUG-0248: what the HU's push or PR said joins what the loop recorded.
396
- const git = await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
397
- const prev = result.git ?? {};
398
- return { ...result, git: { ...prev, prUrl: prev.prUrl ?? git.prUrl, errors: [...(prev.errors ?? []), ...git.errors] } };
399
- }
400
- return result;
401
- },
402
- emitter,
403
- eventBase: ctx.eventBase,
404
- logger,
405
- config: ctx.config,
406
- // PR1 (live HU status): forward the per-HU status updater so
407
- // hu-sub-pipeline can patch the plan JSON on every transition,
408
- // not only at the end of the run. The board's chokidar then
409
- // fires per-HU and the Kanban columns update in real time.
410
- onStatusChange: ctx.session?._liveStatusUpdater || null,
411
- // PR3 (per-HU outcome): same pattern, but for the rich
412
- // outcome blob written ONCE per HU at the end of runSingleHu.
413
- onOutcome: ctx.session?._liveOutcomeUpdater || null,
414
- // Cost D (KJC-TSK-0515): the sub-pipeline slices BudgetTracker
415
- // entries per HU and stamps `outcome.cost_usd` so the board can
416
- // show per-HU spend. Without this the tracker is invisible to it.
417
- budgetTracker: ctx.budgetTracker || null
418
- });
419
-
420
- emitProgress(emitter, makeEvent("hu:sub-pipeline:end", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
421
- status: subPipelineResult.approved ? "ok" : "fail",
422
- message: subPipelineResult.approved
423
- ? "All HUs completed successfully"
424
- : `Sub-pipeline finished with failures (${subPipelineResult.blockedIds.length} blocked)`,
425
- detail: { results: subPipelineResult.results, blockedIds: subPipelineResult.blockedIds }
426
- }));
427
-
428
- // Sync results back to plan file if this was a plan-based run
429
- if (ctx.session._syncResultsToPlan) {
430
- try {
431
- await ctx.session._syncResultsToPlan(subPipelineResult);
432
- logger.info("Plan updated with execution results");
433
- } catch (err) {
434
- logger.warn(`Failed to sync results to plan: ${err.message}`);
435
- }
436
- }
437
-
438
- // KJC-BUG-0248 (#1895): a push or a PR that failed is said in the result
439
- // the host reads, not only in a warn line of the log.
440
- const warnings = collectGitWarnings(subPipelineResult.results);
441
- for (const w of warnings) logger.warn(`git automation: ${w}`);
442
- // KJC-TSK-0987 (#1894): the HUs approved themselves against their own stories;
443
- // the card's criteria are read against the diff of the whole run before the
444
- // run says approved. Uncovered: approved:false with the list, for the host.
445
- let coverage = { action: "skip" };
446
- if (subPipelineResult.approved) {
447
- const diff = await generateDiff({ baseRef: ctx.session.head_at_start || ctx.session.session_start_sha });
448
- coverage = await runAcceptanceCoverageStage({
449
- config: ctx.config, logger, emitter, eventBase: ctx.eventBase, trackBudget: ctx.trackBudget,
450
- iteration: 0, task, criteria: criteriaOfCard(ctx.session.pg_card), diff,
451
- });
452
- if (coverage.stageResult) ctx.stageResults.acceptanceCoverage = coverage.stageResult;
453
- }
454
- const uncovered = coverage.action === "retry" ? { reason: "acceptance_criteria_uncovered", uncovered: coverage.stageResult.uncovered } : {};
455
- const unavailable = coverage.action === "fail" ? { reason: "acceptance_coverage_unavailable", error: coverage.stageResult.summary } : {};
456
- const finalResult = {
457
- approved: subPipelineResult.approved && coverage.action !== "retry" && coverage.action !== "fail",
458
- sessionId: ctx.session.id,
459
- huResults: subPipelineResult.results,
460
- blockedIds: subPipelineResult.blockedIds,
461
- planId: ctx.session._planRef?.planId || null,
462
- ...uncovered,
463
- ...unavailable,
464
- ...(warnings.length > 0 ? { warnings } : {})
465
- };
466
- await writeHistoryRecord({ sessionId: ctx.session.id, task, result: finalResult, logger });
467
- return { handled: true, result: finalResult };
468
- }
@@ -1,140 +0,0 @@
1
- // Feedback enrichment: transform vague feedback into actionable instructions.
2
- // Extracts file hints, line hints, and suggests fixes from feedback text.
3
-
4
- /**
5
- * Extract file path hints from feedback description.
6
- * Matches patterns like: packages/server/auth.js, src/routes/*, auth/middleware.ts
7
- */
8
- export function extractFileHints(description) {
9
- if (!description) return [];
10
- const hints = new Set();
11
-
12
- // Explicit paths with extensions (supports bare filenames too)
13
- const pathRe = /\b([\w./_-]+\.(?:js|ts|jsx|tsx|py|go|rs|java|rb|php|cs|swift|dart|html|css|vue|svelte|astro|json|yml|yaml|md|sql))\b/gi;
14
- const paths = description.match(pathRe) || [];
15
- for (const p of paths) hints.add(p);
16
-
17
- // Directory references
18
- const dirRe = /\b(packages\/[\w-]+|src\/[\w/-]+|tests?\/[\w/-]+|lib\/[\w/-]+)/gi;
19
- const dirs = description.match(dirRe) || [];
20
- for (const d of dirs) hints.add(d);
21
-
22
- return [...hints];
23
- }
24
-
25
- /**
26
- * Detect the category of feedback from description keywords.
27
- */
28
- export function detectCategory(description) {
29
- const text = (description || "").toLowerCase();
30
- if (/\b(injection|xss|csrf|httponly|sql\s*injection|exposed\s*secret|auth(?:entication)?\s*bypass|authoriza(?:tion)?\s*bypass|cryptograph|token\s*expos)/i.test(text)) return "security";
31
- if (/\btest|coverage|assert|spec|mock\b/.test(text)) return "tests";
32
- if (/\bperf(ormance)?|slow|optimize|bottleneck|latency\b/.test(text)) return "performance";
33
- if (/\b(style|format(?:ting)?|naming|rename|indent|whitespace|comment|jsdoc|cosmetic)\b/.test(text)) return "style";
34
- if (/\b(bug|incorrect|wrong|fails?|error|crash|broken)\b/.test(text)) return "correctness";
35
- return "other";
36
- }
37
-
38
- /**
39
- * Detect severity from description keywords and existing severity field.
40
- */
41
- export function detectSeverity(description, existingSeverity) {
42
- if (existingSeverity) return existingSeverity;
43
- const text = (description || "").toLowerCase();
44
- if (/\bcritical|blocker|severe|production\s*risk\b/.test(text)) return "critical";
45
- if (/\b(high|security|vulnerab|breaking|regression|injection|xss|csrf)\b/.test(text)) return "high";
46
- if (/\blow|minor|cosmetic|nitpick\b/.test(text)) return "low";
47
- return "medium";
48
- }
49
-
50
- /**
51
- * Generate actionable instructions from a feedback entry.
52
- * Adds concrete steps the coder can follow.
53
- */
54
- export function generateActionPlan(entry) {
55
- const steps = [];
56
- const cat = entry.category || detectCategory(entry.description);
57
-
58
- // Always start with locating the problem
59
- if (entry.file) {
60
- steps.push(`Open ${entry.file}${entry.line ? ` at line ${entry.line}` : ""}`);
61
- } else {
62
- const hints = extractFileHints(entry.description);
63
- if (hints.length > 0) {
64
- steps.push(`Look in: ${hints.join(", ")}`);
65
- } else {
66
- steps.push("Identify the relevant file(s) using grep/search");
67
- }
68
- }
69
-
70
- // Category-specific actions
71
- if (cat === "security") {
72
- steps.push("Apply the security fix (validate input, escape output, use parameterized queries, etc.)");
73
- } else if (cat === "tests") {
74
- steps.push("Write the missing test cases covering the scenario described");
75
- steps.push("Run tests to verify they pass: npm test or equivalent");
76
- } else if (cat === "correctness") {
77
- steps.push("Fix the bug described");
78
- steps.push("Add a regression test");
79
- } else if (cat === "performance") {
80
- steps.push("Optimize the identified bottleneck");
81
- steps.push("Verify improvement with measurements");
82
- } else if (cat === "style") {
83
- steps.push("Apply the style fix (run linter if available)");
84
- }
85
-
86
- // Include suggested fix if provided
87
- if (entry.suggestedFix) {
88
- steps.push(`Suggested approach: ${entry.suggestedFix}`);
89
- }
90
-
91
- return steps;
92
- }
93
-
94
- /**
95
- * Enrich a single feedback entry with file hints, category, severity, and action plan.
96
- */
97
- export function enrichEntry(entry) {
98
- const description = entry.description || "";
99
- // Re-detect category when missing or set to default "other"
100
- const category = (!entry.category || entry.category === "other")
101
- ? detectCategory(description)
102
- : entry.category;
103
- const severity = detectSeverity(description, entry.severity);
104
- const fileHints = entry.file ? [entry.file] : extractFileHints(description);
105
-
106
- return {
107
- ...entry,
108
- category,
109
- severity,
110
- fileHints,
111
- actionPlan: generateActionPlan({ ...entry, category })
112
- };
113
- }
114
-
115
- /**
116
- * Enrich all entries in a queue in-place.
117
- */
118
- export function enrichQueue(queue) {
119
- queue.entries = queue.entries.map(enrichEntry);
120
- return queue;
121
- }
122
-
123
- /**
124
- * Format enriched entries as actionable coder prompt sections.
125
- */
126
- export function formatEnrichedForCoder(enrichedEntries) {
127
- if (!enrichedEntries?.length) return "";
128
-
129
- const sections = [];
130
- enrichedEntries.forEach((e, i) => {
131
- const header = `### Issue ${i + 1}: [${e.severity}] ${e.category} — ${e.description}`;
132
- const location = e.fileHints?.length
133
- ? `**Location hints:** ${e.fileHints.join(", ")}`
134
- : "**Location:** search the project for relevant files";
135
- const plan = `**Action plan:**\n${e.actionPlan.map(s => `- ${s}`).join("\n")}`;
136
- sections.push([header, location, plan].join("\n"));
137
- });
138
-
139
- return sections.join("\n\n");
140
- }