autonomous-coding-toolkit 1.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 (324) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/LICENSE +21 -0
  4. package/Makefile +21 -0
  5. package/README.md +140 -0
  6. package/SECURITY.md +28 -0
  7. package/agents/bash-expert.md +113 -0
  8. package/agents/dependency-auditor.md +138 -0
  9. package/agents/integration-tester.md +120 -0
  10. package/agents/lesson-scanner.md +149 -0
  11. package/agents/python-expert.md +179 -0
  12. package/agents/service-monitor.md +141 -0
  13. package/agents/shell-expert.md +147 -0
  14. package/benchmarks/runner.sh +147 -0
  15. package/benchmarks/tasks/01-rest-endpoint/rubric.sh +29 -0
  16. package/benchmarks/tasks/01-rest-endpoint/task.md +17 -0
  17. package/benchmarks/tasks/02-refactor-module/task.md +8 -0
  18. package/benchmarks/tasks/03-fix-integration-bug/task.md +8 -0
  19. package/benchmarks/tasks/04-add-test-coverage/task.md +8 -0
  20. package/benchmarks/tasks/05-multi-file-feature/task.md +8 -0
  21. package/bin/act.js +238 -0
  22. package/commands/autocode.md +6 -0
  23. package/commands/cancel-ralph.md +18 -0
  24. package/commands/code-factory.md +53 -0
  25. package/commands/create-prd.md +55 -0
  26. package/commands/ralph-loop.md +18 -0
  27. package/commands/run-plan.md +117 -0
  28. package/commands/submit-lesson.md +122 -0
  29. package/docs/ARCHITECTURE.md +630 -0
  30. package/docs/CONTRIBUTING.md +125 -0
  31. package/docs/lessons/0001-bare-exception-swallowing.md +34 -0
  32. package/docs/lessons/0002-async-def-without-await.md +28 -0
  33. package/docs/lessons/0003-create-task-without-callback.md +28 -0
  34. package/docs/lessons/0004-hardcoded-test-counts.md +28 -0
  35. package/docs/lessons/0005-sqlite-without-closing.md +33 -0
  36. package/docs/lessons/0006-venv-pip-path.md +27 -0
  37. package/docs/lessons/0007-runner-state-self-rejection.md +35 -0
  38. package/docs/lessons/0008-quality-gate-blind-spot.md +33 -0
  39. package/docs/lessons/0009-parser-overcount-empty-batches.md +36 -0
  40. package/docs/lessons/0010-local-outside-function-bash.md +33 -0
  41. package/docs/lessons/0011-batch-tests-for-unimplemented-code.md +36 -0
  42. package/docs/lessons/0012-api-markdown-unescaped-chars.md +33 -0
  43. package/docs/lessons/0013-export-prefix-env-parsing.md +33 -0
  44. package/docs/lessons/0014-decorator-registry-import-side-effect.md +43 -0
  45. package/docs/lessons/0015-frontend-backend-schema-drift.md +43 -0
  46. package/docs/lessons/0016-event-driven-cold-start-seeding.md +44 -0
  47. package/docs/lessons/0017-copy-paste-logic-diverges.md +43 -0
  48. package/docs/lessons/0018-layer-passes-pipeline-broken.md +45 -0
  49. package/docs/lessons/0019-systemd-envfile-ignores-export.md +41 -0
  50. package/docs/lessons/0020-persist-state-incrementally.md +44 -0
  51. package/docs/lessons/0021-dual-axis-testing.md +48 -0
  52. package/docs/lessons/0022-jsx-factory-shadowing.md +43 -0
  53. package/docs/lessons/0023-static-analysis-spiral.md +51 -0
  54. package/docs/lessons/0024-shared-pipeline-implementation.md +55 -0
  55. package/docs/lessons/0025-defense-in-depth-all-entry-points.md +65 -0
  56. package/docs/lessons/0026-linter-no-rules-false-enforcement.md +54 -0
  57. package/docs/lessons/0027-jsx-silent-prop-drop.md +64 -0
  58. package/docs/lessons/0028-no-infrastructure-in-client-code.md +49 -0
  59. package/docs/lessons/0029-never-write-secrets-to-files.md +61 -0
  60. package/docs/lessons/0030-cache-merge-not-replace.md +62 -0
  61. package/docs/lessons/0031-verify-units-at-boundaries.md +66 -0
  62. package/docs/lessons/0032-module-lifecycle-subscribe-unsubscribe.md +89 -0
  63. package/docs/lessons/0033-async-iteration-mutable-snapshot.md +72 -0
  64. package/docs/lessons/0034-caller-missing-await-silent-discard.md +65 -0
  65. package/docs/lessons/0035-duplicate-registration-silent-overwrite.md +85 -0
  66. package/docs/lessons/0036-websocket-dirty-disconnect.md +33 -0
  67. package/docs/lessons/0037-parallel-agents-worktree-corruption.md +31 -0
  68. package/docs/lessons/0038-subscribe-no-stored-ref.md +36 -0
  69. package/docs/lessons/0039-fallback-or-default-hides-bugs.md +34 -0
  70. package/docs/lessons/0040-event-firehose-filter-first.md +36 -0
  71. package/docs/lessons/0041-ambiguous-base-dir-path-nesting.md +32 -0
  72. package/docs/lessons/0042-spec-compliance-insufficient.md +36 -0
  73. package/docs/lessons/0043-exact-count-extensible-collections.md +32 -0
  74. package/docs/lessons/0044-relative-file-deps-worktree.md +39 -0
  75. package/docs/lessons/0045-iterative-design-improvement.md +33 -0
  76. package/docs/lessons/0046-plan-assertion-math-bugs.md +38 -0
  77. package/docs/lessons/0047-pytest-single-threaded-default.md +37 -0
  78. package/docs/lessons/0048-integration-wiring-batch.md +40 -0
  79. package/docs/lessons/0049-ab-verification.md +41 -0
  80. package/docs/lessons/0050-editing-sourced-files-during-execution.md +33 -0
  81. package/docs/lessons/0051-infrastructure-fixes-cant-self-heal.md +30 -0
  82. package/docs/lessons/0052-uncommitted-changes-poison-quality-gates.md +31 -0
  83. package/docs/lessons/0053-jq-compact-flag-inconsistency.md +31 -0
  84. package/docs/lessons/0054-parser-matches-inside-code-blocks.md +30 -0
  85. package/docs/lessons/0055-agents-compensate-for-garbled-prompts.md +31 -0
  86. package/docs/lessons/0056-grep-count-exit-code-on-zero.md +42 -0
  87. package/docs/lessons/0057-new-artifacts-break-git-clean-gates.md +42 -0
  88. package/docs/lessons/0058-dead-config-keys-never-consumed.md +49 -0
  89. package/docs/lessons/0059-contract-test-shared-structures.md +53 -0
  90. package/docs/lessons/0060-set-e-silent-death-in-runners.md +53 -0
  91. package/docs/lessons/0061-context-injection-dirty-state.md +50 -0
  92. package/docs/lessons/0062-sibling-bug-neighborhood-scan.md +29 -0
  93. package/docs/lessons/0063-one-flag-two-lifetimes.md +31 -0
  94. package/docs/lessons/0064-test-passes-wrong-reason.md +31 -0
  95. package/docs/lessons/0065-pipefail-grep-count-double-output.md +39 -0
  96. package/docs/lessons/0066-local-keyword-outside-function.md +37 -0
  97. package/docs/lessons/0067-stdin-hang-non-interactive-shell.md +36 -0
  98. package/docs/lessons/0068-agent-builds-wrong-thing-correctly.md +31 -0
  99. package/docs/lessons/0069-plan-quality-dominates-execution.md +30 -0
  100. package/docs/lessons/0070-spec-echo-back-prevents-drift.md +31 -0
  101. package/docs/lessons/0071-positive-instructions-outperform-negative.md +30 -0
  102. package/docs/lessons/0072-lost-in-the-middle-context-placement.md +30 -0
  103. package/docs/lessons/0073-unscoped-lessons-cause-false-positives.md +30 -0
  104. package/docs/lessons/0074-stale-context-injection-wrong-batch.md +32 -0
  105. package/docs/lessons/0075-research-artifacts-must-persist.md +32 -0
  106. package/docs/lessons/0076-wrong-decomposition-contaminates-downstream.md +30 -0
  107. package/docs/lessons/0077-cherry-pick-merges-need-manual-resolution.md +30 -0
  108. package/docs/lessons/0078-static-review-without-live-test.md +30 -0
  109. package/docs/lessons/0079-integration-wiring-batch-required.md +32 -0
  110. package/docs/lessons/FRAMEWORK.md +161 -0
  111. package/docs/lessons/SUMMARY.md +201 -0
  112. package/docs/lessons/TEMPLATE.md +85 -0
  113. package/docs/plans/2026-02-21-code-factory-v2-design.md +204 -0
  114. package/docs/plans/2026-02-21-code-factory-v2-implementation-plan.md +2189 -0
  115. package/docs/plans/2026-02-21-code-factory-v2-phase4-design.md +537 -0
  116. package/docs/plans/2026-02-21-code-factory-v2-phase4-implementation-plan.md +2012 -0
  117. package/docs/plans/2026-02-21-hardening-pass-design.md +108 -0
  118. package/docs/plans/2026-02-21-hardening-pass-plan.md +1378 -0
  119. package/docs/plans/2026-02-21-mab-research-report.md +406 -0
  120. package/docs/plans/2026-02-21-marketplace-restructure-design.md +240 -0
  121. package/docs/plans/2026-02-21-marketplace-restructure-plan.md +832 -0
  122. package/docs/plans/2026-02-21-phase4-completion-plan.md +697 -0
  123. package/docs/plans/2026-02-21-validator-suite-design.md +148 -0
  124. package/docs/plans/2026-02-21-validator-suite-plan.md +540 -0
  125. package/docs/plans/2026-02-22-mab-research-round2.md +556 -0
  126. package/docs/plans/2026-02-22-mab-run-design.md +462 -0
  127. package/docs/plans/2026-02-22-mab-run-plan.md +2046 -0
  128. package/docs/plans/2026-02-22-operations-design-methodology-research.md +681 -0
  129. package/docs/plans/2026-02-22-research-agent-failure-taxonomy.md +532 -0
  130. package/docs/plans/2026-02-22-research-code-guideline-policies.md +886 -0
  131. package/docs/plans/2026-02-22-research-codebase-audit-refactoring.md +908 -0
  132. package/docs/plans/2026-02-22-research-coding-standards-documentation.md +541 -0
  133. package/docs/plans/2026-02-22-research-competitive-landscape.md +687 -0
  134. package/docs/plans/2026-02-22-research-comprehensive-testing.md +1076 -0
  135. package/docs/plans/2026-02-22-research-context-utilization.md +459 -0
  136. package/docs/plans/2026-02-22-research-cost-quality-tradeoff.md +548 -0
  137. package/docs/plans/2026-02-22-research-lesson-transferability.md +508 -0
  138. package/docs/plans/2026-02-22-research-multi-agent-coordination.md +312 -0
  139. package/docs/plans/2026-02-22-research-phase-integration.md +602 -0
  140. package/docs/plans/2026-02-22-research-plan-quality.md +428 -0
  141. package/docs/plans/2026-02-22-research-prompt-engineering.md +558 -0
  142. package/docs/plans/2026-02-22-research-unconventional-perspectives.md +528 -0
  143. package/docs/plans/2026-02-22-research-user-adoption.md +638 -0
  144. package/docs/plans/2026-02-22-research-verification-effectiveness.md +433 -0
  145. package/docs/plans/2026-02-23-agent-suite-design.md +299 -0
  146. package/docs/plans/2026-02-23-agent-suite-plan.md +578 -0
  147. package/docs/plans/2026-02-23-phase3-cost-infrastructure-design.md +148 -0
  148. package/docs/plans/2026-02-23-phase3-cost-infrastructure-plan.md +1062 -0
  149. package/docs/plans/2026-02-23-research-bash-expert-agent.md +543 -0
  150. package/docs/plans/2026-02-23-research-dependency-auditor-agent.md +564 -0
  151. package/docs/plans/2026-02-23-research-improving-existing-agents.md +503 -0
  152. package/docs/plans/2026-02-23-research-integration-tester-agent.md +454 -0
  153. package/docs/plans/2026-02-23-research-python-expert-agent.md +429 -0
  154. package/docs/plans/2026-02-23-research-service-monitor-agent.md +425 -0
  155. package/docs/plans/2026-02-23-research-shell-expert-agent.md +533 -0
  156. package/docs/plans/2026-02-23-roadmap-to-completion.md +530 -0
  157. package/docs/plans/2026-02-24-headless-module-split-design.md +98 -0
  158. package/docs/plans/2026-02-24-headless-module-split.md +443 -0
  159. package/docs/plans/2026-02-24-lesson-scope-metadata-design.md +228 -0
  160. package/docs/plans/2026-02-24-lesson-scope-metadata-plan.md +968 -0
  161. package/docs/plans/2026-02-24-npm-packaging-design.md +841 -0
  162. package/docs/plans/2026-02-24-npm-packaging-plan.md +1965 -0
  163. package/docs/plans/audit-findings.md +186 -0
  164. package/docs/telegram-notification-format.md +98 -0
  165. package/examples/example-plan.md +51 -0
  166. package/examples/example-prd.json +72 -0
  167. package/examples/example-roadmap.md +33 -0
  168. package/examples/quickstart-plan.md +63 -0
  169. package/hooks/hooks.json +26 -0
  170. package/hooks/setup-symlinks.sh +48 -0
  171. package/hooks/stop-hook.sh +135 -0
  172. package/package.json +47 -0
  173. package/policies/bash.md +71 -0
  174. package/policies/python.md +71 -0
  175. package/policies/testing.md +61 -0
  176. package/policies/universal.md +60 -0
  177. package/scripts/analyze-report.sh +97 -0
  178. package/scripts/architecture-map.sh +145 -0
  179. package/scripts/auto-compound.sh +273 -0
  180. package/scripts/batch-audit.sh +42 -0
  181. package/scripts/batch-test.sh +101 -0
  182. package/scripts/entropy-audit.sh +221 -0
  183. package/scripts/failure-digest.sh +51 -0
  184. package/scripts/generate-ast-rules.sh +96 -0
  185. package/scripts/init.sh +112 -0
  186. package/scripts/lesson-check.sh +428 -0
  187. package/scripts/lib/common.sh +61 -0
  188. package/scripts/lib/cost-tracking.sh +153 -0
  189. package/scripts/lib/ollama.sh +60 -0
  190. package/scripts/lib/progress-writer.sh +128 -0
  191. package/scripts/lib/run-plan-context.sh +215 -0
  192. package/scripts/lib/run-plan-echo-back.sh +231 -0
  193. package/scripts/lib/run-plan-headless.sh +396 -0
  194. package/scripts/lib/run-plan-notify.sh +57 -0
  195. package/scripts/lib/run-plan-parser.sh +81 -0
  196. package/scripts/lib/run-plan-prompt.sh +215 -0
  197. package/scripts/lib/run-plan-quality-gate.sh +132 -0
  198. package/scripts/lib/run-plan-routing.sh +315 -0
  199. package/scripts/lib/run-plan-sampling.sh +170 -0
  200. package/scripts/lib/run-plan-scoring.sh +146 -0
  201. package/scripts/lib/run-plan-state.sh +142 -0
  202. package/scripts/lib/run-plan-team.sh +199 -0
  203. package/scripts/lib/telegram.sh +54 -0
  204. package/scripts/lib/thompson-sampling.sh +176 -0
  205. package/scripts/license-check.sh +74 -0
  206. package/scripts/mab-run.sh +575 -0
  207. package/scripts/module-size-check.sh +146 -0
  208. package/scripts/patterns/async-no-await.yml +5 -0
  209. package/scripts/patterns/bare-except.yml +6 -0
  210. package/scripts/patterns/empty-catch.yml +6 -0
  211. package/scripts/patterns/hardcoded-localhost.yml +9 -0
  212. package/scripts/patterns/retry-loop-no-backoff.yml +12 -0
  213. package/scripts/pipeline-status.sh +197 -0
  214. package/scripts/policy-check.sh +226 -0
  215. package/scripts/prior-art-search.sh +133 -0
  216. package/scripts/promote-mab-lessons.sh +126 -0
  217. package/scripts/prompts/agent-a-superpowers.md +29 -0
  218. package/scripts/prompts/agent-b-ralph.md +29 -0
  219. package/scripts/prompts/judge-agent.md +61 -0
  220. package/scripts/prompts/planner-agent.md +44 -0
  221. package/scripts/pull-community-lessons.sh +90 -0
  222. package/scripts/quality-gate.sh +266 -0
  223. package/scripts/research-gate.sh +90 -0
  224. package/scripts/run-plan.sh +329 -0
  225. package/scripts/scope-infer.sh +159 -0
  226. package/scripts/setup-ralph-loop.sh +155 -0
  227. package/scripts/telemetry.sh +230 -0
  228. package/scripts/tests/run-all-tests.sh +52 -0
  229. package/scripts/tests/test-act-cli.sh +46 -0
  230. package/scripts/tests/test-agents-md.sh +87 -0
  231. package/scripts/tests/test-analyze-report.sh +114 -0
  232. package/scripts/tests/test-architecture-map.sh +89 -0
  233. package/scripts/tests/test-auto-compound.sh +169 -0
  234. package/scripts/tests/test-batch-test.sh +65 -0
  235. package/scripts/tests/test-benchmark-runner.sh +25 -0
  236. package/scripts/tests/test-common.sh +168 -0
  237. package/scripts/tests/test-cost-tracking.sh +158 -0
  238. package/scripts/tests/test-echo-back.sh +180 -0
  239. package/scripts/tests/test-entropy-audit.sh +146 -0
  240. package/scripts/tests/test-failure-digest.sh +66 -0
  241. package/scripts/tests/test-generate-ast-rules.sh +145 -0
  242. package/scripts/tests/test-helpers.sh +82 -0
  243. package/scripts/tests/test-init.sh +47 -0
  244. package/scripts/tests/test-lesson-check.sh +278 -0
  245. package/scripts/tests/test-lesson-local.sh +55 -0
  246. package/scripts/tests/test-license-check.sh +109 -0
  247. package/scripts/tests/test-mab-run.sh +182 -0
  248. package/scripts/tests/test-ollama-lib.sh +49 -0
  249. package/scripts/tests/test-ollama.sh +60 -0
  250. package/scripts/tests/test-pipeline-status.sh +198 -0
  251. package/scripts/tests/test-policy-check.sh +124 -0
  252. package/scripts/tests/test-prior-art-search.sh +96 -0
  253. package/scripts/tests/test-progress-writer.sh +140 -0
  254. package/scripts/tests/test-promote-mab-lessons.sh +110 -0
  255. package/scripts/tests/test-pull-community-lessons.sh +149 -0
  256. package/scripts/tests/test-quality-gate.sh +241 -0
  257. package/scripts/tests/test-research-gate.sh +132 -0
  258. package/scripts/tests/test-run-plan-cli.sh +86 -0
  259. package/scripts/tests/test-run-plan-context.sh +305 -0
  260. package/scripts/tests/test-run-plan-e2e.sh +153 -0
  261. package/scripts/tests/test-run-plan-headless.sh +424 -0
  262. package/scripts/tests/test-run-plan-notify.sh +124 -0
  263. package/scripts/tests/test-run-plan-parser.sh +217 -0
  264. package/scripts/tests/test-run-plan-prompt.sh +254 -0
  265. package/scripts/tests/test-run-plan-quality-gate.sh +222 -0
  266. package/scripts/tests/test-run-plan-routing.sh +178 -0
  267. package/scripts/tests/test-run-plan-scoring.sh +148 -0
  268. package/scripts/tests/test-run-plan-state.sh +261 -0
  269. package/scripts/tests/test-run-plan-team.sh +157 -0
  270. package/scripts/tests/test-scope-infer.sh +150 -0
  271. package/scripts/tests/test-setup-ralph-loop.sh +63 -0
  272. package/scripts/tests/test-telegram-env.sh +38 -0
  273. package/scripts/tests/test-telegram.sh +121 -0
  274. package/scripts/tests/test-telemetry.sh +46 -0
  275. package/scripts/tests/test-thompson-sampling.sh +139 -0
  276. package/scripts/tests/test-validate-all.sh +60 -0
  277. package/scripts/tests/test-validate-commands.sh +89 -0
  278. package/scripts/tests/test-validate-hooks.sh +98 -0
  279. package/scripts/tests/test-validate-lessons.sh +150 -0
  280. package/scripts/tests/test-validate-plan-quality.sh +235 -0
  281. package/scripts/tests/test-validate-plans.sh +187 -0
  282. package/scripts/tests/test-validate-plugin.sh +106 -0
  283. package/scripts/tests/test-validate-prd.sh +184 -0
  284. package/scripts/tests/test-validate-skills.sh +134 -0
  285. package/scripts/validate-all.sh +57 -0
  286. package/scripts/validate-commands.sh +67 -0
  287. package/scripts/validate-hooks.sh +89 -0
  288. package/scripts/validate-lessons.sh +98 -0
  289. package/scripts/validate-plan-quality.sh +369 -0
  290. package/scripts/validate-plans.sh +120 -0
  291. package/scripts/validate-plugin.sh +86 -0
  292. package/scripts/validate-policies.sh +42 -0
  293. package/scripts/validate-prd.sh +118 -0
  294. package/scripts/validate-skills.sh +96 -0
  295. package/skills/autocode/SKILL.md +285 -0
  296. package/skills/autocode/ab-verification.md +51 -0
  297. package/skills/autocode/code-quality-standards.md +37 -0
  298. package/skills/autocode/competitive-mode.md +364 -0
  299. package/skills/brainstorming/SKILL.md +97 -0
  300. package/skills/capture-lesson/SKILL.md +187 -0
  301. package/skills/check-lessons/SKILL.md +116 -0
  302. package/skills/dispatching-parallel-agents/SKILL.md +110 -0
  303. package/skills/executing-plans/SKILL.md +85 -0
  304. package/skills/finishing-a-development-branch/SKILL.md +201 -0
  305. package/skills/receiving-code-review/SKILL.md +72 -0
  306. package/skills/requesting-code-review/SKILL.md +59 -0
  307. package/skills/requesting-code-review/code-reviewer.md +82 -0
  308. package/skills/research/SKILL.md +145 -0
  309. package/skills/roadmap/SKILL.md +115 -0
  310. package/skills/subagent-driven-development/SKILL.md +98 -0
  311. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +18 -0
  312. package/skills/subagent-driven-development/implementer-prompt.md +73 -0
  313. package/skills/subagent-driven-development/spec-reviewer-prompt.md +57 -0
  314. package/skills/systematic-debugging/SKILL.md +134 -0
  315. package/skills/systematic-debugging/condition-based-waiting.md +64 -0
  316. package/skills/systematic-debugging/defense-in-depth.md +32 -0
  317. package/skills/systematic-debugging/root-cause-tracing.md +55 -0
  318. package/skills/test-driven-development/SKILL.md +167 -0
  319. package/skills/using-git-worktrees/SKILL.md +219 -0
  320. package/skills/using-superpowers/SKILL.md +54 -0
  321. package/skills/verification-before-completion/SKILL.md +140 -0
  322. package/skills/verify/SKILL.md +82 -0
  323. package/skills/writing-plans/SKILL.md +128 -0
  324. package/skills/writing-skills/SKILL.md +93 -0
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bash
2
+ # Test license-check.sh — verifies CLI, project detection, and license scanning
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ LC="$SCRIPT_DIR/../license-check.sh"
7
+
8
+ FAILURES=0
9
+ TESTS=0
10
+
11
+ assert_eq() {
12
+ local desc="$1" expected="$2" actual="$3"
13
+ TESTS=$((TESTS + 1))
14
+ if [[ "$expected" != "$actual" ]]; then
15
+ echo "FAIL: $desc"
16
+ echo " expected: $expected"
17
+ echo " actual: $actual"
18
+ FAILURES=$((FAILURES + 1))
19
+ else
20
+ echo "PASS: $desc"
21
+ fi
22
+ }
23
+
24
+ assert_exit() {
25
+ local desc="$1" expected_exit="$2"
26
+ shift 2
27
+ local actual_exit=0
28
+ "$@" >/dev/null 2>&1 || actual_exit=$?
29
+ TESTS=$((TESTS + 1))
30
+ if [[ "$expected_exit" != "$actual_exit" ]]; then
31
+ echo "FAIL: $desc"
32
+ echo " expected exit: $expected_exit"
33
+ echo " actual exit: $actual_exit"
34
+ FAILURES=$((FAILURES + 1))
35
+ else
36
+ echo "PASS: $desc"
37
+ fi
38
+ }
39
+
40
+ assert_contains() {
41
+ local desc="$1" needle="$2" haystack="$3"
42
+ TESTS=$((TESTS + 1))
43
+ if echo "$haystack" | grep -qF "$needle"; then
44
+ echo "PASS: $desc"
45
+ else
46
+ echo "FAIL: $desc"
47
+ echo " expected to contain: $needle"
48
+ echo " in: $(echo "$haystack" | head -5)"
49
+ FAILURES=$((FAILURES + 1))
50
+ fi
51
+ }
52
+
53
+ WORK=$(mktemp -d)
54
+ trap 'rm -rf "$WORK"' EXIT
55
+
56
+ # === CLI tests ===
57
+
58
+ assert_exit "--help exits 0" 0 bash "$LC" --help
59
+ assert_exit "unknown flag exits 1" 1 bash "$LC" --bogus
60
+
61
+ # === Help text ===
62
+
63
+ output=$(bash "$LC" --help 2>&1)
64
+ assert_contains "help mentions license" "license" "$output"
65
+
66
+ # === Sources common.sh ===
67
+
68
+ TESTS=$((TESTS + 1))
69
+ if grep -q 'source.*lib/common.sh' "$LC"; then
70
+ echo "PASS: license-check.sh sources lib/common.sh"
71
+ else
72
+ echo "FAIL: license-check.sh sources lib/common.sh"
73
+ FAILURES=$((FAILURES + 1))
74
+ fi
75
+
76
+ # === Unknown project type exits clean ===
77
+
78
+ mkdir -p "$WORK/empty-proj"
79
+ output=$(bash "$LC" --project-root "$WORK/empty-proj" 2>&1)
80
+ exit_code=$?
81
+ assert_eq "unknown project exits 0" "0" "$exit_code"
82
+ assert_contains "unknown project says CLEAN" "CLEAN" "$output"
83
+ assert_contains "shows License Check header" "License Check" "$output"
84
+
85
+ # === Python project without pip-licenses skips gracefully ===
86
+
87
+ mkdir -p "$WORK/py-proj"
88
+ touch "$WORK/py-proj/pyproject.toml"
89
+ output=$(bash "$LC" --project-root "$WORK/py-proj" 2>&1)
90
+ exit_code=$?
91
+ assert_eq "python without pip-licenses exits 0" "0" "$exit_code"
92
+ assert_contains "python skip message" "skipping" "$output"
93
+
94
+ # === Node project without npx skips gracefully ===
95
+ # (npx is likely available, so test the output structure instead)
96
+
97
+ mkdir -p "$WORK/node-proj"
98
+ echo '{"name":"test"}' > "$WORK/node-proj/package.json"
99
+ output=$(bash "$LC" --project-root "$WORK/node-proj" 2>&1) || true
100
+ assert_contains "node project checks deps" "Node dependencies" "$output"
101
+
102
+ # === Summary ===
103
+ echo ""
104
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
105
+ if [[ $FAILURES -gt 0 ]]; then
106
+ echo "FAILURES: $FAILURES"
107
+ exit 1
108
+ fi
109
+ echo "ALL PASSED"
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ # Save test script dir — sourcing mab-run.sh overwrites SCRIPT_DIR
6
+ TEST_DIR="$SCRIPT_DIR"
7
+ source "$SCRIPT_DIR/test-helpers.sh"
8
+
9
+ MAB_RUN="$TEST_DIR/../mab-run.sh"
10
+
11
+ # --- Test: --help exits 0 and mentions key concepts ---
12
+ help_output=$("$MAB_RUN" --help 2>&1) || true
13
+ assert_exit "--help exits 0" 0 "$MAB_RUN" --help
14
+ assert_contains "--help mentions worktree" "worktree" "$help_output"
15
+ assert_contains "--help mentions judge" "judge" "$help_output"
16
+
17
+ # --- Test: missing plan exits 1 ---
18
+ assert_exit "missing plan exits 1" 1 "$MAB_RUN" --plan /tmp/nonexistent-plan-$$.md --batch 1 --work-unit "test" --worktree /tmp
19
+
20
+ # --- Test: non-numeric batch exits 1 ---
21
+ TEST_TMPDIR=$(mktemp -d)
22
+ trap 'rm -rf "$TEST_TMPDIR"' EXIT
23
+
24
+ # Create a minimal plan file
25
+ cat > "$TEST_TMPDIR/plan.md" <<'MD'
26
+ # Test Plan
27
+
28
+ ## Batch 1: Test batch
29
+
30
+ Do something.
31
+ MD
32
+
33
+ assert_exit "non-numeric batch exits 1" 1 "$MAB_RUN" --plan "$TEST_TMPDIR/plan.md" --batch abc --work-unit "test" --worktree "$TEST_TMPDIR"
34
+
35
+ # --- Test: --dry-run exits 0 with valid args ---
36
+ dry_output=$("$MAB_RUN" --plan "$TEST_TMPDIR/plan.md" --batch 1 --work-unit "test batch" --worktree "$TEST_TMPDIR" --dry-run 2>&1) || true
37
+ dry_exit=0
38
+ "$MAB_RUN" --plan "$TEST_TMPDIR/plan.md" --batch 1 --work-unit "test batch" --worktree "$TEST_TMPDIR" --dry-run > /dev/null 2>&1 || dry_exit=$?
39
+ assert_eq "--dry-run exits 0" "0" "$dry_exit"
40
+ assert_contains "--dry-run shows planned actions" "DRY RUN" "$dry_output"
41
+
42
+ # --- Test: --init-data creates valid JSON files ---
43
+ init_dir=$(mktemp -d)
44
+ trap 'rm -rf "$TEST_TMPDIR" "$init_dir"' EXIT
45
+ "$MAB_RUN" --init-data --worktree "$init_dir" > /dev/null 2>&1 || true
46
+
47
+ TESTS=$((TESTS + 1))
48
+ if [[ -f "$init_dir/logs/strategy-perf.json" ]] && jq . "$init_dir/logs/strategy-perf.json" > /dev/null 2>&1; then
49
+ echo "PASS: --init-data creates valid strategy-perf.json"
50
+ else
51
+ echo "FAIL: --init-data did not create valid strategy-perf.json"
52
+ FAILURES=$((FAILURES + 1))
53
+ fi
54
+
55
+ TESTS=$((TESTS + 1))
56
+ if [[ -f "$init_dir/logs/mab-lessons.json" ]] && jq . "$init_dir/logs/mab-lessons.json" > /dev/null 2>&1; then
57
+ echo "PASS: --init-data creates valid mab-lessons.json"
58
+ else
59
+ echo "FAIL: --init-data did not create valid mab-lessons.json"
60
+ FAILURES=$((FAILURES + 1))
61
+ fi
62
+ rm -rf "$init_dir"
63
+
64
+ # --- Test: select_winner_with_gate_override ---
65
+ # Source mab-run functions for unit testing — fail loudly if sourcing breaks
66
+ source "$MAB_RUN" --source-only 2>/dev/null || true
67
+
68
+ TESTS=$((TESTS + 1))
69
+ if ! type select_winner_with_gate_override &>/dev/null; then
70
+ echo "FAIL: select_winner_with_gate_override not found after sourcing mab-run.sh"
71
+ FAILURES=$((FAILURES + 1))
72
+ else
73
+ echo "PASS: select_winner_with_gate_override available after --source-only"
74
+
75
+ # Only A passes → A wins
76
+ result=$(select_winner_with_gate_override 0 1 "agent-b")
77
+ assert_eq "only A passes → agent-a" "agent-a" "$result"
78
+
79
+ # Only B passes → B wins
80
+ result=$(select_winner_with_gate_override 1 0 "agent-a")
81
+ assert_eq "only B passes → agent-b" "agent-b" "$result"
82
+
83
+ # Neither passes → none
84
+ result=$(select_winner_with_gate_override 1 1 "agent-a")
85
+ assert_eq "neither passes → none" "none" "$result"
86
+
87
+ # Both pass → judge winner
88
+ result=$(select_winner_with_gate_override 0 0 "agent-b")
89
+ assert_eq "both pass → judge winner" "agent-b" "$result"
90
+ fi
91
+
92
+ # --- Test: assemble_agent_prompt substitutes placeholders ---
93
+ TESTS=$((TESTS + 1))
94
+ if ! type assemble_agent_prompt &>/dev/null; then
95
+ echo "FAIL: assemble_agent_prompt not found after sourcing mab-run.sh"
96
+ FAILURES=$((FAILURES + 1))
97
+ else
98
+ echo "PASS: assemble_agent_prompt available after --source-only"
99
+
100
+ prompt_template="Work: {WORK_UNIT_DESCRIPTION}, PRD: {PRD_PATH}, Gate: {QUALITY_GATE_CMD}"
101
+ result=$(assemble_agent_prompt "$prompt_template" \
102
+ "implement feature X" \
103
+ "tasks/prd.json" \
104
+ "docs/ARCHITECTURE-MAP.json" \
105
+ "no lessons yet" \
106
+ "scripts/quality-gate.sh --project-root .")
107
+
108
+ assert_contains "substitutes WORK_UNIT_DESCRIPTION" "implement feature X" "$result"
109
+ assert_contains "substitutes PRD_PATH" "tasks/prd.json" "$result"
110
+ assert_contains "substitutes QUALITY_GATE_CMD" "scripts/quality-gate.sh --project-root ." "$result"
111
+ assert_not_contains "no remaining placeholders" "{WORK_UNIT_DESCRIPTION}" "$result"
112
+ fi
113
+
114
+ # --- Test: update_mab_data lesson deduplication ---
115
+ # Call update_mab_data twice with the same lesson — should have 1 entry with occurrences=2
116
+ TESTS=$((TESTS + 1))
117
+ if ! type update_mab_data &>/dev/null; then
118
+ echo "FAIL: update_mab_data not found after sourcing mab-run.sh"
119
+ FAILURES=$((FAILURES + 1))
120
+ else
121
+ echo "PASS: update_mab_data available after --source-only"
122
+
123
+ dedup_dir=$(mktemp -d)
124
+ MAB_WORKTREE="$dedup_dir"
125
+ mkdir -p "$dedup_dir/logs"
126
+ # init_strategy_perf is already available (sourced via mab-run.sh --source-only)
127
+ init_strategy_perf "$dedup_dir/logs/strategy-perf.json"
128
+
129
+ # First call — creates new lesson entry
130
+ update_mab_data "agent-a" "always check imports" "new-file"
131
+
132
+ TESTS=$((TESTS + 1))
133
+ count1=$(jq 'length' "$dedup_dir/logs/mab-lessons.json" 2>/dev/null || echo "0")
134
+ if [[ "$count1" == "1" ]]; then
135
+ echo "PASS: update_mab_data: first call creates 1 lesson entry"
136
+ else
137
+ echo "FAIL: update_mab_data: expected 1 entry after first call, got $count1"
138
+ FAILURES=$((FAILURES + 1))
139
+ fi
140
+
141
+ # Second call with same lesson — should deduplicate (increment occurrences, not add)
142
+ update_mab_data "agent-b" "always check imports" "new-file"
143
+
144
+ TESTS=$((TESTS + 1))
145
+ count2=$(jq 'length' "$dedup_dir/logs/mab-lessons.json" 2>/dev/null || echo "0")
146
+ if [[ "$count2" == "1" ]]; then
147
+ echo "PASS: update_mab_data: second call deduplicates (still 1 entry)"
148
+ else
149
+ echo "FAIL: update_mab_data: expected 1 entry after dedup, got $count2"
150
+ FAILURES=$((FAILURES + 1))
151
+ fi
152
+
153
+ occ=$(jq '.[0].occurrences' "$dedup_dir/logs/mab-lessons.json" 2>/dev/null || echo "0")
154
+ assert_eq "update_mab_data: occurrences incremented to 2" "2" "$occ"
155
+
156
+ rm -rf "$dedup_dir"
157
+ fi
158
+
159
+ # --- Test: --mab flag wiring (run-plan.sh → run-plan-headless.sh → mab-run.sh) ---
160
+ # run-plan.sh parses --mab and sets MAB=true
161
+ RP_MAIN="$TEST_DIR/../run-plan.sh"
162
+ RP_HEADLESS="$TEST_DIR/../lib/run-plan-headless.sh"
163
+
164
+ TESTS=$((TESTS + 1))
165
+ if grep -q '\-\-mab) MAB=true' "$RP_MAIN" 2>/dev/null; then
166
+ echo "PASS: run-plan.sh parses --mab flag and sets MAB=true"
167
+ else
168
+ echo "FAIL: run-plan.sh should parse --mab and set MAB=true"
169
+ FAILURES=$((FAILURES + 1))
170
+ fi
171
+
172
+ # run-plan-headless.sh checks MAB flag and invokes mab-run.sh
173
+ TESTS=$((TESTS + 1))
174
+ if grep -q 'MAB.*true' "$RP_HEADLESS" 2>/dev/null && \
175
+ grep -q 'mab-run.sh' "$RP_HEADLESS" 2>/dev/null; then
176
+ echo "PASS: run-plan-headless.sh checks MAB flag and invokes mab-run.sh"
177
+ else
178
+ echo "FAIL: run-plan-headless.sh should check MAB flag and invoke mab-run.sh"
179
+ FAILURES=$((FAILURES + 1))
180
+ fi
181
+
182
+ report_results
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # Test scripts/lib/ollama.sh
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "$SCRIPT_DIR/test-helpers.sh"
7
+
8
+ OLLAMA_LIB="$SCRIPT_DIR/../lib/ollama.sh"
9
+
10
+ # === Test: Health check curl has timeout flags (#25) ===
11
+ # Read the source and verify --connect-timeout and --max-time are present on the health check line
12
+ health_line=$(grep -F 'OLLAMA_QUEUE_URL/health' "$OLLAMA_LIB")
13
+ # Use [[ ]] to check for substrings since grep chokes on --flag patterns
14
+ TESTS=$((TESTS + 1))
15
+ if [[ "$health_line" == *"--connect-timeout"* ]]; then
16
+ echo "PASS: health check: has --connect-timeout"
17
+ else
18
+ echo "FAIL: health check: has --connect-timeout"; FAILURES=$((FAILURES + 1))
19
+ fi
20
+ TESTS=$((TESTS + 1))
21
+ if [[ "$health_line" == *"--max-time"* ]]; then
22
+ echo "PASS: health check: has --max-time"
23
+ else
24
+ echo "FAIL: health check: has --max-time"; FAILURES=$((FAILURES + 1))
25
+ fi
26
+
27
+ # === Test: API call curl has --max-time ===
28
+ api_line=$(grep -F 'curl -s "$api_url"' "$OLLAMA_LIB")
29
+ TESTS=$((TESTS + 1))
30
+ if [[ "$api_line" == *"--max-time"* ]]; then
31
+ echo "PASS: api call: has --max-time"
32
+ else
33
+ echo "FAIL: api call: has --max-time"; FAILURES=$((FAILURES + 1))
34
+ fi
35
+
36
+ # === Test: ollama_build_payload produces valid JSON ===
37
+ # Source common.sh first (required dependency), then ollama.sh
38
+ COMMON_LIB="$SCRIPT_DIR/../lib/common.sh"
39
+ if [[ -f "$COMMON_LIB" ]]; then
40
+ source "$COMMON_LIB"
41
+ fi
42
+ source "$OLLAMA_LIB"
43
+ payload=$(ollama_build_payload "test-model" "test prompt")
44
+ model=$(echo "$payload" | jq -r '.model')
45
+ assert_eq "build_payload: model" "test-model" "$model"
46
+ prompt=$(echo "$payload" | jq -r '.prompt')
47
+ assert_eq "build_payload: prompt" "test prompt" "$prompt"
48
+
49
+ report_results
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ # Test ollama.sh shared library functions
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "$SCRIPT_DIR/../lib/common.sh"
7
+ source "$SCRIPT_DIR/../lib/ollama.sh"
8
+
9
+ FAILURES=0
10
+ TESTS=0
11
+
12
+ assert_eq() {
13
+ local desc="$1" expected="$2" actual="$3"
14
+ TESTS=$((TESTS + 1))
15
+ if [[ "$expected" != "$actual" ]]; then
16
+ echo "FAIL: $desc"
17
+ echo " expected: $expected"
18
+ echo " actual: $actual"
19
+ FAILURES=$((FAILURES + 1))
20
+ else
21
+ echo "PASS: $desc"
22
+ fi
23
+ }
24
+
25
+ # === ollama_build_payload tests ===
26
+
27
+ val=$(ollama_build_payload "deepseek-r1:8b" "Hello world")
28
+ model=$(echo "$val" | jq -r '.model')
29
+ assert_eq "ollama_build_payload: model set" "deepseek-r1:8b" "$model"
30
+
31
+ stream=$(echo "$val" | jq -r '.stream')
32
+ assert_eq "ollama_build_payload: stream false" "false" "$stream"
33
+
34
+ # === ollama_parse_response tests ===
35
+
36
+ val=$(echo '{"response":"hello"}' | ollama_parse_response)
37
+ assert_eq "ollama_parse_response: extracts response" "hello" "$val"
38
+
39
+ val=$(echo '{}' | ollama_parse_response)
40
+ assert_eq "ollama_parse_response: empty on missing field" "" "$val"
41
+
42
+ # === ollama_extract_json tests ===
43
+
44
+ val=$(echo '```json
45
+ {"key":"value"}
46
+ ```' | ollama_extract_json)
47
+ key=$(echo "$val" | jq -r '.key')
48
+ assert_eq "ollama_extract_json: strips fences and validates" "value" "$key"
49
+
50
+ val=$(echo 'not json at all' | ollama_extract_json 2>/dev/null)
51
+ assert_eq "ollama_extract_json: returns empty on invalid" "" "$val"
52
+
53
+ # === Summary ===
54
+ echo ""
55
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
56
+ if [[ $FAILURES -gt 0 ]]; then
57
+ echo "FAILURES: $FAILURES"
58
+ exit 1
59
+ fi
60
+ echo "ALL PASSED"
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env bash
2
+ # Test pipeline-status.sh
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ STATUS_SCRIPT="$SCRIPT_DIR/../pipeline-status.sh"
7
+
8
+ FAILURES=0
9
+ TESTS=0
10
+
11
+ assert_exit() {
12
+ local desc="$1" expected_exit="$2"
13
+ shift 2
14
+ local actual_exit=0
15
+ "$@" >/dev/null 2>&1 || actual_exit=$?
16
+ TESTS=$((TESTS + 1))
17
+ if [[ "$expected_exit" != "$actual_exit" ]]; then
18
+ echo "FAIL: $desc"
19
+ echo " expected exit: $expected_exit"
20
+ echo " actual exit: $actual_exit"
21
+ FAILURES=$((FAILURES + 1))
22
+ else
23
+ echo "PASS: $desc"
24
+ fi
25
+ }
26
+
27
+ assert_contains() {
28
+ local desc="$1" needle="$2" haystack="$3"
29
+ TESTS=$((TESTS + 1))
30
+ if echo "$haystack" | grep -qF -- "$needle"; then
31
+ echo "PASS: $desc"
32
+ else
33
+ echo "FAIL: $desc"
34
+ echo " expected to contain: $needle"
35
+ echo " in: $(echo "$haystack" | head -5)"
36
+ FAILURES=$((FAILURES + 1))
37
+ fi
38
+ }
39
+
40
+ WORK=$(mktemp -d)
41
+ trap 'rm -rf "$WORK"' EXIT
42
+
43
+ # --- Test: --help exits 0 ---
44
+ assert_exit "pipeline-status --help exits 0" 0 \
45
+ bash "$STATUS_SCRIPT" --help
46
+
47
+ # --- Test: runs on empty directory ---
48
+ mkdir -p "$WORK/empty-proj"
49
+ cd "$WORK/empty-proj" && git init --quiet
50
+ output=$(bash "$STATUS_SCRIPT" "$WORK/empty-proj" 2>&1) || true
51
+ assert_contains "shows Pipeline Status header" "Pipeline Status" "$output"
52
+ assert_contains "shows no active run-plan" "No active run-plan" "$output"
53
+ assert_contains "shows no PRD" "No PRD found" "$output"
54
+
55
+ # --- Test: shows run-plan state when present ---
56
+ mkdir -p "$WORK/proj-with-state"
57
+ cd "$WORK/proj-with-state" && git init --quiet
58
+ cat > "$WORK/proj-with-state/.run-plan-state.json" <<'JSON'
59
+ {
60
+ "plan_file": "docs/plans/test-plan.md",
61
+ "mode": "headless",
62
+ "current_batch": 2,
63
+ "completed_batches": [1],
64
+ "started_at": "2026-02-21T10:00:00Z",
65
+ "last_quality_gate": {"passed": true, "test_count": 42}
66
+ }
67
+ JSON
68
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
69
+ assert_contains "shows plan file" "test-plan.md" "$output"
70
+ assert_contains "shows mode" "headless" "$output"
71
+ assert_contains "shows gate result" "passed=true" "$output"
72
+
73
+ # --- Test: shows PRD status when present ---
74
+ mkdir -p "$WORK/proj-with-state/tasks"
75
+ echo '[{"id":1,"passes":true},{"id":2,"passes":false},{"id":3,"passes":true}]' > "$WORK/proj-with-state/tasks/prd.json"
76
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
77
+ assert_contains "shows PRD counts" "2/3 passing" "$output"
78
+
79
+ # --- Test: shows routing decisions when log exists ---
80
+ mkdir -p "$WORK/proj-with-state/logs"
81
+ echo "[14:30:01] MODE: headless mode selected" > "$WORK/proj-with-state/logs/routing-decisions.log"
82
+ echo "[14:30:02] MODEL: batch 1 routed to sonnet" >> "$WORK/proj-with-state/logs/routing-decisions.log"
83
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
84
+ assert_contains "shows routing decisions header" "Routing Decisions" "$output"
85
+ assert_contains "shows routing log content" "headless mode selected" "$output"
86
+
87
+ # --- Test: no routing section when log missing ---
88
+ rm -f "$WORK/proj-with-state/logs/routing-decisions.log"
89
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
90
+ TESTS=$((TESTS + 1))
91
+ if echo "$output" | grep -qF "Routing Decisions"; then
92
+ echo "FAIL: no routing section when log missing"
93
+ FAILURES=$((FAILURES + 1))
94
+ else
95
+ echo "PASS: no routing section when log missing"
96
+ fi
97
+
98
+ # --- Test: shows total cost when costs present in state file ---
99
+ mkdir -p "$WORK/proj-with-costs"
100
+ cd "$WORK/proj-with-costs" && git init --quiet
101
+ cat > "$WORK/proj-with-costs/.run-plan-state.json" <<'JSON'
102
+ {
103
+ "plan_file": "docs/plans/test-plan.md",
104
+ "mode": "headless",
105
+ "current_batch": 3,
106
+ "completed_batches": [1, 2],
107
+ "started_at": "2026-02-21T10:00:00Z",
108
+ "last_quality_gate": {"passed": true, "test_count": 10},
109
+ "costs": {"1": "0.05", "2": "0.10"}
110
+ }
111
+ JSON
112
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-costs" 2>&1) || true
113
+ assert_contains "shows total cost line" "Total cost:" "$output"
114
+
115
+ # --- Test: --show-costs shows per-batch breakdown ---
116
+ output=$(bash "$STATUS_SCRIPT" --show-costs "$WORK/proj-with-costs" 2>&1) || true
117
+ assert_contains "--show-costs shows Cost Breakdown header" "Cost Breakdown" "$output"
118
+ assert_contains "--show-costs shows batch 1 cost" "Batch 1:" "$output"
119
+ assert_contains "--show-costs shows batch 2 cost" "Batch 2:" "$output"
120
+
121
+ # --- Test: --show-costs with non-numeric batch key does not crash (#42) ---
122
+ mkdir -p "$WORK/proj-with-final-key"
123
+ cd "$WORK/proj-with-final-key" && git init --quiet
124
+ cat > "$WORK/proj-with-final-key/.run-plan-state.json" <<'JSON'
125
+ {
126
+ "plan_file": "docs/plans/test-plan.md",
127
+ "mode": "headless",
128
+ "current_batch": 2,
129
+ "completed_batches": [1, "final"],
130
+ "started_at": "2026-02-21T10:00:00Z",
131
+ "last_quality_gate": {"passed": true, "test_count": 5},
132
+ "costs": {"1": "0.03", "final": "0.01"}
133
+ }
134
+ JSON
135
+ output=$(bash "$STATUS_SCRIPT" --show-costs "$WORK/proj-with-final-key" 2>&1) || true
136
+ assert_contains "--show-costs with 'final' key does not crash" "Cost Breakdown" "$output"
137
+ # Should show batch 1 (numeric) but not crash on "final"
138
+ assert_contains "--show-costs numeric key still shown" "Batch 1:" "$output"
139
+
140
+ # --- Test: --show-costs with no cost data shows informative message ---
141
+ mkdir -p "$WORK/proj-no-costs"
142
+ cd "$WORK/proj-no-costs" && git init --quiet
143
+ cat > "$WORK/proj-no-costs/.run-plan-state.json" <<'JSON'
144
+ {
145
+ "plan_file": "docs/plans/test-plan.md",
146
+ "mode": "headless",
147
+ "current_batch": 1,
148
+ "completed_batches": [],
149
+ "started_at": "2026-02-21T10:00:00Z",
150
+ "last_quality_gate": null
151
+ }
152
+ JSON
153
+ output=$(bash "$STATUS_SCRIPT" --show-costs "$WORK/proj-no-costs" 2>&1) || true
154
+ assert_contains "--show-costs with no costs shows informative message" "No cost data" "$output"
155
+
156
+ # --- Test: --help exits 0 and mentions --show-costs ---
157
+ output=$(bash "$STATUS_SCRIPT" --help 2>&1) || true
158
+ assert_contains "--help mentions --show-costs" "--show-costs" "$output"
159
+
160
+ # --- Test: MAB section shown when strategy-perf.json exists ---
161
+ mkdir -p "$WORK/proj-with-state/logs"
162
+ cat > "$WORK/proj-with-state/logs/strategy-perf.json" <<'JSON'
163
+ {
164
+ "new-file": {"superpowers": {"wins": 5, "losses": 2}, "ralph": {"wins": 3, "losses": 4}},
165
+ "refactoring": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
166
+ "integration": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
167
+ "test-only": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
168
+ "calibration_count": 3,
169
+ "calibration_complete": false
170
+ }
171
+ JSON
172
+ echo '[{"pattern": "test pattern", "context": "new-file"}]' > "$WORK/proj-with-state/logs/mab-lessons.json"
173
+
174
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
175
+ assert_contains "MAB section header shown" "MAB" "$output"
176
+ assert_contains "MAB calibration shown" "3/10" "$output"
177
+ assert_contains "MAB win rates shown" "superpowers=5W/2L" "$output"
178
+ assert_contains "MAB lesson count shown" "1 patterns recorded" "$output"
179
+
180
+ # --- Test: no MAB section when no perf file ---
181
+ rm -f "$WORK/proj-with-state/logs/strategy-perf.json" "$WORK/proj-with-state/logs/mab-lessons.json"
182
+ output=$(bash "$STATUS_SCRIPT" "$WORK/proj-with-state" 2>&1) || true
183
+ TESTS=$((TESTS + 1))
184
+ if echo "$output" | grep -qF "Multi-Armed Bandit"; then
185
+ echo "FAIL: MAB section should not appear without perf file"
186
+ FAILURES=$((FAILURES + 1))
187
+ else
188
+ echo "PASS: no MAB section when perf file absent"
189
+ fi
190
+
191
+ # === Summary ===
192
+ echo ""
193
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
194
+ if [[ $FAILURES -gt 0 ]]; then
195
+ echo "FAILURES: $FAILURES"
196
+ exit 1
197
+ fi
198
+ echo "ALL PASSED"