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.
- package/CLAUDE.md +3 -4
- package/README.md +10 -5
- package/agents/planner.md +52 -0
- package/agents/verifier.md +180 -32
- package/bin/cli.js +403 -9
- package/bin/install.js +118 -65
- package/bin/qualia-ui.js +11 -11
- package/bin/state.js +200 -6
- package/bin/statusline.js +4 -4
- package/docs/erp-contract.md +161 -0
- package/hooks/branch-guard.js +23 -2
- package/hooks/migration-guard.js +23 -0
- package/hooks/pre-compact.js +20 -0
- package/hooks/pre-deploy-gate.js +39 -0
- package/hooks/pre-push.js +20 -0
- package/hooks/session-start.js +16 -43
- package/package.json +5 -4
- package/rules/infrastructure.md +87 -0
- package/skills/qualia/SKILL.md +1 -0
- package/skills/qualia-build/SKILL.md +18 -0
- package/skills/qualia-design/SKILL.md +14 -8
- package/skills/qualia-help/SKILL.md +60 -0
- package/skills/qualia-learn/SKILL.md +27 -4
- package/skills/qualia-polish/SKILL.md +167 -117
- package/skills/qualia-report/SKILL.md +17 -8
- package/skills/qualia-review/SKILL.md +126 -41
- package/skills/qualia-test/SKILL.md +134 -0
- package/skills/qualia-verify/SKILL.md +1 -1
- package/templates/DESIGN.md +440 -102
- package/templates/help.html +476 -0
- package/templates/plan.md +14 -0
- package/tests/bin.test.sh +20 -6
- package/tests/hooks.test.sh +76 -7
- package/tests/runner.js +1915 -0
- package/tests/state.test.sh +189 -11
package/tests/state.test.sh
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ==="
|