karajan-code 4.70.4 → 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 (380) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -14
  3. package/packages/hu-board/package.json +1 -1
  4. package/packages/hu-board/public/app.js +4 -18
  5. package/packages/hu-board/public/index.html +5 -2
  6. package/packages/hu-board/public/styles.css +26 -0
  7. package/packages/hu-board/public/utils/api.js +0 -74
  8. package/packages/hu-board/public/utils/board-tour.js +62 -0
  9. package/packages/hu-board/public/utils/board-view.js +49 -144
  10. package/packages/hu-board/public/utils/command-launcher.js +8 -70
  11. package/packages/hu-board/public/utils/formatters.js +10 -0
  12. package/packages/hu-board/public/utils/governance-view.js +13 -1
  13. package/packages/hu-board/public/utils/hu-actions.js +17 -68
  14. package/packages/hu-board/public/utils/init-listeners.js +7 -8
  15. package/packages/hu-board/public/utils/log-panel.js +3 -16
  16. package/packages/hu-board/public/utils/maggle-mode.js +2 -12
  17. package/packages/hu-board/public/utils/modals.js +7 -0
  18. package/packages/hu-board/public/utils/project-picker-view.js +1 -1
  19. package/packages/hu-board/public/utils/server-push.js +3 -22
  20. package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
  21. package/packages/hu-board/public/utils/story-detail-view.js +5 -19
  22. package/packages/hu-board/src/cleanup-zombies.js +0 -1
  23. package/packages/hu-board/src/command-runner.js +36 -25
  24. package/packages/hu-board/src/db-connection.js +282 -0
  25. package/packages/hu-board/src/db-paths.js +113 -0
  26. package/packages/hu-board/src/db-queries.js +188 -0
  27. package/packages/hu-board/src/db-tombstones.js +51 -0
  28. package/packages/hu-board/src/db.js +28 -619
  29. package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
  30. package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
  31. package/packages/hu-board/src/plan-mutations.js +3 -153
  32. package/packages/hu-board/src/plans-service.js +55 -0
  33. package/packages/hu-board/src/routes/api/commands.js +122 -0
  34. package/packages/hu-board/src/routes/api/plans.js +55 -0
  35. package/packages/hu-board/src/routes/api/projects.js +267 -0
  36. package/packages/hu-board/src/routes/api/prompts.js +135 -0
  37. package/packages/hu-board/src/routes/api/sessions.js +95 -0
  38. package/packages/hu-board/src/routes/api/stories.js +183 -0
  39. package/packages/hu-board/src/routes/api/system.js +241 -0
  40. package/packages/hu-board/src/routes/api/tombstones.js +38 -0
  41. package/packages/hu-board/src/routes/api.js +24 -1371
  42. package/packages/hu-board/src/routes/governance.js +23 -1
  43. package/packages/hu-board/src/routes/lanes.js +40 -0
  44. package/packages/hu-board/src/routes/rag.js +4 -3
  45. package/packages/hu-board/src/server.js +20 -34
  46. package/packages/hu-board/src/sync-files.js +318 -0
  47. package/packages/hu-board/src/sync-project.js +99 -0
  48. package/packages/hu-board/src/sync.js +22 -566
  49. package/packages/hu-board/src/zombie-reaper.js +1 -1
  50. package/scripts/dual-publish.mjs +19 -3
  51. package/scripts/postinstall.js +15 -2
  52. package/scripts/verify-pack-mcp.mjs +11 -7
  53. package/scripts/verify-pack.mjs +4 -4
  54. package/src/audit/audit-history.js +3 -2
  55. package/src/audit/dead-exports.js +68 -59
  56. package/src/audit/deterministic-summary.js +33 -13
  57. package/src/audit/npm-signatures.js +41 -0
  58. package/src/audit/sbom.js +35 -0
  59. package/src/checks/agent-token.js +112 -0
  60. package/src/checks/dir-setup.js +34 -1
  61. package/src/checks/native-build.js +21 -39
  62. package/src/checks/runner.js +128 -142
  63. package/src/cli/advanced-commands.js +6 -6
  64. package/src/cli/register-meta.js +42 -82
  65. package/src/cli/register-pipeline.js +8 -143
  66. package/src/cli/register-plan.js +9 -100
  67. package/src/cli/retired-commands.js +49 -0
  68. package/src/cli.js +18 -5
  69. package/src/commands/agent-run.js +40 -26
  70. package/src/commands/audit.js +127 -124
  71. package/src/commands/board.js +5 -9
  72. package/src/commands/check.js +29 -16
  73. package/src/commands/doctor.js +2 -0
  74. package/src/commands/env.js +176 -150
  75. package/src/commands/go.js +71 -53
  76. package/src/commands/harden.js +90 -81
  77. package/src/commands/hu-start.js +52 -0
  78. package/src/commands/hu.js +288 -221
  79. package/src/commands/identity.js +111 -78
  80. package/src/commands/init-atril.js +47 -0
  81. package/src/commands/init.js +11 -40
  82. package/src/commands/install-tools.js +70 -85
  83. package/src/commands/main-guard.js +78 -0
  84. package/src/commands/policy.js +226 -184
  85. package/src/commands/qmd.js +27 -21
  86. package/src/commands/rag.js +75 -48
  87. package/src/commands/review-gate.js +421 -341
  88. package/src/commands/start.js +4 -5
  89. package/src/commands/steward.js +96 -47
  90. package/src/commands/tournament.js +46 -37
  91. package/src/commands/worktree.js +94 -25
  92. package/src/config/loader.js +34 -17
  93. package/src/config/secrets.js +36 -0
  94. package/src/config.js +1 -0
  95. package/src/environment/adr.js +16 -2
  96. package/src/git/karajan-label.js +20 -8
  97. package/src/guards/main-guard.js +54 -0
  98. package/src/harden/hook-templates.js +16 -1
  99. package/src/harden/sentinel/posttooluse.mjs +18 -6
  100. package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
  101. package/src/harden/sentinel/sentinel-lib.mjs +129 -26
  102. package/src/harden/sentinel/sentinel-shell.mjs +5 -16
  103. package/src/harden/sentinel/sessionstart.mjs +3 -2
  104. package/src/harden/sentinel/stop.mjs +5 -4
  105. package/src/harden/workflow-templates.js +1 -1
  106. package/src/hu/board-lookup.js +29 -0
  107. package/src/hu/gh-identity.js +23 -0
  108. package/src/hu/github-board.js +4 -2
  109. package/src/hu/lane-prune.js +15 -7
  110. package/src/hu/lane-setup.js +65 -0
  111. package/src/hu/lanes.js +66 -0
  112. package/src/hu/team-board-mirror.js +3 -2
  113. package/src/infrastructure/command-runner.js +1 -1
  114. package/src/infrastructure/file-system-service.js +1 -1
  115. package/src/mcp/handlers/direct-handlers.js +2 -292
  116. package/src/mcp/handlers/hu-handlers.js +1 -35
  117. package/src/mcp/handlers/management-handlers.js +2 -42
  118. package/src/mcp/server-handlers.js +16 -22
  119. package/src/mcp/tools.js +1 -196
  120. package/src/plan/lane-plans.js +54 -0
  121. package/src/plan/plan-store.js +7 -4
  122. package/src/policy/add.js +26 -12
  123. package/src/policy/adr-number-check.js +31 -0
  124. package/src/prompts/audit.js +299 -339
  125. package/src/prompts/card-context.js +10 -5
  126. package/src/prompts/reviewer.js +5 -2
  127. package/src/rag/auto-update.js +56 -43
  128. package/src/rag/bootstrap-index.js +28 -54
  129. package/src/rag/migrate.js +1 -1
  130. package/src/rag/onnx-fallback.js +2 -2
  131. package/src/rag/pid-lease.js +70 -0
  132. package/src/rag/project-store.js +2 -12
  133. package/src/review/card-first.js +57 -46
  134. package/src/review/gate-gitignore.js +23 -4
  135. package/src/review/gate-hook.js +12 -4
  136. package/src/review/one-shot-review.js +45 -35
  137. package/src/review/publish-pr.js +14 -2
  138. package/src/review/repo-docs.js +73 -0
  139. package/src/review/repo-stack.js +59 -0
  140. package/src/review/reviewer-fallback.js +19 -4
  141. package/src/review/seal-commit.js +29 -0
  142. package/src/review/sonar-pregate.js +59 -44
  143. package/src/review/third-party-review.js +90 -16
  144. package/src/review/third-party-size.js +43 -0
  145. package/src/review/third-party-skills.js +54 -0
  146. package/src/review/verify-pr.js +1 -24
  147. package/src/roles/audit-role.js +106 -125
  148. package/src/skills/atril-skills.js +5 -4
  149. package/src/sonar/scanner.js +116 -101
  150. package/src/steward/invariants.js +5 -4
  151. package/src/steward/phantom-sweep.js +66 -0
  152. package/src/tournament/judge.js +60 -53
  153. package/src/utils/gh-account.js +37 -0
  154. package/src/utils/git.js +0 -13
  155. package/src/utils/home-migration.js +7 -1
  156. package/src/utils/main-checkout.js +53 -0
  157. package/src/utils/qmd-collection.js +3 -1
  158. package/src/utils/welcome.js +3 -3
  159. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  160. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  161. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  162. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  163. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  164. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  165. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  166. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  167. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  168. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  169. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  172. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  173. package/packages/hu-board/src/run-tracker.js +0 -121
  174. package/src/activity-log.js +0 -59
  175. package/src/agents/host-agent.js +0 -38
  176. package/src/agents/model-registry.js +0 -142
  177. package/src/audit/plan-adherence.js +0 -224
  178. package/src/autonomy/arbiter-role.js +0 -71
  179. package/src/autonomy/arbiter.js +0 -22
  180. package/src/autonomy/decider.js +0 -18
  181. package/src/autonomy/decision-resolver.js +0 -58
  182. package/src/autonomy/outcome-report.js +0 -55
  183. package/src/autonomy/stage-ask.js +0 -30
  184. package/src/brain/decision-tracker.js +0 -125
  185. package/src/brain/decisor.js +0 -243
  186. package/src/brain/solomon-consult.js +0 -171
  187. package/src/brain/standby-scheduler.js +0 -8
  188. package/src/budget/comparison.js +0 -96
  189. package/src/canvas/parse-md.js +0 -238
  190. package/src/canvas/schema.js +0 -101
  191. package/src/canvas/to-flat.js +0 -85
  192. package/src/canvas/validate.js +0 -163
  193. package/src/ci/dispatch.js +0 -99
  194. package/src/ci/index.js +0 -3
  195. package/src/ci/pr-diff.js +0 -26
  196. package/src/cli/register-standby.js +0 -26
  197. package/src/commands/architect.js +0 -111
  198. package/src/commands/autorun.js +0 -49
  199. package/src/commands/researcher.js +0 -54
  200. package/src/commands/resume.js +0 -72
  201. package/src/commands/review.js +0 -89
  202. package/src/commands/run.js +0 -185
  203. package/src/commands/standby.js +0 -117
  204. package/src/commands/status.js +0 -36
  205. package/src/commands/triage.js +0 -78
  206. package/src/commands/undo.js +0 -62
  207. package/src/domains/domain-loader.js +0 -245
  208. package/src/domains/domain-registry.js +0 -140
  209. package/src/domains/domain-synthesizer.js +0 -129
  210. package/src/git/automation.js +0 -433
  211. package/src/git/coder-commit-guard.js +0 -40
  212. package/src/git/hu-automation.js +0 -243
  213. package/src/git/hu-snapshot.js +0 -10
  214. package/src/git/symlink-targets.js +0 -57
  215. package/src/golden/asserter.js +0 -92
  216. package/src/golden/loader.js +0 -52
  217. package/src/golden/runner.js +0 -92
  218. package/src/golden/schema.js +0 -41
  219. package/src/guards/duplicate-members.js +0 -86
  220. package/src/guards/intent-guard.js +0 -161
  221. package/src/guards/perf-guard.js +0 -126
  222. package/src/guards/policy-guard.js +0 -90
  223. package/src/guards/policy-resolver.js +0 -85
  224. package/src/guards/secret-redactor.js +0 -71
  225. package/src/hu/acceptance-runner.js +0 -138
  226. package/src/hu/lazy-planner.js +0 -109
  227. package/src/hu/parallel-executor.js +0 -94
  228. package/src/hu/routing-metrics.js +0 -17
  229. package/src/hu/split-validator.js +0 -143
  230. package/src/hu/splitting-detector.js +0 -158
  231. package/src/hu/splitting-generator.js +0 -175
  232. package/src/hu/store.js +0 -394
  233. package/src/infrastructure/mocks.js +0 -182
  234. package/src/mcp/handlers/run-handler.js +0 -318
  235. package/src/mcp/sovereignty-guard.js +0 -113
  236. package/src/mcp/tool-arg-normalizers.js +0 -16
  237. package/src/mutate/reviewer-signal.js +0 -76
  238. package/src/orchestrator/agent-fallback.js +0 -81
  239. package/src/orchestrator/brain-coordinator.js +0 -186
  240. package/src/orchestrator/ci-integration.js +0 -139
  241. package/src/orchestrator/config-init.js +0 -426
  242. package/src/orchestrator/direct-actions.js +0 -217
  243. package/src/orchestrator/drivers/error-recovery.js +0 -287
  244. package/src/orchestrator/drivers/init-context.js +0 -238
  245. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  246. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  247. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  248. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  249. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  250. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  251. package/src/orchestrator/drivers/post-loop.js +0 -404
  252. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  253. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  254. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  255. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  256. package/src/orchestrator/drivers/pre-loop.js +0 -524
  257. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  258. package/src/orchestrator/feedback-enrichment.js +0 -140
  259. package/src/orchestrator/feedback-queue.js +0 -151
  260. package/src/orchestrator/flow-control.js +0 -170
  261. package/src/orchestrator/flow-runner.js +0 -365
  262. package/src/orchestrator/fs-leak-detector.js +0 -228
  263. package/src/orchestrator/hu-scheduler.js +0 -114
  264. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  265. package/src/orchestrator/iteration-gate.js +0 -73
  266. package/src/orchestrator/iteration-stages.js +0 -9
  267. package/src/orchestrator/parallel-limiter.js +0 -74
  268. package/src/orchestrator/pipeline-context.js +0 -72
  269. package/src/orchestrator/post-loop-stages.js +0 -468
  270. package/src/orchestrator/pre-loop-stages.js +0 -11
  271. package/src/orchestrator/preflight-checks.js +0 -487
  272. package/src/orchestrator/repair/repair-detector.js +0 -117
  273. package/src/orchestrator/repair/repair-runner.js +0 -142
  274. package/src/orchestrator/reviewer-fallback.js +0 -45
  275. package/src/orchestrator/role-output-compressor.js +0 -189
  276. package/src/orchestrator/session-journal.js +0 -308
  277. package/src/orchestrator/solomon-escalation.js +0 -331
  278. package/src/orchestrator/solomon-rules.js +0 -212
  279. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  280. package/src/orchestrator/stages/architect-stage.js +0 -172
  281. package/src/orchestrator/stages/coder-stage.js +0 -506
  282. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  283. package/src/orchestrator/stages/format-stage.js +0 -61
  284. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  285. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  286. package/src/orchestrator/stages/perf-stage.js +0 -140
  287. package/src/orchestrator/stages/planner-stage.js +0 -85
  288. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  289. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  290. package/src/orchestrator/stages/research-stage.js +0 -69
  291. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  292. package/src/orchestrator/stages/sonar-stage.js +0 -439
  293. package/src/orchestrator/stages/stage-classes.js +0 -84
  294. package/src/orchestrator/stages/stage-executor.js +0 -155
  295. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  296. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  297. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  298. package/src/orchestrator/stages/triage-stage.js +0 -172
  299. package/src/orchestrator/standby.js +0 -71
  300. package/src/orchestrator/verification-gate.js +0 -183
  301. package/src/orchestrator.js +0 -22
  302. package/src/plan/adr-generator.js +0 -170
  303. package/src/plan/adr-loader.js +0 -60
  304. package/src/plan/plan-executor.js +0 -100
  305. package/src/plan/plan-validation.js +0 -4
  306. package/src/prompts/acceptance-coverage.js +0 -32
  307. package/src/prompts/arbiter.js +0 -38
  308. package/src/prompts/architect.js +0 -103
  309. package/src/prompts/hu-reviewer.js +0 -244
  310. package/src/prompts/tool-correctness-judge.js +0 -31
  311. package/src/prompts/triage.js +0 -57
  312. package/src/rag/embedder.js +0 -2
  313. package/src/rag/embedders/_cloud-base.js +0 -2
  314. package/src/rag/embedders/cohere.js +0 -2
  315. package/src/rag/embedders/mistral.js +0 -2
  316. package/src/rag/embedders/onnx.js +0 -2
  317. package/src/rag/embedders/openai.js +0 -2
  318. package/src/rag/embedders/voyage.js +0 -2
  319. package/src/rag/rerank.js +0 -4
  320. package/src/rag/where-parser.js +0 -4
  321. package/src/repeat-detector.js +0 -88
  322. package/src/review/diff-generator.js +0 -112
  323. package/src/review/git-stash-helper.js +0 -104
  324. package/src/review/schema.js +0 -31
  325. package/src/review/scope-filter.js +0 -152
  326. package/src/review/snapshot-diff.js +0 -122
  327. package/src/review/tdd-discipline.js +0 -90
  328. package/src/review/tdd-policy.js +0 -69
  329. package/src/roles/acceptance-coverage-role.js +0 -78
  330. package/src/roles/architect-role.js +0 -77
  331. package/src/roles/coder-role.js +0 -113
  332. package/src/roles/commiter-role.js +0 -113
  333. package/src/roles/domain-curator-role.js +0 -134
  334. package/src/roles/hu-reviewer-role.js +0 -64
  335. package/src/roles/impeccable-role.js +0 -85
  336. package/src/roles/index.js +0 -18
  337. package/src/roles/karajan-brain-role.js +0 -134
  338. package/src/roles/perf-role.js +0 -122
  339. package/src/roles/planner-role.js +0 -151
  340. package/src/roles/refactorer-role.js +0 -30
  341. package/src/roles/repairer-role.js +0 -181
  342. package/src/roles/researcher-role.js +0 -70
  343. package/src/roles/reviewer-role.js +0 -115
  344. package/src/roles/security-role.js +0 -73
  345. package/src/roles/solomon-role.js +0 -271
  346. package/src/roles/sonar-role.js +0 -72
  347. package/src/roles/tester-role.js +0 -235
  348. package/src/roles/tool-correctness-judge-role.js +0 -78
  349. package/src/roles/triage-role.js +0 -99
  350. package/src/session/journal/decisions-writer.js +0 -200
  351. package/src/session/journal/iteration-logger.js +0 -194
  352. package/src/session/journal/summary-writer.js +0 -320
  353. package/src/session/journal/tree-writer.js +0 -164
  354. package/src/session/journal/write-all.js +0 -89
  355. package/src/session-cleanup.js +0 -116
  356. package/src/skills/semantic-detector.js +0 -167
  357. package/src/skills/skill-detector.js +0 -385
  358. package/src/sonar/cloud-scanner.js +0 -78
  359. package/src/tracker-bootstrap.js +0 -44
  360. package/src/types/index.js +0 -38
  361. package/src/types/policy.js +0 -43
  362. package/src/types/solomon.js +0 -87
  363. package/src/utils/async-lock.js +0 -31
  364. package/src/utils/budget.js +0 -229
  365. package/src/utils/cwd-confirm.js +0 -39
  366. package/src/utils/display/event-handlers.js +0 -403
  367. package/src/utils/display/formatters.js +0 -132
  368. package/src/utils/display/header.js +0 -47
  369. package/src/utils/display/session-summary.js +0 -118
  370. package/src/utils/display/solomon.js +0 -42
  371. package/src/utils/pricing.js +0 -29
  372. package/src/utils/quota-simulator.js +0 -56
  373. package/src/utils/role-assigner.js +0 -137
  374. package/src/utils/rtk-wrapper.js +0 -87
  375. package/src/utils/run-registry.js +0 -9
  376. package/src/utils/status-dashboard.js +0 -254
  377. package/src/utils/tool-call-extractor.js +0 -56
  378. package/src/webperf/devtools-detect.js +0 -112
  379. package/src/webperf/webperf-role-integration.js +0 -66
  380. package/templates/skills/kj-run.md +0 -73
