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,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
- }