qualia-framework 2.6.0 → 3.2.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 (321) hide show
  1. package/CLAUDE.md +64 -0
  2. package/README.md +103 -30
  3. package/agents/builder.md +110 -0
  4. package/agents/planner.md +134 -0
  5. package/agents/qa-browser.md +186 -0
  6. package/agents/verifier.md +221 -0
  7. package/bin/cli.js +336 -531
  8. package/bin/install.js +570 -0
  9. package/bin/qualia-ui.js +299 -0
  10. package/bin/state.js +630 -0
  11. package/bin/statusline.js +252 -0
  12. package/guide.md +63 -0
  13. package/hooks/auto-update.js +139 -0
  14. package/hooks/branch-guard.js +47 -0
  15. package/hooks/migration-guard.js +60 -0
  16. package/hooks/pre-compact.js +32 -0
  17. package/hooks/pre-deploy-gate.js +110 -0
  18. package/hooks/pre-push.js +33 -0
  19. package/hooks/session-start.js +170 -0
  20. package/package.json +29 -20
  21. package/rules/design-reference.md +179 -0
  22. package/rules/frontend.md +126 -0
  23. package/skills/qualia/SKILL.md +87 -0
  24. package/skills/qualia-build/SKILL.md +97 -0
  25. package/skills/qualia-debug/SKILL.md +87 -0
  26. package/skills/qualia-design/SKILL.md +93 -0
  27. package/skills/qualia-handoff/SKILL.md +66 -0
  28. package/skills/qualia-idk/SKILL.md +8 -0
  29. package/skills/qualia-learn/SKILL.md +88 -0
  30. package/skills/qualia-new/SKILL.md +323 -0
  31. package/{framework/skills → skills}/qualia-optimize/SKILL.md +1 -1
  32. package/skills/qualia-pause/SKILL.md +63 -0
  33. package/skills/qualia-plan/SKILL.md +101 -0
  34. package/skills/qualia-polish/SKILL.md +157 -0
  35. package/skills/qualia-quick/SKILL.md +37 -0
  36. package/skills/qualia-report/SKILL.md +105 -0
  37. package/skills/qualia-resume/SKILL.md +49 -0
  38. package/skills/qualia-review/SKILL.md +76 -0
  39. package/skills/qualia-ship/SKILL.md +90 -0
  40. package/skills/qualia-skill-new/SKILL.md +167 -0
  41. package/skills/qualia-task/SKILL.md +91 -0
  42. package/skills/qualia-verify/SKILL.md +113 -0
  43. package/templates/DESIGN.md +137 -0
  44. package/templates/plan.md +28 -0
  45. package/templates/project.md +22 -0
  46. package/templates/state.md +27 -0
  47. package/templates/tracking.json +20 -0
  48. package/tests/bin.test.sh +673 -0
  49. package/tests/hooks.test.sh +315 -0
  50. package/tests/state.test.sh +535 -0
  51. package/tests/statusline.test.sh +243 -0
  52. package/bin/collect-metrics.sh +0 -62
  53. package/framework/.claudeignore +0 -51
  54. package/framework/CLAUDE.md +0 -51
  55. package/framework/MCP_SETUP.md +0 -229
  56. package/framework/agents/architecture-strategist.md +0 -53
  57. package/framework/agents/backend-agent.md +0 -150
  58. package/framework/agents/code-simplicity-reviewer.md +0 -86
  59. package/framework/agents/frontend-agent.md +0 -111
  60. package/framework/agents/kieran-typescript-reviewer.md +0 -96
  61. package/framework/agents/performance-oracle.md +0 -111
  62. package/framework/agents/qualia-codebase-mapper.md +0 -761
  63. package/framework/agents/qualia-debugger.md +0 -1204
  64. package/framework/agents/qualia-executor.md +0 -882
  65. package/framework/agents/qualia-integration-checker.md +0 -424
  66. package/framework/agents/qualia-phase-researcher.md +0 -457
  67. package/framework/agents/qualia-plan-checker.md +0 -700
  68. package/framework/agents/qualia-planner.md +0 -1245
  69. package/framework/agents/qualia-project-researcher.md +0 -603
  70. package/framework/agents/qualia-research-synthesizer.md +0 -200
  71. package/framework/agents/qualia-roadmapper.md +0 -606
  72. package/framework/agents/qualia-verifier.md +0 -686
  73. package/framework/agents/red-team-qa.md +0 -130
  74. package/framework/agents/security-auditor.md +0 -72
  75. package/framework/agents/team-orchestrator.md +0 -229
  76. package/framework/agents/teams/framework-audit-team.md +0 -66
  77. package/framework/agents/teams/full-stack-team.md +0 -48
  78. package/framework/agents/teams/optimize-team.md +0 -53
  79. package/framework/agents/teams/review-team.md +0 -70
  80. package/framework/agents/teams/ship-team.md +0 -86
  81. package/framework/agents/test-agent.md +0 -182
  82. package/framework/hooks/auto-format.sh +0 -54
  83. package/framework/hooks/block-env-edit.sh +0 -42
  84. package/framework/hooks/branch-guard.sh +0 -43
  85. package/framework/hooks/confirm-delete.sh +0 -59
  86. package/framework/hooks/migration-validate.sh +0 -77
  87. package/framework/hooks/notification-speak.sh +0 -16
  88. package/framework/hooks/pre-commit.sh +0 -100
  89. package/framework/hooks/pre-compact.sh +0 -56
  90. package/framework/hooks/pre-deploy-gate.sh +0 -160
  91. package/framework/hooks/qualia-colors.sh +0 -32
  92. package/framework/hooks/retention-cleanup.sh +0 -62
  93. package/framework/hooks/save-session-state.sh +0 -185
  94. package/framework/hooks/session-context-loader.sh +0 -96
  95. package/framework/hooks/session-learn.sh +0 -32
  96. package/framework/hooks/skill-announce.sh +0 -123
  97. package/framework/hooks/tool-error-announce.sh +0 -27
  98. package/framework/install.ps1 +0 -323
  99. package/framework/install.sh +0 -313
  100. package/framework/qualia-framework/VERSION +0 -1
  101. package/framework/qualia-framework/assets/qualia-logo.png +0 -0
  102. package/framework/qualia-framework/bin/collect-metrics.sh +0 -67
  103. package/framework/qualia-framework/bin/generate-report-docx.py +0 -429
  104. package/framework/qualia-framework/bin/qualia-tools.js +0 -2201
  105. package/framework/qualia-framework/bin/qualia-tools.test.js +0 -1054
  106. package/framework/qualia-framework/references/checkpoints.md +0 -775
  107. package/framework/qualia-framework/references/completion-checklists.md +0 -359
  108. package/framework/qualia-framework/references/continuation-format.md +0 -249
  109. package/framework/qualia-framework/references/continuation-prompt.md +0 -97
  110. package/framework/qualia-framework/references/decimal-phase-calculation.md +0 -65
  111. package/framework/qualia-framework/references/design-quality.md +0 -56
  112. package/framework/qualia-framework/references/employee-guide.md +0 -167
  113. package/framework/qualia-framework/references/git-integration.md +0 -254
  114. package/framework/qualia-framework/references/git-planning-commit.md +0 -50
  115. package/framework/qualia-framework/references/model-profile-resolution.md +0 -32
  116. package/framework/qualia-framework/references/model-profiles.md +0 -73
  117. package/framework/qualia-framework/references/phase-argument-parsing.md +0 -61
  118. package/framework/qualia-framework/references/planning-config.md +0 -195
  119. package/framework/qualia-framework/references/questioning.md +0 -141
  120. package/framework/qualia-framework/references/tdd.md +0 -263
  121. package/framework/qualia-framework/references/ui-brand.md +0 -160
  122. package/framework/qualia-framework/references/verification-patterns.md +0 -612
  123. package/framework/qualia-framework/templates/DEBUG.md +0 -159
  124. package/framework/qualia-framework/templates/DESIGN.md +0 -81
  125. package/framework/qualia-framework/templates/UAT.md +0 -247
  126. package/framework/qualia-framework/templates/codebase/architecture.md +0 -255
  127. package/framework/qualia-framework/templates/codebase/concerns.md +0 -310
  128. package/framework/qualia-framework/templates/codebase/conventions.md +0 -307
  129. package/framework/qualia-framework/templates/codebase/integrations.md +0 -280
  130. package/framework/qualia-framework/templates/codebase/stack.md +0 -186
  131. package/framework/qualia-framework/templates/codebase/structure.md +0 -285
  132. package/framework/qualia-framework/templates/codebase/testing.md +0 -480
  133. package/framework/qualia-framework/templates/config.json +0 -35
  134. package/framework/qualia-framework/templates/context.md +0 -283
  135. package/framework/qualia-framework/templates/continue-here.md +0 -78
  136. package/framework/qualia-framework/templates/debug-subagent-prompt.md +0 -91
  137. package/framework/qualia-framework/templates/discovery.md +0 -146
  138. package/framework/qualia-framework/templates/lab-notes.md +0 -16
  139. package/framework/qualia-framework/templates/milestone-archive.md +0 -123
  140. package/framework/qualia-framework/templates/milestone.md +0 -115
  141. package/framework/qualia-framework/templates/phase-prompt.md +0 -567
  142. package/framework/qualia-framework/templates/planner-subagent-prompt.md +0 -117
  143. package/framework/qualia-framework/templates/project.md +0 -184
  144. package/framework/qualia-framework/templates/projects/ai-agent.md +0 -156
  145. package/framework/qualia-framework/templates/projects/mobile-app.md +0 -181
  146. package/framework/qualia-framework/templates/projects/voice-agent.md +0 -134
  147. package/framework/qualia-framework/templates/projects/website.md +0 -137
  148. package/framework/qualia-framework/templates/requirements.md +0 -231
  149. package/framework/qualia-framework/templates/research-project/ARCHITECTURE.md +0 -204
  150. package/framework/qualia-framework/templates/research-project/FEATURES.md +0 -147
  151. package/framework/qualia-framework/templates/research-project/PITFALLS.md +0 -200
  152. package/framework/qualia-framework/templates/research-project/STACK.md +0 -120
  153. package/framework/qualia-framework/templates/research-project/SUMMARY.md +0 -170
  154. package/framework/qualia-framework/templates/research.md +0 -552
  155. package/framework/qualia-framework/templates/roadmap.md +0 -206
  156. package/framework/qualia-framework/templates/state.md +0 -179
  157. package/framework/qualia-framework/templates/summary-complex.md +0 -59
  158. package/framework/qualia-framework/templates/summary-minimal.md +0 -41
  159. package/framework/qualia-framework/templates/summary-standard.md +0 -48
  160. package/framework/qualia-framework/templates/summary.md +0 -246
  161. package/framework/qualia-framework/templates/user-setup.md +0 -311
  162. package/framework/qualia-framework/templates/verification-report.md +0 -322
  163. package/framework/qualia-framework/workflows/add-phase.md +0 -179
  164. package/framework/qualia-framework/workflows/add-todo.md +0 -157
  165. package/framework/qualia-framework/workflows/audit-milestone.md +0 -241
  166. package/framework/qualia-framework/workflows/check-todos.md +0 -176
  167. package/framework/qualia-framework/workflows/complete-milestone.md +0 -858
  168. package/framework/qualia-framework/workflows/diagnose-issues.md +0 -219
  169. package/framework/qualia-framework/workflows/discovery-phase.md +0 -289
  170. package/framework/qualia-framework/workflows/discuss-phase.md +0 -534
  171. package/framework/qualia-framework/workflows/execute-phase.md +0 -559
  172. package/framework/qualia-framework/workflows/execute-plan.md +0 -438
  173. package/framework/qualia-framework/workflows/help.md +0 -470
  174. package/framework/qualia-framework/workflows/insert-phase.md +0 -220
  175. package/framework/qualia-framework/workflows/list-phase-assumptions.md +0 -178
  176. package/framework/qualia-framework/workflows/map-codebase.md +0 -327
  177. package/framework/qualia-framework/workflows/new-milestone.md +0 -363
  178. package/framework/qualia-framework/workflows/new-project.md +0 -982
  179. package/framework/qualia-framework/workflows/pause-work.md +0 -122
  180. package/framework/qualia-framework/workflows/plan-milestone-gaps.md +0 -256
  181. package/framework/qualia-framework/workflows/plan-phase.md +0 -422
  182. package/framework/qualia-framework/workflows/progress.md +0 -389
  183. package/framework/qualia-framework/workflows/quick.md +0 -252
  184. package/framework/qualia-framework/workflows/remove-phase.md +0 -326
  185. package/framework/qualia-framework/workflows/research-phase.md +0 -74
  186. package/framework/qualia-framework/workflows/resume-project.md +0 -306
  187. package/framework/qualia-framework/workflows/set-profile.md +0 -80
  188. package/framework/qualia-framework/workflows/settings.md +0 -145
  189. package/framework/qualia-framework/workflows/transition.md +0 -556
  190. package/framework/qualia-framework/workflows/update.md +0 -197
  191. package/framework/qualia-framework/workflows/verify-phase.md +0 -195
  192. package/framework/qualia-framework/workflows/verify-work.md +0 -625
  193. package/framework/rules/context7.md +0 -14
  194. package/framework/rules/frontend.md +0 -33
  195. package/framework/rules/speed.md +0 -23
  196. package/framework/scripts/__pycache__/say.cpython-314.pyc +0 -0
  197. package/framework/scripts/apply-retention.sh +0 -120
  198. package/framework/scripts/bootstrap-pop-os.sh +0 -354
  199. package/framework/scripts/claude-voice +0 -13
  200. package/framework/scripts/cleanup.sh +0 -131
  201. package/framework/scripts/cowork-mode.sh +0 -141
  202. package/framework/scripts/generate-project-claude-md.sh +0 -153
  203. package/framework/scripts/load-test-webhook.js +0 -172
  204. package/framework/scripts/say.py +0 -236
  205. package/framework/scripts/showcase-video-recorder/ffmpeg-builder.js +0 -167
  206. package/framework/scripts/showcase-video-recorder/playwright-helpers.js +0 -216
  207. package/framework/scripts/speak.py +0 -55
  208. package/framework/scripts/speak.sh +0 -18
  209. package/framework/scripts/status.sh +0 -138
  210. package/framework/scripts/sync-to-framework.sh +0 -65
  211. package/framework/scripts/voice-hotkey.py +0 -227
  212. package/framework/scripts/voice-input.sh +0 -51
  213. package/framework/skills/animate/SKILL.md +0 -202
  214. package/framework/skills/bolder/SKILL.md +0 -144
  215. package/framework/skills/browser-qa/SKILL.md +0 -536
  216. package/framework/skills/clarify/SKILL.md +0 -179
  217. package/framework/skills/client-handoff/SKILL.md +0 -135
  218. package/framework/skills/collab-onboard/SKILL.md +0 -111
  219. package/framework/skills/colorize/SKILL.md +0 -170
  220. package/framework/skills/critique/SKILL.md +0 -126
  221. package/framework/skills/deep-research/SKILL.md +0 -240
  222. package/framework/skills/delight/SKILL.md +0 -329
  223. package/framework/skills/deploy/SKILL.md +0 -261
  224. package/framework/skills/deploy-verify/SKILL.md +0 -377
  225. package/framework/skills/deploy-verify/scripts/canary-check.sh +0 -206
  226. package/framework/skills/deploy-verify/scripts/check-console-errors.js +0 -147
  227. package/framework/skills/deploy-verify/scripts/check-cwv.js +0 -139
  228. package/framework/skills/deploy-verify/scripts/project-detect.sh +0 -84
  229. package/framework/skills/deploy-verify/scripts/verify.sh +0 -548
  230. package/framework/skills/design-quieter/SKILL.md +0 -130
  231. package/framework/skills/distill/SKILL.md +0 -149
  232. package/framework/skills/docs-lookup/SKILL.md +0 -79
  233. package/framework/skills/fcm-notifications/SKILL.md +0 -125
  234. package/framework/skills/financial-ledger/SKILL.md +0 -1039
  235. package/framework/skills/frontend-master/NOTICE.md +0 -4
  236. package/framework/skills/frontend-master/SKILL.md +0 -127
  237. package/framework/skills/frontend-master/reference/color-and-contrast.md +0 -132
  238. package/framework/skills/frontend-master/reference/interaction-design.md +0 -123
  239. package/framework/skills/frontend-master/reference/motion-design.md +0 -99
  240. package/framework/skills/frontend-master/reference/responsive-design.md +0 -114
  241. package/framework/skills/frontend-master/reference/spatial-design.md +0 -100
  242. package/framework/skills/frontend-master/reference/typography.md +0 -131
  243. package/framework/skills/frontend-master/reference/ux-writing.md +0 -107
  244. package/framework/skills/harden/SKILL.md +0 -357
  245. package/framework/skills/i18n-rtl/SKILL.md +0 -752
  246. package/framework/skills/learn/SKILL.md +0 -95
  247. package/framework/skills/memory/SKILL.md +0 -50
  248. package/framework/skills/mobile-expo/SKILL.md +0 -977
  249. package/framework/skills/mobile-expo/references/store-checklist.md +0 -550
  250. package/framework/skills/nestjs-backend/README.md +0 -73
  251. package/framework/skills/nestjs-backend/SKILL.md +0 -446
  252. package/framework/skills/nestjs-backend/references/templates.md +0 -1173
  253. package/framework/skills/normalize/SKILL.md +0 -79
  254. package/framework/skills/onboard/SKILL.md +0 -242
  255. package/framework/skills/openrouter-agent/SKILL.md +0 -922
  256. package/framework/skills/polish/SKILL.md +0 -209
  257. package/framework/skills/pr/SKILL.md +0 -66
  258. package/framework/skills/qualia/SKILL.md +0 -199
  259. package/framework/skills/qualia-add-todo/SKILL.md +0 -68
  260. package/framework/skills/qualia-audit-milestone/SKILL.md +0 -95
  261. package/framework/skills/qualia-check-todos/SKILL.md +0 -55
  262. package/framework/skills/qualia-complete-milestone/SKILL.md +0 -134
  263. package/framework/skills/qualia-debug/SKILL.md +0 -149
  264. package/framework/skills/qualia-design/SKILL.md +0 -203
  265. package/framework/skills/qualia-discuss-phase/SKILL.md +0 -72
  266. package/framework/skills/qualia-evolve/SKILL.md +0 -200
  267. package/framework/skills/qualia-execute-phase/SKILL.md +0 -89
  268. package/framework/skills/qualia-framework-audit/SKILL.md +0 -604
  269. package/framework/skills/qualia-guide/SKILL.md +0 -32
  270. package/framework/skills/qualia-help/SKILL.md +0 -114
  271. package/framework/skills/qualia-idk/SKILL.md +0 -352
  272. package/framework/skills/qualia-list-phase-assumptions/SKILL.md +0 -67
  273. package/framework/skills/qualia-new-milestone/SKILL.md +0 -72
  274. package/framework/skills/qualia-new-project/SKILL.md +0 -232
  275. package/framework/skills/qualia-pause-work/SKILL.md +0 -96
  276. package/framework/skills/qualia-plan-milestone-gaps/SKILL.md +0 -57
  277. package/framework/skills/qualia-plan-phase/SKILL.md +0 -104
  278. package/framework/skills/qualia-production-check/SKILL.md +0 -0
  279. package/framework/skills/qualia-progress/SKILL.md +0 -53
  280. package/framework/skills/qualia-quick/SKILL.md +0 -89
  281. package/framework/skills/qualia-report/SKILL.md +0 -166
  282. package/framework/skills/qualia-research-phase/SKILL.md +0 -88
  283. package/framework/skills/qualia-resume-work/SKILL.md +0 -62
  284. package/framework/skills/qualia-review/SKILL.md +0 -263
  285. package/framework/skills/qualia-start/SKILL.md +0 -161
  286. package/framework/skills/qualia-verify-work/SKILL.md +0 -132
  287. package/framework/skills/rag/SKILL.md +0 -750
  288. package/framework/skills/responsive/SKILL.md +0 -231
  289. package/framework/skills/retro/SKILL.md +0 -284
  290. package/framework/skills/sakani-conventions/SKILL.md +0 -136
  291. package/framework/skills/sakani-conventions/evals/evals.json +0 -23
  292. package/framework/skills/sakani-conventions/references/entities.md +0 -365
  293. package/framework/skills/sakani-conventions/references/error-codes.md +0 -95
  294. package/framework/skills/seo-master/SKILL.md +0 -490
  295. package/framework/skills/seo-master/references/checklist.md +0 -199
  296. package/framework/skills/seo-master/references/structured-data.md +0 -609
  297. package/framework/skills/ship/SKILL.md +0 -239
  298. package/framework/skills/stack-researcher/SKILL.md +0 -215
  299. package/framework/skills/status/SKILL.md +0 -154
  300. package/framework/skills/status/scripts/health-check.sh +0 -562
  301. package/framework/skills/subscription-payments/SKILL.md +0 -250
  302. package/framework/skills/supabase/SKILL.md +0 -973
  303. package/framework/skills/supabase/references/templates.md +0 -159
  304. package/framework/skills/team/SKILL.md +0 -67
  305. package/framework/skills/test-runner/SKILL.md +0 -202
  306. package/framework/skills/voice-agent/SKILL.md +0 -1312
  307. package/framework/skills/zoho-workflow/SKILL.md +0 -51
  308. package/framework/statusline-command.sh +0 -117
  309. package/framework/teams/default/inboxes/plan-04.json +0 -9
  310. package/framework/teams/review-team.md +0 -75
  311. package/framework/teams/ship-team.md +0 -86
  312. package/profiles/fawzi.json +0 -16
  313. package/profiles/hasan.json +0 -16
  314. package/profiles/moayad.json +0 -16
  315. package/templates/CLAUDE-owner.md +0 -52
  316. package/templates/CLAUDE.md.hbs +0 -58
  317. package/templates/env.claude.template +0 -12
  318. package/templates/settings.json +0 -172
  319. package/uninstall.sh +0 -90
  320. /package/{framework/rules → rules}/deployment.md +0 -0
  321. /package/{framework/rules → rules}/security.md +0 -0
