karajan-code 4.70.4 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -14
  3. package/packages/hu-board/package.json +1 -1
  4. package/packages/hu-board/public/app.js +4 -18
  5. package/packages/hu-board/public/index.html +5 -2
  6. package/packages/hu-board/public/styles.css +26 -0
  7. package/packages/hu-board/public/utils/api.js +0 -74
  8. package/packages/hu-board/public/utils/board-tour.js +62 -0
  9. package/packages/hu-board/public/utils/board-view.js +49 -144
  10. package/packages/hu-board/public/utils/command-launcher.js +8 -70
  11. package/packages/hu-board/public/utils/formatters.js +10 -0
  12. package/packages/hu-board/public/utils/governance-view.js +13 -1
  13. package/packages/hu-board/public/utils/hu-actions.js +17 -68
  14. package/packages/hu-board/public/utils/init-listeners.js +7 -8
  15. package/packages/hu-board/public/utils/log-panel.js +3 -16
  16. package/packages/hu-board/public/utils/maggle-mode.js +2 -12
  17. package/packages/hu-board/public/utils/modals.js +7 -0
  18. package/packages/hu-board/public/utils/project-picker-view.js +1 -1
  19. package/packages/hu-board/public/utils/server-push.js +3 -22
  20. package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
  21. package/packages/hu-board/public/utils/story-detail-view.js +5 -19
  22. package/packages/hu-board/src/cleanup-zombies.js +0 -1
  23. package/packages/hu-board/src/command-runner.js +36 -25
  24. package/packages/hu-board/src/db-connection.js +282 -0
  25. package/packages/hu-board/src/db-paths.js +113 -0
  26. package/packages/hu-board/src/db-queries.js +188 -0
  27. package/packages/hu-board/src/db-tombstones.js +51 -0
  28. package/packages/hu-board/src/db.js +28 -619
  29. package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
  30. package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
  31. package/packages/hu-board/src/plan-mutations.js +3 -153
  32. package/packages/hu-board/src/plans-service.js +55 -0
  33. package/packages/hu-board/src/routes/api/commands.js +122 -0
  34. package/packages/hu-board/src/routes/api/plans.js +55 -0
  35. package/packages/hu-board/src/routes/api/projects.js +267 -0
  36. package/packages/hu-board/src/routes/api/prompts.js +135 -0
  37. package/packages/hu-board/src/routes/api/sessions.js +95 -0
  38. package/packages/hu-board/src/routes/api/stories.js +183 -0
  39. package/packages/hu-board/src/routes/api/system.js +241 -0
  40. package/packages/hu-board/src/routes/api/tombstones.js +38 -0
  41. package/packages/hu-board/src/routes/api.js +24 -1371
  42. package/packages/hu-board/src/routes/governance.js +23 -1
  43. package/packages/hu-board/src/routes/lanes.js +40 -0
  44. package/packages/hu-board/src/routes/rag.js +4 -3
  45. package/packages/hu-board/src/server.js +20 -34
  46. package/packages/hu-board/src/sync-files.js +318 -0
  47. package/packages/hu-board/src/sync-project.js +99 -0
  48. package/packages/hu-board/src/sync.js +22 -566
  49. package/packages/hu-board/src/zombie-reaper.js +1 -1
  50. package/scripts/dual-publish.mjs +19 -3
  51. package/scripts/postinstall.js +15 -2
  52. package/scripts/verify-pack-mcp.mjs +11 -7
  53. package/scripts/verify-pack.mjs +4 -4
  54. package/src/audit/audit-history.js +3 -2
  55. package/src/audit/dead-exports.js +68 -59
  56. package/src/audit/deterministic-summary.js +33 -13
  57. package/src/audit/npm-signatures.js +41 -0
  58. package/src/audit/sbom.js +35 -0
  59. package/src/checks/agent-token.js +112 -0
  60. package/src/checks/dir-setup.js +34 -1
  61. package/src/checks/native-build.js +21 -39
  62. package/src/checks/runner.js +128 -142
  63. package/src/cli/advanced-commands.js +6 -6
  64. package/src/cli/register-meta.js +42 -82
  65. package/src/cli/register-pipeline.js +8 -143
  66. package/src/cli/register-plan.js +9 -100
  67. package/src/cli/register-roles-skills.js +1 -1
  68. package/src/cli/retired-commands.js +49 -0
  69. package/src/cli.js +18 -5
  70. package/src/commands/agent-run.js +40 -26
  71. package/src/commands/audit.js +127 -124
  72. package/src/commands/board.js +5 -9
  73. package/src/commands/check.js +29 -16
  74. package/src/commands/doctor.js +2 -0
  75. package/src/commands/env.js +176 -150
  76. package/src/commands/go.js +71 -53
  77. package/src/commands/harden.js +90 -81
  78. package/src/commands/hu-start.js +52 -0
  79. package/src/commands/hu.js +288 -221
  80. package/src/commands/identity.js +111 -78
  81. package/src/commands/init-atril.js +47 -0
  82. package/src/commands/init.js +11 -40
  83. package/src/commands/install-tools.js +70 -85
  84. package/src/commands/main-guard.js +78 -0
  85. package/src/commands/policy.js +226 -184
  86. package/src/commands/qmd.js +27 -21
  87. package/src/commands/rag.js +121 -55
  88. package/src/commands/review-gate.js +421 -341
  89. package/src/commands/skills.js +4 -1
  90. package/src/commands/start.js +4 -5
  91. package/src/commands/steward.js +96 -47
  92. package/src/commands/tournament.js +46 -37
  93. package/src/commands/worktree.js +107 -26
  94. package/src/config/defaults.js +1 -0
  95. package/src/config/loader.js +34 -17
  96. package/src/config/role-resolver.js +1 -0
  97. package/src/config/schema.js +1 -0
  98. package/src/config/secrets.js +36 -0
  99. package/src/config.js +1 -0
  100. package/src/environment/adr.js +16 -2
  101. package/src/git/karajan-label.js +20 -8
  102. package/src/guards/main-guard.js +54 -0
  103. package/src/harden/hook-templates.js +16 -1
  104. package/src/harden/sentinel/posttooluse.mjs +18 -6
  105. package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
  106. package/src/harden/sentinel/sentinel-lib.mjs +129 -26
  107. package/src/harden/sentinel/sentinel-shell.mjs +5 -16
  108. package/src/harden/sentinel/sessionstart.mjs +3 -2
  109. package/src/harden/sentinel/stop.mjs +5 -4
  110. package/src/harden/workflow-templates.js +1 -1
  111. package/src/hu/board-lookup.js +29 -0
  112. package/src/hu/gh-identity.js +23 -0
  113. package/src/hu/github-board.js +4 -2
  114. package/src/hu/lane-prune.js +83 -13
  115. package/src/hu/lane-setup.js +65 -0
  116. package/src/hu/lanes.js +66 -0
  117. package/src/hu/team-board-mirror.js +3 -2
  118. package/src/infrastructure/command-runner.js +1 -1
  119. package/src/infrastructure/file-system-service.js +1 -1
  120. package/src/mcp/handlers/direct-handlers.js +2 -292
  121. package/src/mcp/handlers/hu-handlers.js +1 -35
  122. package/src/mcp/handlers/management-handlers.js +2 -42
  123. package/src/mcp/handlers/rag-handler.js +9 -1
  124. package/src/mcp/server-handlers.js +16 -22
  125. package/src/mcp/tools.js +1 -196
  126. package/src/plan/lane-plans.js +54 -0
  127. package/src/plan/plan-store.js +7 -4
  128. package/src/policy/add.js +26 -12
  129. package/src/policy/adr-number-check.js +31 -0
  130. package/src/prompts/audit.js +299 -339
  131. package/src/prompts/card-context.js +10 -5
  132. package/src/prompts/reviewer.js +5 -2
  133. package/src/rag/auto-update.js +71 -44
  134. package/src/rag/bootstrap-index.js +46 -54
  135. package/src/rag/indexer.js +9 -4
  136. package/src/rag/migrate.js +1 -1
  137. package/src/rag/onnx-fallback.js +2 -2
  138. package/src/rag/pid-lease.js +181 -0
  139. package/src/rag/project-store.js +2 -12
  140. package/src/review/card-first.js +57 -46
  141. package/src/review/gate-gitignore.js +23 -4
  142. package/src/review/gate-hook.js +12 -4
  143. package/src/review/one-shot-review.js +45 -35
  144. package/src/review/publish-pr.js +14 -2
  145. package/src/review/repo-docs.js +73 -0
  146. package/src/review/repo-stack.js +59 -0
  147. package/src/review/reviewer-fallback.js +19 -4
  148. package/src/review/seal-commit.js +29 -0
  149. package/src/review/sonar-pregate.js +59 -44
  150. package/src/review/third-party-review.js +90 -16
  151. package/src/review/third-party-size.js +43 -0
  152. package/src/review/third-party-skills.js +54 -0
  153. package/src/review/verify-pr.js +1 -24
  154. package/src/roles/audit-role.js +106 -125
  155. package/src/skills/atril-cli.js +19 -1
  156. package/src/skills/atril-skills.js +5 -4
  157. package/src/skills/skills-search.js +72 -0
  158. package/src/skills/translate.js +62 -0
  159. package/src/sonar/scanner.js +116 -101
  160. package/src/steward/invariants.js +5 -4
  161. package/src/steward/phantom-sweep.js +66 -0
  162. package/src/tournament/judge.js +60 -53
  163. package/src/utils/gh-account.js +37 -0
  164. package/src/utils/git.js +0 -13
  165. package/src/utils/home-migration.js +7 -1
  166. package/src/utils/main-checkout.js +53 -0
  167. package/src/utils/qmd-collection.js +3 -1
  168. package/src/utils/welcome.js +3 -3
  169. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  172. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  173. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  174. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  175. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  176. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  177. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  178. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  179. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  180. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  181. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  182. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  183. package/packages/hu-board/src/run-tracker.js +0 -121
  184. package/src/activity-log.js +0 -59
  185. package/src/agents/host-agent.js +0 -38
  186. package/src/agents/model-registry.js +0 -142
  187. package/src/audit/plan-adherence.js +0 -224
  188. package/src/autonomy/arbiter-role.js +0 -71
  189. package/src/autonomy/arbiter.js +0 -22
  190. package/src/autonomy/decider.js +0 -18
  191. package/src/autonomy/decision-resolver.js +0 -58
  192. package/src/autonomy/outcome-report.js +0 -55
  193. package/src/autonomy/stage-ask.js +0 -30
  194. package/src/brain/decision-tracker.js +0 -125
  195. package/src/brain/decisor.js +0 -243
  196. package/src/brain/solomon-consult.js +0 -171
  197. package/src/brain/standby-scheduler.js +0 -8
  198. package/src/budget/comparison.js +0 -96
  199. package/src/canvas/parse-md.js +0 -238
  200. package/src/canvas/schema.js +0 -101
  201. package/src/canvas/to-flat.js +0 -85
  202. package/src/canvas/validate.js +0 -163
  203. package/src/ci/dispatch.js +0 -99
  204. package/src/ci/index.js +0 -3
  205. package/src/ci/pr-diff.js +0 -26
  206. package/src/cli/register-standby.js +0 -26
  207. package/src/commands/architect.js +0 -111
  208. package/src/commands/autorun.js +0 -49
  209. package/src/commands/researcher.js +0 -54
  210. package/src/commands/resume.js +0 -72
  211. package/src/commands/review.js +0 -89
  212. package/src/commands/run.js +0 -185
  213. package/src/commands/standby.js +0 -117
  214. package/src/commands/status.js +0 -36
  215. package/src/commands/triage.js +0 -78
  216. package/src/commands/undo.js +0 -62
  217. package/src/domains/domain-loader.js +0 -245
  218. package/src/domains/domain-registry.js +0 -140
  219. package/src/domains/domain-synthesizer.js +0 -129
  220. package/src/git/automation.js +0 -433
  221. package/src/git/coder-commit-guard.js +0 -40
  222. package/src/git/hu-automation.js +0 -243
  223. package/src/git/hu-snapshot.js +0 -10
  224. package/src/git/symlink-targets.js +0 -57
  225. package/src/golden/asserter.js +0 -92
  226. package/src/golden/loader.js +0 -52
  227. package/src/golden/runner.js +0 -92
  228. package/src/golden/schema.js +0 -41
  229. package/src/guards/duplicate-members.js +0 -86
  230. package/src/guards/intent-guard.js +0 -161
  231. package/src/guards/perf-guard.js +0 -126
  232. package/src/guards/policy-guard.js +0 -90
  233. package/src/guards/policy-resolver.js +0 -85
  234. package/src/guards/secret-redactor.js +0 -71
  235. package/src/hu/acceptance-runner.js +0 -138
  236. package/src/hu/lazy-planner.js +0 -109
  237. package/src/hu/parallel-executor.js +0 -94
  238. package/src/hu/routing-metrics.js +0 -17
  239. package/src/hu/split-validator.js +0 -143
  240. package/src/hu/splitting-detector.js +0 -158
  241. package/src/hu/splitting-generator.js +0 -175
  242. package/src/hu/store.js +0 -394
  243. package/src/infrastructure/mocks.js +0 -182
  244. package/src/mcp/handlers/run-handler.js +0 -318
  245. package/src/mcp/sovereignty-guard.js +0 -113
  246. package/src/mcp/tool-arg-normalizers.js +0 -16
  247. package/src/mutate/reviewer-signal.js +0 -76
  248. package/src/orchestrator/agent-fallback.js +0 -81
  249. package/src/orchestrator/brain-coordinator.js +0 -186
  250. package/src/orchestrator/ci-integration.js +0 -139
  251. package/src/orchestrator/config-init.js +0 -426
  252. package/src/orchestrator/direct-actions.js +0 -217
  253. package/src/orchestrator/drivers/error-recovery.js +0 -287
  254. package/src/orchestrator/drivers/init-context.js +0 -238
  255. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  256. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  257. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  258. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  259. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  260. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  261. package/src/orchestrator/drivers/post-loop.js +0 -404
  262. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  263. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  264. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  265. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  266. package/src/orchestrator/drivers/pre-loop.js +0 -524
  267. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  268. package/src/orchestrator/feedback-enrichment.js +0 -140
  269. package/src/orchestrator/feedback-queue.js +0 -151
  270. package/src/orchestrator/flow-control.js +0 -170
  271. package/src/orchestrator/flow-runner.js +0 -365
  272. package/src/orchestrator/fs-leak-detector.js +0 -228
  273. package/src/orchestrator/hu-scheduler.js +0 -114
  274. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  275. package/src/orchestrator/iteration-gate.js +0 -73
  276. package/src/orchestrator/iteration-stages.js +0 -9
  277. package/src/orchestrator/parallel-limiter.js +0 -74
  278. package/src/orchestrator/pipeline-context.js +0 -72
  279. package/src/orchestrator/post-loop-stages.js +0 -468
  280. package/src/orchestrator/pre-loop-stages.js +0 -11
  281. package/src/orchestrator/preflight-checks.js +0 -487
  282. package/src/orchestrator/repair/repair-detector.js +0 -117
  283. package/src/orchestrator/repair/repair-runner.js +0 -142
  284. package/src/orchestrator/reviewer-fallback.js +0 -45
  285. package/src/orchestrator/role-output-compressor.js +0 -189
  286. package/src/orchestrator/session-journal.js +0 -308
  287. package/src/orchestrator/solomon-escalation.js +0 -331
  288. package/src/orchestrator/solomon-rules.js +0 -212
  289. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  290. package/src/orchestrator/stages/architect-stage.js +0 -172
  291. package/src/orchestrator/stages/coder-stage.js +0 -506
  292. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  293. package/src/orchestrator/stages/format-stage.js +0 -61
  294. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  295. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  296. package/src/orchestrator/stages/perf-stage.js +0 -140
  297. package/src/orchestrator/stages/planner-stage.js +0 -85
  298. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  299. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  300. package/src/orchestrator/stages/research-stage.js +0 -69
  301. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  302. package/src/orchestrator/stages/sonar-stage.js +0 -439
  303. package/src/orchestrator/stages/stage-classes.js +0 -84
  304. package/src/orchestrator/stages/stage-executor.js +0 -155
  305. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  306. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  307. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  308. package/src/orchestrator/stages/triage-stage.js +0 -172
  309. package/src/orchestrator/standby.js +0 -71
  310. package/src/orchestrator/verification-gate.js +0 -183
  311. package/src/orchestrator.js +0 -22
  312. package/src/plan/adr-generator.js +0 -170
  313. package/src/plan/adr-loader.js +0 -60
  314. package/src/plan/plan-executor.js +0 -100
  315. package/src/plan/plan-validation.js +0 -4
  316. package/src/prompts/acceptance-coverage.js +0 -32
  317. package/src/prompts/arbiter.js +0 -38
  318. package/src/prompts/architect.js +0 -103
  319. package/src/prompts/hu-reviewer.js +0 -244
  320. package/src/prompts/tool-correctness-judge.js +0 -31
  321. package/src/prompts/triage.js +0 -57
  322. package/src/rag/embedder.js +0 -2
  323. package/src/rag/embedders/_cloud-base.js +0 -2
  324. package/src/rag/embedders/cohere.js +0 -2
  325. package/src/rag/embedders/mistral.js +0 -2
  326. package/src/rag/embedders/onnx.js +0 -2
  327. package/src/rag/embedders/openai.js +0 -2
  328. package/src/rag/embedders/voyage.js +0 -2
  329. package/src/rag/rerank.js +0 -4
  330. package/src/rag/where-parser.js +0 -4
  331. package/src/repeat-detector.js +0 -88
  332. package/src/review/diff-generator.js +0 -112
  333. package/src/review/git-stash-helper.js +0 -104
  334. package/src/review/schema.js +0 -31
  335. package/src/review/scope-filter.js +0 -152
  336. package/src/review/snapshot-diff.js +0 -122
  337. package/src/review/tdd-discipline.js +0 -90
  338. package/src/review/tdd-policy.js +0 -69
  339. package/src/roles/acceptance-coverage-role.js +0 -78
  340. package/src/roles/architect-role.js +0 -77
  341. package/src/roles/coder-role.js +0 -113
  342. package/src/roles/commiter-role.js +0 -113
  343. package/src/roles/domain-curator-role.js +0 -134
  344. package/src/roles/hu-reviewer-role.js +0 -64
  345. package/src/roles/impeccable-role.js +0 -85
  346. package/src/roles/index.js +0 -18
  347. package/src/roles/karajan-brain-role.js +0 -134
  348. package/src/roles/perf-role.js +0 -122
  349. package/src/roles/planner-role.js +0 -151
  350. package/src/roles/refactorer-role.js +0 -30
  351. package/src/roles/repairer-role.js +0 -181
  352. package/src/roles/researcher-role.js +0 -70
  353. package/src/roles/reviewer-role.js +0 -115
  354. package/src/roles/security-role.js +0 -73
  355. package/src/roles/solomon-role.js +0 -271
  356. package/src/roles/sonar-role.js +0 -72
  357. package/src/roles/tester-role.js +0 -235
  358. package/src/roles/tool-correctness-judge-role.js +0 -78
  359. package/src/roles/triage-role.js +0 -99
  360. package/src/session/journal/decisions-writer.js +0 -200
  361. package/src/session/journal/iteration-logger.js +0 -194
  362. package/src/session/journal/summary-writer.js +0 -320
  363. package/src/session/journal/tree-writer.js +0 -164
  364. package/src/session/journal/write-all.js +0 -89
  365. package/src/session-cleanup.js +0 -116
  366. package/src/skills/semantic-detector.js +0 -167
  367. package/src/skills/skill-detector.js +0 -385
  368. package/src/sonar/cloud-scanner.js +0 -78
  369. package/src/tracker-bootstrap.js +0 -44
  370. package/src/types/index.js +0 -38
  371. package/src/types/policy.js +0 -43
  372. package/src/types/solomon.js +0 -87
  373. package/src/utils/async-lock.js +0 -31
  374. package/src/utils/budget.js +0 -229
  375. package/src/utils/cwd-confirm.js +0 -39
  376. package/src/utils/display/event-handlers.js +0 -403
  377. package/src/utils/display/formatters.js +0 -132
  378. package/src/utils/display/header.js +0 -47
  379. package/src/utils/display/session-summary.js +0 -118
  380. package/src/utils/display/solomon.js +0 -42
  381. package/src/utils/pricing.js +0 -29
  382. package/src/utils/quota-simulator.js +0 -56
  383. package/src/utils/role-assigner.js +0 -137
  384. package/src/utils/rtk-wrapper.js +0 -87
  385. package/src/utils/run-registry.js +0 -9
  386. package/src/utils/status-dashboard.js +0 -254
  387. package/src/utils/tool-call-extractor.js +0 -56
  388. package/src/webperf/devtools-detect.js +0 -112
  389. package/src/webperf/webperf-role-integration.js +0 -66
  390. package/templates/skills/kj-run.md +0 -73
