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,157 @@
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
+ source "$SCRIPT_DIR/../lib/run-plan-team.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
+ WORK=$(mktemp -d)
26
+ trap 'rm -rf "$WORK"' EXIT
27
+
28
+ # Plan with parallel structure: batch 1 is foundation, 2+3 are parallel, 4 depends on both
29
+ cat > "$WORK/parallel-plan.md" << 'PLAN'
30
+ ## Batch 1: Foundation
31
+
32
+ **Files:**
33
+ - Create: `src/lib.sh`
34
+
35
+ ### Task 1: Create 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 compute_parallel_groups with parallel plan
64
+ dep_graph=$(build_dependency_graph "$WORK/parallel-plan.md")
65
+ groups=$(compute_parallel_groups "$dep_graph" 1 4)
66
+
67
+ # Should produce 3 groups: [1], [2,3], [4]
68
+ group_count=$(echo "$groups" | jq 'length')
69
+ assert_eq "parallel groups: 3 groups" "3" "$group_count"
70
+
71
+ # First group has batch 1
72
+ first_group=$(echo "$groups" | jq -c '.[0] | sort')
73
+ assert_eq "parallel groups: group 1 = [1]" '[1]' "$first_group"
74
+
75
+ # Second group has batches 2 and 3 (in some order)
76
+ second_group=$(echo "$groups" | jq -c '.[1] | sort')
77
+ assert_eq "parallel groups: group 2 = [2,3]" '[2,3]' "$second_group"
78
+
79
+ # Third group has batch 4
80
+ third_group=$(echo "$groups" | jq -c '.[2] | sort')
81
+ assert_eq "parallel groups: group 3 = [4]" '[4]' "$third_group"
82
+
83
+ # Sequential plan (each 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
+ dep_graph=$(build_dependency_graph "$WORK/sequential-plan.md")
110
+ groups=$(compute_parallel_groups "$dep_graph" 1 3)
111
+
112
+ # Sequential plan: each batch is its own group
113
+ group_count=$(echo "$groups" | jq 'length')
114
+ assert_eq "sequential groups: 3 groups (no parallelism)" "3" "$group_count"
115
+
116
+ # Each group has exactly one batch
117
+ for ((g = 0; g < 3; g++)); do
118
+ size=$(echo "$groups" | jq ".[$g] | length")
119
+ assert_eq "sequential groups: group $((g+1)) has 1 batch" "1" "$size"
120
+ done
121
+
122
+ # Test with subset range (start_batch=2, end_batch=3)
123
+ dep_graph=$(build_dependency_graph "$WORK/parallel-plan.md")
124
+ groups=$(compute_parallel_groups "$dep_graph" 2 3)
125
+ group_count=$(echo "$groups" | jq 'length')
126
+ # Batches 2 and 3 both depend on batch 1 which is outside our range
127
+ # Since batch 1 is not in range, its deps should be treated as satisfied
128
+ assert_eq "subset range: batches 2,3 form 1 group (deps outside range)" "1" "$group_count"
129
+
130
+ # === Bug #1: Team mode emits shared-worktree WARNING ===
131
+
132
+ TEAM_FILE="$SCRIPT_DIR/../lib/run-plan-team.sh"
133
+
134
+ TESTS=$((TESTS + 1))
135
+ if grep -q 'WARNING.*shared worktree\|WARNING.*Team mode.*shared\|WARNING: Team mode' "$TEAM_FILE"; then
136
+ echo "PASS: run_mode_team emits shared-worktree WARNING"
137
+ else
138
+ echo "FAIL: run_mode_team should emit a WARNING about shared worktree (bug #1)"
139
+ FAILURES=$((FAILURES + 1))
140
+ fi
141
+
142
+ # The warning should be emitted to stderr (>&2)
143
+ TESTS=$((TESTS + 1))
144
+ if grep -q 'WARNING.*>&2' "$TEAM_FILE"; then
145
+ echo "PASS: team mode WARNING goes to stderr"
146
+ else
147
+ echo "FAIL: team mode WARNING should be sent to stderr (>&2) (bug #1)"
148
+ FAILURES=$((FAILURES + 1))
149
+ fi
150
+
151
+ echo ""
152
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
153
+ if [[ $FAILURES -gt 0 ]]; then
154
+ echo "FAILURES: $FAILURES"
155
+ exit 1
156
+ fi
157
+ echo "ALL PASSED"
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/test-helpers.sh"
6
+
7
+ INFER="$SCRIPT_DIR/../scope-infer.sh"
8
+
9
+ # --- Test: --help exits 0 ---
10
+ assert_exit "--help exits 0" 0 "$INFER" --help
11
+
12
+ # --- Test: --dry-run shows proposed scope without modifying files ---
13
+ WORK=$(mktemp -d)
14
+ trap 'rm -rf "$WORK"' EXIT
15
+
16
+ # Create a lesson with no scope field, mentioning "HA" and "entity"
17
+ cat > "$WORK/0099-test-ha-lesson.md" <<'LESSON'
18
+ ---
19
+ id: 99
20
+ title: "HA entity resolution fails on restart"
21
+ severity: should-fix
22
+ languages: [python]
23
+ category: data-model
24
+ pattern:
25
+ type: semantic
26
+ description: "HA entity lookup returns stale area"
27
+ fix: "Refresh entity registry on restart"
28
+ ---
29
+
30
+ ## Observation
31
+ Home Assistant entity area resolution uses a cached registry.
32
+ LESSON
33
+
34
+ dry_output=$("$INFER" --dir "$WORK" --dry-run 2>&1 || true)
35
+ assert_contains "--dry-run mentions ha-aria" "domain:ha-aria" "$dry_output"
36
+
37
+ # Verify file was NOT modified (dry run)
38
+ TESTS=$((TESTS + 1))
39
+ if grep -q '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null; then
40
+ echo "FAIL: --dry-run should not modify lesson files"
41
+ FAILURES=$((FAILURES + 1))
42
+ else
43
+ echo "PASS: --dry-run does not modify lesson files"
44
+ fi
45
+
46
+ # --- Test: --apply writes scope field to lesson ---
47
+ "$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
48
+
49
+ TESTS=$((TESTS + 1))
50
+ if grep -q '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null; then
51
+ echo "PASS: --apply writes scope field to lesson"
52
+ else
53
+ echo "FAIL: --apply should write scope field to lesson"
54
+ FAILURES=$((FAILURES + 1))
55
+ fi
56
+
57
+ # Verify inferred scope is correct
58
+ scope_line=$(grep '^scope:' "$WORK/0099-test-ha-lesson.md" 2>/dev/null || true)
59
+ assert_contains "--apply infers domain:ha-aria" "domain:ha-aria" "$scope_line"
60
+
61
+ # --- Test: Lesson with existing scope is not modified ---
62
+ cat > "$WORK/0098-already-scoped.md" <<'LESSON'
63
+ ---
64
+ id: 98
65
+ title: "Already scoped lesson"
66
+ severity: should-fix
67
+ scope: [language:python]
68
+ languages: [python]
69
+ category: silent-failures
70
+ pattern:
71
+ type: semantic
72
+ description: "test"
73
+ fix: "test"
74
+ ---
75
+ LESSON
76
+
77
+ apply_output=$("$INFER" --dir "$WORK" --apply 2>&1 || true)
78
+ scope_line=$(grep '^scope:' "$WORK/0098-already-scoped.md" 2>/dev/null || true)
79
+ assert_contains "existing scope preserved" "language:python" "$scope_line"
80
+
81
+ # --- Test: Python-only lesson with no domain signals → language:python ---
82
+ cat > "$WORK/0097-python-only.md" <<'LESSON'
83
+ ---
84
+ id: 97
85
+ title: "Generic Python anti-pattern"
86
+ severity: should-fix
87
+ languages: [python]
88
+ category: async-traps
89
+ pattern:
90
+ type: syntactic
91
+ regex: "some_pattern"
92
+ description: "test"
93
+ fix: "test"
94
+ ---
95
+
96
+ ## Observation
97
+ This is a generic Python lesson with no domain signals.
98
+ LESSON
99
+
100
+ "$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
101
+ scope_line=$(grep '^scope:' "$WORK/0097-python-only.md" 2>/dev/null || true)
102
+ assert_contains "python-only → language:python" "language:python" "$scope_line"
103
+
104
+ # --- Test: No signals → universal ---
105
+ cat > "$WORK/0096-universal.md" <<'LESSON'
106
+ ---
107
+ id: 96
108
+ title: "Generic coding practice"
109
+ severity: nice-to-have
110
+ languages: [all]
111
+ category: test-anti-patterns
112
+ pattern:
113
+ type: syntactic
114
+ regex: "some_other_pattern"
115
+ description: "test"
116
+ fix: "test"
117
+ ---
118
+
119
+ ## Observation
120
+ This applies to all projects everywhere.
121
+ LESSON
122
+
123
+ "$INFER" --dir "$WORK" --apply > /dev/null 2>&1 || true
124
+ scope_line=$(grep '^scope:' "$WORK/0096-universal.md" 2>/dev/null || true)
125
+ assert_contains "no signals → universal" "universal" "$scope_line"
126
+
127
+ # --- Test: Summary output shows counts ---
128
+ WORK2=$(mktemp -d)
129
+ trap 'rm -rf "$WORK" "$WORK2"' EXIT
130
+
131
+ cat > "$WORK2/0001-test.md" <<'LESSON'
132
+ ---
133
+ id: 1
134
+ title: "Test lesson"
135
+ severity: should-fix
136
+ languages: [python]
137
+ category: silent-failures
138
+ pattern:
139
+ type: syntactic
140
+ regex: "test"
141
+ description: "test"
142
+ fix: "test"
143
+ ---
144
+ Generic content.
145
+ LESSON
146
+
147
+ summary_output=$("$INFER" --dir "$WORK2" --dry-run 2>&1 || true)
148
+ assert_contains "summary shows count" "Inferred scope for" "$summary_output"
149
+
150
+ report_results
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bash
2
+ # Test setup-ralph-loop.sh
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "$SCRIPT_DIR/test-helpers.sh"
7
+
8
+ SETUP_SCRIPT="$SCRIPT_DIR/../setup-ralph-loop.sh"
9
+ WORK=$(mktemp -d)
10
+ trap 'rm -rf "$WORK"' EXIT
11
+
12
+ # Run setup-ralph-loop in a temp dir so it writes .claude/ there
13
+ run_setup() {
14
+ (cd "$WORK" && bash "$SETUP_SCRIPT" "$@" 2>&1)
15
+ }
16
+
17
+ get_yaml_promise() {
18
+ sed -n 's/^completion_promise: *//p' "$WORK/.claude/ralph-loop.local.md"
19
+ }
20
+
21
+ # === Test: Basic prompt creates state file ===
22
+ rm -rf "$WORK/.claude"
23
+ output=$(run_setup "Hello world")
24
+ assert_contains "basic: creates state file" "Ralph loop activated" "$output"
25
+ assert_contains "basic: shows prompt" "Hello world" "$output"
26
+
27
+ # === Test: Completion promise with double quotes is safely quoted (#22) ===
28
+ rm -rf "$WORK/.claude"
29
+ output=$(run_setup 'Build it' --completion-promise 'say "hello" done')
30
+ yaml_val=$(get_yaml_promise)
31
+ # jq produces JSON-quoted string: "say \"hello\" done"
32
+ assert_contains "double quotes: safely quoted" '\"hello\"' "$yaml_val"
33
+
34
+ # === Test: Completion promise with backslashes is safely quoted (#22) ===
35
+ rm -rf "$WORK/.claude"
36
+ output=$(run_setup 'Build it' --completion-promise 'path\to\thing')
37
+ yaml_val=$(get_yaml_promise)
38
+ # jq produces: "path\\to\\thing"
39
+ assert_contains "backslash: safely quoted" '\\' "$yaml_val"
40
+
41
+ # === Test: Completion promise with single quotes is safely quoted (#22) ===
42
+ rm -rf "$WORK/.claude"
43
+ output=$(run_setup 'Build it' --completion-promise "it's done")
44
+ yaml_val=$(get_yaml_promise)
45
+ assert_contains "single quote: safely quoted" "it's done" "$yaml_val"
46
+
47
+ # === Test: Null completion promise stays null ===
48
+ rm -rf "$WORK/.claude"
49
+ output=$(run_setup "Build it")
50
+ yaml_val=$(get_yaml_promise)
51
+ assert_eq "null promise: stays null" "null" "$yaml_val"
52
+
53
+ # === Test: --help exits 0 ===
54
+ output=$(bash "$SETUP_SCRIPT" --help 2>&1 || echo "EXIT:$?")
55
+ assert_contains "--help: shows usage" "USAGE:" "$output"
56
+ assert_not_contains "--help: exit 0" "EXIT:" "$output"
57
+
58
+ # === Test: No prompt exits 1 ===
59
+ rm -rf "$WORK/.claude"
60
+ output=$(run_setup 2>&1 || echo "EXIT:$?")
61
+ assert_contains "no prompt: exits 1" "EXIT:1" "$output"
62
+
63
+ report_results
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ # Test telegram.sh — ACT_ENV_FILE support
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7
+
8
+ source "$SCRIPT_DIR/test-helpers.sh"
9
+
10
+ # --- Setup ---
11
+ WORK=$(mktemp -d)
12
+ trap 'rm -rf "$WORK"' EXIT
13
+
14
+ # Create a fake .env
15
+ cat > "$WORK/test.env" <<'ENV'
16
+ TELEGRAM_BOT_TOKEN=test-token-123
17
+ TELEGRAM_CHAT_ID=test-chat-456
18
+ ENV
19
+
20
+ # --- Test 1: ACT_ENV_FILE overrides default ---
21
+ unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID 2>/dev/null || true
22
+ ACT_ENV_FILE="$WORK/test.env" source "$REPO_ROOT/scripts/lib/telegram.sh"
23
+ ACT_ENV_FILE="$WORK/test.env" _load_telegram_env
24
+ assert_eq "ACT_ENV_FILE loads token" "test-token-123" "$TELEGRAM_BOT_TOKEN"
25
+ assert_eq "ACT_ENV_FILE loads chat id" "test-chat-456" "$TELEGRAM_CHAT_ID"
26
+
27
+ # --- Test 2: Explicit argument still works ---
28
+ unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID 2>/dev/null || true
29
+ _load_telegram_env "$WORK/test.env"
30
+ assert_eq "Explicit arg loads token" "test-token-123" "$TELEGRAM_BOT_TOKEN"
31
+
32
+ # --- Test 3: Missing file returns error ---
33
+ unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID 2>/dev/null || true
34
+ exit_code=0
35
+ _load_telegram_env "$WORK/nonexistent.env" 2>/dev/null || exit_code=$?
36
+ assert_eq "Missing env file returns 1" "1" "$exit_code"
37
+
38
+ report_results
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ # Test telegram.sh shared library
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ source "$SCRIPT_DIR/../lib/telegram.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
+ "$@" || 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
+ WORK=$(mktemp -d)
41
+ trap 'rm -rf "$WORK"' EXIT
42
+
43
+ # === _load_telegram_env tests ===
44
+
45
+ # Missing file
46
+ assert_exit "_load_telegram_env: missing file returns 1" 1 \
47
+ _load_telegram_env "$WORK/nonexistent"
48
+
49
+ # File without keys
50
+ echo "SOME_OTHER_KEY=value" > "$WORK/empty.env"
51
+ assert_exit "_load_telegram_env: missing keys returns 1" 1 \
52
+ _load_telegram_env "$WORK/empty.env"
53
+
54
+ # File with both keys
55
+ cat > "$WORK/valid.env" << 'ENVFILE'
56
+ TELEGRAM_BOT_TOKEN=test-token-123
57
+ TELEGRAM_CHAT_ID=test-chat-456
58
+ ENVFILE
59
+ # File with export prefix
60
+ cat > "$WORK/export.env" << 'ENVFILE'
61
+ export TELEGRAM_BOT_TOKEN=export-token-789
62
+ export TELEGRAM_CHAT_ID=export-chat-012
63
+ ENVFILE
64
+ assert_exit "_load_telegram_env: export prefix returns 0" 0 \
65
+ _load_telegram_env "$WORK/export.env"
66
+ assert_eq "_load_telegram_env: export token parsed" "export-token-789" "$TELEGRAM_BOT_TOKEN"
67
+ assert_eq "_load_telegram_env: export chat_id parsed" "export-chat-012" "$TELEGRAM_CHAT_ID"
68
+
69
+ # File with both keys (no export)
70
+ assert_exit "_load_telegram_env: valid file returns 0" 0 \
71
+ _load_telegram_env "$WORK/valid.env"
72
+ assert_eq "_load_telegram_env: token loaded" "test-token-123" "$TELEGRAM_BOT_TOKEN"
73
+ assert_eq "_load_telegram_env: chat_id loaded" "test-chat-456" "$TELEGRAM_CHAT_ID"
74
+
75
+ # === Quote stripping (#7): values wrapped in double quotes ===
76
+
77
+ cat > "$WORK/quoted-double.env" << 'ENVFILE'
78
+ TELEGRAM_BOT_TOKEN="quoted-token-abc"
79
+ TELEGRAM_CHAT_ID="quoted-chat-def"
80
+ ENVFILE
81
+ assert_exit "_load_telegram_env: double-quoted values returns 0" 0 \
82
+ _load_telegram_env "$WORK/quoted-double.env"
83
+ assert_eq "_load_telegram_env: double-quoted token stripped" "quoted-token-abc" "$TELEGRAM_BOT_TOKEN"
84
+ assert_eq "_load_telegram_env: double-quoted chat_id stripped" "quoted-chat-def" "$TELEGRAM_CHAT_ID"
85
+
86
+ # === Quote stripping (#7): values wrapped in single quotes ===
87
+
88
+ cat > "$WORK/quoted-single.env" << 'ENVFILE'
89
+ TELEGRAM_BOT_TOKEN='single-token-ghi'
90
+ TELEGRAM_CHAT_ID='single-chat-jkl'
91
+ ENVFILE
92
+ assert_exit "_load_telegram_env: single-quoted values returns 0" 0 \
93
+ _load_telegram_env "$WORK/quoted-single.env"
94
+ assert_eq "_load_telegram_env: single-quoted token stripped" "single-token-ghi" "$TELEGRAM_BOT_TOKEN"
95
+ assert_eq "_load_telegram_env: single-quoted chat_id stripped" "single-chat-jkl" "$TELEGRAM_CHAT_ID"
96
+
97
+ # === Quote stripping (#7): export with double quotes ===
98
+
99
+ cat > "$WORK/export-quoted.env" << 'ENVFILE'
100
+ export TELEGRAM_BOT_TOKEN="export-quoted-mno"
101
+ export TELEGRAM_CHAT_ID="export-quoted-pqr"
102
+ ENVFILE
103
+ assert_exit "_load_telegram_env: export double-quoted returns 0" 0 \
104
+ _load_telegram_env "$WORK/export-quoted.env"
105
+ assert_eq "_load_telegram_env: export double-quoted token stripped" "export-quoted-mno" "$TELEGRAM_BOT_TOKEN"
106
+ assert_eq "_load_telegram_env: export double-quoted chat_id stripped" "export-quoted-pqr" "$TELEGRAM_CHAT_ID"
107
+
108
+ # === _send_telegram without credentials ===
109
+
110
+ unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
111
+ assert_exit "_send_telegram: no creds returns 0 (skip)" 0 \
112
+ _send_telegram "test message"
113
+
114
+ # === Summary ===
115
+ echo ""
116
+ echo "Results: $((TESTS - FAILURES))/$TESTS passed"
117
+ if [[ $FAILURES -gt 0 ]]; then
118
+ echo "FAILURES: $FAILURES"
119
+ exit 1
120
+ fi
121
+ echo "ALL PASSED"
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ # Test scripts/telemetry.sh — telemetry capture, show, export, reset
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
7
+ TELEMETRY="$REPO_ROOT/scripts/telemetry.sh"
8
+
9
+ source "$SCRIPT_DIR/test-helpers.sh"
10
+
11
+ # --- Setup ---
12
+ WORK=$(mktemp -d)
13
+ trap 'rm -rf "$WORK"' EXIT
14
+ mkdir -p "$WORK/logs"
15
+
16
+ # --- Test 1: record writes to telemetry.jsonl ---
17
+ bash "$TELEMETRY" record --project-root "$WORK" \
18
+ --batch-number 1 --passed true --strategy superpowers \
19
+ --duration 120 --cost 0.42 --test-delta 5 2>&1 || true
20
+ assert_eq "record creates telemetry.jsonl" "true" \
21
+ "$([ -f "$WORK/logs/telemetry.jsonl" ] && echo true || echo false)"
22
+
23
+ # --- Test 2: record appends valid JSON ---
24
+ line=$(head -1 "$WORK/logs/telemetry.jsonl")
25
+ jq_exit=0
26
+ echo "$line" | jq . >/dev/null 2>&1 || jq_exit=$?
27
+ assert_eq "record writes valid JSON" "0" "$jq_exit"
28
+
29
+ # --- Test 3: show produces dashboard output ---
30
+ output=$(bash "$TELEMETRY" show --project-root "$WORK" 2>&1 || true)
31
+ assert_contains "show displays header" "Telemetry Dashboard" "$output"
32
+
33
+ # --- Test 4: export produces anonymized output ---
34
+ bash "$TELEMETRY" export --project-root "$WORK" > "$WORK/export.json" 2>&1 || true
35
+ assert_eq "export creates output" "true" "$([ -s "$WORK/export.json" ] && echo true || echo false)"
36
+
37
+ # --- Test 5: reset clears telemetry ---
38
+ bash "$TELEMETRY" reset --project-root "$WORK" --yes 2>&1 || true
39
+ if [[ -f "$WORK/logs/telemetry.jsonl" ]]; then
40
+ line_count=$(wc -l < "$WORK/logs/telemetry.jsonl")
41
+ assert_eq "reset clears telemetry" "0" "$line_count"
42
+ else
43
+ pass "reset removes telemetry file"
44
+ fi
45
+
46
+ report_results