qualia-framework 4.5.0 → 5.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 +24 -0
- package/CLAUDE.md +12 -75
- package/README.md +23 -16
- package/agents/builder.md +9 -21
- package/agents/planner.md +8 -0
- package/agents/verifier.md +8 -0
- package/agents/visual-evaluator.md +132 -0
- package/bin/cli.js +54 -18
- package/bin/install.js +369 -29
- package/bin/qualia-ui.js +208 -1
- package/bin/slop-detect.mjs +5 -0
- package/bin/state.js +34 -1
- package/docs/install-redesign-builder-prompt.md +290 -0
- package/docs/install-redesign-pilot.md +234 -0
- package/docs/playwright-loop-builder-prompt.md +185 -0
- package/docs/playwright-loop-design-notes.md +108 -0
- package/docs/playwright-loop-pilot-results.md +170 -0
- package/docs/playwright-loop-tester-prompt.md +213 -0
- package/docs/polish-loop-supervised-run.md +111 -0
- package/docs/reviews/matt-pocock-skills-analysis.md +300 -0
- package/guide.md +9 -5
- package/hooks/env-empty-guard.js +74 -0
- package/hooks/pre-compact.js +19 -9
- package/hooks/pre-deploy-gate.js +8 -2
- package/hooks/pre-push.js +26 -12
- package/hooks/supabase-destructive-guard.js +62 -0
- package/hooks/vercel-account-guard.js +91 -0
- package/package.json +2 -1
- package/rules/design-brand.md +4 -0
- package/rules/design-laws.md +4 -0
- package/rules/design-product.md +4 -0
- package/rules/design-rubric.md +4 -0
- package/rules/grounding.md +4 -0
- package/skills/qualia-build/SKILL.md +40 -46
- package/skills/qualia-discuss/SKILL.md +51 -68
- package/skills/qualia-handoff/SKILL.md +1 -0
- package/skills/qualia-hook-gen/SKILL.md +206 -0
- package/skills/qualia-issues/SKILL.md +151 -0
- package/skills/qualia-map/SKILL.md +78 -35
- package/skills/qualia-new/REFERENCE.md +139 -0
- package/skills/qualia-new/SKILL.md +45 -121
- package/skills/qualia-optimize/REFERENCE.md +265 -0
- package/skills/qualia-optimize/SKILL.md +92 -232
- package/skills/qualia-plan/SKILL.md +58 -65
- package/skills/qualia-polish-loop/REFERENCE.md +265 -0
- package/skills/qualia-polish-loop/SKILL.md +201 -0
- package/skills/qualia-polish-loop/fixtures/broken.html +117 -0
- package/skills/qualia-polish-loop/fixtures/clean.html +196 -0
- package/skills/qualia-polish-loop/scripts/loop.mjs +323 -0
- package/skills/qualia-polish-loop/scripts/playwright-capture.mjs +206 -0
- package/skills/qualia-polish-loop/scripts/score.mjs +176 -0
- package/skills/qualia-prd/SKILL.md +199 -0
- package/skills/qualia-report/SKILL.md +141 -200
- package/skills/qualia-research/SKILL.md +28 -33
- package/skills/qualia-road/SKILL.md +103 -0
- package/skills/qualia-ship/SKILL.md +1 -0
- package/skills/qualia-task/SKILL.md +1 -1
- package/skills/qualia-test/SKILL.md +50 -2
- package/skills/qualia-triage/SKILL.md +152 -0
- package/skills/qualia-verify/SKILL.md +63 -104
- package/skills/qualia-zoom/SKILL.md +51 -0
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/CONTEXT.md +36 -0
- package/templates/decisions/ADR-template.md +30 -0
- package/tests/bin.test.sh +598 -7
- package/tests/state.test.sh +58 -0
package/tests/bin.test.sh
CHANGED
|
@@ -9,6 +9,7 @@ NODE="${NODE:-node}"
|
|
|
9
9
|
CLI_JS="$FRAMEWORK_DIR/bin/cli.js"
|
|
10
10
|
UI_JS="$FRAMEWORK_DIR/bin/qualia-ui.js"
|
|
11
11
|
INSTALL_JS="$FRAMEWORK_DIR/bin/install.js"
|
|
12
|
+
STATE_JS="$FRAMEWORK_DIR/bin/state.js"
|
|
12
13
|
PKG_VERSION=$($NODE -e 'console.log(require("'"$FRAMEWORK_DIR"'/package.json").version)')
|
|
13
14
|
|
|
14
15
|
# Track tmp dirs we create so we can clean them up on exit
|
|
@@ -476,11 +477,12 @@ else
|
|
|
476
477
|
fail_case "CLAUDE.md role substitution"
|
|
477
478
|
fi
|
|
478
479
|
|
|
479
|
-
# 31. All
|
|
480
|
-
# git-guardrails + stop-session-log added in v4.2.0
|
|
480
|
+
# 31. All 12 hooks installed (block-env-edit removed in v3.2.0;
|
|
481
|
+
# git-guardrails + stop-session-log added in v4.2.0;
|
|
482
|
+
# vercel-account-guard + env-empty-guard + supabase-destructive-guard added in v5.0.0)
|
|
481
483
|
HOOK_COUNT=$(ls "$TMP/.claude/hooks/"*.js 2>/dev/null | wc -l)
|
|
482
|
-
if [ "$HOOK_COUNT" -eq
|
|
483
|
-
pass "
|
|
484
|
+
if [ "$HOOK_COUNT" -eq 12 ]; then
|
|
485
|
+
pass "12 hooks installed in hooks/"
|
|
484
486
|
else
|
|
485
487
|
fail_case "hook count" "got $HOOK_COUNT"
|
|
486
488
|
fi
|
|
@@ -496,7 +498,7 @@ else
|
|
|
496
498
|
fail_case "settings.json contents"
|
|
497
499
|
fi
|
|
498
500
|
|
|
499
|
-
# 33. settings.json contains all
|
|
501
|
+
# 33. settings.json contains all 12 hooks wired correctly
|
|
500
502
|
if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
501
503
|
&& grep -q 'migration-guard.js' "$TMP/.claude/settings.json" \
|
|
502
504
|
&& grep -q 'pre-push.js' "$TMP/.claude/settings.json" \
|
|
@@ -505,8 +507,11 @@ if grep -q 'branch-guard.js' "$TMP/.claude/settings.json" \
|
|
|
505
507
|
&& grep -q 'session-start.js' "$TMP/.claude/settings.json" \
|
|
506
508
|
&& grep -q 'pre-compact.js' "$TMP/.claude/settings.json" \
|
|
507
509
|
&& grep -q 'git-guardrails.js' "$TMP/.claude/settings.json" \
|
|
508
|
-
&& grep -q 'stop-session-log.js' "$TMP/.claude/settings.json"
|
|
509
|
-
|
|
510
|
+
&& grep -q 'stop-session-log.js' "$TMP/.claude/settings.json" \
|
|
511
|
+
&& grep -q 'vercel-account-guard.js' "$TMP/.claude/settings.json" \
|
|
512
|
+
&& grep -q 'env-empty-guard.js' "$TMP/.claude/settings.json" \
|
|
513
|
+
&& grep -q 'supabase-destructive-guard.js' "$TMP/.claude/settings.json"; then
|
|
514
|
+
pass "settings.json has all 12 hooks wired"
|
|
510
515
|
else
|
|
511
516
|
fail_case "settings.json missing hooks"
|
|
512
517
|
fi
|
|
@@ -992,6 +997,592 @@ else
|
|
|
992
997
|
fail_case "help missing flush" "exit=$EXIT"
|
|
993
998
|
fi
|
|
994
999
|
|
|
1000
|
+
echo ""
|
|
1001
|
+
echo "--- v5.0.0 (alignment substrate + new skills) ---"
|
|
1002
|
+
|
|
1003
|
+
# 79. CONTEXT.md template installs to qualia-templates/
|
|
1004
|
+
TMP=$(mktmp)
|
|
1005
|
+
echo "QS-FAWZI-01" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1006
|
+
if [ -f "$TMP/.claude/qualia-templates/CONTEXT.md" ]; then
|
|
1007
|
+
pass "CONTEXT.md template installs to qualia-templates/"
|
|
1008
|
+
else
|
|
1009
|
+
fail_case "CONTEXT.md template missing"
|
|
1010
|
+
fi
|
|
1011
|
+
|
|
1012
|
+
# 80. decisions/ADR-template.md installs (validates nested template dir copy)
|
|
1013
|
+
if [ -f "$TMP/.claude/qualia-templates/decisions/ADR-template.md" ]; then
|
|
1014
|
+
pass "decisions/ADR-template.md installs (nested template dir)"
|
|
1015
|
+
else
|
|
1016
|
+
fail_case "decisions/ADR-template.md missing"
|
|
1017
|
+
fi
|
|
1018
|
+
|
|
1019
|
+
# 81-84. The 4 new v5 skills install with their SKILL.md
|
|
1020
|
+
for SKILL in qualia-zoom qualia-road qualia-issues qualia-triage; do
|
|
1021
|
+
if [ -f "$TMP/.claude/skills/$SKILL/SKILL.md" ]; then
|
|
1022
|
+
pass "$SKILL skill installs"
|
|
1023
|
+
else
|
|
1024
|
+
fail_case "$SKILL skill missing after install"
|
|
1025
|
+
fi
|
|
1026
|
+
done
|
|
1027
|
+
|
|
1028
|
+
# 85. qualia-discuss SKILL.md mentions "grilling" (validates v5 fold of qualia-grill into discuss)
|
|
1029
|
+
if grep -qi "grilling" "$TMP/.claude/skills/qualia-discuss/SKILL.md"; then
|
|
1030
|
+
pass "qualia-discuss documents grilling pattern (folded from qualia-grill)"
|
|
1031
|
+
else
|
|
1032
|
+
fail_case "qualia-discuss missing grilling reference"
|
|
1033
|
+
fi
|
|
1034
|
+
|
|
1035
|
+
# 86. qualia-discuss SKILL.md references CONTEXT.md (validates v5 substrate wiring)
|
|
1036
|
+
if grep -q "CONTEXT.md" "$TMP/.claude/skills/qualia-discuss/SKILL.md"; then
|
|
1037
|
+
pass "qualia-discuss references CONTEXT.md"
|
|
1038
|
+
else
|
|
1039
|
+
fail_case "qualia-discuss missing CONTEXT.md reference"
|
|
1040
|
+
fi
|
|
1041
|
+
|
|
1042
|
+
# 87. qualia-road has disable-model-invocation: true (pure reference skill, user-only)
|
|
1043
|
+
if grep -q "disable-model-invocation: true" "$TMP/.claude/skills/qualia-road/SKILL.md"; then
|
|
1044
|
+
pass "qualia-road has disable-model-invocation: true"
|
|
1045
|
+
else
|
|
1046
|
+
fail_case "qualia-road missing disable-model-invocation"
|
|
1047
|
+
fi
|
|
1048
|
+
|
|
1049
|
+
# 88. qualia-road has NO dead skill refs (qualia-grill, qualia-deepen, qualia-tdd, qualia-onboard
|
|
1050
|
+
# were folded; if they appear as bare /qualia-* commands the road documentation is broken)
|
|
1051
|
+
if grep -qE '/qualia-(grill|deepen|tdd|onboard)\b' "$TMP/.claude/skills/qualia-road/SKILL.md"; then
|
|
1052
|
+
fail_case "qualia-road still references folded skills as commands"
|
|
1053
|
+
else
|
|
1054
|
+
pass "qualia-road has no dead-skill references"
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# 89. CONTEXT.md template has NO /qualia-grill reference (was folded into /qualia-discuss)
|
|
1058
|
+
if grep -q '/qualia-grill\b' "$TMP/.claude/qualia-templates/CONTEXT.md"; then
|
|
1059
|
+
fail_case "CONTEXT.md template still references /qualia-grill"
|
|
1060
|
+
else
|
|
1061
|
+
pass "CONTEXT.md template has no /qualia-grill reference"
|
|
1062
|
+
fi
|
|
1063
|
+
|
|
1064
|
+
# 90. Builder agent has trust-boundary block (defends against CONTEXT.md prompt injection)
|
|
1065
|
+
if grep -q "Trust boundary" "$TMP/.claude/agents/builder.md"; then
|
|
1066
|
+
pass "builder.md has Trust boundary block (prompt-injection defense)"
|
|
1067
|
+
else
|
|
1068
|
+
fail_case "builder.md missing Trust boundary block"
|
|
1069
|
+
fi
|
|
1070
|
+
|
|
1071
|
+
# 91. qualia-issues uses --body-file (not vulnerable heredoc) for gh issue create
|
|
1072
|
+
if grep -q -- "--body-file" "$TMP/.claude/skills/qualia-issues/SKILL.md" \
|
|
1073
|
+
&& ! grep -q "gh issue create.*--body \"\$(cat <<" "$TMP/.claude/skills/qualia-issues/SKILL.md"; then
|
|
1074
|
+
pass "qualia-issues uses --body-file (no heredoc shell injection)"
|
|
1075
|
+
else
|
|
1076
|
+
fail_case "qualia-issues still uses heredoc for gh issue body"
|
|
1077
|
+
fi
|
|
1078
|
+
|
|
1079
|
+
# 92-94. v5.0 insights-driven hooks parse as valid Node and install
|
|
1080
|
+
for HOOK in vercel-account-guard env-empty-guard supabase-destructive-guard; do
|
|
1081
|
+
HOOK_FILE="$TMP/.claude/hooks/${HOOK}.js"
|
|
1082
|
+
if [ -f "$HOOK_FILE" ]; then
|
|
1083
|
+
EXIT=0; $NODE -c "$HOOK_FILE" 2>/dev/null || EXIT=$?
|
|
1084
|
+
if [ "$EXIT" -eq 0 ]; then
|
|
1085
|
+
pass "${HOOK}.js installs and parses as valid Node"
|
|
1086
|
+
else
|
|
1087
|
+
fail_case "${HOOK}.js parse error"
|
|
1088
|
+
fi
|
|
1089
|
+
else
|
|
1090
|
+
fail_case "${HOOK}.js missing after install"
|
|
1091
|
+
fi
|
|
1092
|
+
done
|
|
1093
|
+
|
|
1094
|
+
# 95. cmdInit guards against suspicious project names (insights friction: throwaway projects)
|
|
1095
|
+
TMP_PROJ=$(mktemp -d)
|
|
1096
|
+
EXIT=0; OUT=$(cd "$TMP_PROJ" && $NODE "$STATE_JS" init --project "test" --phases '[{"name":"X","goal":"y"}]' 2>&1) || EXIT=$?
|
|
1097
|
+
if [ "$EXIT" -ne 0 ] && echo "$OUT" | grep -q "SUSPICIOUS_NAME"; then
|
|
1098
|
+
pass "state.js init blocks suspicious project name 'test' without --force"
|
|
1099
|
+
else
|
|
1100
|
+
fail_case "state.js init allowed 'test' as project name"
|
|
1101
|
+
fi
|
|
1102
|
+
rm -rf "$TMP_PROJ"
|
|
1103
|
+
|
|
1104
|
+
# 96. cmdInit allows real client name
|
|
1105
|
+
TMP_PROJ=$(mktemp -d)
|
|
1106
|
+
EXIT=0; OUT=$(cd "$TMP_PROJ" && $NODE "$STATE_JS" init --project "RealClient" --phases '[{"name":"X","goal":"y"}]' 2>&1) || EXIT=$?
|
|
1107
|
+
if [ "$EXIT" -eq 0 ] && echo "$OUT" | grep -q '"action": "init"'; then
|
|
1108
|
+
pass "state.js init allows real project name 'RealClient'"
|
|
1109
|
+
else
|
|
1110
|
+
fail_case "state.js init rejected real name" "exit=$EXIT"
|
|
1111
|
+
fi
|
|
1112
|
+
rm -rf "$TMP_PROJ"
|
|
1113
|
+
|
|
1114
|
+
# 97. cmdInit allows suspicious name with --force (escape hatch)
|
|
1115
|
+
TMP_PROJ=$(mktemp -d)
|
|
1116
|
+
EXIT=0; OUT=$(cd "$TMP_PROJ" && $NODE "$STATE_JS" init --project "test" --phases '[{"name":"X","goal":"y"}]' --force 2>&1) || EXIT=$?
|
|
1117
|
+
if [ "$EXIT" -eq 0 ]; then
|
|
1118
|
+
pass "state.js init --force bypasses suspicious-name guard"
|
|
1119
|
+
else
|
|
1120
|
+
fail_case "state.js init --force was blocked" "exit=$EXIT"
|
|
1121
|
+
fi
|
|
1122
|
+
rm -rf "$TMP_PROJ"
|
|
1123
|
+
|
|
1124
|
+
# 98. pre-deploy-gate honors QUALIA_SKIP_LINT escape (insights friction: lint blocks ship)
|
|
1125
|
+
if grep -q "QUALIA_SKIP_LINT" "$TMP/.claude/hooks/pre-deploy-gate.js"; then
|
|
1126
|
+
pass "pre-deploy-gate.js honors QUALIA_SKIP_LINT escape"
|
|
1127
|
+
else
|
|
1128
|
+
fail_case "pre-deploy-gate missing QUALIA_SKIP_LINT escape"
|
|
1129
|
+
fi
|
|
1130
|
+
|
|
1131
|
+
echo ""
|
|
1132
|
+
echo "--- v5.0.0 (visual-polish loop addendum) ---"
|
|
1133
|
+
|
|
1134
|
+
# 99. qualia-polish-loop SKILL.md installs
|
|
1135
|
+
TMP=$(mktmp)
|
|
1136
|
+
echo "QS-FAWZI-01" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1137
|
+
if [ -f "$TMP/.claude/skills/qualia-polish-loop/SKILL.md" ]; then
|
|
1138
|
+
pass "qualia-polish-loop SKILL.md installs"
|
|
1139
|
+
else
|
|
1140
|
+
fail_case "qualia-polish-loop SKILL.md missing after install"
|
|
1141
|
+
fi
|
|
1142
|
+
|
|
1143
|
+
# 100. qualia-polish-loop REFERENCE.md installs
|
|
1144
|
+
if [ -f "$TMP/.claude/skills/qualia-polish-loop/REFERENCE.md" ]; then
|
|
1145
|
+
pass "qualia-polish-loop REFERENCE.md installs"
|
|
1146
|
+
else
|
|
1147
|
+
fail_case "qualia-polish-loop REFERENCE.md missing after install"
|
|
1148
|
+
fi
|
|
1149
|
+
|
|
1150
|
+
# 101. qualia-polish-loop SKILL.md references the design rubric
|
|
1151
|
+
if grep -q "design-rubric.md" "$TMP/.claude/skills/qualia-polish-loop/SKILL.md"; then
|
|
1152
|
+
pass "qualia-polish-loop references design-rubric.md"
|
|
1153
|
+
else
|
|
1154
|
+
fail_case "qualia-polish-loop missing rubric reference"
|
|
1155
|
+
fi
|
|
1156
|
+
|
|
1157
|
+
# 102. qualia-polish-loop REFERENCE.md has the anchored vision eval prompt
|
|
1158
|
+
if grep -q "DEFAULT TO 3" "$TMP/.claude/skills/qualia-polish-loop/REFERENCE.md"; then
|
|
1159
|
+
pass "qualia-polish-loop REFERENCE.md has anchored rubric prompt"
|
|
1160
|
+
else
|
|
1161
|
+
fail_case "qualia-polish-loop REFERENCE.md missing anchored rubric"
|
|
1162
|
+
fi
|
|
1163
|
+
|
|
1164
|
+
# 103. qualia-polish-loop SKILL.md has regression detection (LOOP_REGRESSION_DETECTED)
|
|
1165
|
+
if grep -q "LOOP_REGRESSION_DETECTED" "$TMP/.claude/skills/qualia-polish-loop/SKILL.md"; then
|
|
1166
|
+
pass "qualia-polish-loop has regression kill-switch"
|
|
1167
|
+
else
|
|
1168
|
+
fail_case "qualia-polish-loop missing regression kill-switch"
|
|
1169
|
+
fi
|
|
1170
|
+
|
|
1171
|
+
# 104. score.mjs exists in the framework skill scripts
|
|
1172
|
+
if [ -f "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/score.mjs" ]; then
|
|
1173
|
+
pass "score.mjs exists in skill scripts"
|
|
1174
|
+
else
|
|
1175
|
+
fail_case "score.mjs missing from skill scripts"
|
|
1176
|
+
fi
|
|
1177
|
+
|
|
1178
|
+
# 105. score.mjs computes pass correctly (all dims >= 3)
|
|
1179
|
+
SCORE_OUT=$(echo '{"typography":3,"color":3,"spatial":3,"layout":3,"shadow":3,"motion":3,"microcopy":3,"container":3}' | $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/score.mjs" 2>&1)
|
|
1180
|
+
EXIT=$?
|
|
1181
|
+
if [ "$EXIT" -eq 0 ] && echo "$SCORE_OUT" | grep -q '"pass": true'; then
|
|
1182
|
+
pass "score.mjs pass on all-3 scores"
|
|
1183
|
+
else
|
|
1184
|
+
fail_case "score.mjs pass computation" "exit=$EXIT"
|
|
1185
|
+
fi
|
|
1186
|
+
|
|
1187
|
+
# 106. score.mjs computes fail correctly (one dim < 3)
|
|
1188
|
+
SCORE_OUT=$(echo '{"typography":2,"color":3,"spatial":3,"layout":3,"shadow":3,"motion":3,"microcopy":3,"container":3}' | $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/score.mjs" 2>&1)
|
|
1189
|
+
EXIT=$?
|
|
1190
|
+
if [ "$EXIT" -eq 1 ] && echo "$SCORE_OUT" | grep -q '"pass": false'; then
|
|
1191
|
+
pass "score.mjs fail on dim < 3"
|
|
1192
|
+
else
|
|
1193
|
+
fail_case "score.mjs fail computation" "exit=$EXIT"
|
|
1194
|
+
fi
|
|
1195
|
+
|
|
1196
|
+
# 107. qualia-road references qualia-polish-loop
|
|
1197
|
+
if grep -q "qualia-polish-loop" "$TMP/.claude/skills/qualia-road/SKILL.md"; then
|
|
1198
|
+
pass "qualia-road references qualia-polish-loop"
|
|
1199
|
+
else
|
|
1200
|
+
fail_case "qualia-road missing qualia-polish-loop reference"
|
|
1201
|
+
fi
|
|
1202
|
+
|
|
1203
|
+
# 108. package.json version is 5.x (5.1+ accepted; v5.1 / v5.2 share the v5 line)
|
|
1204
|
+
if grep -qE '"5\.[123]\.' "$FRAMEWORK_DIR/package.json"; then
|
|
1205
|
+
pass "package.json version is 5.x"
|
|
1206
|
+
else
|
|
1207
|
+
fail_case "package.json version not 5.x"
|
|
1208
|
+
fi
|
|
1209
|
+
|
|
1210
|
+
# 109. loop.mjs installs (orchestrator)
|
|
1211
|
+
if [ -f "$TMP/.claude/skills/qualia-polish-loop/scripts/loop.mjs" ]; then
|
|
1212
|
+
pass "qualia-polish-loop scripts/loop.mjs installs"
|
|
1213
|
+
else
|
|
1214
|
+
fail_case "scripts/loop.mjs missing — install.js scripts/ subfolder copy broken"
|
|
1215
|
+
fi
|
|
1216
|
+
|
|
1217
|
+
# 110. playwright-capture.mjs installs (capture helper)
|
|
1218
|
+
if [ -f "$TMP/.claude/skills/qualia-polish-loop/scripts/playwright-capture.mjs" ]; then
|
|
1219
|
+
pass "qualia-polish-loop scripts/playwright-capture.mjs installs"
|
|
1220
|
+
else
|
|
1221
|
+
fail_case "scripts/playwright-capture.mjs missing"
|
|
1222
|
+
fi
|
|
1223
|
+
|
|
1224
|
+
# 111. fixtures/ subfolder installs (self-test pages)
|
|
1225
|
+
if [ -f "$TMP/.claude/skills/qualia-polish-loop/fixtures/clean.html" ] && \
|
|
1226
|
+
[ -f "$TMP/.claude/skills/qualia-polish-loop/fixtures/broken.html" ]; then
|
|
1227
|
+
pass "qualia-polish-loop fixtures/ installs (clean.html + broken.html)"
|
|
1228
|
+
else
|
|
1229
|
+
fail_case "fixtures/ subfolder not copied by install.js"
|
|
1230
|
+
fi
|
|
1231
|
+
|
|
1232
|
+
# 112. visual-evaluator agent installs
|
|
1233
|
+
if [ -f "$TMP/.claude/agents/visual-evaluator.md" ]; then
|
|
1234
|
+
pass "agents/visual-evaluator.md installs"
|
|
1235
|
+
else
|
|
1236
|
+
fail_case "visual-evaluator agent missing after install"
|
|
1237
|
+
fi
|
|
1238
|
+
|
|
1239
|
+
# 113. loop.mjs parses as valid Node ESM
|
|
1240
|
+
EXIT=0; $NODE --check "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" 2>/dev/null || EXIT=$?
|
|
1241
|
+
if [ "$EXIT" -eq 0 ]; then
|
|
1242
|
+
pass "loop.mjs parses as valid Node ESM"
|
|
1243
|
+
else
|
|
1244
|
+
fail_case "loop.mjs parse error"
|
|
1245
|
+
fi
|
|
1246
|
+
|
|
1247
|
+
# 114. playwright-capture.mjs parses as valid Node ESM
|
|
1248
|
+
EXIT=0; $NODE --check "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/playwright-capture.mjs" 2>/dev/null || EXIT=$?
|
|
1249
|
+
if [ "$EXIT" -eq 0 ]; then
|
|
1250
|
+
pass "playwright-capture.mjs parses as valid Node ESM"
|
|
1251
|
+
else
|
|
1252
|
+
fail_case "playwright-capture.mjs parse error"
|
|
1253
|
+
fi
|
|
1254
|
+
|
|
1255
|
+
# 115. loop.mjs init creates a valid state file
|
|
1256
|
+
TMP_STATE=$(mktmp)/qpl-state.json
|
|
1257
|
+
mkdir -p "$(dirname "$TMP_STATE")"
|
|
1258
|
+
EXIT=0; $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1259
|
+
--state "$TMP_STATE" --url "http://localhost:3000" --max 4 --budget 50000 >/dev/null 2>&1 || EXIT=$?
|
|
1260
|
+
if [ "$EXIT" -eq 0 ] && [ -f "$TMP_STATE" ] && grep -q '"verdict": "pending"' "$TMP_STATE"; then
|
|
1261
|
+
pass "loop.mjs init creates valid state.json"
|
|
1262
|
+
else
|
|
1263
|
+
fail_case "loop.mjs init failed (exit=$EXIT, state present? $([ -f "$TMP_STATE" ] && echo yes || echo no))"
|
|
1264
|
+
fi
|
|
1265
|
+
|
|
1266
|
+
# 116. loop.mjs record kill-switch fires on 3 consecutive recurrences of same fingerprint
|
|
1267
|
+
TMP_STATE2=$(mktmp)/qpl-kill.json
|
|
1268
|
+
TMP_EVAL=$(mktmp)/qpl-eval.json
|
|
1269
|
+
mkdir -p "$(dirname "$TMP_STATE2")" "$(dirname "$TMP_EVAL")"
|
|
1270
|
+
$NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1271
|
+
--state "$TMP_STATE2" --url "http://localhost:3000" --max 8 >/dev/null 2>&1
|
|
1272
|
+
# write 3 identical evals
|
|
1273
|
+
KILL_OK=true
|
|
1274
|
+
for ITER in 1 2 3; do
|
|
1275
|
+
cat > "$TMP_EVAL" <<EOF
|
|
1276
|
+
{
|
|
1277
|
+
"iteration": $ITER,
|
|
1278
|
+
"tokens_used": 100,
|
|
1279
|
+
"viewport_results": [],
|
|
1280
|
+
"aggregate_scores": {"typography":1,"color":3,"spatial":3,"layout":3,"shadow":3,"motion":3,"microcopy":3,"container":3},
|
|
1281
|
+
"top_issues": [{"dim":"typography","severity":"critical","description":"banned font Inter visible","likely_file":"src/styles.css","fix":"replace"}],
|
|
1282
|
+
"pass": false
|
|
1283
|
+
}
|
|
1284
|
+
EOF
|
|
1285
|
+
EXIT=0; $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" record \
|
|
1286
|
+
--state "$TMP_STATE2" --eval "$TMP_EVAL" >/dev/null 2>&1 || EXIT=$?
|
|
1287
|
+
if [ "$ITER" -lt 3 ] && [ "$EXIT" -ne 1 ]; then KILL_OK=false; fi
|
|
1288
|
+
if [ "$ITER" -eq 3 ] && [ "$EXIT" -ne 3 ]; then KILL_OK=false; fi
|
|
1289
|
+
done
|
|
1290
|
+
if [ "$KILL_OK" = true ] && grep -q '"verdict": "killed_regression"' "$TMP_STATE2"; then
|
|
1291
|
+
pass "loop.mjs kill-switch fires after 3 consecutive recurrences"
|
|
1292
|
+
else
|
|
1293
|
+
fail_case "loop.mjs kill-switch did not fire correctly"
|
|
1294
|
+
fi
|
|
1295
|
+
|
|
1296
|
+
# 117. visual-evaluator agent has trust-boundary block (security-critical)
|
|
1297
|
+
if grep -q "Trust boundary" "$TMP/.claude/agents/visual-evaluator.md"; then
|
|
1298
|
+
pass "visual-evaluator has trust-boundary block (refuses inlined-file injection)"
|
|
1299
|
+
else
|
|
1300
|
+
fail_case "visual-evaluator missing trust-boundary block"
|
|
1301
|
+
fi
|
|
1302
|
+
|
|
1303
|
+
echo ""
|
|
1304
|
+
echo "--- v5.1.0 (multi-target install: Claude / Codex / Both) ---"
|
|
1305
|
+
|
|
1306
|
+
# 118. Target=1 (Claude only) installs to ~/.claude/, not ~/.codex/
|
|
1307
|
+
TMP=$(mktmp)
|
|
1308
|
+
printf 'QS-FAWZI-01\n1\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1309
|
+
EXIT=$?
|
|
1310
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1311
|
+
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
1312
|
+
&& [ ! -d "$TMP/.codex" ]; then
|
|
1313
|
+
pass "target=1 → ~/.claude/ populated, ~/.codex/ untouched"
|
|
1314
|
+
else
|
|
1315
|
+
fail_case "target=1 Claude-only" "exit=$EXIT codex_exists=$(test -d "$TMP/.codex" && echo yes || echo no)"
|
|
1316
|
+
fi
|
|
1317
|
+
|
|
1318
|
+
# 119. Target=2 (Codex only) writes ~/.codex/AGENTS.md, skips ~/.claude/
|
|
1319
|
+
TMP=$(mktmp)
|
|
1320
|
+
printf 'QS-FAWZI-01\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1321
|
+
EXIT=$?
|
|
1322
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1323
|
+
&& [ -f "$TMP/.codex/AGENTS.md" ] \
|
|
1324
|
+
&& [ ! -d "$TMP/.claude" ] \
|
|
1325
|
+
&& grep -q "Role: OWNER" "$TMP/.codex/AGENTS.md" \
|
|
1326
|
+
&& ! grep -q "{{ROLE}}" "$TMP/.codex/AGENTS.md"; then
|
|
1327
|
+
pass "target=2 → ~/.codex/AGENTS.md with Role: OWNER, ~/.claude/ skipped"
|
|
1328
|
+
else
|
|
1329
|
+
fail_case "target=2 Codex-only" "exit=$EXIT claude_exists=$(test -d "$TMP/.claude" && echo yes || echo no)"
|
|
1330
|
+
fi
|
|
1331
|
+
|
|
1332
|
+
# 120. Target=3 (Both) populates both directories with the right artifacts
|
|
1333
|
+
TMP=$(mktmp)
|
|
1334
|
+
printf 'QS-FAWZI-01\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1335
|
+
EXIT=$?
|
|
1336
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1337
|
+
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
1338
|
+
&& [ -f "$TMP/.codex/AGENTS.md" ] \
|
|
1339
|
+
&& grep -q "Role: OWNER" "$TMP/.codex/AGENTS.md"; then
|
|
1340
|
+
pass "target=3 → both ~/.claude/ and ~/.codex/AGENTS.md populated"
|
|
1341
|
+
else
|
|
1342
|
+
fail_case "target=3 Both" "exit=$EXIT"
|
|
1343
|
+
fi
|
|
1344
|
+
|
|
1345
|
+
# 121. Backward compat: legacy single-line piped install (no target line)
|
|
1346
|
+
# defaults to Claude only. Same exact invocation as pre-v5.1.
|
|
1347
|
+
TMP=$(mktmp)
|
|
1348
|
+
echo "QS-FAWZI-01" | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1349
|
+
EXIT=$?
|
|
1350
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1351
|
+
&& [ -f "$TMP/.claude/.qualia-config.json" ] \
|
|
1352
|
+
&& [ ! -d "$TMP/.codex" ]; then
|
|
1353
|
+
pass "legacy single-line piped install → defaults to Claude only"
|
|
1354
|
+
else
|
|
1355
|
+
fail_case "backward compat" "exit=$EXIT"
|
|
1356
|
+
fi
|
|
1357
|
+
|
|
1358
|
+
# 122. Codex AGENTS.md is backed up before overwrite (matches v5.0 CLAUDE.md
|
|
1359
|
+
# / settings.json discipline — never silently destroy user-edited files).
|
|
1360
|
+
TMP=$(mktmp)
|
|
1361
|
+
mkdir -p "$TMP/.codex"
|
|
1362
|
+
echo "OLD USER CONTENT" > "$TMP/.codex/AGENTS.md"
|
|
1363
|
+
printf 'QS-FAWZI-01\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1364
|
+
EXIT=$?
|
|
1365
|
+
BAK_COUNT=$(ls "$TMP/.codex/"AGENTS.md.bak.* 2>/dev/null | wc -l)
|
|
1366
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1367
|
+
&& [ "$BAK_COUNT" -ge 1 ] \
|
|
1368
|
+
&& grep -q "OLD USER CONTENT" "$TMP/.codex/"AGENTS.md.bak.* \
|
|
1369
|
+
&& ! grep -q "OLD USER CONTENT" "$TMP/.codex/AGENTS.md"; then
|
|
1370
|
+
pass "Codex AGENTS.md backup-before-overwrite preserves prior content"
|
|
1371
|
+
else
|
|
1372
|
+
fail_case "Codex backup discipline" "exit=$EXIT bak_count=$BAK_COUNT"
|
|
1373
|
+
fi
|
|
1374
|
+
|
|
1375
|
+
# 123. Codex install with same content does NOT create a redundant .bak
|
|
1376
|
+
# (the v5.0 CLAUDE.md backup discipline only backs up if content differs).
|
|
1377
|
+
TMP=$(mktmp)
|
|
1378
|
+
printf 'QS-FAWZI-01\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1379
|
+
# Re-run with same input — content should be identical, no new backup.
|
|
1380
|
+
printf 'QS-FAWZI-01\n2\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log2.txt" 2>&1
|
|
1381
|
+
BAK_COUNT=$(ls "$TMP/.codex/"AGENTS.md.bak.* 2>/dev/null | wc -l)
|
|
1382
|
+
if [ "$BAK_COUNT" -eq 0 ]; then
|
|
1383
|
+
pass "Codex re-install with identical content → no redundant .bak"
|
|
1384
|
+
else
|
|
1385
|
+
fail_case "Codex backup over-zealous" "bak_count=$BAK_COUNT"
|
|
1386
|
+
fi
|
|
1387
|
+
|
|
1388
|
+
# 124. Non-TTY install log is free of cursor-control / line-clear escape
|
|
1389
|
+
# sequences (\r, \x1b[?25l/h hide-cursor, \x1b[2K clear-line). Spinner +
|
|
1390
|
+
# overwrite primitives must degrade cleanly when output is piped.
|
|
1391
|
+
TMP=$(mktmp)
|
|
1392
|
+
printf 'QS-FAWZI-01\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1393
|
+
# Look for any of: bare CR, hide-cursor (?25), clear-line (\x1b[2K).
|
|
1394
|
+
if [ -s "$TMP/log.txt" ] \
|
|
1395
|
+
&& ! grep -q $'\r' "$TMP/log.txt" \
|
|
1396
|
+
&& ! grep -q '?25' "$TMP/log.txt" \
|
|
1397
|
+
&& ! grep -q '\[2K' "$TMP/log.txt"; then
|
|
1398
|
+
pass "non-TTY install log degrades cleanly (no cursor-control escapes)"
|
|
1399
|
+
else
|
|
1400
|
+
fail_case "non-TTY log has orphan escape sequences"
|
|
1401
|
+
fi
|
|
1402
|
+
|
|
1403
|
+
# 125. Final summary card includes the new "Targets" + "Time" rows
|
|
1404
|
+
TMP=$(mktmp)
|
|
1405
|
+
printf 'QS-FAWZI-01\n3\n' | HOME="$TMP" $NODE "$INSTALL_JS" > "$TMP/log.txt" 2>&1
|
|
1406
|
+
CLEAN=$(strip_ansi < "$TMP/log.txt")
|
|
1407
|
+
if echo "$CLEAN" | grep -q "Targets" \
|
|
1408
|
+
&& echo "$CLEAN" | grep -q "Claude Code · Codex" \
|
|
1409
|
+
&& echo "$CLEAN" | grep -qE "Time +[0-9]+(\.[0-9]+s|ms|s)"; then
|
|
1410
|
+
pass "summary card shows Targets + Time rows"
|
|
1411
|
+
else
|
|
1412
|
+
fail_case "summary card missing v5.1 rows"
|
|
1413
|
+
fi
|
|
1414
|
+
|
|
1415
|
+
# 126. qualia-ui.js exports the v5.1 live-progress primitives
|
|
1416
|
+
EXPORTED=$($NODE -e 'const u=require("'"$UI_JS"'"); console.log(["step","spinner","progress","box","kv","divider","section","sectionClose"].every(k=>typeof u[k]==="function") ? "ok" : "missing")')
|
|
1417
|
+
if [ "$EXPORTED" = "ok" ]; then
|
|
1418
|
+
pass "qualia-ui.js exports step/spinner/progress/box/kv/divider/section/sectionClose"
|
|
1419
|
+
else
|
|
1420
|
+
fail_case "qualia-ui exports missing" "got=$EXPORTED"
|
|
1421
|
+
fi
|
|
1422
|
+
|
|
1423
|
+
# 127. qualia-ui.js still works as CLI when invoked directly (require-vs-CLI gate)
|
|
1424
|
+
OUT=$($NODE "$UI_JS" ok "test" 2>&1)
|
|
1425
|
+
if echo "$OUT" | grep -q "test"; then
|
|
1426
|
+
pass "qualia-ui.js CLI dispatch still works after exports refactor"
|
|
1427
|
+
else
|
|
1428
|
+
fail_case "qualia-ui CLI broke"
|
|
1429
|
+
fi
|
|
1430
|
+
|
|
1431
|
+
# 128. package.json bumped to 5.x (5.1+ accepted; 5.2 is the v5.2 release)
|
|
1432
|
+
PKG_V=$($NODE -e 'console.log(require("'"$FRAMEWORK_DIR"'/package.json").version)')
|
|
1433
|
+
if echo "$PKG_V" | grep -qE "^5\.[123]\."; then
|
|
1434
|
+
pass "package.json version bumped to 5.x ($PKG_V)"
|
|
1435
|
+
else
|
|
1436
|
+
fail_case "package.json version not 5.x" "got=$PKG_V"
|
|
1437
|
+
fi
|
|
1438
|
+
|
|
1439
|
+
echo ""
|
|
1440
|
+
echo "--- v5.2.0 (polish-loop reliability) ---"
|
|
1441
|
+
|
|
1442
|
+
# 129. loop.mjs init accepts --routes and stores the URL list
|
|
1443
|
+
TMP_S=$(mktmp)/qpl-routes.json
|
|
1444
|
+
mkdir -p "$(dirname "$TMP_S")"
|
|
1445
|
+
EXIT=0; $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1446
|
+
--state "$TMP_S" \
|
|
1447
|
+
--routes "http://x.test/a,http://x.test/b,http://x.test/c" \
|
|
1448
|
+
--max 4 >/dev/null 2>&1 || EXIT=$?
|
|
1449
|
+
if [ "$EXIT" -eq 0 ] \
|
|
1450
|
+
&& grep -q '"urls"' "$TMP_S" \
|
|
1451
|
+
&& grep -q '"http://x.test/a"' "$TMP_S" \
|
|
1452
|
+
&& grep -q '"http://x.test/b"' "$TMP_S" \
|
|
1453
|
+
&& grep -q '"http://x.test/c"' "$TMP_S"; then
|
|
1454
|
+
pass "loop.mjs init --routes stores URL list (multi-route)"
|
|
1455
|
+
else
|
|
1456
|
+
fail_case "loop.mjs --routes failed (exit=$EXIT)"
|
|
1457
|
+
fi
|
|
1458
|
+
|
|
1459
|
+
# 130. state.url is the first --routes entry (backward compat with single-route SKILL.md)
|
|
1460
|
+
if grep -q '"url": "http://x.test/a"' "$TMP_S"; then
|
|
1461
|
+
pass "loop.mjs init --routes sets state.url = first URL (backward compat)"
|
|
1462
|
+
else
|
|
1463
|
+
fail_case "loop.mjs --routes did not set state.url to first entry"
|
|
1464
|
+
fi
|
|
1465
|
+
|
|
1466
|
+
# 131. loop.mjs init accepts --reduced-motion and records it in state
|
|
1467
|
+
TMP_S2=$(mktmp)/qpl-rm.json
|
|
1468
|
+
mkdir -p "$(dirname "$TMP_S2")"
|
|
1469
|
+
EXIT=0; $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1470
|
+
--state "$TMP_S2" --url "http://x.test/" --reduced-motion >/dev/null 2>&1 || EXIT=$?
|
|
1471
|
+
if [ "$EXIT" -eq 0 ] && grep -q '"reduced_motion": true' "$TMP_S2"; then
|
|
1472
|
+
pass "loop.mjs init --reduced-motion records state.reduced_motion=true"
|
|
1473
|
+
else
|
|
1474
|
+
fail_case "loop.mjs --reduced-motion not recorded (exit=$EXIT)"
|
|
1475
|
+
fi
|
|
1476
|
+
|
|
1477
|
+
# 132. playwright-capture.mjs accepts --reduced-motion (parses without error)
|
|
1478
|
+
EXIT=0; OUT=$($NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/playwright-capture.mjs" --help 2>&1) || EXIT=$?
|
|
1479
|
+
if [ "$EXIT" -eq 0 ] && echo "$OUT" | grep -q -- "--reduced-motion"; then
|
|
1480
|
+
pass "playwright-capture.mjs --help documents --reduced-motion"
|
|
1481
|
+
else
|
|
1482
|
+
fail_case "playwright-capture --reduced-motion not in --help"
|
|
1483
|
+
fi
|
|
1484
|
+
|
|
1485
|
+
# 133. loop.mjs init rejects when neither --url nor --routes given
|
|
1486
|
+
TMP_S3=$(mktmp)/qpl-nourl.json
|
|
1487
|
+
mkdir -p "$(dirname "$TMP_S3")"
|
|
1488
|
+
EXIT=0; $NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1489
|
+
--state "$TMP_S3" --max 4 >/dev/null 2>&1 || EXIT=$?
|
|
1490
|
+
if [ "$EXIT" -eq 2 ]; then
|
|
1491
|
+
pass "loop.mjs init rejects missing --url/--routes (exit 2)"
|
|
1492
|
+
else
|
|
1493
|
+
fail_case "loop.mjs init did not reject missing URL (exit=$EXIT)"
|
|
1494
|
+
fi
|
|
1495
|
+
|
|
1496
|
+
# 134. loop.mjs report mentions multi-route when state.urls > 1
|
|
1497
|
+
TMP_S4=$(mktmp)/qpl-rep.json
|
|
1498
|
+
mkdir -p "$(dirname "$TMP_S4")"
|
|
1499
|
+
$NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" init \
|
|
1500
|
+
--state "$TMP_S4" --routes "http://a/,http://b/" >/dev/null 2>&1
|
|
1501
|
+
REP=$($NODE "$FRAMEWORK_DIR/skills/qualia-polish-loop/scripts/loop.mjs" report --state "$TMP_S4" 2>&1)
|
|
1502
|
+
if echo "$REP" | grep -q "URLs (2)"; then
|
|
1503
|
+
pass "loop.mjs report renders multi-route header"
|
|
1504
|
+
else
|
|
1505
|
+
fail_case "loop.mjs report missing multi-route header"
|
|
1506
|
+
fi
|
|
1507
|
+
|
|
1508
|
+
echo ""
|
|
1509
|
+
echo "--- v5.3.0 (Matt Pocock gaps: prd, hook-gen, parallel-interface) ---"
|
|
1510
|
+
|
|
1511
|
+
# Re-install for v5.3 assertions (TMP from #99 may have v5.1 state)
|
|
1512
|
+
TMP=$(mktmp)
|
|
1513
|
+
echo "QS-FAWZI-01" | HOME="$TMP" $NODE "$INSTALL_JS" >/dev/null 2>&1
|
|
1514
|
+
|
|
1515
|
+
# 135. qualia-prd skill installs
|
|
1516
|
+
if [ -f "$TMP/.claude/skills/qualia-prd/SKILL.md" ]; then
|
|
1517
|
+
pass "qualia-prd skill installs"
|
|
1518
|
+
else
|
|
1519
|
+
fail_case "qualia-prd SKILL.md missing after install"
|
|
1520
|
+
fi
|
|
1521
|
+
|
|
1522
|
+
# 136. qualia-prd description mentions PRD synthesis from conversation
|
|
1523
|
+
if grep -q "synthesize\|Synthesize" "$TMP/.claude/skills/qualia-prd/SKILL.md" \
|
|
1524
|
+
&& grep -q "/qualia-issues" "$TMP/.claude/skills/qualia-prd/SKILL.md"; then
|
|
1525
|
+
pass "qualia-prd describes synthesis flow + pairs with /qualia-issues"
|
|
1526
|
+
else
|
|
1527
|
+
fail_case "qualia-prd missing synthesis or /qualia-issues link"
|
|
1528
|
+
fi
|
|
1529
|
+
|
|
1530
|
+
# 137. qualia-prd documents fork-based token discipline
|
|
1531
|
+
if grep -qE "[Ff]orked subagent|fork.*subagent" "$TMP/.claude/skills/qualia-prd/SKILL.md" \
|
|
1532
|
+
&& grep -q "Token discipline" "$TMP/.claude/skills/qualia-prd/SKILL.md"; then
|
|
1533
|
+
pass "qualia-prd documents fork-based synthesis (token discipline)"
|
|
1534
|
+
else
|
|
1535
|
+
fail_case "qualia-prd missing fork/token-discipline section"
|
|
1536
|
+
fi
|
|
1537
|
+
|
|
1538
|
+
# 138. qualia-hook-gen skill installs
|
|
1539
|
+
if [ -f "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" ]; then
|
|
1540
|
+
pass "qualia-hook-gen skill installs"
|
|
1541
|
+
else
|
|
1542
|
+
fail_case "qualia-hook-gen SKILL.md missing after install"
|
|
1543
|
+
fi
|
|
1544
|
+
|
|
1545
|
+
# 139. qualia-hook-gen documents the three enforcement patterns (block/rewrite/warn)
|
|
1546
|
+
if grep -q "Block" "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" \
|
|
1547
|
+
&& grep -q "Rewrite" "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" \
|
|
1548
|
+
&& grep -q "Warn" "$TMP/.claude/skills/qualia-hook-gen/SKILL.md"; then
|
|
1549
|
+
pass "qualia-hook-gen documents block/rewrite/warn patterns"
|
|
1550
|
+
else
|
|
1551
|
+
fail_case "qualia-hook-gen missing one of block/rewrite/warn patterns"
|
|
1552
|
+
fi
|
|
1553
|
+
|
|
1554
|
+
# 140. qualia-hook-gen mandates Node hooks (cross-platform), not .sh scripts
|
|
1555
|
+
if grep -q "pure Node\|pure-node\|cross-platform" "$TMP/.claude/skills/qualia-hook-gen/SKILL.md" \
|
|
1556
|
+
&& grep -q "No \`.sh\`\|No \\.sh\|exit 0/2\|exit 2 to" "$TMP/.claude/skills/qualia-hook-gen/SKILL.md"; then
|
|
1557
|
+
pass "qualia-hook-gen mandates pure-Node hooks (cross-platform discipline)"
|
|
1558
|
+
else
|
|
1559
|
+
fail_case "qualia-hook-gen missing pure-Node mandate"
|
|
1560
|
+
fi
|
|
1561
|
+
|
|
1562
|
+
# 141. qualia-optimize SKILL.md adds Step 5b (parallel-interface design)
|
|
1563
|
+
if grep -q "Step 5b\|Parallel Interface Design\|Wave 3" "$TMP/.claude/skills/qualia-optimize/SKILL.md" \
|
|
1564
|
+
&& grep -q "radically different" "$TMP/.claude/skills/qualia-optimize/SKILL.md"; then
|
|
1565
|
+
pass "qualia-optimize Step 5b parallel-interface fan-out documented"
|
|
1566
|
+
else
|
|
1567
|
+
fail_case "qualia-optimize missing Step 5b parallel-interface stage"
|
|
1568
|
+
fi
|
|
1569
|
+
|
|
1570
|
+
# 142. qualia-optimize REFERENCE.md has the parallel-interface spawn template
|
|
1571
|
+
if grep -q "Parallel interface design prompt" "$TMP/.claude/skills/qualia-optimize/REFERENCE.md" \
|
|
1572
|
+
&& grep -q "Variant 1\|variant 1" "$TMP/.claude/skills/qualia-optimize/REFERENCE.md"; then
|
|
1573
|
+
pass "qualia-optimize REFERENCE.md has parallel-interface spawn template"
|
|
1574
|
+
else
|
|
1575
|
+
fail_case "qualia-optimize REFERENCE.md missing parallel-interface template"
|
|
1576
|
+
fi
|
|
1577
|
+
|
|
1578
|
+
# 143. package.json version is 5.x (5.1+ accepted; v5.3 is the v5.3 release)
|
|
1579
|
+
PKG_V=$($NODE -e 'console.log(require("'"$FRAMEWORK_DIR"'/package.json").version)')
|
|
1580
|
+
if echo "$PKG_V" | grep -qE "^5\.[123]\."; then
|
|
1581
|
+
pass "package.json version is 5.x ($PKG_V) — v5.3 accepted"
|
|
1582
|
+
else
|
|
1583
|
+
fail_case "package.json version not 5.x" "got=$PKG_V"
|
|
1584
|
+
fi
|
|
1585
|
+
|
|
995
1586
|
echo ""
|
|
996
1587
|
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
997
1588
|
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|