@@ -0,0 +1,535 @@
1
+ #!/bin/bash
2
+ # Qualia Framework v2 — state.js behavioral tests
3
+ # Run: bash tests/state.test.sh
4
+
5
+ PASS=0
6
+ FAIL=0
7
+ # Resolve STATE_JS to an ABSOLUTE path so `cd` inside subshells doesn't break it.
8
+ STATE_JS="$(cd "$(dirname "$0")/../bin" && pwd)/state.js"
9
+ NODE="${NODE:-node}"
10
+
11
+ # Track tmp dirs we create so we can clean them up on exit
12
+ TMP_DIRS=()
13
+ cleanup() {
14
+ for d in "${TMP_DIRS[@]}"; do
15
+ [ -d "$d" ] && rm -rf "$d"
16
+ done
17
+ }
18
+ trap cleanup EXIT
19
+
20
+ # Make a fresh temp project with 2 phases, already initialized.
21
+ # Prints the absolute path to the new tmp dir (does NOT cd).
22
+ make_project() {
23
+ local TMP
24
+ TMP=$(mktemp -d)
25
+ TMP_DIRS+=("$TMP")
26
+ (
27
+ cd "$TMP" || exit 1
28
+ $NODE "$STATE_JS" init \
29
+ --project "TestProject" \
30
+ --phases '[{"name":"Foundation","goal":"Auth"},{"name":"Core","goal":"Features"}]' \
31
+ >/dev/null 2>&1
32
+ )
33
+ echo "$TMP"
34
+ }
35
+
36
+ # pass "name" — record a passing assertion
37
+ pass() {
38
+ echo " ✓ $1"
39
+ PASS=$((PASS + 1))
40
+ }
41
+
42
+ # fail "name" "detail"
43
+ fail_case() {
44
+ echo " ✗ $1${2:+ — $2}"
45
+ FAIL=$((FAIL + 1))
46
+ }
47
+
48
+ echo "=== state.js Behavioral Tests ==="
49
+ echo ""
50
+
51
+ # Sanity check
52
+ if [ ! -f "$STATE_JS" ]; then
53
+ echo "FATAL: state.js not found at $STATE_JS"
54
+ exit 1
55
+ fi
56
+
57
+ # ─── Basic I/O ───────────────────────────────────────────
58
+ echo "basic I/O:"
59
+
60
+ # 1. cmdInit produces valid tracking.json + STATE.md
61
+ TMP=$(mktemp -d); TMP_DIRS+=("$TMP")
62
+ (
63
+ cd "$TMP" || exit 1
64
+ $NODE "$STATE_JS" init \
65
+ --project "TestProject" \
66
+ --phases '[{"name":"Foundation","goal":"Auth"},{"name":"Core","goal":"Features"}]' \
67
+ >/tmp/qualia-state-test.out 2>&1
68
+ )
69
+ INIT_EXIT=$?
70
+ if [ "$INIT_EXIT" -eq 0 ] \
71
+ && [ -f "$TMP/.planning/tracking.json" ] \
72
+ && [ -f "$TMP/.planning/STATE.md" ] \
73
+ && grep -q '"ok": true' /tmp/qualia-state-test.out \
74
+ && grep -q '"action": "init"' /tmp/qualia-state-test.out; then
75
+ pass "cmdInit creates tracking.json + STATE.md"
76
+ else
77
+ fail_case "cmdInit creates tracking.json + STATE.md" "exit=$INIT_EXIT"
78
+ fi
79
+
80
+ # tracking.json content sanity
81
+ if grep -q '"project": "TestProject"' "$TMP/.planning/tracking.json" \
82
+ && grep -q '"total_phases": 2' "$TMP/.planning/tracking.json" \
83
+ && grep -q '"phase": 1' "$TMP/.planning/tracking.json" \
84
+ && grep -q '"status": "setup"' "$TMP/.planning/tracking.json"; then
85
+ pass "cmdInit tracking.json has correct fields"
86
+ else
87
+ fail_case "cmdInit tracking.json fields"
88
+ fi
89
+
90
+ # STATE.md content sanity
91
+ if grep -q 'Phase: 1 of 2 — Foundation' "$TMP/.planning/STATE.md" \
92
+ && grep -q 'Status: setup' "$TMP/.planning/STATE.md"; then
93
+ pass "cmdInit STATE.md has correct header"
94
+ else
95
+ fail_case "cmdInit STATE.md header"
96
+ fi
97
+
98
+ # 2. cmdCheck reads back init state
99
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
100
+ CHECK_EXIT=$?
101
+ if [ "$CHECK_EXIT" -eq 0 ] \
102
+ && echo "$OUT" | grep -q '"ok": true' \
103
+ && echo "$OUT" | grep -q '"phase": 1' \
104
+ && echo "$OUT" | grep -q '"status": "setup"' \
105
+ && echo "$OUT" | grep -q '"total_phases": 2'; then
106
+ pass "cmdCheck returns phase=1 status=setup total_phases=2"
107
+ else
108
+ fail_case "cmdCheck returns init state" "exit=$CHECK_EXIT"
109
+ fi
110
+
111
+ # 3. cmdCheck with no project → ok:false NO_PROJECT, exit 1
112
+ TMP2=$(mktemp -d); TMP_DIRS+=("$TMP2")
113
+ OUT=$(cd "$TMP2" && $NODE "$STATE_JS" check 2>&1)
114
+ CHECK_EXIT=$?
115
+ if [ "$CHECK_EXIT" -eq 1 ] \
116
+ && echo "$OUT" | grep -q '"ok": false' \
117
+ && echo "$OUT" | grep -q '"error": "NO_PROJECT"'; then
118
+ pass "cmdCheck without .planning → NO_PROJECT, exit 1"
119
+ else
120
+ fail_case "cmdCheck NO_PROJECT" "exit=$CHECK_EXIT"
121
+ fi
122
+
123
+ # ─── Happy path transitions ──────────────────────────────
124
+ echo ""
125
+ echo "happy path transitions:"
126
+
127
+ # 4. setup → planned (with plan file)
128
+ TMP=$(make_project)
129
+ touch "$TMP/.planning/phase-1-plan.md"
130
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
131
+ EXIT=$?
132
+ if [ "$EXIT" -eq 0 ] \
133
+ && echo "$OUT" | grep -q '"ok": true' \
134
+ && echo "$OUT" | grep -q '"status": "planned"' \
135
+ && echo "$OUT" | grep -q '"previous_status": "setup"'; then
136
+ pass "setup → planned succeeds with plan file"
137
+ else
138
+ fail_case "setup → planned" "exit=$EXIT out=$OUT"
139
+ fi
140
+
141
+ # 5. planned → built (records tasks_done/tasks_total)
142
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 5 --tasks-total 5 2>&1)
143
+ EXIT=$?
144
+ if [ "$EXIT" -eq 0 ] \
145
+ && echo "$OUT" | grep -q '"ok": true' \
146
+ && echo "$OUT" | grep -q '"status": "built"' \
147
+ && grep -q '"tasks_done": 5' "$TMP/.planning/tracking.json" \
148
+ && grep -q '"tasks_total": 5' "$TMP/.planning/tracking.json"; then
149
+ pass "planned → built records tasks_done/tasks_total"
150
+ else
151
+ fail_case "planned → built" "exit=$EXIT"
152
+ fi
153
+
154
+ # 6. built → verified(pass) auto-advances to phase 2, resets status to setup
155
+ touch "$TMP/.planning/phase-1-verification.md"
156
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
157
+ EXIT=$?
158
+ if [ "$EXIT" -eq 0 ] \
159
+ && echo "$OUT" | grep -q '"ok": true' \
160
+ && echo "$OUT" | grep -q '"phase": 2' \
161
+ && echo "$OUT" | grep -q '"status": "setup"'; then
162
+ pass "built → verified(pass) auto-advances phase and resets to setup"
163
+ else
164
+ fail_case "built → verified(pass) auto-advance" "exit=$EXIT out=$OUT"
165
+ fi
166
+
167
+ # 7. built → verified(fail) stays on phase 1, records verification=fail
168
+ TMP=$(make_project)
169
+ touch "$TMP/.planning/phase-1-plan.md"
170
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
171
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 3 --tasks-total 5 >/dev/null 2>&1)
172
+ touch "$TMP/.planning/phase-1-verification.md"
173
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail 2>&1)
174
+ EXIT=$?
175
+ if [ "$EXIT" -eq 0 ] \
176
+ && echo "$OUT" | grep -q '"ok": true' \
177
+ && echo "$OUT" | grep -q '"phase": 1' \
178
+ && echo "$OUT" | grep -q '"status": "verified"' \
179
+ && echo "$OUT" | grep -q '"verification": "fail"'; then
180
+ pass "built → verified(fail) stays on phase 1"
181
+ else
182
+ fail_case "built → verified(fail)" "exit=$EXIT out=$OUT"
183
+ fi
184
+
185
+ # ─── Precondition failures ───────────────────────────────
186
+ echo ""
187
+ echo "precondition failures:"
188
+
189
+ # 8. setup → built fails with PRECONDITION_FAILED
190
+ TMP=$(make_project)
191
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to built 2>&1)
192
+ EXIT=$?
193
+ if [ "$EXIT" -eq 1 ] \
194
+ && echo "$OUT" | grep -q '"ok": false' \
195
+ && echo "$OUT" | grep -q '"error": "PRECONDITION_FAILED"' \
196
+ && echo "$OUT" | grep -q "Cannot go from 'setup' to 'built'"; then
197
+ pass "setup → built fails with PRECONDITION_FAILED"
198
+ else
199
+ fail_case "setup → built precondition" "exit=$EXIT out=$OUT"
200
+ fi
201
+
202
+ # 9. planned → verified fails (requires status=built)
203
+ TMP=$(make_project)
204
+ touch "$TMP/.planning/phase-1-plan.md"
205
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
206
+ touch "$TMP/.planning/phase-1-verification.md"
207
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
208
+ EXIT=$?
209
+ if [ "$EXIT" -eq 1 ] \
210
+ && echo "$OUT" | grep -q '"error": "PRECONDITION_FAILED"' \
211
+ && echo "$OUT" | grep -q "Cannot go from 'planned' to 'verified'"; then
212
+ pass "planned → verified fails (requires built)"
213
+ else
214
+ fail_case "planned → verified precondition" "exit=$EXIT out=$OUT"
215
+ fi
216
+
217
+ # 10. planned with missing plan file → MISSING_FILE
218
+ TMP=$(make_project)
219
+ # no phase-1-plan.md created
220
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
221
+ EXIT=$?
222
+ if [ "$EXIT" -eq 1 ] \
223
+ && echo "$OUT" | grep -q '"error": "MISSING_FILE"' \
224
+ && echo "$OUT" | grep -q "phase-1-plan.md"; then
225
+ pass "setup → planned fails without plan file (MISSING_FILE)"
226
+ else
227
+ fail_case "setup → planned MISSING_FILE" "exit=$EXIT out=$OUT"
228
+ fi
229
+
230
+ # 11. built → verified with missing verification file → MISSING_FILE
231
+ TMP=$(make_project)
232
+ touch "$TMP/.planning/phase-1-plan.md"
233
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
234
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
235
+ # NO verification file
236
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
237
+ EXIT=$?
238
+ if [ "$EXIT" -eq 1 ] \
239
+ && echo "$OUT" | grep -q '"error": "MISSING_FILE"' \
240
+ && echo "$OUT" | grep -q "phase-1-verification.md"; then
241
+ pass "built → verified fails without verification file (MISSING_FILE)"
242
+ else
243
+ fail_case "built → verified MISSING_FILE" "exit=$EXIT out=$OUT"
244
+ fi
245
+
246
+ # 12. built → verified without --verification → MISSING_ARG
247
+ TMP=$(make_project)
248
+ touch "$TMP/.planning/phase-1-plan.md"
249
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
250
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
251
+ touch "$TMP/.planning/phase-1-verification.md"
252
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified 2>&1)
253
+ EXIT=$?
254
+ if [ "$EXIT" -eq 1 ] \
255
+ && echo "$OUT" | grep -q '"error": "MISSING_ARG"' \
256
+ && echo "$OUT" | grep -q "verification"; then
257
+ pass "built → verified without --verification → MISSING_ARG"
258
+ else
259
+ fail_case "built → verified MISSING_ARG" "exit=$EXIT out=$OUT"
260
+ fi
261
+
262
+ # 13. → shipped without --deployed-url → MISSING_ARG
263
+ # Must go through polished first, so fabricate state by transitioning through the full path.
264
+ TMP=$(make_project)
265
+ touch "$TMP/.planning/phase-1-plan.md"
266
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
267
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
268
+ touch "$TMP/.planning/phase-1-verification.md"
269
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass >/dev/null 2>&1)
270
+ # Now on phase 2, status=setup. Run phase 2 to completion.
271
+ touch "$TMP/.planning/phase-2-plan.md"
272
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
273
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
274
+ touch "$TMP/.planning/phase-2-verification.md"
275
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass >/dev/null 2>&1)
276
+ # Status should now be "verified" on last phase (no auto-advance past last phase)
277
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to polished >/dev/null 2>&1)
278
+ # Now try ship without deployed-url
279
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to shipped 2>&1)
280
+ EXIT=$?
281
+ if [ "$EXIT" -eq 1 ] \
282
+ && echo "$OUT" | grep -q '"error": "MISSING_ARG"' \
283
+ && echo "$OUT" | grep -q "deployed-url"; then
284
+ pass "→ shipped without --deployed-url → MISSING_ARG"
285
+ else
286
+ fail_case "→ shipped MISSING_ARG" "exit=$EXIT out=$OUT"
287
+ fi
288
+
289
+ # 14. Unknown target --to frobnicate → INVALID_STATUS
290
+ TMP=$(make_project)
291
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to frobnicate 2>&1)
292
+ EXIT=$?
293
+ if [ "$EXIT" -eq 1 ] \
294
+ && echo "$OUT" | grep -q '"error": "INVALID_STATUS"'; then
295
+ pass "--to frobnicate → INVALID_STATUS"
296
+ else
297
+ fail_case "invalid target" "exit=$EXIT out=$OUT"
298
+ fi
299
+
300
+ # ─── Gap cycle circuit breaker ───────────────────────────
301
+ echo ""
302
+ echo "gap cycle circuit breaker:"
303
+
304
+ # 15. First gap closure: verified(fail) → planned, gap_cycles[1]=1
305
+ TMP=$(make_project)
306
+ touch "$TMP/.planning/phase-1-plan.md"
307
+ touch "$TMP/.planning/phase-1-verification.md"
308
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
309
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
310
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
311
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
312
+ EXIT=$?
313
+ if [ "$EXIT" -eq 0 ] \
314
+ && echo "$OUT" | grep -q '"ok": true' \
315
+ && echo "$OUT" | grep -q '"gap_cycles": 1'; then
316
+ pass "first gap closure: verified(fail) → planned, gap_cycles=1"
317
+ else
318
+ fail_case "first gap closure" "exit=$EXIT out=$OUT"
319
+ fi
320
+
321
+ # 16. Second gap closure: gap_cycles[1]=2
322
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
323
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
324
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
325
+ EXIT=$?
326
+ if [ "$EXIT" -eq 0 ] \
327
+ && echo "$OUT" | grep -q '"ok": true' \
328
+ && echo "$OUT" | grep -q '"gap_cycles": 2'; then
329
+ pass "second gap closure: gap_cycles=2"
330
+ else
331
+ fail_case "second gap closure" "exit=$EXIT out=$OUT"
332
+ fi
333
+
334
+ # 17. Third gap closure attempt → GAP_CYCLE_LIMIT
335
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
336
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
337
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
338
+ EXIT=$?
339
+ if [ "$EXIT" -eq 1 ] \
340
+ && echo "$OUT" | grep -q '"error": "GAP_CYCLE_LIMIT"'; then
341
+ pass "third gap closure attempt blocked (GAP_CYCLE_LIMIT)"
342
+ else
343
+ fail_case "gap cycle limit" "exit=$EXIT out=$OUT"
344
+ fi
345
+
346
+ # 18. verified(pass) resets gap_cycles[1] to 0
347
+ # Set up a fresh project, do ONE failed cycle, then pass on the next attempt.
348
+ TMP=$(make_project)
349
+ touch "$TMP/.planning/phase-1-plan.md"
350
+ touch "$TMP/.planning/phase-1-verification.md"
351
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
352
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
353
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
354
+ # gap_cycles[1] is now 0 before the gap closure; becomes 1 after
355
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
356
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
357
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass >/dev/null 2>&1)
358
+ # After pass, gap_cycles[1] should be reset to 0 in tracking.json
359
+ if grep -q '"1": 0' "$TMP/.planning/tracking.json"; then
360
+ pass "verified(pass) resets gap_cycles[1] to 0"
361
+ else
362
+ fail_case "gap cycle reset on pass"
363
+ fi
364
+
365
+ # ─── Special transitions ─────────────────────────────────
366
+ echo ""
367
+ echo "special transitions:"
368
+
369
+ # 19. --to note --notes "foo" succeeds, records notes
370
+ TMP=$(make_project)
371
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to note --notes "hello world" 2>&1)
372
+ EXIT=$?
373
+ if [ "$EXIT" -eq 0 ] \
374
+ && echo "$OUT" | grep -q '"ok": true' \
375
+ && echo "$OUT" | grep -q '"action": "note"' \
376
+ && echo "$OUT" | grep -q '"status": "setup"' \
377
+ && grep -q '"notes": "hello world"' "$TMP/.planning/tracking.json"; then
378
+ pass "--to note records notes, status unchanged"
379
+ else
380
+ fail_case "--to note" "exit=$EXIT out=$OUT"
381
+ fi
382
+
383
+ # 20. --to activity succeeds without status change
384
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to activity 2>&1)
385
+ EXIT=$?
386
+ if [ "$EXIT" -eq 0 ] \
387
+ && echo "$OUT" | grep -q '"ok": true' \
388
+ && echo "$OUT" | grep -q '"action": "activity"' \
389
+ && echo "$OUT" | grep -q '"status": "setup"'; then
390
+ pass "--to activity succeeds without status change"
391
+ else
392
+ fail_case "--to activity" "exit=$EXIT out=$OUT"
393
+ fi
394
+
395
+ # ─── Parse schema errors ─────────────────────────────────
396
+ echo ""
397
+ echo "parse schema errors:"
398
+
399
+ # 21. Well-formed STATE.md: no schema_errors field in check output
400
+ TMP=$(make_project)
401
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
402
+ EXIT=$?
403
+ if [ "$EXIT" -eq 0 ] \
404
+ && echo "$OUT" | grep -q '"ok": true' \
405
+ && ! echo "$OUT" | grep -q 'schema_errors'; then
406
+ pass "well-formed STATE.md: check has no schema_errors"
407
+ else
408
+ fail_case "well-formed no schema_errors" "exit=$EXIT out=$OUT"
409
+ fi
410
+
411
+ # 22. Missing Phase: header → schema_errors with phase_header (error)
412
+ TMP=$(make_project)
413
+ sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
414
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
415
+ EXIT=$?
416
+ if [ "$EXIT" -eq 0 ] \
417
+ && echo "$OUT" | grep -q 'schema_errors' \
418
+ && echo "$OUT" | grep -q 'phase_header'; then
419
+ pass "missing Phase: header → schema_errors contains phase_header"
420
+ else
421
+ fail_case "missing phase header" "exit=$EXIT out=$OUT"
422
+ fi
423
+
424
+ # 23. Missing roadmap table header → schema_errors with roadmap_table
425
+ TMP=$(make_project)
426
+ sed -i.bak '/^| # | Phase | Goal | Status |$/d' "$TMP/.planning/STATE.md"
427
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
428
+ EXIT=$?
429
+ if [ "$EXIT" -eq 0 ] \
430
+ && echo "$OUT" | grep -q 'schema_errors' \
431
+ && echo "$OUT" | grep -q 'roadmap_table'; then
432
+ pass "missing roadmap table → schema_errors contains roadmap_table"
433
+ else
434
+ fail_case "missing roadmap_table" "exit=$EXIT out=$OUT"
435
+ fi
436
+
437
+ # 24. Missing Status: line → schema_errors warning status_field, ok:true
438
+ TMP=$(make_project)
439
+ sed -i.bak '/^Status:/d' "$TMP/.planning/STATE.md"
440
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
441
+ EXIT=$?
442
+ if [ "$EXIT" -eq 0 ] \
443
+ && echo "$OUT" | grep -q '"ok": true' \
444
+ && echo "$OUT" | grep -q 'schema_errors' \
445
+ && echo "$OUT" | grep -q 'status_field' \
446
+ && echo "$OUT" | grep -q '"severity": "warning"'; then
447
+ pass "missing Status: → warning status_field, ok:true"
448
+ else
449
+ fail_case "missing Status field" "exit=$EXIT out=$OUT"
450
+ fi
451
+
452
+ # 25. Roadmap row count mismatch → schema_errors warning roadmap_rows
453
+ # Hand-edit header to claim 3 phases when only 2 rows exist.
454
+ TMP=$(make_project)
455
+ sed -i.bak 's/^Phase: 1 of 2 — Foundation/Phase: 1 of 3 — Foundation/' "$TMP/.planning/STATE.md"
456
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
457
+ EXIT=$?
458
+ if [ "$EXIT" -eq 0 ] \
459
+ && echo "$OUT" | grep -q 'schema_errors' \
460
+ && echo "$OUT" | grep -q 'roadmap_rows'; then
461
+ pass "roadmap row count mismatch → warning roadmap_rows"
462
+ else
463
+ fail_case "roadmap row count mismatch" "exit=$EXIT out=$OUT"
464
+ fi
465
+
466
+ # 26. Transition refuses on severity=error (missing Phase: header)
467
+ TMP=$(make_project)
468
+ touch "$TMP/.planning/phase-1-plan.md"
469
+ sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
470
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
471
+ EXIT=$?
472
+ if [ "$EXIT" -eq 1 ] \
473
+ && echo "$OUT" | grep -q '"error": "STATE_SCHEMA_ERROR"'; then
474
+ pass "transition refused on severity=error (STATE_SCHEMA_ERROR)"
475
+ else
476
+ fail_case "transition STATE_SCHEMA_ERROR" "exit=$EXIT out=$OUT"
477
+ fi
478
+
479
+ # 27. fix rewrites malformed STATE.md into canonical form
480
+ TMP=$(make_project)
481
+ sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
482
+ # Confirm it's broken first
483
+ (cd "$TMP" && $NODE "$STATE_JS" check 2>&1 | grep -q schema_errors) || \
484
+ fail_case "fix pretest: check should show errors"
485
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" fix 2>&1)
486
+ EXIT=$?
487
+ OUT2=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
488
+ if [ "$EXIT" -eq 0 ] \
489
+ && echo "$OUT" | grep -q '"action": "fix"' \
490
+ && echo "$OUT" | grep -q '"fixed": true' \
491
+ && echo "$OUT" | grep -q '"previous_errors": 1' \
492
+ && ! echo "$OUT2" | grep -q 'schema_errors'; then
493
+ pass "fix repairs malformed STATE.md"
494
+ else
495
+ fail_case "fix repair" "exit=$EXIT fix=$OUT check=$OUT2"
496
+ fi
497
+
498
+ # 28. fix on well-formed STATE.md is a no-op (still parses clean)
499
+ TMP=$(make_project)
500
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" fix 2>&1)
501
+ EXIT=$?
502
+ OUT2=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
503
+ if [ "$EXIT" -eq 0 ] \
504
+ && echo "$OUT" | grep -q '"action": "fix"' \
505
+ && echo "$OUT" | grep -q '"previous_errors": 0' \
506
+ && ! echo "$OUT2" | grep -q 'schema_errors' \
507
+ && echo "$OUT2" | grep -q '"phase": 1' \
508
+ && echo "$OUT2" | grep -q '"total_phases": 2'; then
509
+ pass "fix on well-formed STATE.md is idempotent"
510
+ else
511
+ fail_case "fix idempotent" "exit=$EXIT fix=$OUT check=$OUT2"
512
+ fi
513
+
514
+ # 29. After fix, transition that was previously blocked now works
515
+ TMP=$(make_project)
516
+ touch "$TMP/.planning/phase-1-plan.md"
517
+ sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
518
+ # Blocked before fix
519
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1 | grep -q STATE_SCHEMA_ERROR) || \
520
+ fail_case "fix unblock pretest: should be blocked"
521
+ (cd "$TMP" && $NODE "$STATE_JS" fix >/dev/null 2>&1)
522
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
523
+ EXIT=$?
524
+ if [ "$EXIT" -eq 0 ] \
525
+ && echo "$OUT" | grep -q '"ok": true' \
526
+ && echo "$OUT" | grep -q '"status": "planned"'; then
527
+ pass "after fix, blocked transition succeeds"
528
+ else
529
+ fail_case "after fix transition" "exit=$EXIT out=$OUT"
530
+ fi
531
+
532
+ # ─── Summary ─────────────────────────────────────────────
533
+ echo ""
534
+ echo "=== Results: $PASS passed, $FAIL failed ==="
535
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1