qualia-framework 6.2.9 → 6.2.10
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/README.md +14 -11
- 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 +111 -14
- package/bin/contract-runner.js +219 -0
- package/bin/host-adapters.js +66 -0
- package/bin/install.js +99 -152
- package/bin/plan-contract.js +99 -2
- package/bin/planning-hygiene.js +262 -0
- package/bin/runtime-manifest.js +32 -0
- package/bin/state-ledger.js +184 -0
- package/bin/state.js +299 -20
- package/bin/trust-score.js +276 -0
- package/docs/onboarding.html +5 -4
- package/guide.md +3 -2
- package/package.json +1 -1
- package/qualia-design/design-rubric.md +17 -5
- package/qualia-design/frontend.md +5 -1
- package/qualia-design/graphics.md +47 -0
- package/rules/command-output.md +35 -0
- package/skills/qualia/SKILL.md +10 -10
- package/skills/qualia-build/SKILL.md +20 -14
- package/skills/qualia-debug/SKILL.md +16 -8
- package/skills/qualia-discuss/SKILL.md +10 -10
- package/skills/qualia-doctor/SKILL.md +140 -0
- package/skills/qualia-feature/SKILL.md +23 -21
- package/skills/qualia-fix/SKILL.md +216 -0
- package/skills/qualia-flush/SKILL.md +9 -9
- package/skills/qualia-handoff/SKILL.md +9 -9
- package/skills/qualia-help/SKILL.md +3 -3
- package/skills/qualia-hook-gen/SKILL.md +1 -1
- package/skills/qualia-idk/SKILL.md +4 -4
- package/skills/qualia-issues/SKILL.md +2 -2
- package/skills/qualia-learn/SKILL.md +10 -10
- 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-pause/SKILL.md +2 -2
- package/skills/qualia-plan/SKILL.md +23 -13
- package/skills/qualia-polish/REFERENCE.md +14 -14
- package/skills/qualia-polish/SKILL.md +64 -19
- package/skills/qualia-polish/scripts/loop.mjs +3 -3
- package/skills/qualia-polish/scripts/score.mjs +9 -3
- 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-resume/SKILL.md +4 -4
- package/skills/qualia-review/SKILL.md +28 -12
- package/skills/qualia-road/SKILL.md +18 -5
- package/skills/qualia-ship/SKILL.md +22 -22
- package/skills/qualia-skill-new/SKILL.md +13 -13
- package/skills/qualia-test/SKILL.md +5 -5
- package/skills/qualia-triage/SKILL.md +1 -1
- package/skills/qualia-verify/SKILL.md +37 -23
- package/skills/qualia-vibe/SKILL.md +13 -10
- package/skills/qualia-vibe/scripts/extract.mjs +1 -1
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/help.html +12 -10
- package/tests/bin.test.sh +34 -4
- package/tests/install-smoke.test.sh +22 -2
- package/tests/lib.test.sh +290 -0
- package/tests/runner.js +3 -0
- package/tests/skills.test.sh +4 -4
- package/tests/state.test.sh +65 -3
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,179 @@ 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
|
+
TMP=$(mktmp)
|
|
369
|
+
RES=$(cd "$TMP" && $NODE -e '
|
|
370
|
+
const ledger = require("'"$SL"'");
|
|
371
|
+
const fs = require("fs");
|
|
372
|
+
fs.mkdirSync(".planning", { recursive: true });
|
|
373
|
+
ledger.append(process.cwd(), {
|
|
374
|
+
action: "init",
|
|
375
|
+
status_after: "setup",
|
|
376
|
+
phase_after: 1,
|
|
377
|
+
state_raw_after: "state-1",
|
|
378
|
+
tracking_raw_after: "{\"status\":\"setup\"}"
|
|
379
|
+
});
|
|
380
|
+
ledger.append(process.cwd(), {
|
|
381
|
+
action: "transition",
|
|
382
|
+
status_before: "setup",
|
|
383
|
+
status_after: "planned",
|
|
384
|
+
phase_before: 1,
|
|
385
|
+
phase_after: 1,
|
|
386
|
+
state_raw_before: "state-1",
|
|
387
|
+
state_raw_after: "state-2",
|
|
388
|
+
tracking_raw_before: "{\"status\":\"setup\"}",
|
|
389
|
+
tracking_raw_after: "{\"status\":\"planned\"}",
|
|
390
|
+
evidence_refs: [".planning/phase-1-plan.md"]
|
|
391
|
+
});
|
|
392
|
+
const v = ledger.validate(process.cwd());
|
|
393
|
+
const lines = fs.readFileSync(ledger.ledgerPath(process.cwd()), "utf8").trim().split(/\n/);
|
|
394
|
+
console.log(v.ok && v.count === 2 && lines.length === 2 ? "CHAIN-OK" : JSON.stringify(v));
|
|
395
|
+
')
|
|
396
|
+
[ "$RES" = "CHAIN-OK" ] && ok "state-ledger appends and validates hash chain" || fail "state-ledger chain: $RES"
|
|
397
|
+
|
|
398
|
+
RES=$(cd "$TMP" && $NODE -e '
|
|
399
|
+
const ledger = require("'"$SL"'");
|
|
400
|
+
const fs = require("fs");
|
|
401
|
+
const file = ledger.ledgerPath(process.cwd());
|
|
402
|
+
const lines = fs.readFileSync(file, "utf8").trim().split(/\n/);
|
|
403
|
+
const first = JSON.parse(lines[0]);
|
|
404
|
+
first.status_after = "tampered";
|
|
405
|
+
lines[0] = JSON.stringify(first);
|
|
406
|
+
fs.writeFileSync(file, lines.join("\n") + "\n");
|
|
407
|
+
const v = ledger.validate(process.cwd());
|
|
408
|
+
console.log(!v.ok && v.errors.some(e => /event_hash/.test(e)) ? "TAMPER-DETECTED" : JSON.stringify(v));
|
|
409
|
+
')
|
|
410
|
+
[ "$RES" = "TAMPER-DETECTED" ] && ok "state-ledger detects tampered event" || fail "state-ledger tamper: $RES"
|
|
411
|
+
|
|
412
|
+
# ─── trust-score ─────────────────────────────────────────
|
|
413
|
+
|
|
414
|
+
PH="$FRAMEWORK_DIR/bin/planning-hygiene.js"
|
|
415
|
+
$NODE --check "$PH" >/dev/null 2>&1 && ok "planning-hygiene.js parses" || fail "planning-hygiene.js parse"
|
|
416
|
+
|
|
417
|
+
TMP=$(mktmp)
|
|
418
|
+
mkdir -p "$TMP/.planning"
|
|
419
|
+
touch "$TMP/.planning/PROJECT.md"
|
|
420
|
+
touch "$TMP/.planning/phase-1-plan.md"
|
|
421
|
+
OUT=$(cd "$TMP" && $NODE "$PH" scan 2>&1)
|
|
422
|
+
EXIT=$?
|
|
423
|
+
if [ "$EXIT" -eq 0 ] && echo "$OUT" | grep -q "clean"; then
|
|
424
|
+
ok "planning-hygiene clean project passes"
|
|
425
|
+
else
|
|
426
|
+
fail "planning-hygiene clean project: exit=$EXIT out=$OUT"
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
TMP=$(mktmp)
|
|
430
|
+
mkdir -p "$TMP/.planning"
|
|
431
|
+
echo debug > "$TMP/.planning/DEBUG-2026-05-23-0100.md"
|
|
432
|
+
echo fix > "$TMP/.planning/FIX-2026-05-23-0101.md"
|
|
433
|
+
echo review > "$TMP/.planning/REVIEW.md"
|
|
434
|
+
echo image > "$TMP/.planning/vibe-after.png"
|
|
435
|
+
OUT=$(cd "$TMP" && $NODE "$PH" scan --json 2>&1)
|
|
436
|
+
EXIT=$?
|
|
437
|
+
if [ "$EXIT" -eq 1 ] \
|
|
438
|
+
&& echo "$OUT" | grep -q '"status": "needs_organizing"' \
|
|
439
|
+
&& echo "$OUT" | grep -q 'reports/debug/DEBUG-2026-05-23-0100.md' \
|
|
440
|
+
&& echo "$OUT" | grep -q 'reports/fix/FIX-2026-05-23-0101.md' \
|
|
441
|
+
&& echo "$OUT" | grep -q 'reports/review/REVIEW.md' \
|
|
442
|
+
&& echo "$OUT" | grep -q 'assets/vibe/vibe-after.png'; then
|
|
443
|
+
ok "planning-hygiene routes loose artifacts"
|
|
444
|
+
else
|
|
445
|
+
fail "planning-hygiene loose routing: exit=$EXIT out=$OUT"
|
|
446
|
+
fi
|
|
447
|
+
|
|
448
|
+
OUT=$(cd "$TMP" && $NODE "$PH" organize --write 2>&1)
|
|
449
|
+
EXIT=$?
|
|
450
|
+
if [ "$EXIT" -eq 0 ] \
|
|
451
|
+
&& [ -f "$TMP/.planning/reports/debug/DEBUG-2026-05-23-0100.md" ] \
|
|
452
|
+
&& [ -f "$TMP/.planning/reports/fix/FIX-2026-05-23-0101.md" ] \
|
|
453
|
+
&& [ -f "$TMP/.planning/reports/review/REVIEW.md" ] \
|
|
454
|
+
&& [ -f "$TMP/.planning/assets/vibe/vibe-after.png" ]; then
|
|
455
|
+
ok "planning-hygiene organize moves loose artifacts"
|
|
456
|
+
else
|
|
457
|
+
fail "planning-hygiene organize: exit=$EXIT out=$OUT"
|
|
458
|
+
fi
|
|
459
|
+
|
|
460
|
+
TS="$FRAMEWORK_DIR/bin/trust-score.js"
|
|
461
|
+
$NODE --check "$TS" >/dev/null 2>&1 && ok "trust-score.js parses" || fail "trust-score.js parse"
|
|
462
|
+
|
|
463
|
+
TMP=$(mktmp)
|
|
464
|
+
OUT=$(HOME="$TMP" $NODE "$TS" --json 2>/dev/null)
|
|
465
|
+
EXIT=$?
|
|
466
|
+
if [ "$EXIT" -eq 1 ] && echo "$OUT" | grep -q '"status": "FAIL"'; then
|
|
467
|
+
ok "trust-score fails closed when no install exists"
|
|
468
|
+
else
|
|
469
|
+
fail "trust-score no-install behavior: exit=$EXIT out=$OUT"
|
|
470
|
+
fi
|
|
471
|
+
|
|
472
|
+
TMP=$(mktmp)
|
|
473
|
+
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"
|
|
474
|
+
echo '{"installed_by":"Test","role":"OWNER","erp":{"enabled":false}}' > "$TMP/.claude/.qualia-config.json"
|
|
475
|
+
touch "$TMP/.claude/CLAUDE.md" "$TMP/.claude/settings.json"
|
|
476
|
+
for f in runtime-manifest.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 trust-score.js agent-runs.js slop-detect.mjs erp-retry.js report-payload.js project-snapshot.js codex-goal.js planning-hygiene.js; do
|
|
477
|
+
touch "$TMP/.claude/bin/$f"
|
|
478
|
+
done
|
|
479
|
+
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 vercel-account-guard.js env-empty-guard.js supabase-destructive-guard.js; do
|
|
480
|
+
touch "$TMP/.claude/hooks/$h"
|
|
481
|
+
done
|
|
482
|
+
touch "$TMP/.claude/knowledge/index.md" "$TMP/.claude/knowledge/agents.md"
|
|
483
|
+
for f in design-laws.md design-rubric.md design-brand.md design-product.md design-reference.md frontend.md graphics.md; do
|
|
484
|
+
touch "$TMP/.claude/qualia-design/$f"
|
|
485
|
+
done
|
|
486
|
+
for s in qualia-doctor qualia-road qualia-resume qualia-pause qualia-report qualia-polish qualia-vibe; do
|
|
487
|
+
mkdir -p "$TMP/.claude/skills/$s"
|
|
488
|
+
touch "$TMP/.claude/skills/$s/SKILL.md"
|
|
489
|
+
done
|
|
490
|
+
touch "$TMP/.claude/agents/visual-evaluator.md" "$TMP/.claude/qualia-guide.md" "$TMP/.claude/qualia-templates/help.html"
|
|
491
|
+
cat > "$TMP/project/.planning/STATE.md" <<'EOF'
|
|
492
|
+
Phase: 1 of 1 — Trust
|
|
493
|
+
Status: planned
|
|
494
|
+
EOF
|
|
495
|
+
echo '{"phase":1,"status":"planned"}' > "$TMP/project/.planning/tracking.json"
|
|
496
|
+
cat > "$TMP/project/.planning/phase-1-plan.md" <<'EOF'
|
|
497
|
+
---
|
|
498
|
+
goal: trust
|
|
499
|
+
---
|
|
500
|
+
# Plan
|
|
501
|
+
## Task 1
|
|
502
|
+
**Acceptance Criteria:**
|
|
503
|
+
- ok
|
|
504
|
+
## Success Criteria
|
|
505
|
+
- [ ] ok
|
|
506
|
+
EOF
|
|
507
|
+
$NODE -e '
|
|
508
|
+
const ledger = require("'"$SL"'");
|
|
509
|
+
ledger.append("'"$TMP"'/project", {
|
|
510
|
+
action: "init",
|
|
511
|
+
status_after: "planned",
|
|
512
|
+
phase_after: 1,
|
|
513
|
+
state_raw_after: "Phase: 1 of 1\nStatus: planned\n",
|
|
514
|
+
tracking_raw_after: "{\"phase\":1,\"status\":\"planned\"}"
|
|
515
|
+
});
|
|
516
|
+
' >/dev/null
|
|
517
|
+
OUT=$(cd "$TMP/project" && HOME="$TMP" $NODE "$TS" --json 2>/dev/null)
|
|
518
|
+
if echo "$OUT" | grep -q '"status": "DEGRADED"' \
|
|
519
|
+
&& echo "$OUT" | grep -q '"score": 88' \
|
|
520
|
+
&& echo "$OUT" | grep -q 'JSON contract missing'; then
|
|
521
|
+
ok "trust-score reports 88 degraded when only contract is missing"
|
|
522
|
+
else
|
|
523
|
+
fail "trust-score degraded contract score: $OUT"
|
|
524
|
+
fi
|
|
525
|
+
|
|
236
526
|
echo ""
|
|
237
527
|
echo "lib.test.sh: $PASS passed, $FAIL failed"
|
|
238
528
|
[ "$FAIL" -eq 0 ]
|
package/tests/runner.js
CHANGED
|
@@ -2801,6 +2801,9 @@ describe("install.js", () => {
|
|
|
2801
2801
|
assert.ok(fs.existsSync(rulesDir));
|
|
2802
2802
|
const files = fs.readdirSync(rulesDir);
|
|
2803
2803
|
assert.ok(files.length > 0);
|
|
2804
|
+
assert.ok(fs.existsSync(path.join(rulesDir, "command-output.md")));
|
|
2805
|
+
assert.match(fs.readFileSync(path.join(rulesDir, "command-output.md"), "utf8"), /alwaysApply: true/);
|
|
2806
|
+
assert.ok(fs.existsSync(path.join(tmpHome, ".claude", "qualia-design", "graphics.md")));
|
|
2804
2807
|
} finally {
|
|
2805
2808
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
2806
2809
|
}
|
package/tests/skills.test.sh
CHANGED
|
@@ -106,7 +106,7 @@ for skill_dir in "$SKILLS_DIR"/*/; do
|
|
|
106
106
|
|
|
107
107
|
# Cache-aware spawn audit (per rules/grounding.md):
|
|
108
108
|
# Every spawn to a CUSTOM (qualia-*) agent must anchor the prompt with
|
|
109
|
-
#
|
|
109
|
+
# `@${QUALIA_AGENTS}/{name}.md` (either `Role: @...` or `Read your role:
|
|
110
110
|
# @...` — both forms accepted). The role file is session-stable; placing
|
|
111
111
|
# it first lets Anthropic's prompt cache reuse the prefix across spawns
|
|
112
112
|
# (documented 81-90% cost reduction). If task-specific content lands
|
|
@@ -125,14 +125,14 @@ for skill_dir in "$SKILLS_DIR"/*/; do
|
|
|
125
125
|
custom_spawn_count=$((custom_spawn_count + $(grep -c 'subagent_type="qualia-' "$ref_md")))
|
|
126
126
|
fi
|
|
127
127
|
if [ "${custom_spawn_count:-0}" -gt 0 ]; then
|
|
128
|
-
role_count=$(grep -
|
|
128
|
+
role_count=$(grep -cF '@${QUALIA_AGENTS}/' "$skill_md")
|
|
129
129
|
if [ -f "$ref_md" ]; then
|
|
130
|
-
role_count=$((role_count + $(grep -
|
|
130
|
+
role_count=$((role_count + $(grep -cF '@${QUALIA_AGENTS}/' "$ref_md")))
|
|
131
131
|
fi
|
|
132
132
|
if [ "${role_count:-0}" -ge "$custom_spawn_count" ]; then
|
|
133
133
|
pass "$name: spawn audit ($custom_spawn_count custom spawn(s), all role-anchored for cache)"
|
|
134
134
|
else
|
|
135
|
-
fail_case "$name: spawn audit" "$custom_spawn_count custom spawn(s) but only ${role_count:-0} '
|
|
135
|
+
fail_case "$name: spawn audit" "$custom_spawn_count custom spawn(s) but only ${role_count:-0} '@\${QUALIA_AGENTS}/' anchors — prompt cache will miss"
|
|
136
136
|
fi
|
|
137
137
|
fi
|
|
138
138
|
done
|
package/tests/state.test.sh
CHANGED
|
@@ -6,6 +6,8 @@ PASS=0
|
|
|
6
6
|
FAIL=0
|
|
7
7
|
# Resolve STATE_JS to an ABSOLUTE path so `cd` inside subshells doesn't break it.
|
|
8
8
|
STATE_JS="$(cd "$(dirname "$0")/../bin" && pwd)/state.js"
|
|
9
|
+
FRAMEWORK_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
STATE_LEDGER_JS="$FRAMEWORK_DIR/bin/state-ledger.js"
|
|
9
11
|
NODE="${NODE:-node}"
|
|
10
12
|
|
|
11
13
|
# Track tmp dirs we create so we can clean them up on exit
|
|
@@ -98,11 +100,18 @@ INIT_EXIT=$?
|
|
|
98
100
|
if [ "$INIT_EXIT" -eq 0 ] \
|
|
99
101
|
&& [ -f "$TMP/.planning/tracking.json" ] \
|
|
100
102
|
&& [ -f "$TMP/.planning/STATE.md" ] \
|
|
103
|
+
&& [ -f "$TMP/.planning/qualia/state.jsonl" ] \
|
|
101
104
|
&& grep -q '"ok": true' /tmp/qualia-state-test.out \
|
|
102
105
|
&& grep -q '"action": "init"' /tmp/qualia-state-test.out; then
|
|
103
|
-
pass "cmdInit creates tracking.json + STATE.md"
|
|
106
|
+
pass "cmdInit creates tracking.json + STATE.md + ledger"
|
|
104
107
|
else
|
|
105
|
-
fail_case "cmdInit creates tracking.json + STATE.md" "exit=$INIT_EXIT"
|
|
108
|
+
fail_case "cmdInit creates tracking.json + STATE.md + ledger" "exit=$INIT_EXIT"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if (cd "$TMP" && $NODE "$STATE_LEDGER_JS" validate 2>&1 | grep -q '"ok": true'); then
|
|
112
|
+
pass "cmdInit writes valid state ledger"
|
|
113
|
+
else
|
|
114
|
+
fail_case "cmdInit state ledger validates"
|
|
106
115
|
fi
|
|
107
116
|
|
|
108
117
|
# tracking.json content sanity
|
|
@@ -175,13 +184,22 @@ EXIT=$?
|
|
|
175
184
|
if [ "$EXIT" -eq 0 ] \
|
|
176
185
|
&& echo "$OUT" | grep -q '"ok": true' \
|
|
177
186
|
&& echo "$OUT" | grep -q '"status": "built"' \
|
|
187
|
+
&& echo "$OUT" | grep -q '"ledger_event_id":' \
|
|
178
188
|
&& grep -q '"tasks_done": 5' "$TMP/.planning/tracking.json" \
|
|
179
189
|
&& grep -q '"tasks_total": 5' "$TMP/.planning/tracking.json"; then
|
|
180
|
-
pass "planned → built records tasks_done/tasks_total"
|
|
190
|
+
pass "planned → built records tasks_done/tasks_total + ledger id"
|
|
181
191
|
else
|
|
182
192
|
fail_case "planned → built" "exit=$EXIT"
|
|
183
193
|
fi
|
|
184
194
|
|
|
195
|
+
LEDGER_COUNT=$(wc -l < "$TMP/.planning/qualia/state.jsonl" | tr -d ' ')
|
|
196
|
+
if [ "$LEDGER_COUNT" -ge 3 ] \
|
|
197
|
+
&& (cd "$TMP" && $NODE "$STATE_LEDGER_JS" validate 2>&1 | grep -q '"ok": true'); then
|
|
198
|
+
pass "state ledger records init + transitions as valid chain"
|
|
199
|
+
else
|
|
200
|
+
fail_case "state ledger transition chain" "count=$LEDGER_COUNT"
|
|
201
|
+
fi
|
|
202
|
+
|
|
185
203
|
# 6. built → verified(pass) auto-advances to phase 2, resets status to setup
|
|
186
204
|
touch "$TMP/.planning/phase-1-verification.md"
|
|
187
205
|
OUT=$(cd "$TMP" && $NODE "$STATE_JS" transition --to verified --verification pass 2>&1)
|
|
@@ -618,6 +636,50 @@ else
|
|
|
618
636
|
fail_case "validate well-formed plan" "exit=$EXIT out=$OUT"
|
|
619
637
|
fi
|
|
620
638
|
|
|
639
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 --require-contract 2>&1)
|
|
640
|
+
EXIT=$?
|
|
641
|
+
if [ "$EXIT" -eq 1 ] \
|
|
642
|
+
&& echo "$OUT" | grep -q 'JSON contract missing'; then
|
|
643
|
+
pass "validate-plan --require-contract fails when JSON contract is missing"
|
|
644
|
+
else
|
|
645
|
+
fail_case "validate-plan --require-contract missing contract" "exit=$EXIT out=$OUT"
|
|
646
|
+
fi
|
|
647
|
+
|
|
648
|
+
HASH=$(cd "$TMP" && $NODE -e "const pc=require('$FRAMEWORK_DIR/bin/plan-contract.js'); const fs=require('fs'); process.stdout.write(pc.hashPlan(fs.readFileSync('.planning/phase-1-plan.md','utf8')))")
|
|
649
|
+
cat > "$TMP/.planning/phase-1-contract.json" <<JSON
|
|
650
|
+
{
|
|
651
|
+
"version": 1,
|
|
652
|
+
"phase": 1,
|
|
653
|
+
"goal": "test",
|
|
654
|
+
"why": "validate require-contract",
|
|
655
|
+
"generated_at": "2026-05-23T00:00:00Z",
|
|
656
|
+
"generated_by": "manual",
|
|
657
|
+
"source_plan_hash": "$HASH",
|
|
658
|
+
"success_criteria": ["ok"],
|
|
659
|
+
"tasks": [{
|
|
660
|
+
"id": "T1",
|
|
661
|
+
"title": "Task",
|
|
662
|
+
"wave": 1,
|
|
663
|
+
"depends_on": [],
|
|
664
|
+
"files_modify": [],
|
|
665
|
+
"files_create": [],
|
|
666
|
+
"files_delete": [],
|
|
667
|
+
"acceptance_criteria": ["ok"],
|
|
668
|
+
"action": "Do task",
|
|
669
|
+
"context_files": [],
|
|
670
|
+
"verification": [{ "type": "file-exists", "path": ".planning/phase-1-plan.md" }]
|
|
671
|
+
}]
|
|
672
|
+
}
|
|
673
|
+
JSON
|
|
674
|
+
OUT=$(cd "$TMP" && $NODE "$STATE_JS" validate-plan --phase 1 --require-contract 2>&1)
|
|
675
|
+
EXIT=$?
|
|
676
|
+
if [ "$EXIT" -eq 0 ] \
|
|
677
|
+
&& echo "$OUT" | grep -q '"contract_status": "valid"'; then
|
|
678
|
+
pass "validate-plan --require-contract accepts valid JSON contract"
|
|
679
|
+
else
|
|
680
|
+
fail_case "validate-plan --require-contract valid contract" "exit=$EXIT out=$OUT"
|
|
681
|
+
fi
|
|
682
|
+
|
|
621
683
|
# 33. validate-plan rejects empty plan
|
|
622
684
|
TMP=$(make_project)
|
|
623
685
|
echo "" > "$TMP/.planning/phase-1-plan.md"
|