qualia-framework 4.5.0 → 5.1.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-review-2026-05-03.md +65 -0
- package/docs/playwright-loop-tester-prompt.md +213 -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-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 +202 -0
- package/skills/qualia-optimize/SKILL.md +72 -237
- 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 +302 -0
- package/skills/qualia-polish-loop/scripts/playwright-capture.mjs +197 -0
- package/skills/qualia-polish-loop/scripts/score.mjs +176 -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 +451 -7
- package/tests/state.test.sh +58 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qualia-zoom
|
|
3
|
+
description: "Zoom out and map an unfamiliar code area in domain-glossary terms — list the modules, who calls them, what they hide, and the smallest safe edit window. User-fired only (never auto). Use when you don't know an area of code well, before editing an unfamiliar file, or when the user says 'zoom out', 'map this area', 'show me the bigger picture', 'I don't know this code'."
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Grep
|
|
8
|
+
- Glob
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# /qualia-zoom — Map an Unfamiliar Area
|
|
12
|
+
|
|
13
|
+
I don't know this area of code well. Go up a layer of abstraction.
|
|
14
|
+
|
|
15
|
+
Give me a map of:
|
|
16
|
+
|
|
17
|
+
1. **Modules** in this area — what they expose, what they hide
|
|
18
|
+
2. **Callers** — who uses these modules and from where
|
|
19
|
+
3. **Dependencies** — what these modules pull in
|
|
20
|
+
4. **Domain terms involved** — quoted from `.planning/CONTEXT.md` if it exists
|
|
21
|
+
|
|
22
|
+
Use the **vocabulary in `.planning/CONTEXT.md`** wherever applicable. If a term in this code conflicts with the glossary, flag it for `/qualia-discuss` (which now grills + updates CONTEXT.md inline).
|
|
23
|
+
|
|
24
|
+
## Output format
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
## Where you are
|
|
28
|
+
{file or path being zoomed out from}
|
|
29
|
+
|
|
30
|
+
## Module map
|
|
31
|
+
- {module/file} — exposes: {x} — hides: {y}
|
|
32
|
+
- ...
|
|
33
|
+
|
|
34
|
+
## Caller map
|
|
35
|
+
- {caller} → {module} (used for {purpose})
|
|
36
|
+
- ...
|
|
37
|
+
|
|
38
|
+
## Domain terms
|
|
39
|
+
- {term from glossary} — appears in {files}
|
|
40
|
+
- {term NOT in glossary but present in code} — flag for /qualia-discuss
|
|
41
|
+
|
|
42
|
+
## Smallest safe edit window
|
|
43
|
+
{which files form the natural unit for this change}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Rules
|
|
47
|
+
|
|
48
|
+
- Keep the report under **50 lines**. Orient, don't refactor.
|
|
49
|
+
- **Don't propose changes** — that's the next skill's job.
|
|
50
|
+
- Read `.planning/CONTEXT.md` first if it exists. Use its language.
|
|
51
|
+
- If the area is huge, pick the entry point the user named and zoom from there.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zoho-workflow
|
|
3
|
-
description: Zoho Invoice and Mail operations -
|
|
3
|
+
description: "Zoho Invoice and Mail operations via ERP-first routing. Creates invoices from templates, sends cover emails, manages contacts, reads inbox, and handles payment reminders. Use when the user says 'invoice this client', 'send an email', 'check inbox', 'create a Zoho contact', 'send payment reminder', or mentions invoicing, billing, or Zoho."
|
|
4
4
|
tags: [zoho, invoice, email, billing, crm]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Domain Glossary
|
|
2
|
+
|
|
3
|
+
<!-- Domain glossary. Loaded by every road agent. Update via /qualia-discuss. Keep entries terse (1 sentence + Avoid line). -->
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
|
|
7
|
+
### Term Name
|
|
8
|
+
One-sentence definition. Use the term consistently in code, comments, commits, and conversation.
|
|
9
|
+
**Avoid:** alternative terms that mean the same thing but aren't this one.
|
|
10
|
+
|
|
11
|
+
### {{Add terms as the project clarifies them. Examples below.}}
|
|
12
|
+
|
|
13
|
+
### User
|
|
14
|
+
**Avoid until disambiguated.** Currently overloaded — see "Flagged ambiguities" below.
|
|
15
|
+
|
|
16
|
+
### Phase
|
|
17
|
+
A unit of work inside a milestone. 2–5 tasks. Ends in a verification gate.
|
|
18
|
+
**Avoid:** epic, story, ticket, sprint.
|
|
19
|
+
|
|
20
|
+
### Task
|
|
21
|
+
A single commit-sized unit with one verification contract.
|
|
22
|
+
**Avoid:** subtask, chore, todo.
|
|
23
|
+
|
|
24
|
+
## Relationships
|
|
25
|
+
- Project holds many Milestones
|
|
26
|
+
- Milestone holds many Phases
|
|
27
|
+
- Phase holds many Tasks
|
|
28
|
+
- Task carries one Verification Contract
|
|
29
|
+
- {{add domain-specific relationships, e.g. "Customer holds many Orders"}}
|
|
30
|
+
|
|
31
|
+
## Flagged ambiguities
|
|
32
|
+
|
|
33
|
+
### "User" → AuthUser vs Customer
|
|
34
|
+
- **AuthUser** = the row in `auth.users` (Supabase)
|
|
35
|
+
- **Customer** = the profile row owned by an AuthUser (1:1)
|
|
36
|
+
- {{replace with real ambiguities as they appear}}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ADR-{{NNNN}} — {{Decision title in present tense}}
|
|
2
|
+
|
|
3
|
+
- **Date:** {{YYYY-MM-DD}}
|
|
4
|
+
- **Phase:** {{N}} — {{phase name}}
|
|
5
|
+
- **Status:** Proposed | Accepted | Superseded by ADR-XXXX
|
|
6
|
+
- **Domain terms:** {{any new/affected terms — also update CONTEXT.md}}
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
What is the situation that requires a decision? Why are we deciding this now? What constraints apply (regulatory, performance, team skill, deadlines)?
|
|
10
|
+
|
|
11
|
+
## Decision
|
|
12
|
+
What we are doing. State it as a single declarative sentence first, then expand.
|
|
13
|
+
|
|
14
|
+
## Consequences
|
|
15
|
+
- **What becomes easier:** ...
|
|
16
|
+
- **What becomes harder:** ...
|
|
17
|
+
- **What is now load-bearing on this decision:** ...
|
|
18
|
+
|
|
19
|
+
## Alternatives considered
|
|
20
|
+
Why each alternative was rejected — keep this honest. Future-you will read it.
|
|
21
|
+
|
|
22
|
+
- **Alt A** — {{rejected because…}}
|
|
23
|
+
- **Alt B** — {{rejected because…}}
|
|
24
|
+
|
|
25
|
+
## Notes
|
|
26
|
+
Any non-obvious context, links to discussion threads, related issues. Optional.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
**When to write an ADR (per Matt Pocock's rule):** only when the decision is hard-to-reverse, surprising-without-context, AND involves real trade-offs. If any of those three is missing, just put it in CONTEXT.md or a phase plan instead. ADRs are scarce on purpose.
|
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,445 @@ 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.1.x (multi-target install + polish-loop in v5.x line)
|
|
1204
|
+
if grep -qE '"5\.1\.' "$FRAMEWORK_DIR/package.json"; then
|
|
1205
|
+
pass "package.json version is 5.1.x"
|
|
1206
|
+
else
|
|
1207
|
+
fail_case "package.json version not 5.1.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.1.x
|
|
1432
|
+
PKG_V=$($NODE -e 'console.log(require("'"$FRAMEWORK_DIR"'/package.json").version)')
|
|
1433
|
+
if echo "$PKG_V" | grep -qE "^5\.1\."; then
|
|
1434
|
+
pass "package.json version bumped to 5.1.x ($PKG_V)"
|
|
1435
|
+
else
|
|
1436
|
+
fail_case "package.json version not bumped to 5.1.x" "got=$PKG_V"
|
|
1437
|
+
fi
|
|
1438
|
+
|
|
995
1439
|
echo ""
|
|
996
1440
|
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
997
1441
|
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|