qualia-framework 6.2.10 → 6.3.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.
- package/AGENTS.md +1 -0
- package/CLAUDE.md +1 -0
- package/README.md +16 -23
- package/bin/cli.js +49 -2
- package/bin/command-surface.js +71 -0
- package/bin/harness-eval.js +296 -0
- package/bin/install.js +17 -20
- package/bin/knowledge-flush.js +21 -10
- package/bin/knowledge.js +1 -1
- package/bin/project-snapshot.js +20 -0
- package/bin/report-payload.js +18 -0
- package/bin/runtime-manifest.js +3 -0
- package/bin/state.js +31 -0
- package/bin/trust-score.js +3 -11
- package/bin/work-packet.js +228 -0
- package/docs/erp-contract.md +81 -1
- package/docs/onboarding.html +0 -11
- package/guide.md +14 -15
- package/hooks/fawzi-approval-guard.js +143 -0
- package/hooks/pre-deploy-gate.js +74 -1
- package/hooks/session-start.js +29 -1
- package/package.json +1 -1
- package/qualia-design/frontend.md +2 -2
- package/rules/codex-goal.md +1 -1
- package/rules/one-opinion.md +2 -2
- package/rules/speed.md +0 -1
- package/skills/qualia/SKILL.md +4 -4
- package/skills/qualia-feature/SKILL.md +1 -1
- package/skills/qualia-fix/SKILL.md +4 -4
- package/skills/qualia-learn/SKILL.md +1 -1
- package/skills/qualia-polish/REFERENCE.md +1 -1
- package/skills/qualia-polish/SKILL.md +19 -4
- package/skills/{qualia-vibe/scripts/extract.mjs → qualia-polish/scripts/vibe-extract.mjs} +4 -4
- package/skills/{qualia-vibe/scripts/tokens.mjs → qualia-polish/scripts/vibe-tokens.mjs} +6 -6
- package/skills/qualia-road/SKILL.md +15 -20
- package/skills/qualia-ship/SKILL.md +12 -5
- package/skills/qualia-verify/SKILL.md +9 -1
- package/templates/help.html +1 -12
- package/tests/bin.test.sh +144 -72
- package/tests/hooks.test.sh +81 -1
- package/tests/install-smoke.test.sh +13 -3
- package/tests/lib.test.sh +145 -3
- package/tests/published-install-smoke.test.sh +4 -3
- package/tests/refs.test.sh +9 -4
- package/tests/runner.js +29 -28
- package/tests/state.test.sh +68 -0
- package/skills/qualia-debug/SKILL.md +0 -193
- package/skills/qualia-flush/SKILL.md +0 -198
- package/skills/qualia-help/SKILL.md +0 -74
- package/skills/qualia-hook-gen/SKILL.md +0 -206
- package/skills/qualia-idk/SKILL.md +0 -166
- package/skills/qualia-issues/SKILL.md +0 -151
- package/skills/qualia-pause/SKILL.md +0 -68
- package/skills/qualia-resume/SKILL.md +0 -52
- package/skills/qualia-skill-new/SKILL.md +0 -173
- package/skills/qualia-triage/SKILL.md +0 -152
- package/skills/qualia-vibe/SKILL.md +0 -229
- package/skills/qualia-zoom/SKILL.md +0 -51
package/tests/bin.test.sh
CHANGED
|
@@ -137,7 +137,7 @@ TMP=$(mktmp)
|
|
|
137
137
|
mkdir -p "$TMP/.claude"
|
|
138
138
|
cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
139
139
|
{
|
|
140
|
-
"code": "QS-FAWZI-
|
|
140
|
+
"code": "QS-FAWZI-11",
|
|
141
141
|
"installed_by": "Fawzi Goussous",
|
|
142
142
|
"role": "OWNER",
|
|
143
143
|
"version": "2.8.1",
|
|
@@ -421,7 +421,7 @@ TMP=$(mktmp)
|
|
|
421
421
|
mkdir -p "$TMP/.claude"
|
|
422
422
|
cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
423
423
|
{
|
|
424
|
-
"code": "QS-FAWZI-
|
|
424
|
+
"code": "QS-FAWZI-11",
|
|
425
425
|
"installed_by": "Fawzi Goussous",
|
|
426
426
|
"role": "OWNER",
|
|
427
427
|
"version": "2.8.1",
|
|
@@ -446,29 +446,32 @@ echo "install.js:"
|
|
|
446
446
|
|
|
447
447
|
# 28. Happy path: valid code installs everything
|
|
448
448
|
TMP=$(mktmp)
|
|
449
|
-
echo "QS-FAWZI-
|
|
449
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
450
450
|
EXIT=$?
|
|
451
451
|
if [ "$EXIT" -eq 0 ] \
|
|
452
452
|
&& [ -f "$TMP/.claude/skills/qualia/SKILL.md" ] \
|
|
453
453
|
&& [ -f "$TMP/.claude/hooks/session-start.js" ] \
|
|
454
454
|
&& [ -f "$TMP/.claude/bin/state.js" ] \
|
|
455
455
|
&& [ -f "$TMP/.claude/bin/runtime-manifest.js" ] \
|
|
456
|
+
&& [ -f "$TMP/.claude/bin/command-surface.js" ] \
|
|
456
457
|
&& [ -f "$TMP/.claude/bin/host-adapters.js" ] \
|
|
457
458
|
&& [ -f "$TMP/.claude/bin/qualia-ui.js" ] \
|
|
458
459
|
&& [ -f "$TMP/.claude/bin/statusline.js" ] \
|
|
459
460
|
&& [ -f "$TMP/.claude/bin/state-ledger.js" ] \
|
|
460
461
|
&& [ -f "$TMP/.claude/bin/contract-runner.js" ] \
|
|
462
|
+
&& [ -f "$TMP/.claude/bin/harness-eval.js" ] \
|
|
461
463
|
&& [ -f "$TMP/.claude/bin/trust-score.js" ] \
|
|
464
|
+
&& [ -f "$TMP/.claude/bin/work-packet.js" ] \
|
|
462
465
|
&& [ -f "$TMP/.claude/bin/project-snapshot.js" ] \
|
|
463
466
|
&& [ -f "$TMP/.claude/bin/planning-hygiene.js" ] \
|
|
464
467
|
&& [ -f "$TMP/.claude/.qualia-config.json" ]; then
|
|
465
|
-
pass "QS-FAWZI-
|
|
468
|
+
pass "QS-FAWZI-11 → installs skills, hooks, bin/, config"
|
|
466
469
|
else
|
|
467
|
-
fail_case "QS-FAWZI-
|
|
470
|
+
fail_case "QS-FAWZI-11 happy path" "exit=$EXIT"
|
|
468
471
|
fi
|
|
469
472
|
|
|
470
473
|
# 29. Config JSON has correct fields after happy path
|
|
471
|
-
if grep -q '"code": "QS-FAWZI-
|
|
474
|
+
if grep -q '"code": "QS-FAWZI-11"' "$TMP/.claude/.qualia-config.json" \
|
|
472
475
|
&& grep -q '"installed_by": "Fawzi Goussous"' "$TMP/.claude/.qualia-config.json" \
|
|
473
476
|
&& grep -q '"role": "OWNER"' "$TMP/.claude/.qualia-config.json"; then
|
|
474
477
|
pass "config JSON has code, installed_by, role=OWNER"
|
|
@@ -484,13 +487,13 @@ else
|
|
|
484
487
|
fail_case "CLAUDE.md role substitution"
|
|
485
488
|
fi
|
|
486
489
|
|
|
487
|
-
# 31. All
|
|
490
|
+
# 31. All 12 hooks installed (block-env-edit removed in v3.2.0;
|
|
488
491
|
# git-guardrails + stop-session-log added in v4.2.0;
|
|
489
492
|
# vercel-account-guard + env-empty-guard + supabase-destructive-guard added in v5.0.0;
|
|
490
|
-
# pre-compact removed in v6.2.0)
|
|
493
|
+
# pre-compact removed in v6.2.0; fawzi-approval-guard added in v6.2.11)
|
|
491
494
|
HOOK_COUNT=$(ls "$TMP/.claude/hooks/"*.js 2>/dev/null | wc -l)
|
|
492
|
-
if [ "$HOOK_COUNT" -eq
|
|
493
|
-
pass "
|
|
495
|
+
if [ "$HOOK_COUNT" -eq 12 ]; then
|
|
496
|
+
pass "12 hooks installed in hooks/"
|
|
494
497
|
else
|
|
495
498
|
fail_case "hook count" "got $HOOK_COUNT"
|
|
496
499
|
fi
|
|
@@ -506,7 +509,7 @@ else
|
|
|
506
509
|
fail_case "settings.json contents"
|
|
507
510
|
fi
|
|
508
511
|
|
|
509
|
-
# 33. settings.json contains all
|
|
512
|
+
# 33. settings.json contains all 12 hooks wired correctly
|
|
510
513
|
# pre-compact.js was removed in v6.2.0 — verify it's NOT in settings.json.
|
|
511
514
|
if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
512
515
|
&& grep -q 'migration-guard.js' "$TMP/.claude/settings.json" \
|
|
@@ -517,33 +520,34 @@ if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
|
517
520
|
&& grep -q 'git-guardrails.js' "$TMP/.claude/settings.json" \
|
|
518
521
|
&& grep -q 'stop-session-log.js' "$TMP/.claude/settings.json" \
|
|
519
522
|
&& grep -q 'vercel-account-guard.js' "$TMP/.claude/settings.json" \
|
|
523
|
+
&& grep -q 'fawzi-approval-guard.js' "$TMP/.claude/settings.json" \
|
|
520
524
|
&& grep -q 'env-empty-guard.js' "$TMP/.claude/settings.json" \
|
|
521
525
|
&& grep -q 'supabase-destructive-guard.js' "$TMP/.claude/settings.json" \
|
|
522
526
|
&& ! grep -q 'pre-compact.js' "$TMP/.claude/settings.json"; then
|
|
523
|
-
pass "settings.json has all
|
|
527
|
+
pass "settings.json has all 12 hooks wired (no pre-compact)"
|
|
524
528
|
else
|
|
525
529
|
fail_case "settings.json hooks misconfigured (check for stale pre-compact entry)"
|
|
526
530
|
fi
|
|
527
531
|
|
|
528
532
|
# 34. Lowercase code works (resolveTeamCode normalizes)
|
|
529
533
|
TMP=$(mktmp)
|
|
530
|
-
echo "qs-fawzi-
|
|
534
|
+
echo "qs-fawzi-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
531
535
|
EXIT=$?
|
|
532
536
|
if [ "$EXIT" -eq 0 ] \
|
|
533
537
|
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
534
|
-
&& grep -q '"code": "QS-FAWZI-
|
|
535
|
-
pass "lowercase 'qs-fawzi-
|
|
538
|
+
&& grep -q '"code": "QS-FAWZI-11"' "$TMP/.claude/.qualia-config.json"; then
|
|
539
|
+
pass "lowercase 'qs-fawzi-11' → canonical 'QS-FAWZI-11'"
|
|
536
540
|
else
|
|
537
541
|
fail_case "lowercase normalization" "exit=$EXIT"
|
|
538
542
|
fi
|
|
539
543
|
|
|
540
|
-
#
|
|
544
|
+
# 34b. O/0 typo tolerance — letter O in suffix normalized to digit 0
|
|
541
545
|
TMP=$(mktmp)
|
|
542
|
-
echo "QS-
|
|
546
|
+
echo "QS-HASAN-O2" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
543
547
|
EXIT=$?
|
|
544
548
|
if [ "$EXIT" -eq 0 ] \
|
|
545
549
|
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
546
|
-
&& grep -q '"code": "QS-
|
|
550
|
+
&& grep -q '"code": "QS-HASAN-02"' "$TMP/.claude/.qualia-config.json"; then
|
|
547
551
|
pass "letter 'O' in suffix → normalized to digit '0'"
|
|
548
552
|
else
|
|
549
553
|
fail_case "O/0 fuzzy match" "exit=$EXIT"
|
|
@@ -598,10 +602,10 @@ fi
|
|
|
598
602
|
|
|
599
603
|
# 39. Code with surrounding whitespace is accepted
|
|
600
604
|
TMP=$(mktmp)
|
|
601
|
-
printf ' QS-FAWZI-
|
|
605
|
+
printf ' QS-FAWZI-11 \n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
602
606
|
EXIT=$?
|
|
603
607
|
if [ "$EXIT" -eq 0 ] \
|
|
604
|
-
&& grep -q '"code": "QS-FAWZI-
|
|
608
|
+
&& grep -q '"code": "QS-FAWZI-11"' "$TMP/.claude/.qualia-config.json"; then
|
|
605
609
|
pass "whitespace-padded code → accepted and trimmed"
|
|
606
610
|
else
|
|
607
611
|
fail_case "whitespace trim" "exit=$EXIT"
|
|
@@ -618,7 +622,7 @@ cat > "$TMP/.claude/settings.json" <<'EOF'
|
|
|
618
622
|
}
|
|
619
623
|
}
|
|
620
624
|
EOF
|
|
621
|
-
echo "QS-FAWZI-
|
|
625
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
622
626
|
EXIT=$?
|
|
623
627
|
MERGED=$($NODE -e 'const s=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));console.log([s.customKey,s.env&&s.env.MY_CUSTOM_VAR,s.env&&s.env.CLAUDE_CODE_NO_FLICKER,!!s.hooks,!!s.statusLine].join("|"))' "$TMP/.claude/settings.json" 2>/dev/null)
|
|
624
628
|
if [ "$EXIT" -eq 0 ] \
|
|
@@ -630,7 +634,7 @@ fi
|
|
|
630
634
|
|
|
631
635
|
# 41. Knowledge files created on first install
|
|
632
636
|
TMP=$(mktmp)
|
|
633
|
-
echo "QS-FAWZI-
|
|
637
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
634
638
|
EXIT=$?
|
|
635
639
|
if [ "$EXIT" -eq 0 ] \
|
|
636
640
|
&& [ -f "$TMP/.claude/knowledge/learned-patterns.md" ] \
|
|
@@ -643,7 +647,7 @@ fi
|
|
|
643
647
|
|
|
644
648
|
# 42. Idempotent re-install preserves user edits to knowledge files
|
|
645
649
|
printf '\n## CUSTOM LEARNING — DO NOT OVERWRITE\n' >> "$TMP/.claude/knowledge/learned-patterns.md"
|
|
646
|
-
echo "QS-FAWZI-
|
|
650
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out2.log" 2>&1
|
|
647
651
|
EXIT=$?
|
|
648
652
|
if [ "$EXIT" -eq 0 ] \
|
|
649
653
|
&& grep -q "CUSTOM LEARNING" "$TMP/.claude/knowledge/learned-patterns.md"; then
|
|
@@ -658,7 +662,7 @@ if [ ! -f "$TMP/.claude/.erp-api-key" ] && [ "$CONFIG_ENABLED" = "disabled" ]; t
|
|
|
658
662
|
echo " ✓ ERP disabled when no API key is provided"
|
|
659
663
|
PASS=$((PASS + 1))
|
|
660
664
|
echo "custom-erp-key" > "$TMP/.claude/.erp-api-key"
|
|
661
|
-
echo "QS-FAWZI-
|
|
665
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out3.log" 2>&1
|
|
662
666
|
if grep -q "custom-erp-key" "$TMP/.claude/.erp-api-key"; then
|
|
663
667
|
pass ".erp-api-key preserved on re-install"
|
|
664
668
|
else
|
|
@@ -670,7 +674,7 @@ fi
|
|
|
670
674
|
|
|
671
675
|
# 44. Templates copied to qualia-templates/
|
|
672
676
|
TMP=$(mktmp)
|
|
673
|
-
echo "QS-FAWZI-
|
|
677
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/out.log" 2>&1
|
|
674
678
|
EXIT=$?
|
|
675
679
|
TMPL_COUNT=$(ls "$TMP/.claude/qualia-templates/" 2>/dev/null | wc -l)
|
|
676
680
|
if [ "$EXIT" -eq 0 ] && [ "$TMPL_COUNT" -gt 0 ]; then
|
|
@@ -719,13 +723,14 @@ fi
|
|
|
719
723
|
echo ""
|
|
720
724
|
echo "--- v4.2.0 phase 3 (flush + forks + model matrix) ---"
|
|
721
725
|
|
|
722
|
-
# 61. qualia-flush
|
|
726
|
+
# 61. qualia-flush is retired from the default slash-command surface
|
|
723
727
|
TMP=$(mktmp)
|
|
724
|
-
echo "QS-FAWZI-
|
|
725
|
-
if [ -f "$TMP/.claude/skills/qualia-flush/SKILL.md" ]
|
|
726
|
-
|
|
728
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
729
|
+
if [ ! -f "$TMP/.claude/skills/qualia-flush/SKILL.md" ] \
|
|
730
|
+
&& [ -f "$TMP/.claude/bin/knowledge-flush.js" ]; then
|
|
731
|
+
pass "qualia-flush retired; knowledge-flush.js remains installed"
|
|
727
732
|
else
|
|
728
|
-
fail_case "qualia-flush
|
|
733
|
+
fail_case "qualia-flush retirement/install state"
|
|
729
734
|
fi
|
|
730
735
|
|
|
731
736
|
# 62. CLAUDE_AGENT_FORK_ENABLED=1 in settings.json
|
|
@@ -966,7 +971,7 @@ fi
|
|
|
966
971
|
|
|
967
972
|
# 70. qualia-postmortem skill installs
|
|
968
973
|
TMP=$(mktmp)
|
|
969
|
-
echo "QS-FAWZI-
|
|
974
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
970
975
|
if [ -f "$TMP/.claude/skills/qualia-postmortem/SKILL.md" ]; then
|
|
971
976
|
pass "qualia-postmortem skill installs"
|
|
972
977
|
else
|
|
@@ -1043,7 +1048,7 @@ echo "--- v5.0.0 (alignment substrate + new skills) ---"
|
|
|
1043
1048
|
|
|
1044
1049
|
# 79. CONTEXT.md template installs to qualia-templates/
|
|
1045
1050
|
TMP=$(mktmp)
|
|
1046
|
-
echo "QS-FAWZI-
|
|
1051
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1047
1052
|
if [ -f "$TMP/.claude/qualia-templates/CONTEXT.md" ]; then
|
|
1048
1053
|
pass "CONTEXT.md template installs to qualia-templates/"
|
|
1049
1054
|
else
|
|
@@ -1057,12 +1062,17 @@ else
|
|
|
1057
1062
|
fail_case "decisions/ADR-template.md missing"
|
|
1058
1063
|
fi
|
|
1059
1064
|
|
|
1060
|
-
# 81-84.
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1065
|
+
# 81-84. v6.3 keeps road active and retires queue/zoom helper commands
|
|
1066
|
+
if [ -f "$TMP/.claude/skills/qualia-road/SKILL.md" ]; then
|
|
1067
|
+
pass "qualia-road skill installs"
|
|
1068
|
+
else
|
|
1069
|
+
fail_case "qualia-road skill missing after install"
|
|
1070
|
+
fi
|
|
1071
|
+
for SKILL in qualia-zoom qualia-issues qualia-triage; do
|
|
1072
|
+
if [ ! -f "$TMP/.claude/skills/$SKILL/SKILL.md" ]; then
|
|
1073
|
+
pass "$SKILL retired from default install"
|
|
1064
1074
|
else
|
|
1065
|
-
fail_case "$SKILL
|
|
1075
|
+
fail_case "$SKILL should be retired from default install"
|
|
1066
1076
|
fi
|
|
1067
1077
|
done
|
|
1068
1078
|
|
|
@@ -1109,12 +1119,11 @@ else
|
|
|
1109
1119
|
fail_case "builder.md missing Trust boundary block"
|
|
1110
1120
|
fi
|
|
1111
1121
|
|
|
1112
|
-
# 91. qualia-issues
|
|
1113
|
-
if
|
|
1114
|
-
|
|
1115
|
-
pass "qualia-issues uses --body-file (no heredoc shell injection)"
|
|
1122
|
+
# 91. qualia-issues source is removed from the shipped command surface
|
|
1123
|
+
if [ ! -d "$FRAMEWORK_DIR/skills/qualia-issues" ]; then
|
|
1124
|
+
pass "qualia-issues source removed from shipped command surface"
|
|
1116
1125
|
else
|
|
1117
|
-
fail_case "qualia-issues
|
|
1126
|
+
fail_case "qualia-issues source directory should not be shipped"
|
|
1118
1127
|
fi
|
|
1119
1128
|
|
|
1120
1129
|
# 92-94. v5.0 insights-driven hooks parse as valid Node and install
|
|
@@ -1174,7 +1183,7 @@ echo "--- v5.0.0 (visual-polish loop addendum) ---"
|
|
|
1174
1183
|
|
|
1175
1184
|
# 99. qualia-polish SKILL.md installs (v5.8: polish-loop consolidated into --loop flag)
|
|
1176
1185
|
TMP=$(mktmp)
|
|
1177
|
-
echo "QS-FAWZI-
|
|
1186
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1178
1187
|
if [ -f "$TMP/.claude/skills/qualia-polish/SKILL.md" ]; then
|
|
1179
1188
|
pass "qualia-polish SKILL.md installs"
|
|
1180
1189
|
else
|
|
@@ -1353,7 +1362,7 @@ echo "--- v5.1.0 (multi-target install: Claude / Codex / Both) ---"
|
|
|
1353
1362
|
|
|
1354
1363
|
# 118. Target=1 (Claude only) installs to ~/.claude/, not ~/.codex/
|
|
1355
1364
|
TMP=$(mktmp)
|
|
1356
|
-
printf 'QS-FAWZI-
|
|
1365
|
+
printf 'QS-FAWZI-11\n1\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1357
1366
|
EXIT=$?
|
|
1358
1367
|
if [ "$EXIT" -eq 0 ] \
|
|
1359
1368
|
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
@@ -1365,7 +1374,7 @@ fi
|
|
|
1365
1374
|
|
|
1366
1375
|
# 119. Target=2 (Codex only) writes native Codex runtime files, skips ~/.claude/
|
|
1367
1376
|
TMP=$(mktmp)
|
|
1368
|
-
printf 'QS-FAWZI-
|
|
1377
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1369
1378
|
EXIT=$?
|
|
1370
1379
|
if [ "$EXIT" -eq 0 ] \
|
|
1371
1380
|
&& [ -f "$TMP/.codex/AGENTS.md" ] \
|
|
@@ -1373,11 +1382,14 @@ if [ "$EXIT" -eq 0 ] \
|
|
|
1373
1382
|
&& [ -f "$TMP/.codex/config.toml" ] \
|
|
1374
1383
|
&& [ -f "$TMP/.codex/hooks.json" ] \
|
|
1375
1384
|
&& [ -f "$TMP/.codex/bin/runtime-manifest.js" ] \
|
|
1385
|
+
&& [ -f "$TMP/.codex/bin/command-surface.js" ] \
|
|
1376
1386
|
&& [ -f "$TMP/.codex/bin/host-adapters.js" ] \
|
|
1377
1387
|
&& [ -f "$TMP/.codex/bin/statusline.js" ] \
|
|
1378
1388
|
&& [ -f "$TMP/.codex/bin/state-ledger.js" ] \
|
|
1379
1389
|
&& [ -f "$TMP/.codex/bin/contract-runner.js" ] \
|
|
1390
|
+
&& [ -f "$TMP/.codex/bin/harness-eval.js" ] \
|
|
1380
1391
|
&& [ -f "$TMP/.codex/bin/trust-score.js" ] \
|
|
1392
|
+
&& [ -f "$TMP/.codex/bin/work-packet.js" ] \
|
|
1381
1393
|
&& [ -f "$TMP/.codex/bin/project-snapshot.js" ] \
|
|
1382
1394
|
&& [ -f "$TMP/.codex/bin/planning-hygiene.js" ] \
|
|
1383
1395
|
&& [ -f "$TMP/.codex/agents/planner.toml" ] \
|
|
@@ -1418,7 +1430,7 @@ fi
|
|
|
1418
1430
|
|
|
1419
1431
|
# 120. Target=3 (Both) populates both directories with the right artifacts
|
|
1420
1432
|
TMP=$(mktmp)
|
|
1421
|
-
printf 'QS-FAWZI-
|
|
1433
|
+
printf 'QS-FAWZI-11\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1422
1434
|
EXIT=$?
|
|
1423
1435
|
if [ "$EXIT" -eq 0 ] \
|
|
1424
1436
|
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
@@ -1437,7 +1449,7 @@ fi
|
|
|
1437
1449
|
# 121. Backward compat: legacy single-line piped install (no target line)
|
|
1438
1450
|
# defaults to Claude only. Same exact invocation as pre-v5.1.
|
|
1439
1451
|
TMP=$(mktmp)
|
|
1440
|
-
echo "QS-FAWZI-
|
|
1452
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1441
1453
|
EXIT=$?
|
|
1442
1454
|
if [ "$EXIT" -eq 0 ] \
|
|
1443
1455
|
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
@@ -1452,7 +1464,7 @@ fi
|
|
|
1452
1464
|
TMP=$(mktmp)
|
|
1453
1465
|
mkdir -p "$TMP/.codex"
|
|
1454
1466
|
echo "OLD USER CONTENT" > "$TMP/.codex/AGENTS.md"
|
|
1455
|
-
printf 'QS-FAWZI-
|
|
1467
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1456
1468
|
EXIT=$?
|
|
1457
1469
|
BAK_COUNT=$(ls "$TMP/.codex/"AGENTS.md.bak.* 2>/dev/null | wc -l)
|
|
1458
1470
|
if [ "$EXIT" -eq 0 ] \
|
|
@@ -1467,9 +1479,9 @@ fi
|
|
|
1467
1479
|
# 123. Codex install with same content does NOT create a redundant .bak
|
|
1468
1480
|
# (the v5.0 CLAUDE.md backup discipline only backs up if content differs).
|
|
1469
1481
|
TMP=$(mktmp)
|
|
1470
|
-
printf 'QS-FAWZI-
|
|
1482
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1471
1483
|
# Re-run with same input — content should be identical, no new backup.
|
|
1472
|
-
printf 'QS-FAWZI-
|
|
1484
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log2.txt" 2>&1
|
|
1473
1485
|
BAK_COUNT=$(ls "$TMP/.codex/"AGENTS.md.bak.* 2>/dev/null | wc -l)
|
|
1474
1486
|
if [ "$BAK_COUNT" -eq 0 ]; then
|
|
1475
1487
|
pass "Codex re-install with identical content → no redundant .bak"
|
|
@@ -1494,7 +1506,7 @@ cat > "$TMP/.codex/hooks.json" <<'JSON'
|
|
|
1494
1506
|
}
|
|
1495
1507
|
}
|
|
1496
1508
|
JSON
|
|
1497
|
-
printf 'QS-FAWZI-
|
|
1509
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1498
1510
|
if grep -q "custom-session-hook.js" "$TMP/.codex/hooks.json" \
|
|
1499
1511
|
&& grep -q "session-start.js" "$TMP/.codex/hooks.json"; then
|
|
1500
1512
|
pass "Codex hooks.json merge preserves non-Qualia hooks"
|
|
@@ -1520,7 +1532,7 @@ cat > "$TMP/.codex/hooks.json" <<'JSON'
|
|
|
1520
1532
|
}
|
|
1521
1533
|
}
|
|
1522
1534
|
JSON
|
|
1523
|
-
printf 'QS-FAWZI-
|
|
1535
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1524
1536
|
EXIT=0; HOME="$TMP" $NODE "$CLI_JS" uninstall --yes > "$TMP/uninstall.log" 2>&1 || EXIT=$?
|
|
1525
1537
|
if [ "$EXIT" -eq 0 ] \
|
|
1526
1538
|
&& [ -f "$TMP/.codex/AGENTS.md" ] \
|
|
@@ -1540,7 +1552,7 @@ fi
|
|
|
1540
1552
|
|
|
1541
1553
|
# 123d. Team management writes to Codex-only installs without creating ~/.claude.
|
|
1542
1554
|
TMP=$(mktmp)
|
|
1543
|
-
printf 'QS-FAWZI-
|
|
1555
|
+
printf 'QS-FAWZI-11\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1544
1556
|
EXIT=0; HOME="$TMP" $NODE "$CLI_JS" team add --code QS-TEST-99 --name "Test User" > "$TMP/team.log" 2>&1 || EXIT=$?
|
|
1545
1557
|
if [ "$EXIT" -eq 0 ] \
|
|
1546
1558
|
&& [ -f "$TMP/.codex/.qualia-team.json" ] \
|
|
@@ -1555,7 +1567,7 @@ fi
|
|
|
1555
1567
|
# sequences (\r, \x1b[?25l/h hide-cursor, \x1b[2K clear-line). Spinner +
|
|
1556
1568
|
# overwrite primitives must degrade cleanly when output is piped.
|
|
1557
1569
|
TMP=$(mktmp)
|
|
1558
|
-
printf 'QS-FAWZI-
|
|
1570
|
+
printf 'QS-FAWZI-11\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1559
1571
|
# Look for any of: bare CR, hide-cursor (?25), clear-line (\x1b[2K).
|
|
1560
1572
|
if [ -s "$TMP/log.txt" ] \
|
|
1561
1573
|
&& ! grep -q $'\r' "$TMP/log.txt" \
|
|
@@ -1568,7 +1580,7 @@ fi
|
|
|
1568
1580
|
|
|
1569
1581
|
# 125. Final summary card includes the new "Targets" + "Time" rows
|
|
1570
1582
|
TMP=$(mktmp)
|
|
1571
|
-
printf 'QS-FAWZI-
|
|
1583
|
+
printf 'QS-FAWZI-11\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1572
1584
|
CLEAN=$(strip_ansi < "$TMP/log.txt")
|
|
1573
1585
|
if echo "$CLEAN" | grep -q "Targets" \
|
|
1574
1586
|
&& echo "$CLEAN" | grep -q "Claude Code · Codex" \
|
|
@@ -1676,7 +1688,7 @@ echo "--- v5.3.0 (Matt Pocock gaps: prd, hook-gen, parallel-interface) ---"
|
|
|
1676
1688
|
|
|
1677
1689
|
# Re-install for v5.3 assertions (TMP from #99 may have v5.1 state)
|
|
1678
1690
|
TMP=$(mktmp)
|
|
1679
|
-
echo "QS-FAWZI-
|
|
1691
|
+
echo "QS-FAWZI-11" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1680
1692
|
|
|
1681
1693
|
# 135-137. /qualia-prd removed in v5.8.0 (was deprecated, overlapped /qualia-discuss).
|
|
1682
1694
|
# Assert non-existence so the framework cannot accidentally re-ship it.
|
|
@@ -1706,28 +1718,28 @@ else
|
|
|
1706
1718
|
fail_case "qualia-feature missing after install"
|
|
1707
1719
|
fi
|
|
1708
1720
|
|
|
1709
|
-
# 138. qualia-hook-gen
|
|
1710
|
-
if [ -f "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" ]
|
|
1711
|
-
|
|
1721
|
+
# 138. qualia-hook-gen is removed from source and retired from default install
|
|
1722
|
+
if [ ! -f "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" ] \
|
|
1723
|
+
&& [ ! -d "$FRAMEWORK_DIR/skills/qualia-hook-gen" ]; then
|
|
1724
|
+
pass "qualia-hook-gen removed from source and default install"
|
|
1712
1725
|
else
|
|
1713
|
-
fail_case "qualia-hook-gen
|
|
1726
|
+
fail_case "qualia-hook-gen should be absent from source and install"
|
|
1714
1727
|
fi
|
|
1715
1728
|
|
|
1716
|
-
# 139.
|
|
1717
|
-
if grep -q "
|
|
1718
|
-
&& grep -q "
|
|
1719
|
-
|
|
1720
|
-
pass "qualia-hook-gen documents block/rewrite/warn patterns"
|
|
1729
|
+
# 139. hook generation knowledge moved out of slash-command source
|
|
1730
|
+
if grep -q "proxy_owner_approval_claim" "$FRAMEWORK_DIR/hooks/fawzi-approval-guard.js" \
|
|
1731
|
+
&& grep -q "/api/v1/policy-events" "$FRAMEWORK_DIR/hooks/fawzi-approval-guard.js"; then
|
|
1732
|
+
pass "hook enforcement lives in runtime hooks, not qualia-hook-gen"
|
|
1721
1733
|
else
|
|
1722
|
-
fail_case "
|
|
1734
|
+
fail_case "runtime hook enforcement guard missing"
|
|
1723
1735
|
fi
|
|
1724
1736
|
|
|
1725
|
-
# 140.
|
|
1726
|
-
if
|
|
1727
|
-
&&
|
|
1728
|
-
pass "
|
|
1737
|
+
# 140. retired hook-gen behavior remains covered by Node hook files
|
|
1738
|
+
if find "$FRAMEWORK_DIR/hooks" -maxdepth 1 -name "*.js" -print | grep -q . \
|
|
1739
|
+
&& ! find "$FRAMEWORK_DIR/hooks" -maxdepth 1 -name "*.sh" -print | grep -q .; then
|
|
1740
|
+
pass "runtime hooks are pure Node files"
|
|
1729
1741
|
else
|
|
1730
|
-
fail_case "
|
|
1742
|
+
fail_case "runtime hooks should remain pure Node"
|
|
1731
1743
|
fi
|
|
1732
1744
|
|
|
1733
1745
|
# 141. qualia-optimize SKILL.md adds Step 5b (parallel-interface design)
|
|
@@ -1892,13 +1904,63 @@ else
|
|
|
1892
1904
|
fail_case "report-payload UUID pass-through failed" "$OUT"
|
|
1893
1905
|
fi
|
|
1894
1906
|
|
|
1907
|
+
# 148b. work-packet links flow into report payloads
|
|
1908
|
+
TMP_PACKET=$(mktmp)
|
|
1909
|
+
OUT=$(FRAMEWORK_DIR="$FRAMEWORK_DIR" TMP_PACKET="$TMP_PACKET" $NODE <<'NODE' 2>&1
|
|
1910
|
+
const assert = require("assert/strict");
|
|
1911
|
+
const fs = require("fs");
|
|
1912
|
+
const path = require("path");
|
|
1913
|
+
const { buildPayload } = require(path.join(process.env.FRAMEWORK_DIR, "bin", "report-payload.js"));
|
|
1914
|
+
const { readLocalWorkPacket } = require(path.join(process.env.FRAMEWORK_DIR, "bin", "work-packet.js"));
|
|
1915
|
+
|
|
1916
|
+
const root = process.env.TMP_PACKET;
|
|
1917
|
+
const cwd = path.join(root, "project");
|
|
1918
|
+
const home = path.join(root, "home");
|
|
1919
|
+
fs.mkdirSync(path.join(cwd, ".planning"), { recursive: true });
|
|
1920
|
+
fs.mkdirSync(path.join(home, ".claude"), { recursive: true });
|
|
1921
|
+
fs.writeFileSync(path.join(home, ".claude", ".qualia-config.json"), JSON.stringify({ version: "test-version" }));
|
|
1922
|
+
fs.writeFileSync(path.join(cwd, ".planning", "tracking.json"), JSON.stringify({
|
|
1923
|
+
project: "acme-portal",
|
|
1924
|
+
project_id: "qs-acme-portal",
|
|
1925
|
+
erp_project_id: "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef",
|
|
1926
|
+
phase: 1,
|
|
1927
|
+
status: "built"
|
|
1928
|
+
}));
|
|
1929
|
+
fs.writeFileSync(path.join(cwd, ".planning", "work-packet.json"), JSON.stringify({
|
|
1930
|
+
id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
1931
|
+
project_id: "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef",
|
|
1932
|
+
assignment_id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
1933
|
+
deadline_date: "2026-06-01",
|
|
1934
|
+
next_command: "/qualia-verify"
|
|
1935
|
+
}));
|
|
1936
|
+
|
|
1937
|
+
const packet = readLocalWorkPacket(cwd);
|
|
1938
|
+
assert.equal(packet.next_command, "/qualia-verify");
|
|
1939
|
+
const payload = buildPayload({
|
|
1940
|
+
cwd,
|
|
1941
|
+
home,
|
|
1942
|
+
env: { SUBMITTED_BY: "Fawzi Goussous", SUBMITTED_AT: "2026-05-21T01:30:00Z" },
|
|
1943
|
+
});
|
|
1944
|
+
assert.equal(payload.work_packet_id, "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa");
|
|
1945
|
+
assert.equal(payload.assignment_id, "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb");
|
|
1946
|
+
assert.equal(payload.assignment_deadline, "2026-06-01");
|
|
1947
|
+
NODE
|
|
1948
|
+
)
|
|
1949
|
+
EXIT=$?
|
|
1950
|
+
if [ "$EXIT" -eq 0 ]; then
|
|
1951
|
+
pass "work-packet links flow into report payloads"
|
|
1952
|
+
else
|
|
1953
|
+
fail_case "work-packet report payload link failed" "$OUT"
|
|
1954
|
+
fi
|
|
1955
|
+
|
|
1895
1956
|
# 149. Installer ships the payload builder used by /qualia-report
|
|
1896
1957
|
TMP_INSTALL=$(mktmp)
|
|
1897
|
-
echo "QS-FAWZI-
|
|
1958
|
+
echo "QS-FAWZI-11" | HOME="$TMP_INSTALL" $NODE "$INSTALL_JS" > "$TMP_INSTALL/out.log" 2>&1
|
|
1898
1959
|
EXIT=$?
|
|
1899
1960
|
if [ "$EXIT" -eq 0 ] \
|
|
1900
1961
|
&& [ -f "$TMP_INSTALL/.claude/bin/report-payload.js" ] \
|
|
1901
1962
|
&& [ -f "$TMP_INSTALL/.claude/bin/project-snapshot.js" ] \
|
|
1963
|
+
&& [ -f "$TMP_INSTALL/.claude/bin/work-packet.js" ] \
|
|
1902
1964
|
&& grep -q "report-payload.js" "$TMP_INSTALL/.claude/skills/qualia-report/SKILL.md"; then
|
|
1903
1965
|
pass "installer ships ERP report/snapshot helpers"
|
|
1904
1966
|
else
|
|
@@ -1954,12 +2016,22 @@ fs.writeFileSync(path.join(cwd, ".planning", "tracking.json"), JSON.stringify({
|
|
|
1954
2016
|
last_closed_milestone: 1
|
|
1955
2017
|
}
|
|
1956
2018
|
}));
|
|
2019
|
+
fs.writeFileSync(path.join(cwd, ".planning", "work-packet.json"), JSON.stringify({
|
|
2020
|
+
id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
2021
|
+
project_id: "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef",
|
|
2022
|
+
assignment_id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
|
2023
|
+
deadline_date: "2026-06-01",
|
|
2024
|
+
next_command: "/qualia-verify"
|
|
2025
|
+
}));
|
|
1957
2026
|
|
|
1958
2027
|
const snapshot = buildSnapshot({ cwd, home, now: "2026-05-21T00:00:00.000Z" });
|
|
1959
2028
|
assert.equal(snapshot.snapshot_version, 1);
|
|
1960
2029
|
assert.equal(snapshot.framework_version, "test-version");
|
|
1961
2030
|
assert.equal(snapshot.identifiers.project_id, "qs-acme-portal");
|
|
1962
2031
|
assert.equal(snapshot.identifiers.erp_project_id, "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef");
|
|
2032
|
+
assert.equal(snapshot.identifiers.work_packet_id, "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa");
|
|
2033
|
+
assert.equal(snapshot.identifiers.assignment_id, "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb");
|
|
2034
|
+
assert.equal(snapshot.identifiers.assignment_deadline, "2026-06-01");
|
|
1963
2035
|
assert.equal(snapshot.project.progress_percent, 42);
|
|
1964
2036
|
assert.equal(snapshot.current.gap_cycles, 1);
|
|
1965
2037
|
assert.equal(snapshot.journey.total_milestones, 3);
|
package/tests/hooks.test.sh
CHANGED
|
@@ -154,6 +154,46 @@ echo '{"role":""}' > "$TMP/.claude/.qualia-config.json"
|
|
|
154
154
|
assert_exit "empty role field → blocked" 2 $?
|
|
155
155
|
rm -rf "$TMP"
|
|
156
156
|
|
|
157
|
+
# --- fawzi-approval-guard.js ---
|
|
158
|
+
echo ""
|
|
159
|
+
echo "fawzi-approval-guard:"
|
|
160
|
+
|
|
161
|
+
TMP=$(mktemp -d)
|
|
162
|
+
mkdir -p "$TMP/.claude"
|
|
163
|
+
cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
164
|
+
{"code":"QS-HASAN-02","installed_by":"Hasan","role":"EMPLOYEE","erp":{"enabled":false}}
|
|
165
|
+
EOF
|
|
166
|
+
OUT=$(echo '{"tool_input":{"content":"Fawzi said ok, ship it now."}}' | HOME="$TMP" $NODE "$HOOKS_DIR/fawzi-approval-guard.js" 2>&1)
|
|
167
|
+
RC=$?
|
|
168
|
+
if [ "$RC" -eq 0 ] \
|
|
169
|
+
&& [ -z "$OUT" ] \
|
|
170
|
+
&& [ -f "$TMP/.claude/.approval-policy-events.json" ] \
|
|
171
|
+
&& grep -q '"proxy_owner_approval_claim"' "$TMP/.claude/.approval-policy-events.json" \
|
|
172
|
+
&& grep -q '"total": 1' "$TMP/.claude/.approval-policy-events.json"; then
|
|
173
|
+
echo " ✓ employee proxy approval claim → silently recorded"
|
|
174
|
+
PASS=$((PASS + 1))
|
|
175
|
+
else
|
|
176
|
+
echo " ✗ employee proxy approval claim record failed (exit=$RC out=$OUT)"
|
|
177
|
+
FAIL=$((FAIL + 1))
|
|
178
|
+
fi
|
|
179
|
+
rm -rf "$TMP"
|
|
180
|
+
|
|
181
|
+
TMP=$(mktemp -d)
|
|
182
|
+
mkdir -p "$TMP/.claude"
|
|
183
|
+
cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
184
|
+
{"code":"QS-FAWZI-11","installed_by":"Fawzi Goussous","role":"OWNER","erp":{"enabled":false}}
|
|
185
|
+
EOF
|
|
186
|
+
echo '{"tool_input":{"content":"Fawzi said ok, ship it now."}}' | HOME="$TMP" $NODE "$HOOKS_DIR/fawzi-approval-guard.js" >/dev/null 2>&1
|
|
187
|
+
RC=$?
|
|
188
|
+
if [ "$RC" -eq 0 ] && [ ! -f "$TMP/.claude/.approval-policy-events.json" ]; then
|
|
189
|
+
echo " ✓ OWNER phrasing → not recorded as employee violation"
|
|
190
|
+
PASS=$((PASS + 1))
|
|
191
|
+
else
|
|
192
|
+
echo " ✗ OWNER phrasing should not record (exit=$RC)"
|
|
193
|
+
FAIL=$((FAIL + 1))
|
|
194
|
+
fi
|
|
195
|
+
rm -rf "$TMP"
|
|
196
|
+
|
|
157
197
|
# --- pre-push.js ---
|
|
158
198
|
echo ""
|
|
159
199
|
echo "pre-push:"
|
|
@@ -184,6 +224,27 @@ rm -rf "$TMP"
|
|
|
184
224
|
echo ""
|
|
185
225
|
echo "pre-deploy-gate:"
|
|
186
226
|
|
|
227
|
+
TMP=$(mktemp -d)
|
|
228
|
+
mkdir -p "$TMP/.claude" "$TMP/proj/.planning"
|
|
229
|
+
cat > "$TMP/.claude/.qualia-config.json" <<'EOF'
|
|
230
|
+
{"code":"QS-HASAN-02","installed_by":"Hasan","role":"EMPLOYEE"}
|
|
231
|
+
EOF
|
|
232
|
+
cat > "$TMP/proj/.planning/tracking.json" <<'EOF'
|
|
233
|
+
{"status":"built","verification":"pending","next_command":"/qualia-report"}
|
|
234
|
+
EOF
|
|
235
|
+
OUT=$(cd "$TMP/proj" && echo '{"tool_input":{"command":"QUALIA_SHIP_FORCE=1 vercel --prod"}}' | HOME="$TMP" $NODE "$HOOKS_DIR/pre-deploy-gate.js" 2>&1)
|
|
236
|
+
RC=$?
|
|
237
|
+
if [ "$RC" -eq 2 ] \
|
|
238
|
+
&& echo "$OUT" | grep -q "OWNER-only" \
|
|
239
|
+
&& echo "$OUT" | grep -q "/qualia-report"; then
|
|
240
|
+
echo " ✓ employee force ship → blocked with short next command"
|
|
241
|
+
PASS=$((PASS + 1))
|
|
242
|
+
else
|
|
243
|
+
echo " ✗ employee force ship block failed (exit=$RC out=$OUT)"
|
|
244
|
+
FAIL=$((FAIL + 1))
|
|
245
|
+
fi
|
|
246
|
+
rm -rf "$TMP"
|
|
247
|
+
|
|
187
248
|
# Empty project (no package.json, no tsconfig) → nothing to gate → exit 0
|
|
188
249
|
TMP=$(mktemp -d)
|
|
189
250
|
(cd "$TMP" && $NODE "$HOOKS_DIR/pre-deploy-gate.js" >/dev/null 2>&1)
|
|
@@ -338,6 +399,25 @@ Status: setup
|
|
|
338
399
|
EOF
|
|
339
400
|
(cd "$TMP" && $NODE "$HOOKS_DIR/session-start.js" >/dev/null 2>&1)
|
|
340
401
|
assert_exit "exits 0 with STATE.md" 0 $?
|
|
402
|
+
|
|
403
|
+
cat > "$TMP/.planning/work-packet.json" <<'EOF'
|
|
404
|
+
{
|
|
405
|
+
"id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
406
|
+
"project_id": "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef",
|
|
407
|
+
"deadline_date": "2026-06-01",
|
|
408
|
+
"next_command": "/qualia-verify",
|
|
409
|
+
"project": { "name": "Acme Portal" }
|
|
410
|
+
}
|
|
411
|
+
EOF
|
|
412
|
+
OUT=$(cd "$TMP" && $NODE "$HOOKS_DIR/session-start.js" 2>&1)
|
|
413
|
+
RC=$?
|
|
414
|
+
if [ "$RC" -eq 0 ] && echo "$OUT" | grep -q "Acme Portal" && echo "$OUT" | grep -q "/qualia-verify"; then
|
|
415
|
+
echo " ✓ renders local ERP work packet context"
|
|
416
|
+
PASS=$((PASS + 1))
|
|
417
|
+
else
|
|
418
|
+
echo " ✗ ERP work packet context missing (exit=$RC)"
|
|
419
|
+
FAIL=$((FAIL + 1))
|
|
420
|
+
fi
|
|
341
421
|
rm -rf "$TMP"
|
|
342
422
|
|
|
343
423
|
# pre-compact.js removed in v6.2.0 — state.js journal provides crash safety.
|
|
@@ -348,7 +428,7 @@ echo "auto-update:"
|
|
|
348
428
|
|
|
349
429
|
TMP=$(mktemp -d)
|
|
350
430
|
mkdir -p "$TMP/.claude"
|
|
351
|
-
echo '{"code":"QS-FAWZI-
|
|
431
|
+
echo '{"code":"QS-FAWZI-11","version":"99.99.99"}' > "$TMP/.claude/.qualia-config.json"
|
|
352
432
|
HOME="$TMP" $NODE "$HOOKS_DIR/auto-update.js" >/dev/null 2>&1
|
|
353
433
|
assert_exit "exits 0 (fast path)" 0 $?
|
|
354
434
|
# Should now have cache file
|