karajan-code 4.70.4 → 5.0.1

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