qualia-framework 6.2.9 → 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 +26 -30
- package/agents/builder.md +7 -7
- package/agents/planner.md +39 -3
- package/agents/research-synthesizer.md +1 -1
- package/agents/researcher.md +3 -3
- package/agents/roadmapper.md +7 -7
- package/agents/verifier.md +18 -6
- package/agents/visual-evaluator.md +8 -7
- package/bin/cli.js +160 -16
- package/bin/command-surface.js +71 -0
- package/bin/contract-runner.js +219 -0
- package/bin/harness-eval.js +296 -0
- package/bin/host-adapters.js +66 -0
- package/bin/install.js +116 -172
- package/bin/knowledge-flush.js +21 -10
- package/bin/knowledge.js +1 -1
- package/bin/plan-contract.js +99 -2
- package/bin/planning-hygiene.js +262 -0
- package/bin/project-snapshot.js +20 -0
- package/bin/report-payload.js +18 -0
- package/bin/runtime-manifest.js +35 -0
- package/bin/state-ledger.js +184 -0
- package/bin/state.js +330 -20
- package/bin/trust-score.js +268 -0
- package/bin/work-packet.js +228 -0
- package/docs/erp-contract.md +81 -1
- package/docs/onboarding.html +4 -14
- package/guide.md +16 -16
- 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/design-rubric.md +17 -5
- package/qualia-design/frontend.md +6 -2
- package/qualia-design/graphics.md +47 -0
- package/rules/codex-goal.md +1 -1
- package/rules/command-output.md +35 -0
- package/rules/one-opinion.md +2 -2
- package/rules/speed.md +0 -1
- package/skills/qualia/SKILL.md +12 -12
- package/skills/qualia-build/SKILL.md +20 -14
- package/skills/qualia-discuss/SKILL.md +10 -10
- package/skills/qualia-doctor/SKILL.md +140 -0
- package/skills/qualia-feature/SKILL.md +24 -22
- package/skills/qualia-fix/SKILL.md +216 -0
- package/skills/qualia-handoff/SKILL.md +9 -9
- package/skills/qualia-learn/SKILL.md +11 -11
- package/skills/qualia-map/SKILL.md +2 -2
- package/skills/qualia-milestone/SKILL.md +15 -15
- package/skills/qualia-new/REFERENCE.md +9 -9
- package/skills/qualia-new/SKILL.md +14 -14
- package/skills/qualia-optimize/REFERENCE.md +1 -1
- package/skills/qualia-optimize/SKILL.md +23 -16
- package/skills/qualia-plan/SKILL.md +23 -13
- package/skills/qualia-polish/REFERENCE.md +15 -15
- package/skills/qualia-polish/SKILL.md +81 -21
- package/skills/qualia-polish/scripts/loop.mjs +3 -3
- package/skills/qualia-polish/scripts/score.mjs +9 -3
- package/skills/{qualia-vibe/scripts/extract.mjs → qualia-polish/scripts/vibe-extract.mjs} +5 -5
- package/skills/{qualia-vibe/scripts/tokens.mjs → qualia-polish/scripts/vibe-tokens.mjs} +6 -6
- package/skills/qualia-postmortem/SKILL.md +9 -9
- package/skills/qualia-report/SKILL.md +23 -23
- package/skills/qualia-research/SKILL.md +5 -5
- package/skills/qualia-review/SKILL.md +28 -12
- package/skills/qualia-road/SKILL.md +30 -22
- package/skills/qualia-ship/SKILL.md +31 -24
- package/skills/qualia-test/SKILL.md +5 -5
- package/skills/qualia-verify/SKILL.md +45 -23
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/help.html +11 -20
- package/tests/bin.test.sh +178 -76
- package/tests/hooks.test.sh +81 -1
- package/tests/install-smoke.test.sh +35 -5
- package/tests/lib.test.sh +432 -0
- package/tests/published-install-smoke.test.sh +4 -3
- package/tests/refs.test.sh +9 -4
- package/tests/runner.js +32 -28
- package/tests/skills.test.sh +4 -4
- package/tests/state.test.sh +133 -3
- package/skills/qualia-debug/SKILL.md +0 -185
- 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 -226
- package/skills/qualia-zoom/SKILL.md +0 -51
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
|
|
@@ -61,8 +61,15 @@ fi
|
|
|
61
61
|
|
|
62
62
|
tar -xzf "$TARBALL_PATH" -C "$TMP" 2>"$TMP/tar.err"
|
|
63
63
|
if [ -f "$TMP/package/bin/install.js" ] \
|
|
64
|
+
&& [ -f "$TMP/package/bin/runtime-manifest.js" ] \
|
|
65
|
+
&& [ -f "$TMP/package/bin/command-surface.js" ] \
|
|
66
|
+
&& [ -f "$TMP/package/bin/host-adapters.js" ] \
|
|
64
67
|
&& [ -f "$TMP/package/bin/report-payload.js" ] \
|
|
68
|
+
&& [ -f "$TMP/package/bin/work-packet.js" ] \
|
|
65
69
|
&& [ -f "$TMP/package/bin/project-snapshot.js" ] \
|
|
70
|
+
&& [ -f "$TMP/package/bin/harness-eval.js" ] \
|
|
71
|
+
&& [ -f "$TMP/package/bin/planning-hygiene.js" ] \
|
|
72
|
+
&& [ -f "$TMP/package/bin/state-ledger.js" ] \
|
|
66
73
|
&& [ -f "$TMP/package/AGENTS.md" ] \
|
|
67
74
|
&& [ -f "$TMP/package/CLAUDE.md" ]; then
|
|
68
75
|
pass "tarball contains installer + Claude/Codex instruction roots"
|
|
@@ -70,7 +77,7 @@ else
|
|
|
70
77
|
fail_case "tarball missing install surfaces" "$(cat "$TMP/tar.err" 2>/dev/null)"
|
|
71
78
|
fi
|
|
72
79
|
|
|
73
|
-
printf 'QS-FAWZI-
|
|
80
|
+
printf 'QS-FAWZI-11\n3\n' | HOME="$HOME_DIR" "$NODE" "$TMP/package/bin/install.js" >"$TMP/install.log" 2>&1
|
|
74
81
|
EXIT=$?
|
|
75
82
|
if [ "$EXIT" -eq 0 ]; then
|
|
76
83
|
pass "packaged installer exits 0 for target=Both"
|
|
@@ -89,14 +96,27 @@ fi
|
|
|
89
96
|
if [ -f "$HOME_DIR/.codex/AGENTS.md" ] \
|
|
90
97
|
&& [ -f "$HOME_DIR/.codex/hooks.json" ] \
|
|
91
98
|
&& [ -f "$HOME_DIR/.codex/config.toml" ] \
|
|
99
|
+
&& [ -f "$HOME_DIR/.codex/bin/runtime-manifest.js" ] \
|
|
100
|
+
&& [ -f "$HOME_DIR/.codex/bin/command-surface.js" ] \
|
|
101
|
+
&& [ -f "$HOME_DIR/.codex/bin/host-adapters.js" ] \
|
|
92
102
|
&& [ -f "$HOME_DIR/.codex/bin/statusline.js" ] \
|
|
103
|
+
&& [ -f "$HOME_DIR/.codex/bin/state-ledger.js" ] \
|
|
104
|
+
&& [ -f "$HOME_DIR/.codex/bin/contract-runner.js" ] \
|
|
105
|
+
&& [ -f "$HOME_DIR/.codex/bin/harness-eval.js" ] \
|
|
106
|
+
&& [ -f "$HOME_DIR/.codex/bin/trust-score.js" ] \
|
|
107
|
+
&& [ -f "$HOME_DIR/.codex/bin/work-packet.js" ] \
|
|
93
108
|
&& [ -f "$HOME_DIR/.codex/bin/project-snapshot.js" ] \
|
|
109
|
+
&& [ -f "$HOME_DIR/.codex/bin/planning-hygiene.js" ] \
|
|
94
110
|
&& [ -f "$HOME_DIR/.codex/agents/planner.toml" ] \
|
|
95
111
|
&& [ -f "$HOME_DIR/.codex/skills/qualia-new/SKILL.md" ] \
|
|
96
112
|
&& [ -f "$HOME_DIR/.codex/qualia-references/questioning.md" ] \
|
|
97
113
|
&& grep -q "Role: OWNER" "$HOME_DIR/.codex/AGENTS.md" \
|
|
98
114
|
&& ! grep -q "{{ROLE}}" "$HOME_DIR/.codex/AGENTS.md" \
|
|
115
|
+
&& grep -q "status_line" "$HOME_DIR/.codex/config.toml" \
|
|
116
|
+
&& grep -q "model-with-reasoning" "$HOME_DIR/.codex/config.toml" \
|
|
117
|
+
&& grep -q "task-progress" "$HOME_DIR/.codex/config.toml" \
|
|
99
118
|
&& grep -q "pre-deploy-gate.js" "$HOME_DIR/.codex/hooks.json" \
|
|
119
|
+
&& ! grep -R "\${QUALIA_BIN}" "$HOME_DIR/.codex/skills" >/dev/null 2>&1 \
|
|
100
120
|
&& ! grep -R "\.claude/bin" "$HOME_DIR/.codex/skills" >/dev/null 2>&1; then
|
|
101
121
|
pass "Codex runtime installed with OWNER role"
|
|
102
122
|
else
|
|
@@ -104,19 +124,29 @@ else
|
|
|
104
124
|
fi
|
|
105
125
|
|
|
106
126
|
if [ -d "$HOME_DIR/.claude/hooks" ] \
|
|
107
|
-
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "
|
|
127
|
+
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "12" ] \
|
|
128
|
+
&& [ -f "$HOME_DIR/.claude/hooks/fawzi-approval-guard.js" ] \
|
|
108
129
|
&& [ ! -f "$HOME_DIR/.claude/hooks/pre-compact.js" ]; then
|
|
109
|
-
pass "packaged install has
|
|
130
|
+
pass "packaged install has 12 hooks and no pre-compact"
|
|
110
131
|
else
|
|
111
132
|
fail_case "packaged hook set mismatch"
|
|
112
133
|
fi
|
|
113
134
|
|
|
114
135
|
if [ -f "$HOME_DIR/.claude/bin/report-payload.js" ] \
|
|
136
|
+
&& [ -f "$HOME_DIR/.claude/bin/work-packet.js" ] \
|
|
115
137
|
&& [ -f "$HOME_DIR/.claude/bin/project-snapshot.js" ] \
|
|
138
|
+
&& [ -f "$HOME_DIR/.claude/bin/runtime-manifest.js" ] \
|
|
139
|
+
&& [ -f "$HOME_DIR/.claude/bin/command-surface.js" ] \
|
|
140
|
+
&& [ -f "$HOME_DIR/.claude/bin/host-adapters.js" ] \
|
|
141
|
+
&& [ -f "$HOME_DIR/.claude/bin/state-ledger.js" ] \
|
|
142
|
+
&& [ -f "$HOME_DIR/.claude/bin/contract-runner.js" ] \
|
|
143
|
+
&& [ -f "$HOME_DIR/.claude/bin/harness-eval.js" ] \
|
|
144
|
+
&& [ -f "$HOME_DIR/.claude/bin/trust-score.js" ] \
|
|
145
|
+
&& [ -f "$HOME_DIR/.claude/bin/planning-hygiene.js" ] \
|
|
116
146
|
&& grep -q "report-payload.js" "$HOME_DIR/.claude/skills/qualia-report/SKILL.md"; then
|
|
117
|
-
pass "packaged install includes ERP report/snapshot helpers"
|
|
147
|
+
pass "packaged install includes ERP report/snapshot + contract helpers"
|
|
118
148
|
else
|
|
119
|
-
fail_case "packaged install missing ERP report/snapshot helpers"
|
|
149
|
+
fail_case "packaged install missing ERP report/snapshot/contract helpers"
|
|
120
150
|
fi
|
|
121
151
|
|
|
122
152
|
PKG_VERSION=$("$NODE" -e "console.log(require(process.argv[1]).version)" "$TMP/package/package.json")
|
package/tests/lib.test.sh
CHANGED
|
@@ -149,6 +149,123 @@ console.log(d2.drift ? "DRIFT-DETECTED" : "FAIL2");
|
|
|
149
149
|
' | tail -1 > /tmp/qfdrift.out
|
|
150
150
|
[ "$(cat /tmp/qfdrift.out)" = "DRIFT-DETECTED" ] && ok "drift detection" || fail "drift detection: $(cat /tmp/qfdrift.out)"
|
|
151
151
|
|
|
152
|
+
# CLI validate
|
|
153
|
+
TMP=$(mktmp)
|
|
154
|
+
cat > "$TMP/contract.json" <<JSON
|
|
155
|
+
{
|
|
156
|
+
"version": 1,
|
|
157
|
+
"phase": 1,
|
|
158
|
+
"goal": "contract cli",
|
|
159
|
+
"why": "prove validator is executable",
|
|
160
|
+
"generated_at": "2026-05-23T00:00:00Z",
|
|
161
|
+
"generated_by": "manual",
|
|
162
|
+
"source_plan_hash": "",
|
|
163
|
+
"success_criteria": ["contract validates"],
|
|
164
|
+
"tasks": [{
|
|
165
|
+
"id": "T1",
|
|
166
|
+
"title": "Create evidence",
|
|
167
|
+
"wave": 1,
|
|
168
|
+
"depends_on": [],
|
|
169
|
+
"files_modify": [],
|
|
170
|
+
"files_create": ["out.txt"],
|
|
171
|
+
"files_delete": [],
|
|
172
|
+
"acceptance_criteria": ["out.txt exists"],
|
|
173
|
+
"action": "Create out.txt",
|
|
174
|
+
"context_files": [],
|
|
175
|
+
"verification": [{ "type": "file-exists", "path": "out.txt" }]
|
|
176
|
+
}]
|
|
177
|
+
}
|
|
178
|
+
JSON
|
|
179
|
+
OUT=$($NODE "$PC" validate "$TMP/contract.json" 2>&1)
|
|
180
|
+
[ "$OUT" = "VALID $TMP/contract.json" ] && ok "plan-contract CLI validates contract" || fail "plan-contract CLI validate: $OUT"
|
|
181
|
+
|
|
182
|
+
# ─── contract-runner ───────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
CR="$FRAMEWORK_DIR/bin/contract-runner.js"
|
|
185
|
+
$NODE --check "$CR" >/dev/null 2>&1 && ok "contract-runner.js parses" || fail "contract-runner.js parse"
|
|
186
|
+
|
|
187
|
+
TMP=$(mktmp)
|
|
188
|
+
mkdir -p "$TMP/src" "$TMP/.planning"
|
|
189
|
+
echo "export const wired = true;" > "$TMP/src/feature.ts"
|
|
190
|
+
cat > "$TMP/.planning/phase-1-contract.json" <<JSON
|
|
191
|
+
{
|
|
192
|
+
"version": 1,
|
|
193
|
+
"phase": 1,
|
|
194
|
+
"goal": "runner pass",
|
|
195
|
+
"why": "prove checks execute",
|
|
196
|
+
"generated_at": "2026-05-23T00:00:00Z",
|
|
197
|
+
"generated_by": "manual",
|
|
198
|
+
"source_plan_hash": "",
|
|
199
|
+
"success_criteria": ["all checks pass"],
|
|
200
|
+
"tasks": [{
|
|
201
|
+
"id": "T1",
|
|
202
|
+
"title": "Check feature",
|
|
203
|
+
"wave": 1,
|
|
204
|
+
"depends_on": [],
|
|
205
|
+
"files_modify": ["src/feature.ts"],
|
|
206
|
+
"files_create": [],
|
|
207
|
+
"files_delete": [],
|
|
208
|
+
"acceptance_criteria": ["feature exists and is wired"],
|
|
209
|
+
"action": "Validate feature",
|
|
210
|
+
"context_files": [],
|
|
211
|
+
"verification": [
|
|
212
|
+
{ "type": "file-exists", "path": "src/feature.ts", "must_contain": "wired" },
|
|
213
|
+
{ "type": "grep-match", "path": "src/feature.ts", "pattern": "wired\\\\s*=\\\\s*true", "expect": "present" },
|
|
214
|
+
{ "type": "command-exit", "command": "printf", "args": ["contract-ok"], "expected_exit": 0, "expect_stdout_match": "contract-ok" },
|
|
215
|
+
{ "type": "behavioral", "description": "evidence file includes wired flag", "evidence_required": [{ "path": "src/feature.ts", "description": "feature evidence", "matcher": "wired" }] }
|
|
216
|
+
]
|
|
217
|
+
}]
|
|
218
|
+
}
|
|
219
|
+
JSON
|
|
220
|
+
OUT=$(cd "$TMP" && $NODE "$CR" .planning/phase-1-contract.json 2>&1)
|
|
221
|
+
if echo "$OUT" | grep -q "PASS phase 1: 4 check" && [ -f "$TMP/.planning/evidence/phase-1-contract-run.json" ]; then
|
|
222
|
+
ok "contract-runner executes passing contract and writes evidence"
|
|
223
|
+
else
|
|
224
|
+
fail "contract-runner pass/evidence: $OUT"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
OUT=$(cd "$TMP" && $NODE "$CR" .planning/phase-1-contract.json --json 2>/dev/null)
|
|
228
|
+
if echo "$OUT" | grep -q '"ok": true' && echo "$OUT" | grep -q '"checked": 4'; then
|
|
229
|
+
ok "contract-runner --json reports checked count"
|
|
230
|
+
else
|
|
231
|
+
fail "contract-runner json output: $OUT"
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
TMP=$(mktmp)
|
|
235
|
+
mkdir -p "$TMP/.planning"
|
|
236
|
+
cat > "$TMP/.planning/phase-1-contract.json" <<JSON
|
|
237
|
+
{
|
|
238
|
+
"version": 1,
|
|
239
|
+
"phase": 1,
|
|
240
|
+
"goal": "runner fail",
|
|
241
|
+
"why": "prove failures block",
|
|
242
|
+
"generated_at": "2026-05-23T00:00:00Z",
|
|
243
|
+
"generated_by": "manual",
|
|
244
|
+
"source_plan_hash": "",
|
|
245
|
+
"success_criteria": ["failure is detected"],
|
|
246
|
+
"tasks": [{
|
|
247
|
+
"id": "T1",
|
|
248
|
+
"title": "Missing file",
|
|
249
|
+
"wave": 1,
|
|
250
|
+
"depends_on": [],
|
|
251
|
+
"files_modify": [],
|
|
252
|
+
"files_create": [],
|
|
253
|
+
"files_delete": [],
|
|
254
|
+
"acceptance_criteria": ["missing file fails"],
|
|
255
|
+
"action": "Validate missing file",
|
|
256
|
+
"context_files": [],
|
|
257
|
+
"verification": [{ "type": "file-exists", "path": "missing.txt" }]
|
|
258
|
+
}]
|
|
259
|
+
}
|
|
260
|
+
JSON
|
|
261
|
+
OUT=$(cd "$TMP" && $NODE "$CR" .planning/phase-1-contract.json 2>&1)
|
|
262
|
+
EXIT=$?
|
|
263
|
+
if [ "$EXIT" -eq 1 ] && echo "$OUT" | grep -q "missing file"; then
|
|
264
|
+
ok "contract-runner fails closed on failed check"
|
|
265
|
+
else
|
|
266
|
+
fail "contract-runner failed-check behavior: exit=$EXIT out=$OUT"
|
|
267
|
+
fi
|
|
268
|
+
|
|
152
269
|
# ─── agent-runs ──────────────────────────────────────────
|
|
153
270
|
|
|
154
271
|
AR="$FRAMEWORK_DIR/bin/agent-runs.js"
|
|
@@ -233,6 +350,321 @@ console.log("removed:" + r.removed);
|
|
|
233
350
|
')
|
|
234
351
|
[ "$RES" = "removed:1" ] && ok "prune --before removes old records" || fail "prune: $RES"
|
|
235
352
|
|
|
353
|
+
# ─── state-ledger ────────────────────────────────────────
|
|
354
|
+
|
|
355
|
+
SL="$FRAMEWORK_DIR/bin/state-ledger.js"
|
|
356
|
+
$NODE --check "$SL" >/dev/null 2>&1 && ok "state-ledger.js parses" || fail "state-ledger.js parse"
|
|
357
|
+
|
|
358
|
+
HA="$FRAMEWORK_DIR/bin/host-adapters.js"
|
|
359
|
+
$NODE --check "$HA" >/dev/null 2>&1 && ok "host-adapters.js parses" || fail "host-adapters.js parse"
|
|
360
|
+
RES=$($NODE -e '
|
|
361
|
+
const { renderText } = require("'"$HA"'");
|
|
362
|
+
const c = renderText("node ${QUALIA_BIN}/state.js @${QUALIA_AGENTS}/planner.md", "codex");
|
|
363
|
+
const d = renderText("node ${QUALIA_BIN}/state.js @${QUALIA_AGENTS}/planner.md", "claude");
|
|
364
|
+
console.log(c.includes(".codex/bin/state.js") && c.includes(".codex/agents/planner.md") && d.includes(".claude/bin/state.js") ? "HOST-RENDER-OK" : c + "\n" + d);
|
|
365
|
+
')
|
|
366
|
+
[ "$RES" = "HOST-RENDER-OK" ] && ok "host-adapters renders Qualia path tokens per host" || fail "host-adapters render: $RES"
|
|
367
|
+
|
|
368
|
+
CS="$FRAMEWORK_DIR/bin/command-surface.js"
|
|
369
|
+
$NODE --check "$CS" >/dev/null 2>&1 && ok "command-surface.js parses" || fail "command-surface.js parse"
|
|
370
|
+
RES=$($NODE -e '
|
|
371
|
+
const cs = require("'"$CS"'");
|
|
372
|
+
console.log(cs.ACTIVE_SKILLS.length === 23 && cs.RETIRED_SKILLS.includes("qualia-debug") && cs.RETIRED_SKILLS.includes("qualia-vibe") ? "SURFACE-OK" : JSON.stringify(cs));
|
|
373
|
+
')
|
|
374
|
+
[ "$RES" = "SURFACE-OK" ] && ok "command-surface defines 23 active skills and retired folds" || fail "command-surface manifest: $RES"
|
|
375
|
+
RES=$($NODE -e '
|
|
376
|
+
const fs = require("fs");
|
|
377
|
+
const path = require("path");
|
|
378
|
+
const root = "'"$FRAMEWORK_DIR"'";
|
|
379
|
+
const { ACTIVE_SKILLS, RETIRED_SKILLS } = require(path.join(root, "bin/command-surface.js"));
|
|
380
|
+
const dirs = fs.readdirSync(path.join(root, "skills"), { withFileTypes: true })
|
|
381
|
+
.filter((d) => d.isDirectory())
|
|
382
|
+
.map((d) => d.name)
|
|
383
|
+
.sort();
|
|
384
|
+
const active = [...ACTIVE_SKILLS].sort();
|
|
385
|
+
const extra = dirs.filter((d) => !active.includes(d));
|
|
386
|
+
const missing = active.filter((d) => !dirs.includes(d));
|
|
387
|
+
const retiredPresent = RETIRED_SKILLS.filter((d) => dirs.includes(d));
|
|
388
|
+
console.log(!extra.length && !missing.length && !retiredPresent.length ? "SKILL-DIRS-OK" : JSON.stringify({ extra, missing, retiredPresent }));
|
|
389
|
+
')
|
|
390
|
+
[ "$RES" = "SKILL-DIRS-OK" ] && ok "skills directory matches active command surface only" || fail "skill directory surface: $RES"
|
|
391
|
+
|
|
392
|
+
TMP=$(mktmp)
|
|
393
|
+
RES=$(cd "$TMP" && $NODE -e '
|
|
394
|
+
const ledger = require("'"$SL"'");
|
|
395
|
+
const fs = require("fs");
|
|
396
|
+
fs.mkdirSync(".planning", { recursive: true });
|
|
397
|
+
ledger.append(process.cwd(), {
|
|
398
|
+
action: "init",
|
|
399
|
+
status_after: "setup",
|
|
400
|
+
phase_after: 1,
|
|
401
|
+
state_raw_after: "state-1",
|
|
402
|
+
tracking_raw_after: "{\"status\":\"setup\"}"
|
|
403
|
+
});
|
|
404
|
+
ledger.append(process.cwd(), {
|
|
405
|
+
action: "transition",
|
|
406
|
+
status_before: "setup",
|
|
407
|
+
status_after: "planned",
|
|
408
|
+
phase_before: 1,
|
|
409
|
+
phase_after: 1,
|
|
410
|
+
state_raw_before: "state-1",
|
|
411
|
+
state_raw_after: "state-2",
|
|
412
|
+
tracking_raw_before: "{\"status\":\"setup\"}",
|
|
413
|
+
tracking_raw_after: "{\"status\":\"planned\"}",
|
|
414
|
+
evidence_refs: [".planning/phase-1-plan.md"]
|
|
415
|
+
});
|
|
416
|
+
const v = ledger.validate(process.cwd());
|
|
417
|
+
const lines = fs.readFileSync(ledger.ledgerPath(process.cwd()), "utf8").trim().split(/\n/);
|
|
418
|
+
console.log(v.ok && v.count === 2 && lines.length === 2 ? "CHAIN-OK" : JSON.stringify(v));
|
|
419
|
+
')
|
|
420
|
+
[ "$RES" = "CHAIN-OK" ] && ok "state-ledger appends and validates hash chain" || fail "state-ledger chain: $RES"
|
|
421
|
+
|
|
422
|
+
RES=$(cd "$TMP" && $NODE -e '
|
|
423
|
+
const ledger = require("'"$SL"'");
|
|
424
|
+
const fs = require("fs");
|
|
425
|
+
const file = ledger.ledgerPath(process.cwd());
|
|
426
|
+
const lines = fs.readFileSync(file, "utf8").trim().split(/\n/);
|
|
427
|
+
const first = JSON.parse(lines[0]);
|
|
428
|
+
first.status_after = "tampered";
|
|
429
|
+
lines[0] = JSON.stringify(first);
|
|
430
|
+
fs.writeFileSync(file, lines.join("\n") + "\n");
|
|
431
|
+
const v = ledger.validate(process.cwd());
|
|
432
|
+
console.log(!v.ok && v.errors.some(e => /event_hash/.test(e)) ? "TAMPER-DETECTED" : JSON.stringify(v));
|
|
433
|
+
')
|
|
434
|
+
[ "$RES" = "TAMPER-DETECTED" ] && ok "state-ledger detects tampered event" || fail "state-ledger tamper: $RES"
|
|
435
|
+
|
|
436
|
+
# ─── trust-score ─────────────────────────────────────────
|
|
437
|
+
|
|
438
|
+
PH="$FRAMEWORK_DIR/bin/planning-hygiene.js"
|
|
439
|
+
$NODE --check "$PH" >/dev/null 2>&1 && ok "planning-hygiene.js parses" || fail "planning-hygiene.js parse"
|
|
440
|
+
|
|
441
|
+
TMP=$(mktmp)
|
|
442
|
+
mkdir -p "$TMP/.planning"
|
|
443
|
+
touch "$TMP/.planning/PROJECT.md"
|
|
444
|
+
touch "$TMP/.planning/phase-1-plan.md"
|
|
445
|
+
OUT=$(cd "$TMP" && $NODE "$PH" scan 2>&1)
|
|
446
|
+
EXIT=$?
|
|
447
|
+
if [ "$EXIT" -eq 0 ] && echo "$OUT" | grep -q "clean"; then
|
|
448
|
+
ok "planning-hygiene clean project passes"
|
|
449
|
+
else
|
|
450
|
+
fail "planning-hygiene clean project: exit=$EXIT out=$OUT"
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
TMP=$(mktmp)
|
|
454
|
+
mkdir -p "$TMP/.planning"
|
|
455
|
+
echo debug > "$TMP/.planning/DEBUG-2026-05-23-0100.md"
|
|
456
|
+
echo fix > "$TMP/.planning/FIX-2026-05-23-0101.md"
|
|
457
|
+
echo review > "$TMP/.planning/REVIEW.md"
|
|
458
|
+
echo image > "$TMP/.planning/vibe-after.png"
|
|
459
|
+
OUT=$(cd "$TMP" && $NODE "$PH" scan --json 2>&1)
|
|
460
|
+
EXIT=$?
|
|
461
|
+
if [ "$EXIT" -eq 1 ] \
|
|
462
|
+
&& echo "$OUT" | grep -q '"status": "needs_organizing"' \
|
|
463
|
+
&& echo "$OUT" | grep -q 'reports/debug/DEBUG-2026-05-23-0100.md' \
|
|
464
|
+
&& echo "$OUT" | grep -q 'reports/fix/FIX-2026-05-23-0101.md' \
|
|
465
|
+
&& echo "$OUT" | grep -q 'reports/review/REVIEW.md' \
|
|
466
|
+
&& echo "$OUT" | grep -q 'assets/vibe/vibe-after.png'; then
|
|
467
|
+
ok "planning-hygiene routes loose artifacts"
|
|
468
|
+
else
|
|
469
|
+
fail "planning-hygiene loose routing: exit=$EXIT out=$OUT"
|
|
470
|
+
fi
|
|
471
|
+
|
|
472
|
+
OUT=$(cd "$TMP" && $NODE "$PH" organize --write 2>&1)
|
|
473
|
+
EXIT=$?
|
|
474
|
+
if [ "$EXIT" -eq 0 ] \
|
|
475
|
+
&& [ -f "$TMP/.planning/reports/debug/DEBUG-2026-05-23-0100.md" ] \
|
|
476
|
+
&& [ -f "$TMP/.planning/reports/fix/FIX-2026-05-23-0101.md" ] \
|
|
477
|
+
&& [ -f "$TMP/.planning/reports/review/REVIEW.md" ] \
|
|
478
|
+
&& [ -f "$TMP/.planning/assets/vibe/vibe-after.png" ]; then
|
|
479
|
+
ok "planning-hygiene organize moves loose artifacts"
|
|
480
|
+
else
|
|
481
|
+
fail "planning-hygiene organize: exit=$EXIT out=$OUT"
|
|
482
|
+
fi
|
|
483
|
+
|
|
484
|
+
TS="$FRAMEWORK_DIR/bin/trust-score.js"
|
|
485
|
+
$NODE --check "$TS" >/dev/null 2>&1 && ok "trust-score.js parses" || fail "trust-score.js parse"
|
|
486
|
+
|
|
487
|
+
HE="$FRAMEWORK_DIR/bin/harness-eval.js"
|
|
488
|
+
$NODE --check "$HE" >/dev/null 2>&1 && ok "harness-eval.js parses" || fail "harness-eval.js parse"
|
|
489
|
+
|
|
490
|
+
TMP=$(mktmp)
|
|
491
|
+
OUT=$(cd "$TMP" && $NODE "$HE" --json 2>/dev/null)
|
|
492
|
+
EXIT=$?
|
|
493
|
+
if [ "$EXIT" -eq 1 ] && echo "$OUT" | grep -q '"status": "FAIL"' && echo "$OUT" | grep -q 'No .planning directory'; then
|
|
494
|
+
ok "harness-eval fails closed without planning state"
|
|
495
|
+
else
|
|
496
|
+
fail "harness-eval no-planning behavior: exit=$EXIT out=$OUT"
|
|
497
|
+
fi
|
|
498
|
+
|
|
499
|
+
TMP=$(mktmp)
|
|
500
|
+
mkdir -p "$TMP/home/.claude/bin" "$TMP/home/.claude/hooks" "$TMP/home/.claude/knowledge/daily-log" "$TMP/home/.claude/qualia-design" "$TMP/home/.claude/agents" "$TMP/home/.claude/qualia-templates" "$TMP/project"
|
|
501
|
+
echo '{"installed_by":"Test","role":"OWNER","version":"6.3.0","erp":{"enabled":false}}' > "$TMP/home/.claude/.qualia-config.json"
|
|
502
|
+
touch "$TMP/home/.claude/CLAUDE.md" "$TMP/home/.claude/settings.json"
|
|
503
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js; do
|
|
504
|
+
touch "$TMP/home/.claude/bin/$f"
|
|
505
|
+
done
|
|
506
|
+
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
507
|
+
touch "$TMP/home/.claude/hooks/$h"
|
|
508
|
+
done
|
|
509
|
+
touch "$TMP/home/.claude/knowledge/index.md" "$TMP/home/.claude/knowledge/agents.md" "$TMP/home/.claude/agents/visual-evaluator.md" "$TMP/home/.claude/qualia-guide.md" "$TMP/home/.claude/qualia-templates/help.html"
|
|
510
|
+
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
511
|
+
touch "$TMP/home/.claude/qualia-design/$f"
|
|
512
|
+
done
|
|
513
|
+
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem zoho-workflow; do
|
|
514
|
+
mkdir -p "$TMP/home/.claude/skills/$s"
|
|
515
|
+
touch "$TMP/home/.claude/skills/$s/SKILL.md"
|
|
516
|
+
done
|
|
517
|
+
(
|
|
518
|
+
cd "$TMP/project" || exit 1
|
|
519
|
+
HOME="$TMP/home" $NODE "$FRAMEWORK_DIR/bin/state.js" init --project "HarnessEvalProject" --phases '[{"name":"Foundation","goal":"Eval"}]' >/dev/null 2>&1
|
|
520
|
+
)
|
|
521
|
+
cat > "$TMP/project/eval-target.txt" <<'EOF'
|
|
522
|
+
contract ok
|
|
523
|
+
EOF
|
|
524
|
+
cat > "$TMP/project/.planning/phase-1-plan.md" <<'EOF'
|
|
525
|
+
# Phase 1 Plan
|
|
526
|
+
|
|
527
|
+
## Task 1
|
|
528
|
+
Create the eval target.
|
|
529
|
+
|
|
530
|
+
**Acceptance Criteria:**
|
|
531
|
+
- eval target exists
|
|
532
|
+
|
|
533
|
+
## Success Criteria
|
|
534
|
+
- [x] eval target exists
|
|
535
|
+
EOF
|
|
536
|
+
$NODE -e '
|
|
537
|
+
const fs = require("fs");
|
|
538
|
+
const path = require("path");
|
|
539
|
+
const pc = require("'"$PC"'");
|
|
540
|
+
const root = "'"$TMP"'/project";
|
|
541
|
+
const plan = fs.readFileSync(path.join(root, ".planning/phase-1-plan.md"), "utf8");
|
|
542
|
+
const trackingPath = path.join(root, ".planning/tracking.json");
|
|
543
|
+
const tracking = JSON.parse(fs.readFileSync(trackingPath, "utf8"));
|
|
544
|
+
Object.assign(tracking, {
|
|
545
|
+
project_id: "harness-eval-project",
|
|
546
|
+
erp_project_id: "7b5d3b4e-2b8a-4de4-91a1-9b2f3182f5ef",
|
|
547
|
+
phase_name: "Foundation",
|
|
548
|
+
tasks_done: 1,
|
|
549
|
+
tasks_total: 1
|
|
550
|
+
});
|
|
551
|
+
fs.writeFileSync(trackingPath, JSON.stringify(tracking, null, 2) + "\n");
|
|
552
|
+
const contract = {
|
|
553
|
+
version: 1,
|
|
554
|
+
phase: 1,
|
|
555
|
+
goal: "Harness eval positive path",
|
|
556
|
+
why: "Prove scoring writes artifacts and passes on machine evidence",
|
|
557
|
+
generated_at: "2026-05-23T00:00:00Z",
|
|
558
|
+
generated_by: "manual",
|
|
559
|
+
source_plan_hash: pc.hashPlan(plan),
|
|
560
|
+
success_criteria: ["eval target exists"],
|
|
561
|
+
tasks: [{
|
|
562
|
+
id: "T1",
|
|
563
|
+
title: "Eval target",
|
|
564
|
+
wave: 1,
|
|
565
|
+
depends_on: [],
|
|
566
|
+
files_modify: [],
|
|
567
|
+
files_create: ["eval-target.txt"],
|
|
568
|
+
files_delete: [],
|
|
569
|
+
acceptance_criteria: ["eval target exists"],
|
|
570
|
+
action: "Create eval target",
|
|
571
|
+
context_files: [],
|
|
572
|
+
verification: [
|
|
573
|
+
{ type: "file-exists", path: "eval-target.txt", must_contain: "contract ok" },
|
|
574
|
+
{ type: "command-exit", command: process.execPath, args: ["-e", "console.log(\"ok\")"], expected_exit: 0, expect_stdout_match: "ok" }
|
|
575
|
+
]
|
|
576
|
+
}]
|
|
577
|
+
};
|
|
578
|
+
fs.writeFileSync(path.join(root, ".planning/phase-1-contract.json"), JSON.stringify(contract, null, 2) + "\n");
|
|
579
|
+
fs.writeFileSync(path.join(root, ".planning/phase-1-verification.md"), "Result: PASS\n\nEvidence checked.\n");
|
|
580
|
+
'
|
|
581
|
+
OUT=$(cd "$TMP/project" && HOME="$TMP/home" $NODE "$HE" --phase 1 --run --write --json 2>/dev/null)
|
|
582
|
+
EXIT=$?
|
|
583
|
+
if [ "$EXIT" -eq 0 ] \
|
|
584
|
+
&& echo "$OUT" | grep -q '"status": "PASS"' \
|
|
585
|
+
&& echo "$OUT" | grep -q '"machine_evidence"' \
|
|
586
|
+
&& [ -f "$TMP/project/.planning/evidence/phase-1-contract-run.json" ] \
|
|
587
|
+
&& ls "$TMP/project"/.planning/evals/harness-eval-*.json >/dev/null 2>&1 \
|
|
588
|
+
&& ls "$TMP/project"/.planning/evals/harness-eval-*.md >/dev/null 2>&1; then
|
|
589
|
+
ok "harness-eval passes with machine evidence and writes artifacts"
|
|
590
|
+
else
|
|
591
|
+
fail "harness-eval positive path: exit=$EXIT out=$OUT"
|
|
592
|
+
fi
|
|
593
|
+
RES=$(cd "$TMP/project" && HOME="$TMP/home" $NODE -e '
|
|
594
|
+
const path = require("path");
|
|
595
|
+
const { latestEval } = require("'"$HE"'");
|
|
596
|
+
const { buildPayload } = require("'"$FRAMEWORK_DIR"'/bin/report-payload.js");
|
|
597
|
+
const { buildSnapshot } = require("'"$FRAMEWORK_DIR"'/bin/project-snapshot.js");
|
|
598
|
+
const ev = latestEval(process.cwd());
|
|
599
|
+
const payload = buildPayload({ cwd: process.cwd(), home: "'"$TMP"'/home", env: { SUBMITTED_BY: "Test", SUBMITTED_AT: "2026-05-23T00:30:00Z" } });
|
|
600
|
+
const snapshot = buildSnapshot({ cwd: process.cwd(), home: "'"$TMP"'/home", now: "2026-05-23T00:30:00Z" });
|
|
601
|
+
console.log(ev && payload.harness_eval && snapshot.quality.harness_eval && payload.harness_eval.score === ev.score && snapshot.quality.harness_eval.score === ev.score ? "ERP-EVAL-OK" : JSON.stringify({ ev, payload: payload.harness_eval, snapshot: snapshot.quality.harness_eval }));
|
|
602
|
+
')
|
|
603
|
+
[ "$RES" = "ERP-EVAL-OK" ] && ok "latest harness eval flows into ERP report and project snapshot" || fail "ERP harness eval wiring: $RES"
|
|
604
|
+
|
|
605
|
+
TMP=$(mktmp)
|
|
606
|
+
OUT=$(HOME="$TMP" $NODE "$TS" --json 2>/dev/null)
|
|
607
|
+
EXIT=$?
|
|
608
|
+
if [ "$EXIT" -eq 1 ] && echo "$OUT" | grep -q '"status": "FAIL"'; then
|
|
609
|
+
ok "trust-score fails closed when no install exists"
|
|
610
|
+
else
|
|
611
|
+
fail "trust-score no-install behavior: exit=$EXIT out=$OUT"
|
|
612
|
+
fi
|
|
613
|
+
|
|
614
|
+
TMP=$(mktmp)
|
|
615
|
+
mkdir -p "$TMP/.claude/bin" "$TMP/.claude/hooks" "$TMP/.claude/knowledge/daily-log" "$TMP/.claude/qualia-design" "$TMP/.claude/agents" "$TMP/.claude/qualia-templates" "$TMP/project/.planning"
|
|
616
|
+
echo '{"installed_by":"Test","role":"OWNER","erp":{"enabled":false}}' > "$TMP/.claude/.qualia-config.json"
|
|
617
|
+
touch "$TMP/.claude/CLAUDE.md" "$TMP/.claude/settings.json"
|
|
618
|
+
for f in runtime-manifest.js command-surface.js host-adapters.js state.js qualia-ui.js statusline.js knowledge.js knowledge-flush.js state-ledger.js plan-contract.js contract-runner.js harness-eval.js trust-score.js agent-runs.js slop-detect.mjs erp-retry.js work-packet.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js; do
|
|
619
|
+
touch "$TMP/.claude/bin/$f"
|
|
620
|
+
done
|
|
621
|
+
for h in session-start.js auto-update.js branch-guard.js pre-push.js pre-deploy-gate.js migration-guard.js git-guardrails.js stop-session-log.js fawzi-approval-guard.js vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
622
|
+
touch "$TMP/.claude/hooks/$h"
|
|
623
|
+
done
|
|
624
|
+
touch "$TMP/.claude/knowledge/index.md" "$TMP/.claude/knowledge/agents.md"
|
|
625
|
+
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
626
|
+
touch "$TMP/.claude/qualia-design/$f"
|
|
627
|
+
done
|
|
628
|
+
for s in qualia qualia-new qualia-discuss qualia-map qualia-research qualia-plan qualia-build qualia-verify qualia-fix qualia-feature qualia-review qualia-optimize qualia-polish qualia-test qualia-milestone qualia-ship qualia-handoff qualia-report qualia-doctor qualia-road qualia-learn qualia-postmortem zoho-workflow; do
|
|
629
|
+
mkdir -p "$TMP/.claude/skills/$s"
|
|
630
|
+
touch "$TMP/.claude/skills/$s/SKILL.md"
|
|
631
|
+
done
|
|
632
|
+
touch "$TMP/.claude/agents/visual-evaluator.md" "$TMP/.claude/qualia-guide.md" "$TMP/.claude/qualia-templates/help.html"
|
|
633
|
+
cat > "$TMP/project/.planning/STATE.md" <<'EOF'
|
|
634
|
+
Phase: 1 of 1 — Trust
|
|
635
|
+
Status: planned
|
|
636
|
+
EOF
|
|
637
|
+
echo '{"phase":1,"status":"planned"}' > "$TMP/project/.planning/tracking.json"
|
|
638
|
+
cat > "$TMP/project/.planning/phase-1-plan.md" <<'EOF'
|
|
639
|
+
---
|
|
640
|
+
goal: trust
|
|
641
|
+
---
|
|
642
|
+
# Plan
|
|
643
|
+
## Task 1
|
|
644
|
+
**Acceptance Criteria:**
|
|
645
|
+
- ok
|
|
646
|
+
## Success Criteria
|
|
647
|
+
- [ ] ok
|
|
648
|
+
EOF
|
|
649
|
+
$NODE -e '
|
|
650
|
+
const ledger = require("'"$SL"'");
|
|
651
|
+
ledger.append("'"$TMP"'/project", {
|
|
652
|
+
action: "init",
|
|
653
|
+
status_after: "planned",
|
|
654
|
+
phase_after: 1,
|
|
655
|
+
state_raw_after: "Phase: 1 of 1\nStatus: planned\n",
|
|
656
|
+
tracking_raw_after: "{\"phase\":1,\"status\":\"planned\"}"
|
|
657
|
+
});
|
|
658
|
+
' >/dev/null
|
|
659
|
+
OUT=$(cd "$TMP/project" && HOME="$TMP" $NODE "$TS" --json 2>/dev/null)
|
|
660
|
+
if echo "$OUT" | grep -q '"status": "DEGRADED"' \
|
|
661
|
+
&& echo "$OUT" | grep -q '"score": 88' \
|
|
662
|
+
&& echo "$OUT" | grep -q 'JSON contract missing'; then
|
|
663
|
+
ok "trust-score reports 88 degraded when only contract is missing"
|
|
664
|
+
else
|
|
665
|
+
fail "trust-score degraded contract score: $OUT"
|
|
666
|
+
fi
|
|
667
|
+
|
|
236
668
|
echo ""
|
|
237
669
|
echo "lib.test.sh: $PASS passed, $FAIL failed"
|
|
238
670
|
[ "$FAIL" -eq 0 ]
|
|
@@ -69,7 +69,7 @@ else
|
|
|
69
69
|
exit 1
|
|
70
70
|
fi
|
|
71
71
|
|
|
72
|
-
printf 'QS-FAWZI-
|
|
72
|
+
printf 'QS-FAWZI-11\n3\n' >"$TMP/install.input"
|
|
73
73
|
timeout "$INSTALL_TIMEOUT_SECONDS" env \
|
|
74
74
|
HOME="$HOME_DIR" \
|
|
75
75
|
NPM_CONFIG_CACHE="$CACHE_DIR" \
|
|
@@ -102,9 +102,10 @@ else
|
|
|
102
102
|
fi
|
|
103
103
|
|
|
104
104
|
if [ -d "$HOME_DIR/.claude/hooks" ] \
|
|
105
|
-
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "
|
|
105
|
+
&& [ "$(find "$HOME_DIR/.claude/hooks" -maxdepth 1 -name '*.js' | wc -l | tr -d ' ')" = "12" ] \
|
|
106
|
+
&& [ -f "$HOME_DIR/.claude/hooks/fawzi-approval-guard.js" ] \
|
|
106
107
|
&& [ ! -f "$HOME_DIR/.claude/hooks/pre-compact.js" ]; then
|
|
107
|
-
pass "public install has
|
|
108
|
+
pass "public install has 12 hooks and no pre-compact"
|
|
108
109
|
else
|
|
109
110
|
fail_case "public install hook set mismatch"
|
|
110
111
|
fi
|