qualia-framework 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,6 +45,34 @@ fail_case() {
45
45
  FAIL=$((FAIL + 1))
46
46
  }
47
47
 
48
+ # Write a minimal valid plan file (passes content validation).
49
+ # Usage: make_valid_plan "$TMP" 1
50
+ make_valid_plan() {
51
+ local dir="$1"
52
+ local phase="${2:-1}"
53
+ cat > "$dir/.planning/phase-${phase}-plan.md" <<'PLAN'
54
+ ---
55
+ phase: 1
56
+ goal: "Test goal"
57
+ tasks: 1
58
+ waves: 1
59
+ ---
60
+
61
+ # Phase 1: Test
62
+
63
+ Goal: Test goal
64
+
65
+ ## Task 1 — Test task
66
+ **Wave:** 1
67
+ **Files:** src/test.ts
68
+ **Action:** Create test file
69
+ **Done when:** File exists
70
+
71
+ ## Success Criteria
72
+ - [ ] Test passes
73
+ PLAN
74
+ }
75
+
48
76
  echo "=== state.js Behavioral Tests ==="
49
77
  echo ""
50
78
 
@@ -126,7 +154,7 @@ echo "happy path transitions:"
126
154
 
127
155
  # 4. setup → planned (with plan file)
128
156
  TMP=$(make_project)
129
- touch "$TMP/.planning/phase-1-plan.md"
157
+ make_valid_plan "$TMP" 1
130
158
  OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
131
159
  EXIT=$?
132
160
  if [ "$EXIT" -eq 0 ] \
@@ -166,7 +194,7 @@ fi
166
194
 
167
195
  # 7. built → verified(fail) stays on phase 1, records verification=fail
168
196
  TMP=$(make_project)
169
- touch "$TMP/.planning/phase-1-plan.md"
197
+ make_valid_plan "$TMP" 1
170
198
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
171
199
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 3 --tasks-total 5 >/dev/null 2>&1)
172
200
  touch "$TMP/.planning/phase-1-verification.md"
@@ -201,7 +229,7 @@ fi
201
229
 
202
230
  # 9. planned → verified fails (requires status=built)
203
231
  TMP=$(make_project)
204
- touch "$TMP/.planning/phase-1-plan.md"
232
+ make_valid_plan "$TMP" 1
205
233
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
206
234
  touch "$TMP/.planning/phase-1-verification.md"
207
235
  OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
@@ -229,7 +257,7 @@ fi
229
257
 
230
258
  # 11. built → verified with missing verification file → MISSING_FILE
231
259
  TMP=$(make_project)
232
- touch "$TMP/.planning/phase-1-plan.md"
260
+ make_valid_plan "$TMP" 1
233
261
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
234
262
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
235
263
  # NO verification file
@@ -245,7 +273,7 @@ fi
245
273
 
246
274
  # 12. built → verified without --verification → MISSING_ARG
247
275
  TMP=$(make_project)
248
- touch "$TMP/.planning/phase-1-plan.md"
276
+ make_valid_plan "$TMP" 1
249
277
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
250
278
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
251
279
  touch "$TMP/.planning/phase-1-verification.md"
@@ -262,13 +290,13 @@ fi
262
290
  # 13. → shipped without --deployed-url → MISSING_ARG
263
291
  # Must go through polished first, so fabricate state by transitioning through the full path.
264
292
  TMP=$(make_project)
265
- touch "$TMP/.planning/phase-1-plan.md"
293
+ make_valid_plan "$TMP" 1
266
294
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
267
295
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
268
296
  touch "$TMP/.planning/phase-1-verification.md"
269
297
  (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass >/dev/null 2>&1)
270
298
  # Now on phase 2, status=setup. Run phase 2 to completion.
271
- touch "$TMP/.planning/phase-2-plan.md"
299
+ make_valid_plan "$TMP" 2
272
300
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
273
301
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
274
302
  touch "$TMP/.planning/phase-2-verification.md"
@@ -303,7 +331,7 @@ echo "gap cycle circuit breaker:"
303
331
 
304
332
  # 15. First gap closure: verified(fail) → planned, gap_cycles[1]=1
305
333
  TMP=$(make_project)
306
- touch "$TMP/.planning/phase-1-plan.md"
334
+ make_valid_plan "$TMP" 1
307
335
  touch "$TMP/.planning/phase-1-verification.md"
