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,178 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/../lib/run-plan-parser.sh"
6
+ source "$SCRIPT_DIR/../lib/run-plan-routing.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
+ WORK=$(mktemp -d)
25
+ trap 'rm -rf "$WORK"' EXIT
26
+
27
+ # Plan with clear parallel batches
28
+ cat > "$WORK/parallel-plan.md" << 'PLAN'
29
+ ## Batch 1: Foundation
30
+
31
+ **Files:**
32
+ - Create: `src/lib.sh`
33
+
34
+ ### Task 1: Create lib
35
+ Write lib.
36
+
37
+ ## Batch 2: Feature A
38
+
39
+ **Files:**
40
+ - Create: `src/feature-a.sh`
41
+ context_refs: src/lib.sh
42
+
43
+ ### Task 2: Build feature A
44
+
45
+ ## Batch 3: Feature B
46
+
47
+ **Files:**
48
+ - Create: `src/feature-b.sh`
49
+ context_refs: src/lib.sh
50
+
51
+ ### Task 3: Build feature B
52
+
53
+ ## Batch 4: Integration
54
+
55
+ **Files:**
56
+ - Modify: `src/feature-a.sh`
57
+ - Modify: `src/feature-b.sh`
58
+ context_refs: src/feature-a.sh, src/feature-b.sh
59
+
60
+ ### Task 4: Wire together
61
+ PLAN
62
+
63
+ # Test dependency graph building
64
+ deps=$(build_dependency_graph "$WORK/parallel-plan.md")
65
+ assert_eq "dep graph: B2 depends on B1" "true" "$(echo "$deps" | jq '.["2"] | contains(["1"])')"
66
+ assert_eq "dep graph: B3 depends on B1" "true" "$(echo "$deps" | jq '.["3"] | contains(["1"])')"
67
+ assert_eq "dep graph: B4 depends on B2 and B3" "true" "$(echo "$deps" | jq '.["4"] | (contains(["2"]) and contains(["3"]))')"
68
+
69
+ # Test parallelism score
70
+ score=$(compute_parallelism_score "$WORK/parallel-plan.md")
71
+ TESTS=$((TESTS + 1))
72
+ if [[ "$score" -gt 40 ]]; then
73
+ echo "PASS: parallelism score: $score > 40 (batches 2,3 can run parallel)"
74
+ else
75
+ echo "FAIL: parallelism score: $score <= 40"
76
+ FAILURES=$((FAILURES + 1))
77
+ fi
78
+
79
+ # Test mode recommendation
80
+ mode=$(recommend_execution_mode "$score" "false" 21)
81
+ assert_eq "recommend: team for high score" "team" "$mode"
82
+
83
+ # Sequential plan (each batch depends on previous)
84
+ cat > "$WORK/sequential-plan.md" << 'PLAN'
85
+ ## Batch 1: Setup
86
+
87
+ **Files:**
88
+ - Create: `src/main.sh`
89
+
90
+ ### Task 1: Setup
91
+
92
+ ## Batch 2: Extend
93
+
94
+ **Files:**
95
+ - Modify: `src/main.sh`
96
+ context_refs: src/main.sh
97
+
98
+ ### Task 2: Extend
99
+
100
+ ## Batch 3: Finalize
101
+
102
+ **Files:**
103
+ - Modify: `src/main.sh`
104
+ context_refs: src/main.sh
105
+
106
+ ### Task 3: Finalize
107
+ PLAN
108
+
109
+ score=$(compute_parallelism_score "$WORK/sequential-plan.md")
110
+ TESTS=$((TESTS + 1))
111
+ if [[ "$score" -lt 30 ]]; then
112
+ echo "PASS: sequential plan score: $score < 30"
113
+ else
114
+ echo "FAIL: sequential plan score: $score >= 30"
115
+ FAILURES=$((FAILURES + 1))
116
+ fi
117
+
118
+ mode=$(recommend_execution_mode "$score" "false" 21)
119
+ assert_eq "recommend: headless for low score" "headless" "$mode"
120
+
121
+ # Test model routing
122
+ model=$(classify_batch_model "$WORK/parallel-plan.md" 1)
123
+ assert_eq "model: batch with Create files = sonnet" "sonnet" "$model"
124
+
125
+ # Verification batch
126
+ cat > "$WORK/verify-plan.md" << 'PLAN'
127
+ ## Batch 1: Verify everything
128
+
129
+ ### Task 1: Run all tests
130
+
131
+ **Step 1: Run tests**
132
+ Run: `bash scripts/tests/run-all-tests.sh`
133
+
134
+ **Step 2: Check line counts**
135
+ Run: `wc -l scripts/*.sh`
136
+ PLAN
137
+
138
+ model=$(classify_batch_model "$WORK/verify-plan.md" 1)
139
+ assert_eq "model: batch with only Run commands = haiku" "haiku" "$model"
140
+
141
+ # === jq timeout guard (bug #15) ===
142
+ # Verify the jq call in compute_parallelism_score is wrapped with timeout
143
+ TESTS=$((TESTS + 1))
144
+ if grep -q 'timeout 30 jq' "$SCRIPT_DIR/../lib/run-plan-routing.sh"; then
145
+ echo "PASS: jq calls wrapped with timeout 30"
146
+ else
147
+ echo "FAIL: jq calls should be wrapped with timeout 30"
148
+ FAILURES=$((FAILURES + 1))
149
+ fi
150
+
151
+ # Verify timeout actually terminates a slow jq process
152
+ # Use a FIFO that blocks forever to simulate corrupted/circular graph data
153
+ TESTS=$((TESTS + 1))
154
+ fifo="$WORK/slow-fifo"
155
+ mkfifo "$fifo"
156
+ # Keep FIFO open in background so jq blocks on read (not EOF)
157
+ sleep 60 > "$fifo" &
158
+ sleep_pid=$!
159
+ start=$(date +%s)
160
+ timeout 2 jq -r '.test' < "$fifo" 2>/dev/null || true
161
+ end=$(date +%s)
162
+ elapsed=$((end - start))
163
+ kill "$sleep_pid" 2>/dev/null || true
164
+ wait "$sleep_pid" 2>/dev/null || true
165
+ if [[ "$elapsed" -le 4 ]]; then
166
+ echo "PASS: timeout kills hanging jq within 4s"
167
+ else
168
+ echo "FAIL: timeout did not kill hanging jq (took ${elapsed}s)"
169
+ FAILURES=$((FAILURES + 1))
170
+ fi
171
+
172
+ echo ""
173
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
174
+ if [[ $FAILURES -gt 0 ]]; then
175
+ echo "FAILURES: $FAILURES"
176
+ exit 1
177
+ fi
178
+ echo "ALL PASSED"
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/../lib/run-plan-parser.sh"
6
+ source "$SCRIPT_DIR/../lib/run-plan-scoring.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
+ # Gate failed = score 0
25
+ score=$(score_candidate 0 50 100 0 0 0)
26
+ assert_eq "score: gate failed = 0" "0" "$score"
27
+
28
+ # Gate passed, good metrics
29
+ score=$(score_candidate 1 50 100 2 0 0)
30
+ TESTS=$((TESTS + 1))
31
+ if [[ "$score" -gt 0 ]]; then
32
+ echo "PASS: score: gate passed = positive ($score)"
33
+ else
34
+ echo "FAIL: score: gate passed should be positive ($score)"
35
+ FAILURES=$((FAILURES + 1))
36
+ fi
37
+
38
+ # More tests = higher score
39
+ score_a=$(score_candidate 1 50 100 0 0 0)
40
+ score_b=$(score_candidate 1 80 100 0 0 0)
41
+ TESTS=$((TESTS + 1))
42
+ if [[ "$score_b" -gt "$score_a" ]]; then
43
+ echo "PASS: score: more tests = higher score ($score_b > $score_a)"
44
+ else
45
+ echo "FAIL: score: more tests should be higher ($score_b <= $score_a)"
46
+ FAILURES=$((FAILURES + 1))
47
+ fi
48
+
49
+ # Lesson violations = penalty
50
+ score_clean=$(score_candidate 1 50 100 0 0 0)
51
+ score_dirty=$(score_candidate 1 50 100 0 2 0)
52
+ TESTS=$((TESTS + 1))
53
+ if [[ "$score_clean" -gt "$score_dirty" ]]; then
54
+ echo "PASS: score: lesson violations penalized ($score_clean > $score_dirty)"
55
+ else
56
+ echo "FAIL: score: lesson violations not penalized ($score_clean <= $score_dirty)"
57
+ FAILURES=$((FAILURES + 1))
58
+ fi
59
+
60
+ # select_winner picks highest score
61
+ winner=$(select_winner "500 300 700 0")
62
+ assert_eq "select_winner: picks index of highest" "2" "$winner"
63
+
64
+ # select_winner returns -1 when all zero
65
+ winner=$(select_winner "0 0 0")
66
+ assert_eq "select_winner: all zero = -1 (no winner)" "-1" "$winner"
67
+
68
+ # === classify_batch_type ===
69
+
70
+ WORK=$(mktemp -d)
71
+ trap 'rm -rf "$WORK"' EXIT
72
+
73
+ # Test: Create files = new-file
74
+ cat > "$WORK/plan-new.md" << 'PLAN'
75
+ ## Batch 1: Setup
76
+ - Create: `src/lib.py`
77
+ - Create: `src/util.py`
78
+ - Test: `tests/test_lib.py`
79
+
80
+ **Step 1:** Write files
81
+ PLAN
82
+
83
+ result=$(classify_batch_type "$WORK/plan-new.md" 1)
84
+ assert_eq "classify: Create dominant = new-file" "new-file" "$result"
85
+
86
+ # Test: Modify only = refactoring
87
+ cat > "$WORK/plan-refactor.md" << 'PLAN'
88
+ ## Batch 1: Refactor auth
89
+ - Modify: `src/auth.py:20-50`
90
+ - Modify: `src/session.py:10-30`
91
+
92
+ **Step 1:** Update auth
93
+ PLAN
94
+
95
+ result=$(classify_batch_type "$WORK/plan-refactor.md" 1)
96
+ assert_eq "classify: Modify only = refactoring" "refactoring" "$result"
97
+
98
+ # Test: Run commands only = test-only
99
+ cat > "$WORK/plan-test.md" << 'PLAN'
100
+ ## Batch 1: Verify
101
+ Run: pytest tests/ -v
102
+ Run: bash scripts/quality-gate.sh --project-root .
103
+
104
+ **Step 1:** Run tests
105
+ PLAN
106
+
107
+ result=$(classify_batch_type "$WORK/plan-test.md" 1)
108
+ assert_eq "classify: Run only = test-only" "test-only" "$result"
109
+
110
+ # Test: Integration title = integration
111
+ cat > "$WORK/plan-integ.md" << 'PLAN'
112
+ ## Batch 1: Integration Wiring
113
+ - Modify: `src/main.py`
114
+ - Create: `src/glue.py`
115
+
116
+ **Step 1:** Wire components
117
+ PLAN
118
+
119
+ result=$(classify_batch_type "$WORK/plan-integ.md" 1)
120
+ assert_eq "classify: integration title = integration" "integration" "$result"
121
+
122
+ # === get_prompt_variants ===
123
+
124
+ # Test: no history = vanilla first
125
+ result=$(get_prompt_variants "new-file" "/nonexistent/outcomes.json" 3)
126
+ first_line=$(echo "$result" | head -1)
127
+ assert_eq "variants: first is vanilla" "vanilla" "$first_line"
128
+
129
+ # Test: returns exactly N lines
130
+ count=$(echo "$result" | wc -l)
131
+ assert_eq "variants: returns N lines" "3" "$count"
132
+
133
+ # Test: with learned history, slot 2 picks winner
134
+ cat > "$WORK/outcomes.json" << 'JSON'
135
+ [{"batch_type": "new-file", "prompt_variant": "check all imports before running tests", "won": true, "score": 500}]
136
+ JSON
137
+
138
+ result=$(get_prompt_variants "new-file" "$WORK/outcomes.json" 3)
139
+ second_line=$(echo "$result" | sed -n '2p')
140
+ assert_eq "variants: learned winner in slot 2" "check all imports before running tests" "$second_line"
141
+
142
+ echo ""
143
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
144
+ if [[ $FAILURES -gt 0 ]]; then
145
+ echo "FAILURES: $FAILURES"
146
+ exit 1
147
+ fi
148
+ echo "ALL PASSED"
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env bash
2
+ # Test state manager functions
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "$SCRIPT_DIR/../lib/run-plan-state.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
+ # --- Temp dir for worktree simulation ---
25
+ WORK=$(mktemp -d)
26
+ trap 'rm -rf "$WORK"' EXIT
27
+
28
+ # --- Test: init_state creates the file ---
29
+ init_state "$WORK" "docs/plans/2026-02-20-feature.md" "headless"
30
+
31
+ TESTS=$((TESTS + 1))
32
+ if [[ -f "$WORK/.run-plan-state.json" ]]; then
33
+ echo "PASS: init_state creates state file"
34
+ else
35
+ echo "FAIL: init_state should create .run-plan-state.json"
36
+ FAILURES=$((FAILURES + 1))
37
+ fi
38
+
39
+ # --- Test: read_state_field reads plan_file ---
40
+ val=$(read_state_field "$WORK" "plan_file")
41
+ assert_eq "read plan_file" "docs/plans/2026-02-20-feature.md" "$val"
42
+
43
+ # --- Test: read_state_field reads mode ---
44
+ val=$(read_state_field "$WORK" "mode")
45
+ assert_eq "read mode" "headless" "$val"
46
+
47
+ # --- Test: read_state_field reads current_batch ---
48
+ val=$(read_state_field "$WORK" "current_batch")
49
+ assert_eq "read current_batch (initial)" "1" "$val"
50
+
51
+ # --- Test: read_state_field reads started_at ---
52
+ val=$(read_state_field "$WORK" "started_at")
53
+ TESTS=$((TESTS + 1))
54
+ if [[ -n "$val" && "$val" != "null" ]]; then
55
+ echo "PASS: started_at is set"
56
+ else
57
+ echo "FAIL: started_at should be a non-null timestamp"
58
+ FAILURES=$((FAILURES + 1))
59
+ fi
60
+
61
+ # --- Test: completed_batches starts empty ---
62
+ val=$(read_state_field "$WORK" "completed_batches")
63
+ assert_eq "completed_batches starts empty" "[]" "$val"
64
+
65
+ # --- Test: last_quality_gate starts null ---
66
+ val=$(read_state_field "$WORK" "last_quality_gate")
67
+ assert_eq "last_quality_gate starts null" "null" "$val"
68
+
69
+ # --- Test: complete_batch updates state ---
70
+ complete_batch "$WORK" 1 42
71
+
72
+ val=$(read_state_field "$WORK" "current_batch")
73
+ assert_eq "current_batch after completing batch 1" "2" "$val"
74
+
75
+ val=$(read_state_field "$WORK" "completed_batches")
76
+ assert_eq "completed_batches has batch 1" "[1]" "$val"
77
+
78
+ val=$(jq -r '.test_counts["1"]' "$WORK/.run-plan-state.json")
79
+ assert_eq "test_count for batch 1" "42" "$val"
80
+
81
+ # --- Test: multiple complete_batch calls accumulate ---
82
+ complete_batch "$WORK" 2 55
83
+
84
+ val=$(read_state_field "$WORK" "current_batch")
85
+ assert_eq "current_batch after completing batch 2" "3" "$val"
86
+
87
+ val=$(read_state_field "$WORK" "completed_batches")
88
+ assert_eq "completed_batches has both" "[1,2]" "$val"
89
+
90
+ val=$(jq -r '.test_counts["2"]' "$WORK/.run-plan-state.json")
91
+ assert_eq "test_count for batch 2" "55" "$val"
92
+
93
+ # Previous batch 1 count still there
94
+ val=$(jq -r '.test_counts["1"]' "$WORK/.run-plan-state.json")
95
+ assert_eq "test_count for batch 1 still intact" "42" "$val"
96
+
97
+ # --- Test: get_previous_test_count ---
98
+ val=$(get_previous_test_count "$WORK")
99
+ assert_eq "previous test count after batch 2" "55" "$val"
100
+
101
+ # --- Test: get_previous_test_count with no completions ---
102
+ WORK2=$(mktemp -d)
103
+ trap 'rm -rf "$WORK" "$WORK2"' EXIT
104
+ init_state "$WORK2" "plan.md" "team"
105
+
106
+ val=$(get_previous_test_count "$WORK2")
107
+ assert_eq "previous test count with no completions" "0" "$val"
108
+
109
+ # --- Test: set_quality_gate ---
110
+ set_quality_gate "$WORK" 2 "true" 55
111
+
112
+ val=$(jq -r '.last_quality_gate.batch' "$WORK/.run-plan-state.json")
113
+ assert_eq "quality gate batch" "2" "$val"
114
+
115
+ val=$(jq -r '.last_quality_gate.passed' "$WORK/.run-plan-state.json")
116
+ assert_eq "quality gate passed" "true" "$val"
117
+
118
+ val=$(jq -r '.last_quality_gate.test_count' "$WORK/.run-plan-state.json")
119
+ assert_eq "quality gate test_count" "55" "$val"
120
+
121
+ # Verify timestamp exists
122
+ val=$(jq -r '.last_quality_gate.timestamp' "$WORK/.run-plan-state.json")
123
+ TESTS=$((TESTS + 1))
124
+ if [[ -n "$val" && "$val" != "null" ]]; then
125
+ echo "PASS: quality gate has timestamp"
126
+ else
127
+ echo "FAIL: quality gate should have timestamp"
128
+ FAILURES=$((FAILURES + 1))
129
+ fi
130
+
131
+ # --- Test: set_quality_gate with failed gate ---
132
+ set_quality_gate "$WORK" 3 "false" 50
133
+
134
+ val=$(jq -r '.last_quality_gate.batch' "$WORK/.run-plan-state.json")
135
+ assert_eq "failed quality gate batch" "3" "$val"
136
+
137
+ val=$(jq -r '.last_quality_gate.passed' "$WORK/.run-plan-state.json")
138
+ assert_eq "failed quality gate passed" "false" "$val"
139
+
140
+ # --- Test: complete_batch stores duration ---
141
+ WORK3=$(mktemp -d)
142
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3"' EXIT
143
+ init_state "$WORK3" "plan.md" "headless"
144
+ complete_batch "$WORK3" 1 42 120
145
+
146
+ duration=$(jq -r '.durations["1"]' "$WORK3/.run-plan-state.json")
147
+ assert_eq "complete_batch: stores duration" "120" "$duration"
148
+
149
+ # --- Test: duration defaults to 0 when not provided ---
150
+ complete_batch "$WORK3" 2 55
151
+ duration=$(jq -r '.durations["2"]' "$WORK3/.run-plan-state.json")
152
+ assert_eq "complete_batch: duration defaults to 0" "0" "$duration"
153
+
154
+ # --- Test: init_state includes durations object ---
155
+ val=$(jq -r '.durations | type' "$WORK3/.run-plan-state.json")
156
+ assert_eq "init_state: has durations object" "object" "$val"
157
+
158
+ # --- Test: complete_batch with non-numeric batch_num ('final') ---
159
+ WORK4=$(mktemp -d)
160
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4"' EXIT
161
+ init_state "$WORK4" "plan.md" "headless"
162
+ complete_batch "$WORK4" 1 42
163
+ complete_batch "$WORK4" "final" 50
164
+
165
+ val=$(jq -r '.test_counts["final"]' "$WORK4/.run-plan-state.json")
166
+ assert_eq "complete_batch: non-numeric batch 'final' stores test count" "50" "$val"
167
+
168
+ val=$(jq -r '.durations["final"]' "$WORK4/.run-plan-state.json")
169
+ assert_eq "complete_batch: non-numeric batch 'final' stores duration" "0" "$val"
170
+
171
+ val=$(jq -r '.completed_batches | last' "$WORK4/.run-plan-state.json")
172
+ assert_eq "complete_batch: non-numeric batch 'final' in completed_batches" "final" "$val"
173
+
174
+ # Numeric batches still work after non-numeric
175
+ val=$(jq -r '.test_counts["1"]' "$WORK4/.run-plan-state.json")
176
+ assert_eq "complete_batch: numeric batch still intact after non-numeric" "42" "$val"
177
+
178
+ # --- Test: get_previous_test_count returns -1 when key missing ---
179
+ WORK5=$(mktemp -d)
180
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4" "$WORK5"' EXIT
181
+ init_state "$WORK5" "plan.md" "headless"
182
+ # Manually add a batch to completed_batches without a corresponding test_count entry
183
+ jq '.completed_batches += [1]' "$WORK5/.run-plan-state.json" > "$WORK5/.tmp.json" && mv "$WORK5/.tmp.json" "$WORK5/.run-plan-state.json"
184
+
185
+ val=$(get_previous_test_count "$WORK5")
186
+ assert_eq "get_previous_test_count: returns -1 when key missing" "-1" "$val"
187
+
188
+ # --- Test: set_quality_gate with non-numeric batch_num ('final') ---
189
+ WORK6=$(mktemp -d)
190
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4" "$WORK5" "$WORK6"' EXIT
191
+ init_state "$WORK6" "plan.md" "headless"
192
+ complete_batch "$WORK6" 1 42
193
+ set_quality_gate "$WORK6" "final" "true" 99
194
+
195
+ val=$(jq -r '.last_quality_gate.batch' "$WORK6/.run-plan-state.json")
196
+ assert_eq "set_quality_gate: non-numeric batch 'final' stored" "final" "$val"
197
+
198
+ val=$(jq -r '.last_quality_gate.passed' "$WORK6/.run-plan-state.json")
199
+ assert_eq "set_quality_gate: non-numeric batch passed=true" "true" "$val"
200
+
201
+ val=$(jq -r '.last_quality_gate.test_count' "$WORK6/.run-plan-state.json")
202
+ assert_eq "set_quality_gate: non-numeric batch test_count" "99" "$val"
203
+
204
+ # --- Test: end-to-end complete_batch 'final' then get_previous_test_count ---
205
+ WORK7=$(mktemp -d)
206
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4" "$WORK5" "$WORK6" "$WORK7"' EXIT
207
+ init_state "$WORK7" "plan.md" "headless"
208
+ complete_batch "$WORK7" 1 42
209
+ complete_batch "$WORK7" "final" 99
210
+
211
+ val=$(get_previous_test_count "$WORK7")
212
+ assert_eq "e2e: complete_batch 'final' then get_previous_test_count returns 99" "99" "$val"
213
+
214
+ # --- Test: init_state includes costs object ---
215
+ WORK_COST=$(mktemp -d)
216
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4" "$WORK5" "$WORK6" "$WORK7" "$WORK_COST" "$WORK8"' EXIT
217
+ init_state "$WORK_COST" "plan.md" "headless"
218
+
219
+ val=$(jq -r '.costs | type' "$WORK_COST/.run-plan-state.json")
220
+ assert_eq "init_state: has costs object" "object" "$val"
221
+
222
+ val=$(jq -r '.total_cost_usd' "$WORK_COST/.run-plan-state.json")
223
+ assert_eq "init_state: total_cost_usd starts at 0" "0" "$val"
224
+
225
+ # --- Test: set_quality_gate normalizes truthy values (#8) ---
226
+ WORK8=$(mktemp -d)
227
+ trap 'rm -rf "$WORK" "$WORK2" "$WORK3" "$WORK4" "$WORK5" "$WORK6" "$WORK7" "$WORK_COST" "$WORK8"' EXIT
228
+ init_state "$WORK8" "plan.md" "headless"
229
+
230
+ # "1" should normalize to true
231
+ set_quality_gate "$WORK8" 1 "1" 10
232
+ val=$(jq -r '.last_quality_gate.passed' "$WORK8/.run-plan-state.json")
233
+ assert_eq "set_quality_gate: '1' normalizes to true" "true" "$val"
234
+
235
+ # "0" should normalize to false
236
+ set_quality_gate "$WORK8" 1 "0" 10
237
+ val=$(jq -r '.last_quality_gate.passed' "$WORK8/.run-plan-state.json")
238
+ assert_eq "set_quality_gate: '0' normalizes to false" "false" "$val"
239
+
240
+ # "yes" should normalize to true
241
+ set_quality_gate "$WORK8" 1 "yes" 10
242
+ val=$(jq -r '.last_quality_gate.passed' "$WORK8/.run-plan-state.json")
243
+ assert_eq "set_quality_gate: 'yes' normalizes to true" "true" "$val"
244
+
245
+ # "no" should normalize to false
246
+ set_quality_gate "$WORK8" 1 "no" 10
247
+ val=$(jq -r '.last_quality_gate.passed' "$WORK8/.run-plan-state.json")
248
+ assert_eq "set_quality_gate: 'no' normalizes to false" "false" "$val"
249
+
250
+ # "random" should normalize to false
251
+ set_quality_gate "$WORK8" 1 "random" 10
252
+ val=$(jq -r '.last_quality_gate.passed' "$WORK8/.run-plan-state.json")
253
+ assert_eq "set_quality_gate: 'random' normalizes to false" "false" "$val"
254
+
255
+ echo ""
256
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
257
+ if [[ $FAILURES -gt 0 ]]; then
258
+ echo "FAILURES: $FAILURES"
259
+ exit 1
260
+ fi
261
+ echo "ALL PASSED"