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
@@ -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
  ];