308
336
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
309
337
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
@@ -346,7 +374,7 @@ fi
346
374
  # 18. verified(pass) resets gap_cycles[1] to 0
347
375
  # Set up a fresh project, do ONE failed cycle, then pass on the next attempt.
348
376
  TMP=$(make_project)
349
- touch "$TMP/.planning/phase-1-plan.md"
377
+ make_valid_plan "$TMP" 1
350
378
  touch "$TMP/.planning/phase-1-verification.md"
351
379
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
352
380
  (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
@@ -465,7 +493,7 @@ fi
465
493
 
466
494
  # 26. Transition refuses on severity=error (missing Phase: header)
467
495
  TMP=$(make_project)
468
- touch "$TMP/.planning/phase-1-plan.md"
496
+ make_valid_plan "$TMP" 1
469
497
  sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
470
498
  OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
471
499
  EXIT=$?
@@ -513,7 +541,7 @@ fi
513
541
 
514
542
  # 29. After fix, transition that was previously blocked now works
515
543
  TMP=$(make_project)
516
- touch "$TMP/.planning/phase-1-plan.md"
544
+ make_valid_plan "$TMP" 1
517
545
  sed -i.bak '/^Phase:/d' "$TMP/.planning/STATE.md"
518
546
  # Blocked before fix
519
547
  (cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1 | grep -q STATE_SCHEMA_ERROR) || \
@@ -529,6 +557,156 @@ else
529
557
  fail_case "after fix transition" "exit=$EXIT out=$OUT"
530
558
  fi
531
559
 
560
+ # ─── Configurable gap cycle limit ────────────────────────
561
+ echo ""
562
+ echo "configurable gap cycle limit:"
563
+
564
+ # 30. gap_cycle_limit=5 allows 3rd gap closure (would fail at default 2)
565
+ TMP=$(make_project)
566
+ make_valid_plan "$TMP" 1
567
+ touch "$TMP/.planning/phase-1-verification.md"
568
+ # Set custom limit in tracking.json
569
+ TRACKING=$(cat "$TMP/.planning/tracking.json")
570
+ echo "$TRACKING" | $NODE -e "
571
+ const t = JSON.parse(require('fs').readFileSync(0,'utf8'));
572
+ t.gap_cycle_limit = 5;
573
+ process.stdout.write(JSON.stringify(t, null, 2));
574
+ " > "$TMP/.planning/tracking.json"
575
+ # Do 3 gap closure cycles
576
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
577
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
578
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
579
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to planned >/dev/null 2>&1)
580
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to built --tasks-done 1 --tasks-total 1 >/dev/null 2>&1)
581
+ (cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification fail >/dev/null 2>&1)
582
+ # 3rd closure should succeed (limit is 5, we're at 2)
583
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
584
+ EXIT=$?
585
+ if [ "$EXIT" -eq 0 ] \
586
+ && echo "$OUT" | grep -q '"ok": true'; then
587
+ pass "gap_cycle_limit=5 allows 3rd closure (default would block)"
588
+ else
589
+ fail_case "custom gap limit" "exit=$EXIT out=$OUT"
590
+ fi
591
+
592
+ # 31. cmdCheck includes gap_cycle_limit in output
593
+ TMP=$(make_project)
594
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" check 2>&1)
595
+ if echo "$OUT" | grep -q '"gap_cycle_limit":'; then
596
+ pass "cmdCheck includes gap_cycle_limit in output"
597
+ else
598
+ fail_case "gap_cycle_limit in check" "out=$OUT"
599
+ fi
600
+
601
+ # ─── Plan content validation ────────────────────────────
602
+ echo ""
603
+ echo "plan content validation:"
604
+
605
+ # 32. validate-plan accepts well-formed plan
606
+ TMP=$(make_project)
607
+ make_valid_plan "$TMP" 1
608
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 2>&1)
609
+ EXIT=$?
610
+ if [ "$EXIT" -eq 0 ] \
611
+ && echo "$OUT" | grep -q '"action": "validate-plan"' \
612
+ && echo "$OUT" | grep -q '"task_count": 1'; then
613
+ pass "validate-plan accepts well-formed plan"
614
+ else
615
+ fail_case "validate well-formed plan" "exit=$EXIT out=$OUT"
616
+ fi
617
+
618
+ # 33. validate-plan rejects empty plan
619
+ TMP=$(make_project)
620
+ echo "" > "$TMP/.planning/phase-1-plan.md"
621
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 2>&1)
622
+ EXIT=$?
623
+ if [ "$EXIT" -eq 1 ] \
624
+ && echo "$OUT" | grep -q '"error": "PLAN_VALIDATION_FAILED"'; then
625
+ pass "validate-plan rejects empty plan"
626
+ else
627
+ fail_case "validate empty plan" "exit=$EXIT out=$OUT"
628
+ fi
629
+
630
+ # 34. validate-plan rejects plan missing Done when
631
+ TMP=$(make_project)
632
+ cat > "$TMP/.planning/phase-1-plan.md" <<'EOF'
633
+ ---
634
+ phase: 1
635
+ goal: "Test"
636
+ tasks: 1
637
+ waves: 1
638
+ ---
639
+ ## Task 1 — Incomplete
640
+ **Wave:** 1
641
+ **Files:** test.ts
642
+ **Action:** Do something
643
+
644
+ ## Success Criteria
645
+ - [ ] Works
646
+ EOF
647
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 2>&1)
648
+ EXIT=$?
649
+ if [ "$EXIT" -eq 1 ] \
650
+ && echo "$OUT" | grep -q "PLAN_VALIDATION_FAILED" \
651
+ && echo "$OUT" | grep -q "Done when"; then
652
+ pass "validate-plan rejects plan missing 'Done when'"
653
+ else
654
+ fail_case "validate missing done-when" "exit=$EXIT out=$OUT"
655
+ fi
656
+
657
+ # 35. Transition to planned with invalid plan content → INVALID_PLAN
658
+ TMP=$(make_project)
659
+ echo "# Empty plan with no tasks" > "$TMP/.planning/phase-1-plan.md"
660
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned 2>&1)
661
+ EXIT=$?
662
+ if [ "$EXIT" -eq 1 ] \
663
+ && echo "$OUT" | grep -q '"error": "INVALID_PLAN"'; then
664
+ pass "transition → planned with invalid plan → INVALID_PLAN"
665
+ else
666
+ fail_case "transition invalid plan" "exit=$EXIT out=$OUT"
667
+ fi
668
+
669
+ # ─── Force flag ──────────────────────────────────────────
670
+ echo ""
671
+ echo "force flag:"
672
+
673
+ # 36. --force bypasses precondition failure
674
+ TMP=$(make_project)
675
+ # setup → built should fail (requires planned first)
676
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to built --force 2>&1)
677
+ EXIT=$?
678
+ if [ "$EXIT" -eq 0 ] \
679
+ && echo "$OUT" | grep -q '"ok": true' \
680
+ && echo "$OUT" | grep -q '"status": "built"'; then
681
+ pass "--force bypasses precondition (setup → built)"
682
+ else
683
+ fail_case "force flag" "exit=$EXIT out=$OUT"
684
+ fi
685
+
686
+ # 37. --force does NOT bypass MISSING_FILE (planned without plan file)
687
+ TMP=$(make_project)
688
+ # No plan file exists — force should NOT help
689
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned --force 2>&1)
690
+ EXIT=$?
691
+ if [ "$EXIT" -eq 1 ] \
692
+ && echo "$OUT" | grep -q '"error": "MISSING_FILE"'; then
693
+ pass "--force does NOT bypass MISSING_FILE"
694
+ else
695
+ fail_case "force vs MISSING_FILE" "exit=$EXIT out=$OUT"
696
+ fi
697
+
698
+ # 38. --force does NOT bypass INVALID_PLAN
699
+ TMP=$(make_project)
700
+ echo "# No tasks here" > "$TMP/.planning/phase-1-plan.md"
701
+ OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to planned --force 2>&1)
702
+ EXIT=$?
703
+ if [ "$EXIT" -eq 1 ] \
704
+ && echo "$OUT" | grep -q '"error": "INVALID_PLAN"'; then
705
+ pass "--force does NOT bypass INVALID_PLAN"
706
+ else
707
+ fail_case "force vs INVALID_PLAN" "exit=$EXIT out=$OUT"
708
+ fi
709
+
532
710
  # ─── Summary ─────────────────────────────────────────────
533
711
  echo ""
534
712
  echo "=== Results: $PASS passed, $FAIL failed ==="