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
@@ -17,39 +17,19 @@
17
17
  // ---- Command launcher modal ----
18
18
  //
19
19
  // Opens a small form inside the existing <dialog> singleton with a
20
- // command picker (plan / architect / clean) and the appropriate
20
+ // command picker (today: clean) and the appropriate
21
21
  // fields per command. Submit POSTs to /api/commands/:command, the
22
22
  // server spawns a detached `kj` child, and we open the existing log
23
- // viewer (re-uses openLogViewer's tail loop, just pointed at the
24
- // new generic /api/runs/:commandId/log endpoint).
23
+ // viewer (openGenericLogPanel's tail loop, pointed at the
24
+ // generic /api/runs/:commandId/log endpoint).
25
25
 
26
26
 
27
27
  async function showCommandLauncher() {
28
28
  const dlg = ensureDialog();
29
29
  // Per-command field schemas — kept in the client because each
30
30
  // command has different inputs and the modal renders accordingly.
31
+ // KJC-TSK-1323: kj plan (generate) and kj architect left with 5.0, which retired them.
31
32
  const SCHEMAS = {
32
- plan: {
33
- label: '📋 kj plan',
34
- help: 'Generate an implementation plan with HUs. Paste the task text OR a file path.',
35
- fields: [
36
- { name: 'taskFile', label: 'SPEC file path (absolute)', type: 'text', placeholder: '/home/me/project/SPEC.md' },
37
- { name: 'task', label: 'OR paste task text directly', type: 'textarea', rows: 6 },
38
- { name: 'context', label: 'Extra context (optional)', type: 'text' },
39
- { name: 'projectDir', label: 'Project directory (optional, defaults to board\'s cwd)', type: 'text' },
40
- { name: 'quick', label: '--quick (skip synth + reviewer)', type: 'checkbox' },
41
- ],
42
- },
43
- architect: {
44
- label: '🏛 kj architect',
45
- help: 'Design an architecture and persist ADRs. Same input as kj plan.',
46
- fields: [
47
- { name: 'taskFile', label: 'SPEC file path (absolute)', type: 'text', placeholder: '/home/me/project/SPEC.md' },
48
- { name: 'task', label: 'OR paste task text directly', type: 'textarea', rows: 6 },
49
- { name: 'context', label: 'Extra context (optional)', type: 'text' },
50
- { name: 'projectDir', label: 'Project directory (optional)', type: 'text' },
51
- ],
52
- },
53
33
  clean: {
54
34
  label: '🧹 kj clean',
55
35
  help: 'Garbage-collect plans / sessions / batches. --nuke wipes everything including the board DB.',
@@ -60,25 +40,10 @@ async function showCommandLauncher() {
60
40
  },
61
41
  };
62
42
 
63
- // Maggle mode (KJC-TSK-0810 AC2): the frequent action leads in plain
64
- // language — one textarea, clear promise of what will happen. The full
65
- // technical launcher (file paths, architect, clean) stays one click
66
- // away behind "Más opciones…".
67
- const maggle = isMaggleMode();
68
- let advancedVisible = !maggle;
69
- if (maggle) {
70
- SCHEMAS.plan.label = maggleText('launcher.planLabel', SCHEMAS.plan.label);
71
- SCHEMAS.plan.help = maggleText('launcher.planHelp', SCHEMAS.plan.help);
72
- SCHEMAS.plan.simpleFields = [
73
- { name: 'task', label: maggleText('launcher.taskLabel', 'Task'), type: 'textarea', rows: 6 },
74
- ];
75
- }
76
-
77
- let active = 'plan';
43
+ let active = 'clean';
78
44
  const renderForm = () => {
79
45
  const s = SCHEMAS[active];
80
- const visibleFields = (!advancedVisible && s.simpleFields) ? s.simpleFields : s.fields;
81
- const fields = visibleFields.map((f) => {
46
+ const fields = s.fields.map((f) => {
82
47
  const id = `cmd-field-${f.name}`;
83
48
  if (f.type === 'textarea') {
84
49
  return `
@@ -112,7 +77,7 @@ async function showCommandLauncher() {
112
77
  </button>
113
78
  </div>
114
79
  <div style="padding:12px 18px;display:flex;gap:8px;border-bottom:1px solid var(--border)">
115
- ${Object.entries(SCHEMAS).filter(([key]) => advancedVisible || key === 'plan').map(([key, s]) => `
80
+ ${Object.entries(SCHEMAS).map(([key, s]) => `
116
81
  <button type="button" data-cmd="${key}"
117
82
  style="padding:6px 12px;border:1px solid var(--border);
118
83
  background:${key === active ? 'var(--color-green)' : 'var(--bg-primary)'};
@@ -121,13 +86,6 @@ async function showCommandLauncher() {
121
86
  ${esc(s.label)}
122
87
  </button>
123
88
  `).join('')}
124
- ${maggle && !advancedVisible ? `
125
- <button type="button" id="cmd-more"
126
- style="margin-left:auto;padding:6px 12px;border:1px solid var(--border);background:var(--bg-primary);color:var(--text-muted);border-radius:var(--radius-sm);cursor:pointer;font-size:0.85rem"
127
- title="Comandos técnicos: kj plan con ficheros, kj architect, kj clean">
128
- ${esc(maggleText('launcher.more', 'More…'))}
129
- </button>
130
- ` : ''}
131
89
  </div>
132
90
  <form id="cmd-form" onsubmit="return false"
133
91
  style="padding:14px 18px;display:flex;flex-direction:column;gap:12px;width:min(640px, 88vw);box-sizing:border-box">
@@ -149,8 +107,6 @@ async function showCommandLauncher() {
149
107
  dlg.querySelectorAll('[data-cmd]').forEach((btn) => {
150
108
  btn.addEventListener('click', () => { active = btn.dataset.cmd; renderForm(); });
151
109
  });
152
- const moreBtn = dlg.querySelector('#cmd-more');
153
- if (moreBtn) moreBtn.addEventListener('click', () => { advancedVisible = true; renderForm(); });
154
110
  dlg.querySelector('#cmd-close').addEventListener('click', () => dlg.close());
155
111
  dlg.querySelector('#cmd-cancel').addEventListener('click', () => dlg.close());
156
112
  dlg.querySelector('#cmd-submit').addEventListener('click', () => submitCommand(active));
@@ -168,23 +124,6 @@ async function showCommandLauncher() {
168
124
  if (v) body[f.name] = v;
169
125
  }
170
126
  }
171
- // plan / architect: at least one of taskFile or task is required.
172
- if ((command === 'plan' || command === 'architect') && !body.taskFile && !body.task) {
173
- await showError(
174
- maggle ? 'Escribe primero qué quieres pedir — con tus palabras vale.' : 'Provide either a SPEC file path or paste the task text.',
175
- { title: 'Missing input' }
176
- );
177
- return;
178
- }
179
- // AC2: before launching, the maggle confirms knowing exactly what
180
- // will happen — in their language, never a command name alone.
181
- if (maggle && command === 'plan') {
182
- const ok = await showConfirm(maggleText('launcher.confirm', ''), {
183
- title: SCHEMAS.plan.label,
184
- okLabel: maggleText('launcher.submit', 'Run'),
185
- });
186
- if (!ok) return;
187
- }
188
127
  if (command === 'clean' && body.nuke) {
189
128
  const ok = await showConfirm(
190
129
  '`kj clean --nuke` will delete EVERY plan / session / batch + wipe the HU Board DB. Cannot be undone.',
@@ -220,8 +159,7 @@ async function showCommandLauncher() {
220
159
 
221
160
  /**
222
161
  * Open the run-log panel pointed at a /api/runs/<commandId>/log
223
- * tail. Reuses the same panel UI as openLogViewer() for plans —
224
- * just swaps the URL it polls.
162
+ * tail, with the generic panel UI (openGenericLogPanel).
225
163
  */
226
164
  function openCommandLogViewer(commandLabel, commandId) {
227
165
  const args = {
@@ -161,6 +161,16 @@ function formatDuration(ms) {
161
161
  return `${min}m ${sec.toString().padStart(2, '0')}s`;
162
162
  }
163
163
 
164
+ // KJC-TSK-0403 + KJC-BUG-0405: the three kanban lanes, one place for every view. A failed
165
+ // HU goes back to Pending with its badge; `running` is what kj hu move and the shared-board
166
+ // mirror write, next to the pipeline's coding/reviewing.
167
+ const KANBAN_RUNNING = new Set(['coding', 'reviewing', 'running']);
168
+ function kanbanColumnOf(status) {
169
+ if (KANBAN_RUNNING.has(status)) return 'running';
170
+ if (status === 'done') return 'done';
171
+ return 'pending';
172
+ }
173
+
164
174
  function scoreClass(score, max = 60) {
165
175
  if (score === null || score === undefined) return '';
166
176
  const pct = score / max;
@@ -23,7 +23,7 @@ async function renderGovernance() {
23
23
  return;
24
24
  }
25
25
  localStorage.setItem(GOV_DIR_KEY, data.dir);
26
- app.innerHTML = govDirForm(data.dir) + govIdentity(data.identity) + govChain(data.report, data.anchor) + govRules(data.policy, data.report) + govGrants(data.report.grants, data.policy) + govSignals(data.report.signals);
26
+ app.innerHTML = govDirForm(data.dir) + govIdentity(data.identity) + govChain(data.report, data.anchor) + govRules(data.policy, data.report) + govGrants(data.report.grants, data.policy) + govSignals(data.report.signals) + govLanes(data.lanes);
27
27
  } catch (err) {
28
28
  app.innerHTML = govDirForm(dir) + `<div class="empty-state"><div class="empty-state__title">Governance unavailable</div><div class="empty-state__text">${esc(err.message)}</div></div>`;
29
29
  }
@@ -146,4 +146,16 @@ function govGrants(g, policy) {
146
146
  return `<div class="section-header"><span class="section-header__title">Standing exceptions</span><span class="section-header__count">${(g.alive || []).length} alive · ${(g.soon || []).length} expiring · ${(g.expired || []).length} expired · ${g.point || 0} one-off</span></div>${alive}${renewals ? `<ul class="gov-list">${renewals}</ul>` : ''}${form}<p id="gov-msg" class="gov-note"></p>`;
147
147
  }
148
148
 
149
+ // KJC-TSK-1314: worktree lanes outside the method, each with what to do about it.
150
+ function govLanes(lanes) {
151
+ if (!lanes) return '';
152
+ const head = '<div class="section-header"><span class="section-header__title">Lanes</span></div>';
153
+ if (lanes.error) return `${head}<p class="gov-note">⚠ ${esc(lanes.error)}</p>`;
154
+ const items = [
155
+ ...lanes.noCard.map((l) => `<li>⚠ ${esc(l.slug)} (${esc(l.branch)}): its branch names no card. Work starts from a card.</li>`),
156
+ ...lanes.merged.map((l) => `<li>⚠ ${esc(l.slug)}: PR #${esc(String(l.pr))} is merged and the lane is still there: <code>${esc(l.done)}</code></li>`),
157
+ ];
158
+ return head + (items.length ? `<ul class="gov-list">${items.join('')}</ul>` : '<p class="gov-note">Every lane works a card and none is left over.</p>');
159
+ }
160
+
149
161
  const govSignals = (signals) => `<div class="section-header"><span class="section-header__title">Signals</span></div>${(signals || []).length ? `<ul class="gov-list">${signals.map((s) => `<li>⚠ ${esc(s)}</li>`).join('')}</ul>` : '<p class="gov-note">None.</p>'}`;
@@ -14,58 +14,6 @@
14
14
  // - lastOpenedLog (mutable in app.js — assignment crosses scripts
15
15
  // transparently because both share the same realm)
16
16
 
17
- /**
18
- * PR4: launch a single HU from the per-card ▶ button. Confirms with
19
- * the user (so an accidental click doesn't kick a run) and POSTs to
20
- * the new /api/hus/:huId/run endpoint, which resolves the planId
21
- * server-side from the SQLite row.
22
- *
23
- * Exposed on `window` because the inline onclick on the card calls it.
24
- */
25
- window.runSingleHuFromCard = async function runSingleHuFromCard(huId, title) {
26
- const friendlyTitle = (title || huId).replace(/&#39;/g, "'");
27
- const ok = await showConfirm(
28
- `¿Lanzar solo esta HU?\n\n${friendlyTitle}\n\nKarajan ejecutará únicamente esta HU; las demás del plan no se tocarán.`,
29
- { title: 'Lanzar HU individual', okLabel: 'Lanzar', cancelLabel: 'Cancelar' }
30
- );
31
- if (!ok) return;
32
- try {
33
- const res = await fetch(`/api/hus/${encodeURIComponent(huId)}/run`, {
34
- method: 'POST',
35
- headers: { 'Content-Type': 'application/json' },
36
- body: JSON.stringify({}),
37
- });
38
- if (!res.ok) {
39
- const msg = (await res.json().catch(() => ({}))).error || `HTTP ${res.status}`;
40
- await showError(msg, { title: 'No se pudo lanzar la HU' });
41
- return;
42
- }
43
- const body = await res.json();
44
- // Surface the log path so the user can tail it from the View log button
45
- // AND auto-open the viewer so the click produces visible feedback. Without
46
- // the open, the user clicks Lanzar, sees nothing change, and concludes the
47
- // run never started — the run IS started, the log IS being written, the
48
- // UI just wasn't showing it.
49
- //
50
- // The tail URL must be derived from body.logPath, not constructed from
51
- // huId. When `kj run --plan ... --hu <id>` is launched, runPlan writes to
52
- // <runsDir>/<planId>--hu-<localHuId>.log
53
- // — not <runsDir>/hu-<huId>.log. The previous code guessed the wrong
54
- // filename and the viewer would have hit a non-existent file even if
55
- // it had been opened.
56
- if (body.logPath) {
57
- const logBasename = body.logPath.split('/').pop().replace(/\.log$/, '');
58
- const tailUrl = (offset) => `/api/runs/${encodeURIComponent(logBasename)}/log?offset=${offset || 0}`;
59
- lastOpenedLog = { id: `hu-${huId}`, label: `HU ${huId}`, tailUrl };
60
- openGenericLogPanel({ id: logBasename, label: `HU ${huId}`, tailUrl });
61
- }
62
- await fetch('/api/sync', { method: 'POST' }).catch(() => {});
63
- await renderBoard();
64
- } catch (err) {
65
- await showError(err.message || String(err), { title: 'Fallo al lanzar la HU' });
66
- }
67
- };
68
-
69
17
  /**
70
18
  * KJC-TSK-0394 step 2: devolver una HU al estado `pending` desde
71
19
  * cualquier estado no-terminal (coding/reviewing/blocked zombi) o
@@ -174,27 +122,28 @@ window.saveHuAssignee = async function saveHuAssignee(storyId) {
174
122
  }
175
123
  };
176
124
 
177
- // KJC-TSK-0408: deshace los cambios de una HU restaurando el snapshot
178
- // git tomado pre-run. ATENCIÓN: es destructivo (git reset --hard) —
179
- // confirmamos siempre antes de invocar.
180
- window.undoHuChanges = async function undoHuChanges(storyId) {
125
+ // KJC-TSK-1324: the ▶ of a card. Board story ids are `<project>::<hu id>` (sync.js).
126
+ window.startCardLane = async function startCardLane(storyId, event) {
127
+ event?.stopPropagation();
128
+ const [project, huId] = String(storyId).split('::');
129
+ if (!project || !huId) return showError(`Not a board card: ${storyId}`, { title: 'Cannot start the card' });
181
130
  const ok = await showConfirm(
182
- '⚠️ Esta acción es destructiva.\n\nSe descartan los cambios de ficheros que esta HU hizo durante su última ejecución y se restaura el estado pre-run. La HU vuelve a pending y podrás relanzarla con otros modelos.\n\n¿Continuar?',
183
- { title: 'Deshacer cambios de la HU', okLabel: 'Deshacer', cancelLabel: 'Cancelar' }
131
+ `Start ${huId}?\n\nkj opens its lane, moves the card to running and your panel's coder writes it there. Reviewing and committing stay with you.`,
132
+ { title: 'Start card', okLabel: 'Start' }
184
133
  );
185
134
  if (!ok) return;
186
135
  try {
187
- const res = await fetch(`/api/stories/${encodeURIComponent(storyId)}/undo`, { method: 'POST' });
188
- if (!res.ok) {
189
- const msg = (await res.json().catch(() => ({}))).error || `HTTP ${res.status}`;
190
- await showError(msg, { title: 'No se pudo deshacer la HU' });
191
- return;
192
- }
193
- closeModal();
194
- await fetch('/api/sync', { method: 'POST' }).catch(() => {});
195
- await renderBoard();
136
+ const res = await fetch(`/api/hu/${encodeURIComponent(huId)}/start`, {
137
+ method: 'POST',
138
+ headers: { 'Content-Type': 'application/json' },
139
+ body: JSON.stringify({ project }),
140
+ });
141
+ const payload = await res.json().catch(() => ({}));
142
+ if (res.status === 409) return showError(`${huId} already has a lane. Open it with:\n\ncd ${payload.lane}`, { title: 'Card already started' });
143
+ if (!res.ok) return showError(payload.error || `HTTP ${res.status}`, { title: 'Could not start the card' });
144
+ openCommandLogViewer(`hu start ${huId}`, payload.commandId);
196
145
  } catch (err) {
197
- await showError(err.message || String(err), { title: 'Fallo al deshacer la HU' });
146
+ await showError(err.message || String(err), { title: 'Could not start the card' });
198
147
  }
199
148
  };
200
149
 
@@ -15,8 +15,7 @@
15
15
  // - showStoryDetail() (utils/story-detail-view.js)
16
16
  // - showSessionDetail() (utils/sessions-view.js)
17
17
  // - selectProject() (app.js)
18
- // - triggerSync(),
19
- // startStandbyPolling() (utils/api.js)
18
+ // - triggerSync() (utils/api.js)
20
19
  // - subscribeToServerEvents(),
21
20
  // smartRefresh() (utils/server-push.js)
22
21
  // - selectedProject, currentView,
@@ -45,7 +44,7 @@ document.getElementById('sync-btn').addEventListener('click', async () => {
45
44
  });
46
45
 
47
46
  // Commands button — opens the launcher modal so the user can run
48
- // `kj plan`, `kj architect`, `kj clean` (and more later) without
47
+ // `kj clean` (and more later) without
49
48
  // dropping to a terminal. Output streams to the existing log panel.
50
49
  document.getElementById('commands-btn').addEventListener('click', () => {
51
50
  showCommandLauncher();
@@ -145,13 +144,13 @@ window.showSessionDetail = showSessionDetail;
145
144
  window.closeModal = closeModal;
146
145
  window.selectProject = selectProject;
147
146
 
148
- // KJC-TSK-0414 PR4: arranca el polling del banner de standby al cargar la página.
149
- startStandbyPolling();
150
-
151
147
  // Initial load — sync disk data first so new batches are visible
152
- triggerSync().then(() => {
153
- handleRoute();
148
+ triggerSync().then(async () => {
149
+ const shown = handleRoute();
154
150
  subscribeToServerEvents();
151
+ // KJC-TSK-1198: the guided tour, by itself on the first visit only, once the view is drawn.
152
+ await shown;
153
+ maybeStartFirstTour();
155
154
  });
156
155
 
157
156
  // Auto-refresh every 60 seconds as a SAFETY NET only — the board is
@@ -1,8 +1,8 @@
1
1
  // KJC-TSK-0501 step 7/8 — Floating run-log panel + window controls.
2
2
  //
3
3
  // Classic script (no exports). Loaded by index.html before app.js so
4
- // the function declarations hoist and inline onclick="openLogViewer(...)"
5
- // handlers emitted by board-view / dashboard-view resolve transparently.
4
+ // the function declarations hoist and the other scripts call
5
+ // openGenericLogPanel() directly.
6
6
  // `logPollTimer` and `logViewerState` are top-level `let` bindings in
7
7
  // this classic script — same realm as app.js, so the cross-script
8
8
  // references keep working unchanged.
@@ -19,23 +19,10 @@
19
19
  let logPollTimer = null;
20
20
  let logViewerState = { planId: null, panel: null, offset: 0, isMax: false, isMin: false };
21
21
 
22
- /**
23
- * Open (or refocus) the run-log panel for the given plan. Thin shim
24
- * over `openGenericLogPanel` — the original entry point used for
25
- * `kj run --plan` runs that the Run-plan button kicks off.
26
- */
27
- function openLogViewer(planId) {
28
- return openGenericLogPanel({
29
- id: planId,
30
- label: 'Run log',
31
- tailUrl: (offset) => `/api/plans/${encodeURIComponent(planId)}/log?offset=${offset}`,
32
- });
33
- }
34
-
35
22
  /**
36
23
  * Generic floating log panel anchored bottom-right. Used by:
37
- * - openLogViewer(planId) → /api/plans/:planId/log
38
24
  * - openCommandLogViewer(cmd, id) → /api/runs/:commandId/log
25
+ * (KJC-TSK-1351: the per-plan kj run log viewer went with kj run)
39
26
  *
40
27
  * The only thing that changes between callers is the tail URL +
41
28
  * the label; the window controls (min/max/close), polling, and ANSI
@@ -18,12 +18,7 @@ const MAGGLE_LABELS = {
18
18
  'board.title': 'Tu trabajo',
19
19
  'board.stories': 'tareas',
20
20
  'board.story': 'tarea',
21
- 'board.run': '▶ Continuar con lo pendiente',
22
- 'board.runTitle': 'Karajan seguirá con las tareas que faltan, una a una. Podrás ver la actividad aquí.',
23
- 'board.stop': '⏹ Parar',
24
- 'board.stopTitle': 'Detiene el trabajo en marcha. Lo ya terminado se conserva.',
25
21
  'board.viewLog': '📜 Ver actividad',
26
- 'board.running': 'en marcha',
27
22
  'board.ragPlaceholder': '🔍 Pregunta lo que quieras sobre este proyecto…',
28
23
  'header.subtitle': 'tu tablero de trabajo',
29
24
  'nav.board': 'Tablero',
@@ -31,14 +26,9 @@ const MAGGLE_LABELS = {
31
26
  'nav.more': 'Más',
32
27
  'picker.hint': 'Elige un proyecto para ver su tablero. Para cambiar de proyecto, vuelve a «Proyectos».',
33
28
  'picker.project': 'proyecto',
34
- 'launcher.title': '📝 Pedir trabajo a Karajan',
35
- 'launcher.planLabel': '📝 Pedir trabajo nuevo',
36
- 'launcher.planHelp': 'Cuéntale a Karajan qué quieres construir o cambiar, con tus palabras. Lo convertirá en tareas que verás en el tablero — todavía no toca tu código.',
37
- 'launcher.taskLabel': 'Qué quieres pedir',
38
- 'launcher.submit': 'Pedir',
29
+ 'launcher.title': '⚡ Mantenimiento de Karajan',
30
+ 'launcher.submit': 'Ejecutar',
39
31
  'launcher.cancel': 'Cancelar',
40
- 'launcher.more': 'Más opciones…',
41
- 'launcher.confirm': 'Voy a convertir tu petición en tareas del tablero. Tardará unos minutos y podrás seguir la actividad en esta ventana. ¿Sigo?',
42
32
  'conversation.title': 'Habla con tu agente',
43
33
  'conversation.hide': 'Ocultar — la conversación sigue viva',
44
34
  'conversation.end': 'Terminar la sesión del agente',
@@ -143,6 +143,11 @@ function showHelp() {
143
143
  Tip: hover any tab in the header for ~1 second to see a one-line summary.
144
144
  </div>
145
145
  <div style="padding:12px 18px;border-top:1px solid var(--border);text-align:right">
146
+ <button id="app-dialog-tour" class="control-btn"
147
+ style="padding:6px 16px;border:none;background:var(--color-blue,#3b82f6);color:#fff;
148
+ border-radius:var(--radius-sm);cursor:pointer;margin-right:8px">
149
+ ▶ Start the guided tour
150
+ </button>
146
151
  <button id="app-dialog-close" class="control-btn"
147
152
  style="padding:6px 16px;border:1px solid var(--border);
148
153
  background:var(--bg-primary);color:var(--text);
@@ -157,6 +162,8 @@ function showHelp() {
157
162
  };
158
163
  dlg.addEventListener('close', done, { once: true });
159
164
  dlg.querySelector('#app-dialog-close').addEventListener('click', done, { once: true });
165
+ // KJC-TSK-1198: the tour highlights the page, so the modal closes first.
166
+ dlg.querySelector('#app-dialog-tour').addEventListener('click', () => { done(); startBoardTour(); }, { once: true });
160
167
  dlg.showModal();
161
168
  });
162
169
  }
@@ -52,7 +52,7 @@ async function renderProjectPicker() {
52
52
  const c = { pending: 0, running: 0, done: 0, failed: 0 };
53
53
  for (const s of stories) {
54
54
  if (['pending', 'certified', 'needs_context', 'blocked'].includes(s.status)) c.pending += 1;
55
- else if (['coding', 'reviewing'].includes(s.status)) c.running += 1;
55
+ else if (kanbanColumnOf(s.status) === 'running') c.running += 1;
56
56
  else if (s.status === 'done') c.done += 1;
57
57
  else if (s.status === 'failed') c.failed += 1;
58
58
  }
@@ -164,20 +164,11 @@ async function patchBoardIncremental() {
164
164
  stories = await api(`/api/projects/${encodeURIComponent(selectedProject)}/stories`);
165
165
  } catch { return false; }
166
166
 
167
- // Re-derive the column → status mapping (kept in lockstep with
168
- // renderBoard's `columns` object — change there, change here).
169
- const statusToColumn = (status) => {
170
- if (['pending', 'certified', 'needs_context', 'blocked'].includes(status)) return 'pending';
171
- if (['coding', 'reviewing'].includes(status)) return 'running';
172
- if (status === 'done') return 'done';
173
- if (status === 'failed') return 'failed';
174
- return 'pending'; // safe default
175
- };
176
-
167
+ // KJC-BUG-0405: the same lanes renderBoard uses (formatters.js::kanbanColumnOf).
177
168
  const seen = new Set();
178
169
  for (const story of stories) {
179
170
  seen.add(story.id);
180
- const targetCol = columns[statusToColumn(story.status)];
171
+ const targetCol = columns[kanbanColumnOf(story.status)];
181
172
  if (!targetCol) continue;
182
173
  const existing = kanban.querySelector(`.story-card[data-story-id="${cssEscape(story.id)}"]`);
183
174
  if (!existing) {
@@ -232,21 +223,11 @@ async function patchBoardIncremental() {
232
223
  setTimeout(() => node.remove(), 160);
233
224
  }
234
225
  }
235
- // Recount each column + update the "N running" badge in the header.
226
+ // Recount each column (the Running column's count is the "how many in progress").
236
227
  for (const [cls, col] of Object.entries(columns)) {
237
228
  const counter = col.querySelector('[data-column-count]');
238
229
  if (counter) counter.textContent = String(col.querySelectorAll('.story-card').length);
239
230
  col.style.opacity = col.querySelectorAll('.story-card').length === 0 ? '0.55' : '1';
240
231
  }
241
- const runningCount = columns.running.querySelectorAll('.story-card').length;
242
- const runningBadge = document.querySelector('.section-header__badge');
243
- if (runningBadge) {
244
- if (runningCount > 0) {
245
- runningBadge.textContent = `⚙ ${runningCount} running…`;
246
- runningBadge.style.display = '';
247
- } else {
248
- runningBadge.style.display = 'none';
249
- }
250
- }
251
232
  return true;
252
233
  }
@@ -0,0 +1,33 @@
1
+ // KJC-TSK-1197 — notice shown above the kanban when the project's board mirrors the
2
+ // shared board (GitHub Issues, ADR 0025): what it is, how it changes, where it lives.
3
+ //
4
+ // Classic script (no exports), loaded before app.js like plan-rollup-view.js.
5
+ // Globals consumed: esc() (app.js).
6
+
7
+ // owner/name as GitHub allows them: nothing that could leave the href.
8
+ const SHARED_BOARD_REPO = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
9
+
10
+ /** The notice for the first plan that mirrors the shared board, or '' when there is none. */
11
+ function sharedBoardNoticeHtml(plans) {
12
+ const mirror = (plans || []).find((p) => p.readonly === 'github');
13
+ if (!mirror) return '';
14
+ const link = SHARED_BOARD_REPO.test(mirror.repo || '')
15
+ ? ` <a href="https://github.com/${mirror.repo}/issues?q=${encodeURIComponent('label:kj:hu')}" target="_blank" rel="noopener" style="color:var(--color-blue,#3b82f6);">Open its issues on GitHub (${esc(mirror.repo)})</a>`
16
+ : '';
17
+ return `
18
+ <div role="note" style="margin:8px 0;padding:10px 14px;background:var(--bg-primary);border:1px solid var(--border);border-left:4px solid var(--color-blue,#3b82f6);border-radius:var(--radius-sm);font-size:0.85rem;">
19
+ <strong>This is the team's shared board.</strong>
20
+ Its stories live as GitHub Issues and the whole team sees the same ones, so it is read-only here:
21
+ change a story with <code>kj hu move</code> from a terminal.${link}
22
+ </div>
23
+ `;
24
+ }
25
+
26
+ /** Fills the notice slot for a project. No-op when the slot is not mounted. */
27
+ async function renderSharedBoardNotice(projectId) {
28
+ const slot = document.getElementById('shared-board-notice');
29
+ if (!slot) return;
30
+ const r = await fetch(`/api/projects/${encodeURIComponent(projectId)}/plans-outcome`);
31
+ if (!r.ok) throw new Error(`plans-outcome answered ${r.status}`);
32
+ slot.innerHTML = sharedBoardNoticeHtml((await r.json()).plans);
33
+ }
@@ -12,7 +12,7 @@
12
12
  // - projectIsSharedCache (mutable, app.js)
13
13
  // - renderStoryEditForm() (app.js)
14
14
  // - closeModal(), changeHuStatusFromModal(), resetHuToPending(),
15
- // undoHuChanges(), saveHuModels(), saveHuAssignee() — bound on
15
+ // saveHuModels(), saveHuAssignee() — bound on
16
16
  // window from app.js so the inline handlers in this template
17
17
  // keep working unchanged.
18
18
 
@@ -104,20 +104,6 @@ async function showStoryDetail(storyId) {
104
104
  ↺ Reset
105
105
  </button>
106
106
  ` : ''}
107
- ${(() => {
108
- // KJC-TSK-0408: Undo solo si la HU tiene snapshot_sha en outcome.
109
- let parsedOutcome = null;
110
- try { parsedOutcome = typeof story.outcome === 'string' ? JSON.parse(story.outcome) : story.outcome; } catch { /* */ }
111
- const canUndo = parsedOutcome?.snapshot_sha && !parsedOutcome?.reverted;
112
- return canUndo ? `
113
- <button id="undo-hu-btn" class="control-btn"
114
- style="padding:6px 12px;border:1px solid var(--color-red,#ef4444);background:var(--bg-primary);color:var(--color-red,#ef4444);border-radius:var(--radius-sm);cursor:pointer;font-size:0.85rem"
115
- title="Deshacer cambios de esta HU: restaura los ficheros al snapshot pre-run y marca pending"
116
- onclick="undoHuChanges('${esc(story.id)}')">
117
- ⏪ Undo
118
- </button>
119
- ` : '';
120
- })()}
121
107
  <button class="modal__close" onclick="closeModal()">&times;</button>
122
108
  </div>
123
109
  </div>
@@ -254,10 +240,10 @@ async function showStoryDetail(storyId) {
254
240
  <div class="modal__section" style="border:1px solid var(--color-yellow,#eab308);background:rgba(234,179,8,0.08);padding:10px 12px;border-radius:var(--radius-sm)">
255
241
  <div class="modal__section-title" style="color:var(--color-yellow,#eab308)">⚠ Missing test contract</div>
256
242
  <div style="font-size:0.85rem;line-height:1.5;margin-top:4px">
257
- This HU has no acceptance_tests declared. The tests-first pipeline (v2.7.5)
258
- refuses to run HUs without an executable contract. Click ✎ Edit above and
259
- add at least one test — a <code>shell</code> command that exits 0 on pass,
260
- or a <code>gherkin</code> Given/When/Then spec.
243
+ This HU has no acceptance tests declared. The method writes the failing
244
+ test first, so declare at least one: ✎ Edit above, or
245
+ <code>kj hu add --tests</code>. A test is a <code>shell</code> command that
246
+ exits 0 on pass, or a <code>gherkin</code> Given/When/Then spec.
261
247
  </div>
262
248
  </div>
263
249
  ` : `
@@ -91,7 +91,6 @@ export function cleanupZombies(opts = {}) {
91
91
  const storyIds = db.prepare('SELECT id FROM stories WHERE project_id = ?').all(proj.id).map((r) => r.id);
92
92
  const sessionIds = db.prepare('SELECT id FROM sessions WHERE project_id = ?').all(proj.id).map((r) => r.id);
93
93
  const fsPaths = [
94
- ...storyIds.map((sid) => path.join(getKjHome(), 'hu-stories', sid)),
95
94
  ...sessionIds.map((sid) => path.join(getKjHome(), 'sessions', sid)),
96
95
  ...plansRoots().map((root) => path.join(root, proj.id)),
97
96
  ];