@@ -1,104 +0,0 @@
1
- // Surgical stash helper for the TDD discipline gate (KJC-TSK-0398 PR2).
2
- // Stashes only the source files we want to hide for the "red" run and
3
- // restores them afterwards. Tracked files use `git stash push -- <paths>`;
4
- // untracked files (not yet known to git) are moved aside to a private
5
- // `.karajan/tmp/stash-<id>/` tree because `git stash` would ignore them.
6
-
7
- import { mkdir, rename, rm } from "node:fs/promises";
8
- import { existsSync } from "node:fs";
9
- import { dirname, join, resolve } from "node:path";
10
- import { runCommand } from "../utils/process.js";
11
-
12
- function defaultRunner(cmd, args, opts) {
13
- return runCommand(cmd, args, opts);
14
- }
15
-
16
- async function isTracked(runner, projectDir, file) {
17
- const res = await runner("git", ["ls-files", "--error-unmatch", "--", file], { cwd: projectDir });
18
- return res.exitCode === 0;
19
- }
20
-
21
- async function stashRefForToken(runner, projectDir, marker) {
22
- const res = await runner("git", ["stash", "list"], { cwd: projectDir });
23
- if (res.exitCode !== 0) return null;
24
- for (const line of String(res.stdout || "").split("\n")) {
25
- if (line.includes(marker)) {
26
- const m = line.match(/^(stash@\{\d+\})/);
27
- if (m) return m[1];
28
- }
29
- }
30
- return null;
31
- }
32
-
33
- // Move `files` aside so a test run sees them as missing. Returns a token to
34
- // pass to `restoreFiles()`. Throws on failure with the partial token attached.
35
- export async function stashFilesAside(files, { projectDir = process.cwd(), runner = defaultRunner, tmpRoot } = {}) {
36
- if (!Array.isArray(files) || files.length === 0) {
37
- return { trackedStashRef: null, untrackedMoves: [], marker: null };
38
- }
39
- const tracked = [];
40
- const untracked = [];
41
- for (const f of files) {
42
- if (await isTracked(runner, projectDir, f)) tracked.push(f);
43
- else if (existsSync(resolve(projectDir, f))) untracked.push(f);
44
- }
45
-
46
- const marker = `kj-tdd-discipline-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
47
- const stashRoot = tmpRoot || join(projectDir, ".karajan", "tmp", marker);
48
- const untrackedMoves = [];
49
- let trackedStashRef = null;
50
-
51
- if (tracked.length > 0) {
52
- const args = ["stash", "push", "--keep-index", "-m", marker, "--", ...tracked];
53
- const res = await runner("git", args, { cwd: projectDir });
54
- if (res.exitCode !== 0) {
55
- throw new Error(`git stash push failed: ${res.stderr || res.stdout}`);
56
- }
57
- trackedStashRef = await stashRefForToken(runner, projectDir, marker);
58
- }
59
-
60
- for (const f of untracked) {
61
- const src = resolve(projectDir, f);
62
- const dst = join(stashRoot, f);
63
- await mkdir(dirname(dst), { recursive: true });
64
- await rename(src, dst);
65
- untrackedMoves.push({ src, dst });
66
- }
67
-
68
- return { trackedStashRef, untrackedMoves, marker, stashRoot };
69
- }
70
-
71
- // Reverse a `stashFilesAside` token. Idempotent: a restored token becomes a no-op.
72
- export async function restoreFiles(token, { projectDir = process.cwd(), runner = defaultRunner } = {}) {
73
- if (!token || (token.trackedStashRef == null && token.untrackedMoves.length === 0)) return;
74
-
75
- for (const { src, dst } of token.untrackedMoves) {
76
- if (existsSync(dst)) {
77
- await mkdir(dirname(src), { recursive: true });
78
- await rename(dst, src);
79
- }
80
- }
81
- if (token.stashRoot && existsSync(token.stashRoot)) {
82
- await rm(token.stashRoot, { recursive: true, force: true });
83
- }
84
-
85
- if (token.trackedStashRef) {
86
- const res = await runner("git", ["stash", "pop", token.trackedStashRef], { cwd: projectDir });
87
- if (res.exitCode !== 0) {
88
- throw new Error(`git stash pop failed: ${res.stderr || res.stdout}`);
89
- }
90
- }
91
- token.trackedStashRef = null;
92
- token.untrackedMoves = [];
93
- }
94
-
95
- // Run `body` with `files` stashed aside, always restoring them — even when
96
- // `body` throws. The TDD discipline stage wraps its "red" run with this.
97
- export async function withStashedFiles(files, body, opts = {}) {
98
- const token = await stashFilesAside(files, opts);
99
- try {
100
- return await body(token);
101
- } finally {
102
- await restoreFiles(token, opts).catch(() => { /* swallow: surface body error */ });
103
- }
104
- }
@@ -1,31 +0,0 @@
1
- export function validateReviewResult(reviewResult) {
2
- if (!reviewResult || typeof reviewResult !== "object") {
3
- throw new TypeError("Reviewer output must be a JSON object");
4
- }
5
-
6
- if (typeof reviewResult.approved !== "boolean") {
7
- throw new TypeError("Reviewer output missing boolean field: approved");
8
- }
9
-
10
- if (!Array.isArray(reviewResult.blocking_issues)) {
11
- throw new TypeError("Reviewer output missing array field: blocking_issues");
12
- }
13
-
14
- if (!Array.isArray(reviewResult.non_blocking_suggestions)) {
15
- reviewResult.non_blocking_suggestions = [];
16
- }
17
-
18
- if (typeof reviewResult.summary !== "string") {
19
- reviewResult.summary = "";
20
- }
21
-
22
- if (typeof reviewResult.confidence !== "number") {
23
- reviewResult.confidence = 0.5;
24
- }
25
-
26
- if (reviewResult.approved && reviewResult.blocking_issues.length > 0) {
27
- throw new Error("Invalid reviewer output: approved=true with blocking issues");
28
- }
29
-
30
- return reviewResult;
31
- }
@@ -1,152 +0,0 @@
1
- /**
2
- * Scope filter — auto-defers reviewer blocking issues that reference
3
- * files NOT present in the diff. This prevents reviewer scope drift
4
- * (flagging missing features, unchanged code, future tasks) from
5
- * stalling the pipeline.
6
- *
7
- * Deferred issues are NOT forgotten — they are tracked in the session
8
- * as technical debt that should be resolved in future iterations or
9
- * follow-up tasks. The coder and planner receive context about what
10
- * was deferred and why.
11
- */
12
-
13
- /**
14
- * Extract the set of changed file paths from a unified diff string.
15
- */
16
- export function extractDiffFiles(diff) {
17
- const files = new Set();
18
- for (const line of (diff || "").split("\n")) {
19
- // Match "+++ b/path" lines in unified diff
20
- const m = /^\+\+\+ b\/(.+)/.exec(line);
21
- if (m) files.add(m[1]);
22
- }
23
- return files;
24
- }
25
-
26
- /**
27
- * Determine whether a blocking issue is within scope of the diff.
28
- *
29
- * An issue is considered IN scope when:
30
- * - It has no `file` field (general concern about the diff)
31
- * - Its `file` matches one of the changed files (exact or suffix match)
32
- * - It references a pattern present in the diff content itself
33
- *
34
- * An issue is OUT of scope when:
35
- * - It explicitly references a file NOT in the diff
36
- */
37
- export function isIssueInScope(issue, diffFiles, diffContent) {
38
- const file = (issue.file || "").trim();
39
-
40
- // No file specified — the reviewer is commenting on the diff generally
41
- if (!file) return true;
42
-
43
- // Direct match
44
- if (diffFiles.has(file)) return true;
45
-
46
- // Suffix match (reviewer might use full path vs relative)
47
- for (const df of diffFiles) {
48
- if (df.endsWith(file) || file.endsWith(df)) return true;
49
- }
50
-
51
- // Check if the file path appears anywhere in the diff content
52
- // (covers cases where the file is referenced in imports/requires)
53
- if (diffContent?.includes(file)) return true;
54
-
55
- return false;
56
- }
57
-
58
- /**
59
- * Filter a review result, demoting out-of-scope blocking issues to
60
- * non-blocking suggestions.
61
- *
62
- * Returns { review, demoted, deferred, allDemoted } where:
63
- * - review: the filtered review (may flip approved to true)
64
- * - demoted: array of original issues that were demoted
65
- * - deferred: structured deferred issues with metadata for session tracking
66
- * - allDemoted: true if ALL blocking issues were out of scope
67
- */
68
- export function filterReviewScope(review, diff) {
69
- if (!review || review.approved) {
70
- return { review, demoted: [], deferred: [], allDemoted: false };
71
- }
72
-
73
- const diffFiles = extractDiffFiles(diff);
74
-
75
- // If we can't parse diff files, don't filter (safety)
76
- if (diffFiles.size === 0) {
77
- return { review, demoted: [], deferred: [], allDemoted: false };
78
- }
79
-
80
- const inScope = [];
81
- const demoted = [];
82
-
83
- for (const issue of review.blocking_issues || []) {
84
- if (isIssueInScope(issue, diffFiles, diff)) {
85
- inScope.push(issue);
86
- } else {
87
- demoted.push(issue);
88
- }
89
- }
90
-
91
- if (demoted.length === 0) {
92
- return { review, demoted: [], deferred: [], allDemoted: false };
93
- }
94
-
95
- const demotedSuggestions = demoted.map(
96
- (issue) => `[auto-demoted] ${issue.file || "unknown"}: ${issue.description || issue.id || "no description"}`
97
- );
98
-
99
- const filtered = {
100
- ...review,
101
- blocking_issues: inScope,
102
- non_blocking_suggestions: [
103
- ...(review.non_blocking_suggestions || []),
104
- ...demotedSuggestions
105
- ]
106
- };
107
-
108
- // If no in-scope blocking issues remain, auto-approve
109
- const allDemoted = inScope.length === 0;
110
- if (allDemoted) {
111
- filtered.approved = true;
112
- filtered.summary = `${review.summary || ""} [Auto-approved: ${demoted.length} out-of-scope issue(s) demoted to suggestions]`.trim();
113
- }
114
-
115
- // Build structured deferred issues for session tracking
116
- const deferred = demoted.map((issue) => ({
117
- id: issue.id || null,
118
- file: issue.file || null,
119
- severity: issue.severity || "medium",
120
- description: issue.description || "no description",
121
- suggested_fix: issue.suggested_fix || null,
122
- deferred_at: new Date().toISOString(),
123
- reason: "out_of_scope"
124
- }));
125
-
126
- return { review: filtered, demoted, deferred, allDemoted };
127
- }
128
-
129
- /**
130
- * Build a human-readable summary of deferred issues for injection
131
- * into coder/planner prompts so they are aware of the tech debt.
132
- */
133
- export function buildDeferredContext(deferredIssues) {
134
- if (!deferredIssues?.length) return "";
135
-
136
- const lines = [
137
- "## Deferred reviewer concerns (technical debt)",
138
- "The following issues were flagged by the reviewer but deferred because they are outside the current diff scope.",
139
- "You do NOT need to fix them now, but be aware of them:",
140
- ""
141
- ];
142
-
143
- for (const issue of deferredIssues) {
144
- const file = issue.file ? `\`${issue.file}\`` : "general";
145
- const fix = issue.suggested_fix ? ` — Suggestion: ${issue.suggested_fix}` : "";
146
- lines.push(`- [${issue.severity}] ${file}: ${issue.description}${fix}`);
147
- }
148
-
149
- lines.push("", "If your current changes naturally address any of these, great. Otherwise, they will be tracked for future resolution.");
150
-
151
- return lines.join("\n");
152
- }
@@ -1,122 +0,0 @@
1
- /**
2
- * Git-free diff generation using filesystem snapshots.
3
- *
4
- * Takes a snapshot of project files (path → content hash), then compares
5
- * a later snapshot to generate a unified diff. Works on any OS, no git needed.
6
- */
7
-
8
- import { readdir, readFile, stat } from "node:fs/promises";
9
- import { join, relative } from "node:path";
10
- import { createHash } from "node:crypto";
11
-
12
- const DEFAULT_IGNORE = new Set([
13
- "node_modules", ".git", ".kj", ".karajan", "dist", "build",
14
- ".next", ".nuxt", ".svelte-kit", "__pycache__", ".venv",
15
- "coverage", ".nyc_output", ".reviews"
16
- ]);
17
-
18
- const MAX_FILE_SIZE = 512 * 1024; // 512KB — skip large files
19
-
20
- /**
21
- * Scan a directory recursively and return a Map of relative path → content hash.
22
- * @param {string} rootDir
23
- * @returns {Promise<Map<string, string>>} path → sha256 hash
24
- */
25
- export async function takeSnapshot(rootDir) {
26
- const snapshot = new Map();
27
- await scanDir(rootDir, rootDir, snapshot);
28
- return snapshot;
29
- }
30
-
31
- async function scanDir(dir, rootDir, snapshot) {
32
- let entries;
33
- try {
34
- entries = await readdir(dir, { withFileTypes: true });
35
- } catch { return; }
36
-
37
- for (const entry of entries) {
38
- if (DEFAULT_IGNORE.has(entry.name) || entry.name.startsWith(".")) continue;
39
-
40
- const fullPath = join(dir, entry.name);
41
- if (entry.isDirectory()) {
42
- await scanDir(fullPath, rootDir, snapshot);
43
- } else if (entry.isFile()) {
44
- try {
45
- const s = await stat(fullPath);
46
- if (s.size > MAX_FILE_SIZE) continue;
47
- const content = await readFile(fullPath);
48
- const hash = createHash("sha256").update(content).digest("hex");
49
- snapshot.set(relative(rootDir, fullPath), hash);
50
- } catch { /* skip unreadable files */ }
51
- }
52
- }
53
- }
54
-
55
- /**
56
- * Compare two snapshots and generate a unified diff string.
57
- * @param {Map<string, string>} before — snapshot before coder
58
- * @param {Map<string, string>|null} after — snapshot after coder (null = read from disk)
59
- * @param {string} rootDir — project root (for reading file contents)
60
- * @returns {Promise<string>} unified diff text
61
- */
62
- export async function generateSnapshotDiff(before, after, rootDir) {
63
- if (!after) {
64
- after = await takeSnapshot(rootDir);
65
- }
66
-
67
- const lines = [];
68
-
69
- // New files (in after but not before)
70
- for (const [path, _hash] of after) {
71
- if (!before.has(path)) {
72
- const content = await safeRead(join(rootDir, path));
73
- if (content !== null) {
74
- lines.push(`diff --snapshot a/${path} b/${path}`);
75
- lines.push("new file");
76
- lines.push(`--- /dev/null`);
77
- lines.push(`+++ b/${path}`);
78
- for (const line of content.split("\n")) {
79
- lines.push(`+${line}`);
80
- }
81
- lines.push("");
82
- }
83
- }
84
- }
85
-
86
- // Modified files (in both, different hash)
87
- for (const [path, hash] of after) {
88
- if (before.has(path) && before.get(path) !== hash) {
89
- const content = await safeRead(join(rootDir, path));
90
- if (content !== null) {
91
- lines.push(`diff --snapshot a/${path} b/${path}`);
92
- lines.push("modified file");
93
- lines.push(`--- a/${path}`);
94
- lines.push(`+++ b/${path}`);
95
- // Show full new content (we don't have the old content)
96
- for (const line of content.split("\n")) {
97
- lines.push(`+${line}`);
98
- }
99
- lines.push("");
100
- }
101
- }
102
- }
103
-
104
- // Deleted files (in before but not after)
105
- for (const [path] of before) {
106
- if (!after.has(path)) {
107
- lines.push(`diff --snapshot a/${path} b/${path}`);
108
- lines.push("deleted file");
109
- lines.push(`--- a/${path}`);
110
- lines.push(`+++ /dev/null`);
111
- lines.push("");
112
- }
113
- }
114
-
115
- return lines.join("\n");
116
- }
117
-
118
- async function safeRead(filePath) {
119
- try {
120
- return await readFile(filePath, "utf-8");
121
- } catch { return null; }
122
- }
@@ -1,90 +0,0 @@
1
- // TDD discipline gate (KJC-TSK-0398 PR1).
2
- // PR1 ships the pure decision logic with injected runTests/stashFiles
3
- // helpers. PR2 ships the real surgical stash. PR3 wires the gate into
4
- // the pipeline behind `development.require_red_then_green`.
5
-
6
- const PARSERS = {
7
- vitest: parseVitestFailures,
8
- jest: parseVitestFailures,
9
- pytest: parsePytestFailures,
10
- };
11
-
12
- function parseVitestFailures(output) {
13
- const failures = new Set();
14
- for (const line of String(output || "").split("\n")) {
15
- const m = line.match(/^\s*(?:FAIL|×|✖)\s+(\S+)/);
16
- if (m) failures.add(m[1].trim());
17
- }
18
- return failures;
19
- }
20
-
21
- function parsePytestFailures(output) {
22
- const failures = new Set();
23
- for (const line of String(output || "").split("\n")) {
24
- const m = line.match(/^FAILED\s+(\S+)/);
25
- if (m) failures.add(m[1].trim());
26
- }
27
- return failures;
28
- }
29
-
30
- function anyNewTestIn(failures, testFiles) {
31
- return [...failures].some((f) => testFiles.some((t) => f.includes(t) || t.includes(f)));
32
- }
33
-
34
- export async function verifyTddDiscipline({
35
- projectDir,
36
- sourceFiles = [],
37
- testFiles = [],
38
- framework = null,
39
- runTests,
40
- stashFiles,
41
- restoreFiles,
42
- } = {}) {
43
- if (sourceFiles.length === 0) {
44
- return { ok: true, reason: "no_source_changes", sourceFiles, testFiles };
45
- }
46
- if (testFiles.length === 0) {
47
- return { ok: true, reason: "no_test_changes", sourceFiles, testFiles };
48
- }
49
-
50
- const parser = framework ? PARSERS[framework] : null;
51
- if (!parser) {
52
- return {
53
- ok: true,
54
- reason: "framework_unsupported",
55
- sourceFiles,
56
- testFiles,
57
- warning: `tdd-discipline: framework "${framework || "unknown"}" has no failure parser; gate skipped`,
58
- };
59
- }
60
-
61
- let stashToken = null;
62
- try {
63
- stashToken = await stashFiles(sourceFiles);
64
- const before = await runTests(projectDir);
65
- if (!anyNewTestIn(parser(before.output), testFiles)) {
66
- return {
67
- ok: false,
68
- reason: "tests_pass_without_impl",
69
- sourceFiles,
70
- testFiles,
71
- message:
72
- "TDD discipline violation: new tests pass without the implementation. Tests must fail first (red), then pass (green).",
73
- };
74
- }
75
- } finally {
76
- if (stashToken) await restoreFiles(stashToken);
77
- }
78
-
79
- const after = await runTests(projectDir);
80
- if (anyNewTestIn(parser(after.output), testFiles)) {
81
- return {
82
- ok: false,
83
- reason: "tests_fail_with_impl",
84
- sourceFiles,
85
- testFiles,
86
- message: "TDD discipline violation: new tests still fail with the implementation present.",
87
- };
88
- }
89
- return { ok: true, reason: "red_then_green_verified", sourceFiles, testFiles };
90
- }
@@ -1,69 +0,0 @@
1
- function extractChangedFiles(diff) {
2
- const files = new Set();
3
- const lines = String(diff || "").split("\n");
4
- for (const line of lines) {
5
- // Support both git diff and snapshot diff formats
6
- if (!line.startsWith("diff --git ") && !line.startsWith("diff --snapshot ")) continue;
7
- const parts = line.split(" ");
8
- const b = parts[3] || "";
9
- if (b.startsWith("b/")) files.add(b.slice(2));
10
- }
11
- return [...files];
12
- }
13
-
14
- function isTestFile(file, patterns = []) {
15
- const normalized = `/${file}`;
16
- return patterns.some((pattern) => normalized.includes(pattern));
17
- }
18
-
19
- function isSourceFile(file, extensions = []) {
20
- return extensions.some((ext) => file.endsWith(ext));
21
- }
22
-
23
- const SKIP_TDD_TASK_TYPES = new Set(["doc", "infra", "no-code"]);
24
-
25
- const DEFAULT_TEST_PATTERNS = ["/tests/", "/__tests__/", ".test.", ".spec.", "/src/test/", "/test/", "Test.java", "Test.kt", "test_", "_test.py", "_test.go", "Test.cs", "_spec.rb", "Test.php", "_test.dart", "Tests.swift"];
26
- const DEFAULT_SOURCE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".py", ".go", ".java", ".kt", ".rb", ".php", ".cs", ".rs", ".swift", ".dart"];
27
-
28
- export function evaluateTddPolicy(diff, developmentConfig = {}, untrackedFiles = [], taskType = null) {
29
- if (taskType && SKIP_TDD_TASK_TYPES.has(taskType)) {
30
- return { ok: true, reason: "tdd_not_applicable_for_task_type", sourceFiles: [], testFiles: [] };
31
- }
32
-
33
- const requireTestChanges = developmentConfig.require_test_changes !== false;
34
- const patterns = developmentConfig.test_file_patterns || DEFAULT_TEST_PATTERNS;
35
- const extensions = developmentConfig.source_file_extensions || DEFAULT_SOURCE_EXTENSIONS;
36
-
37
- const diffFiles = extractChangedFiles(diff);
38
- const extra = Array.isArray(untrackedFiles) ? untrackedFiles : [];
39
- const files = [...new Set([...diffFiles, ...extra])];
40
- const sourceFiles = files.filter((f) => isSourceFile(f, extensions) && !isTestFile(f, patterns));
41
- const testFiles = files.filter((f) => isTestFile(f, patterns));
42
-
43
- if (!requireTestChanges) {
44
- return {
45
- ok: true,
46
- reason: "test_changes_not_required",
47
- sourceFiles,
48
- testFiles
49
- };
50
- }
51
-
52
- if (sourceFiles.length === 0) {
53
- const reason = files.length === 0 ? "no_diff_available" : "no_source_changes";
54
- return { ok: true, reason, sourceFiles, testFiles };
55
- }
56
-
57
- if (testFiles.length === 0) {
58
- return {
59
- ok: false,
60
- reason: "source_changes_without_tests",
61
- sourceFiles,
62
- testFiles,
63
- message:
64
- "TDD policy violation: source code changed without test changes. Add/adjust tests first, then implementation."
65
- };
66
- }
67
-
68
- return { ok: true, reason: "tests_present", sourceFiles, testFiles };
69
- }
@@ -1,78 +0,0 @@
1
- // KJC-TSK-0987 (issue #1894): AcceptanceCoverageRole, the judge that reads each
2
- // acceptance criterion of the card against the diff before the run is approved.
3
- // Judged by a different AI than the coder (the reviewer's provider by default).
4
- // Role + helpers ship here; the stage wires it (same PR), the loops next.
5
-
6
- import { AgentRole } from "./agent-role.js";
7
- import { buildAcceptanceCoveragePrompt } from "../prompts/acceptance-coverage.js";
8
- import { extractFirstJson } from "../utils/json-extract.js";
9
-
10
- /**
11
- * The card's acceptance criteria as plain sentences, one per criterion.
12
- * Structured Given/When/Then first; else the free text, one per line or bullet.
13
- * @param {object|null} card
14
- * @returns {string[]}
15
- */
16
- export function criteriaOfCard(card) {
17
- const structured = Array.isArray(card?.acceptanceCriteriaStructured) ? card.acceptanceCriteriaStructured : [];
18
- const fromStructured = structured
19
- .map((ac) => (ac?.given && ac?.when && ac?.then ? `Given ${ac.given}, when ${ac.when}, then ${ac.then}` : String(ac?.raw || "").trim()))
20
- .filter(Boolean);
21
- if (fromStructured.length > 0) return fromStructured;
22
- return String(card?.acceptanceCriteria || "")
23
- .split("\n")
24
- .map((line) => line.replace(/^\s*(?:[-*•]|\d+[.)])\s*/, "").trim())
25
- .filter(Boolean);
26
- }
27
-
28
- const normalizeVerdicts = (parsed, criteria) => criteria.map((text, i) => {
29
- const v = Array.isArray(parsed?.criteria) ? parsed.criteria.find((c) => Number(c?.index) === i + 1) : null;
30
- return {
31
- index: i + 1,
32
- text,
33
- // No entry for a criterion is no evidence for it: not covered, never assumed.
34
- covered: v ? Boolean(v.covered) : false,
35
- evidence: typeof v?.evidence === "string" ? v.evidence.trim() : "",
36
- };
37
- });
38
-
39
- export class AcceptanceCoverageRole extends AgentRole {
40
- constructor(opts) {
41
- super({ ...opts, name: "acceptance-coverage" });
42
- }
43
-
44
- // A different AI than the coder: the reviewer's, unless the role has its own.
45
- resolveProvider() {
46
- return this.config?.roles?.["acceptance-coverage"]?.provider
47
- || this.config?.roles?.reviewer?.provider
48
- || this.config?.reviewer
49
- || "codex";
50
- }
51
-
52
- extractInput(input) {
53
- return { ...super.extractInput(input), criteria: Array.isArray(input?.criteria) ? input.criteria : [], diff: String(input?.diff || "") };
54
- }
55
-
56
- async buildPrompt({ task, criteria, diff }) {
57
- this._criteria = criteria;
58
- return { prompt: buildAcceptanceCoveragePrompt({ criteria, diff, task, instructions: this.instructions }) };
59
- }
60
-
61
- parseOutput(raw) { return extractFirstJson(raw); }
62
-
63
- // A verdict with no `criteria` array is not a verdict: the reviewer's failure.
64
- isSuccessful(parsed) { return Array.isArray(parsed?.criteria); }
65
-
66
- buildSuccessResult(parsed, provider) {
67
- const criteria = normalizeVerdicts(parsed, this._criteria || []);
68
- const uncovered = criteria.filter((c) => !c.covered);
69
- return { criteria, uncovered, provider, error: Array.isArray(parsed?.criteria) ? undefined : "no criteria array in the verdict" };
70
- }
71
-
72
- buildSummary(parsed) {
73
- if (!Array.isArray(parsed?.criteria)) return "acceptance-coverage: unreadable verdict (no criteria array)";
74
- const total = (this._criteria || []).length;
75
- const uncovered = normalizeVerdicts(parsed, this._criteria || []).filter((c) => !c.covered).length;
76
- return uncovered === 0 ? `acceptance-coverage: ${total}/${total} criteria covered` : `acceptance-coverage: ${uncovered} of ${total} criteria NOT covered`;
77
- }
78
- }