@@ -1,228 +0,0 @@
1
- /**
2
- * KJC-BUG-0032: detect coder writes outside projectDir.
3
- *
4
- * The coder subprocess runs with `cwd: projectDir`, but its Bash
5
- * tool can `cd` anywhere. Real-world repro: a HU titled
6
- * "Initialize project skeleton: create assistant/ directory…"
7
- * had Claude run `cd /home/manu/assistant && pnpm init …`. The
8
- * resulting 36 MB of code lived in $HOME/assistant — outside the
9
- * projectDir, outside any git repo, lost to tracking, commits and
10
- * review. The coder was happy, the user only noticed by accident.
11
- *
12
- * This module is a deterministic guard: snapshot $HOME's top-level
13
- * directory entries before the coder runs, scan again after, and
14
- * any new entry that isn't projectDir itself is a leak.
15
- *
16
- * Why top-level only:
17
- * - Catches the realistic failure mode (coder creates a sibling
18
- * `~/something` for the project skeleton).
19
- * - Cheap (single readdirSync) — no `find -newer` traversal of
20
- * $HOME's potentially massive tree.
21
- * - Doesn't thrash on writes to existing dirs like ~/.npm or
22
- * ~/.cache that legitimate node tooling touches.
23
- *
24
- * What it WON'T catch (acknowledged trade-offs):
25
- * - Writes deep inside existing $HOME subdirs (e.g. modifying
26
- * ~/.bashrc — would need a per-file mtime scan).
27
- * - Writes to /tmp (high false-positive rate; /tmp is ephemeral
28
- * by spec; less risky).
29
- * - Writes to /etc, /var, etc. (would require root anyway).
30
- *
31
- * The first-pass guard is the right ROI. Subsequent iterations
32
- * can extend coverage if real-world cases warrant it.
33
- */
34
-
35
- import fs from "node:fs";
36
- import os from "node:os";
37
- import path from "node:path";
38
-
39
- /**
40
- * Snapshot the immediate children of $HOME. Returns a Set of names
41
- * (not full paths) so detectNewHomeEntries can do a quick set
42
- * difference. Empty set on any FS error — treats "can't snapshot"
43
- * the same as "snapshot OK with nothing".
44
- *
45
- * @returns {Set<string>}
46
- */
47
- export function snapshotHomeTopLevel() {
48
- try {
49
- return new Set(fs.readdirSync(os.homedir()));
50
- } catch {
51
- return new Set();
52
- }
53
- }
54
-
55
- /**
56
- * Detect $HOME top-level entries that didn't exist when `before`
57
- * was snapshotted. Excludes projectDir itself in case the user
58
- * keeps their projects directly under $HOME (no false positive
59
- * if `projectDir = $HOME/some-project` already existed).
60
- *
61
- * @param {Set<string>} before — snapshot from snapshotHomeTopLevel()
62
- * @param {string|null} projectDir — current run's projectDir
63
- * @returns {string[]} absolute paths of new entries (potential leaks)
64
- */
65
- export function detectNewHomeEntries(before, projectDir) {
66
- const home = os.homedir();
67
- let after;
68
- try {
69
- after = fs.readdirSync(home);
70
- } catch {
71
- return [];
72
- }
73
- const projectDirAbs = projectDir ? path.resolve(projectDir) : null;
74
- const leaks = [];
75
- for (const name of after) {
76
- if (before.has(name)) continue;
77
- const full = path.join(home, name);
78
- // Skip projectDir itself (defensive — shouldn't happen since
79
- // projectDir is pre-existing, but a paranoid check costs nothing).
80
- if (projectDirAbs && path.resolve(full) === projectDirAbs) continue;
81
- leaks.push(full);
82
- }
83
- return leaks;
84
- }
85
-
86
- /**
87
- * Issue #546: filter potential leaks by attribution. Snapshot diff
88
- * cannot tell coder writes from concurrent writes by other processes
89
- * (host AI scratch files, parallel tooling, manual edits). Real-world
90
- * 2026-04-29 incident: a host-side write of `~/karajan-status-snapshot.md`
91
- * during a kj run aborted HU 003 with a false positive.
92
- *
93
- * Heuristic: if the coder genuinely created the leaked path, its
94
- * transcript will mention the path or its basename — the coder
95
- * narrates what it does (Bash commands, file operations, todos). If
96
- * neither the absolute path nor the basename appears anywhere in
97
- * the transcript, the entry was created by some other process during
98
- * the iteration window and should NOT abort the HU.
99
- *
100
- * False-negative trade-off: a coder that creates a file silently
101
- * (e.g. via a script that doesn't echo paths) escapes this filter.
102
- * That trade-off is acceptable: the original leak class (coder
103
- * skeleton-creating in $HOME) always emits the path in commands; the
104
- * silent-write case has never been observed.
105
- *
106
- * Back-compat: when `transcript` is empty/null/undefined, NO filtering
107
- * happens — every detected leak is passed through unchanged. This
108
- * preserves the strict behaviour for callers that don't have a
109
- * transcript handy.
110
- *
111
- * @param {string[]} leaks — output of detectNewHomeEntries()
112
- * @param {string|null|undefined} transcript — coder's full output text
113
- * @returns {string[]} filtered leaks (the subset attributable to coder)
114
- */
115
- /**
116
- * KJC-BUG-0180 — attribution is not severity. `verifyLeaksAgainstTranscript`
117
- * stays strict on purpose: with no transcript it returns everything. But
118
- * "the snapshot changed and there is NO evidence the coder did it" is a
119
- * different fact from "the coder wrote there", and only the second one
120
- * should kill the run. The flaky CI failure was exactly this: other test
121
- * files writing under $HOME during the iteration window, an empty
122
- * transcript from a mocked coder, and the whole flow failing and retrying.
123
- *
124
- * @returns {{attributed: string[], unattributed: string[]}} — `attributed`
125
- * fails the run; `unattributed` is reported and the work goes on.
126
- */
127
- export function splitLeakEvidence(leaks, transcript) {
128
- const hasTranscript = typeof transcript === "string" && transcript.length > 0;
129
- const found = verifyLeaksAgainstTranscript(leaks, transcript);
130
- return hasTranscript ? { attributed: found, unattributed: [] } : { attributed: [], unattributed: found };
131
- }
132
-
133
- export function verifyLeaksAgainstTranscript(leaks, transcript) {
134
- if (!Array.isArray(leaks) || leaks.length === 0) return [];
135
- if (!transcript || typeof transcript !== "string" || transcript.length === 0) {
136
- return leaks; // no transcript ⇒ no filtering, original behaviour
137
- }
138
- return leaks.filter((p) => {
139
- const base = path.basename(p);
140
- // Skip artefacts whose basename is too short to be discriminating
141
- // (e.g. ".cache") — a 3-char basename matched against a long
142
- // transcript is meaningless. For such cases we prefer the strict
143
- // behaviour (flag) since they're rare and the user can inspect.
144
- if (base.length < 4) return true;
145
- return transcript.includes(p) || transcript.includes(base);
146
- });
147
- }
148
-
149
- /**
150
- * Layer 2 detection (KJC-BUG-0032): scan the coder's transcript for
151
- * `cd <abs-path> && <write-cmd>` patterns where <abs-path> is OUTSIDE
152
- * projectDir. Catches the bug class even when the target directory
153
- * already existed (so snapshot-diff misses it) — e.g.
154
- * `cd /home/manu/assistant && pnpm init -y`
155
- * when `~/assistant` was pre-existing.
156
- *
157
- * Filters applied:
158
- * - Only absolute paths (`/`, `~/`, `$HOME/`) — relative `cd subdir`
159
- * is assumed inside projectDir.
160
- * - Skips `/tmp/...` (ephemeral, legitimate scratch).
161
- * - Skips targets inside projectDir.
162
- * - Only flags if followed (same line, within 200 chars) by a
163
- * write/creation command: mkdir / touch / cp / mv / git init /
164
- * {pnpm,npm,yarn} init / {pnpm,npm,yarn,npx} create / cat > /
165
- * echo > / shell redirect (>, >>).
166
- * - Pure read commands (ls, which, cat <file>, grep) do NOT flag.
167
- *
168
- * @param {string|null|undefined} transcript
169
- * @param {string|null} projectDir
170
- * @returns {string[]} deduplicated absolute leak paths
171
- */
172
- export function detectTranscriptCdLeaks(transcript, projectDir) {
173
- if (!transcript || typeof transcript !== "string") return [];
174
- const projectDirAbs = projectDir ? path.resolve(projectDir) : null;
175
- const homeDir = os.homedir();
176
- // Capture cd target + the next ~200 chars of the same line for write-cmd lookup.
177
- // Two-step (S5843, complexity 31): capture ANY cd target with one simple
178
- // class, then filter to the absolute/home-rooted shapes the original
179
- // single alternation required (~/, $HOME/, /).
180
- const cdRegex = /\bcd\s+([^\s&|;`'"\n]+)([^\n]{0,200})/g;
181
- const absoluteTargetRe = /^(?:~\/|\$HOME\/|\/)/;
182
- const writeCmdRegex = /\b(mkdir|touch|cp|mv|git\s+init|(?:pnpm|npm|yarn)\s+(?:init|create)|npx\s+create|cat\s*>|echo\s+[^|]*>|>>?\s*\S)/i;
183
- const leaks = new Set();
184
- let match;
185
- while ((match = cdRegex.exec(transcript)) !== null) {
186
- if (!absoluteTargetRe.test(match[1])) continue;
187
- const original = match[1];
188
- let target = original;
189
- const rest = match[2] || "";
190
- if (target.startsWith("~/")) target = path.join(homeDir, target.slice(2));
191
- else if (target.startsWith("$HOME/")) target = path.join(homeDir, target.slice(6));
192
- const abs = path.resolve(target);
193
- // /tmp guard only applies to literal /tmp/... paths (ephemeral scratch),
194
- // not to ~/ expansions that happen to land in /tmp during tests.
195
- if (original === "/tmp" || original.startsWith("/tmp/")) continue;
196
- if (projectDirAbs && (abs === projectDirAbs || abs.startsWith(projectDirAbs + path.sep))) continue;
197
- if (!writeCmdRegex.test(rest)) continue;
198
- leaks.add(abs);
199
- }
200
- return Array.from(leaks);
201
- }
202
-
203
- /**
204
- * Format a leak list into a multiline plain-Spanish error message.
205
- * Includes a hint about how to recover (move-or-delete) so the user
206
- * isn't left guessing.
207
- *
208
- * @param {string[]} leaks — output of detectNewHomeEntries()
209
- * @param {string} projectDir
210
- * @returns {string}
211
- */
212
- export function formatLeakMessage(leaks, projectDir) {
213
- const list = leaks.map((p) => ` - ${p}`).join("\n");
214
- return [
215
- `El coder creó archivos FUERA del proyecto (${projectDir}).`,
216
- "",
217
- "Rutas detectadas:",
218
- list,
219
- "",
220
- "Esto suele pasar cuando una HU usa una ruta absoluta o relativa a $HOME y el coder la sigue literalmente. Las rutas SIEMPRE deben ser relativas a la raíz del proyecto.",
221
- "",
222
- "Qué hacer:",
223
- " 1. Inspecciona las rutas listadas. Si tienen código útil, muévelo a tu proyecto manualmente.",
224
- " 2. Borra las rutas listadas si son basura.",
225
- " 3. Edita el título / scope de la HU para que no contenga rutas absolutas.",
226
- " 4. Vuelve a lanzar la HU.",
227
- ].join("\n");
228
- }
@@ -1,114 +0,0 @@
1
- /**
2
- * hu-scheduler — pure scheduling for parallel HU execution (KJC-TSK-0623,
3
- * épica KJC-PCS-0065). Decides WHICH HUs may run concurrently; it never
4
- * launches anything. Two rules, both conservative by design:
5
- *
6
- * 1. Dependencies: every blocked_by id must be in completedIds.
7
- * 2. Scope isolation: two HUs whose scopes touch the same path prefix are
8
- * never scheduled together — predictable merge conflicts cost more than
9
- * the parallelism they'd buy. An HU with NO scope has an unknown blast
10
- * radius, so it runs alone (exclusive with everything).
11
- */
12
-
13
- /**
14
- * Extract path-like tokens from a free-form scope. The planner writes scope
15
- * as prose ("src/auth y tests/auth", "docs/*.md"); we keep anything that
16
- * looks like a path or glob, normalized to its base directory.
17
- */
18
- export function scopeTokens(scope) {
19
- if (!scope || typeof scope !== "string") return [];
20
- const matches = scope.match(/[\w.-]+(?:\/[\w*.{}-]+)+|[\w.-]+\/(?=\s|$|,)/g) || [];
21
- return matches
22
- .map((raw) => {
23
- // Cut the token at its first glob segment: "src/**/*.js" → "src".
24
- const segments = raw.replace(/\/+$/, "").split("/");
25
- const firstGlob = segments.findIndex((s) => s.includes("*") || s.includes("{"));
26
- return (firstGlob === -1 ? segments : segments.slice(0, firstGlob)).join("/");
27
- })
28
- .filter(Boolean);
29
- }
30
-
31
- /** True when two token lists share a path-prefix relationship. */
32
- function tokensOverlap(a, b) {
33
- for (const ta of a) {
34
- for (const tb of b) {
35
- if (ta === tb || ta.startsWith(`${tb}/`) || tb.startsWith(`${ta}/`)) return true;
36
- }
37
- }
38
- return false;
39
- }
40
-
41
- /**
42
- * True when two HUs cannot run concurrently: either one has no parseable
43
- * scope (unknown blast radius ⇒ exclusive) or their scopes overlap.
44
- */
45
- export function husConflict(huA, huB) {
46
- const a = scopeTokens(huA.scope);
47
- const b = scopeTokens(huB.scope);
48
- if (a.length === 0 || b.length === 0) return true;
49
- return tokensOverlap(a, b);
50
- }
51
-
52
- /**
53
- * Split one dependency-level group into conflict-free chunks of at most
54
- * `maxParallel` HUs (KJC-TSK-0626). Every chunk runs concurrently; chunks
55
- * run one after another. maxParallel 1 ⇒ singletons (fully sequential).
56
- * Same conservative rules as husConflict: no parseable scope ⇒ runs alone.
57
- *
58
- * @param {Array<{id: string, scope?: string|null}>} stories - full story list
59
- * @param {string[]} groupIds - ids of one dependency-level group, in order
60
- * @param {number} maxParallel
61
- * @returns {string[][]} chunks of ids
62
- */
63
- export function partitionConflictFree(stories, groupIds, maxParallel = 1) {
64
- if (maxParallel <= 1) return groupIds.map((id) => [id]);
65
- const byId = new Map(stories.map((s) => [s.id, s]));
66
- const chunks = [];
67
- for (const id of groupIds) {
68
- const story = byId.get(id) ?? { id };
69
- const fit = chunks.find(
70
- (chunk) => chunk.length < maxParallel && !chunk.some((otherId) => husConflict(story, byId.get(otherId) ?? { id: otherId })),
71
- );
72
- if (fit) fit.push(id);
73
- else chunks.push([id]);
74
- }
75
- return chunks;
76
- }
77
-
78
- /**
79
- * Select the next HUs to launch, order-preserving and greedy.
80
- *
81
- * @param {object} args
82
- * @param {Array<{id: string, blocked_by?: string[], scope?: string|null}>} args.hus - candidates, in plan order
83
- * @param {string[]} [args.completedIds] - HUs already done (dependency source)
84
- * @param {Array<{id: string, scope?: string|null}>} [args.inFlight] - HUs currently running
85
- * @param {number} [args.maxParallel] - total concurrency cap (in-flight included)
86
- * @returns {{ selected: object[], blocked: Array<{id: string, reason: string}> }}
87
- */
88
- export function selectRunnableHus({ hus = [], completedIds = [], inFlight = [], maxParallel = 1 } = {}) {
89
- const done = new Set(completedIds);
90
- const flying = new Set(inFlight.map((h) => h.id));
91
- const slots = Math.max(0, maxParallel - inFlight.length);
92
- const selected = [];
93
- const blocked = [];
94
-
95
- for (const hu of hus) {
96
- if (flying.has(hu.id) || done.has(hu.id)) continue;
97
- const missing = (hu.blocked_by || []).filter((dep) => !done.has(dep));
98
- if (missing.length > 0) {
99
- blocked.push({ id: hu.id, reason: `waiting for ${missing.join(", ")}` });
100
- continue;
101
- }
102
- if (selected.length >= slots) {
103
- blocked.push({ id: hu.id, reason: "no free slot" });
104
- continue;
105
- }
106
- const rival = [...inFlight, ...selected].find((other) => husConflict(hu, other));
107
- if (rival) {
108
- blocked.push({ id: hu.id, reason: `scope conflicts with ${rival.id}` });
109
- continue;
110
- }
111
- selected.push(hu);
112
- }
113
- return { selected, blocked };
114
- }