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.
Files changed (72) hide show
  1. package/README.md +14 -11
  2. package/agents/builder.md +7 -7
  3. package/agents/planner.md +39 -3
  4. package/agents/research-synthesizer.md +1 -1
  5. package/agents/researcher.md +3 -3
  6. package/agents/roadmapper.md +7 -7
  7. package/agents/verifier.md +18 -6
  8. package/agents/visual-evaluator.md +8 -7
  9. package/bin/cli.js +111 -14
  10. package/bin/contract-runner.js +219 -0
  11. package/bin/host-adapters.js +66 -0
  12. package/bin/install.js +99 -152
  13. package/bin/plan-contract.js +99 -2
  14. package/bin/planning-hygiene.js +262 -0
  15. package/bin/runtime-manifest.js +32 -0
  16. package/bin/state-ledger.js +184 -0
  17. package/bin/state.js +299 -20
  18. package/bin/trust-score.js +276 -0
  19. package/docs/onboarding.html +5 -4
  20. package/guide.md +3 -2
  21. package/package.json +1 -1
  22. package/qualia-design/design-rubric.md +17 -5
  23. package/qualia-design/frontend.md +5 -1
  24. package/qualia-design/graphics.md +47 -0
  25. package/rules/command-output.md +35 -0
  26. package/skills/qualia/SKILL.md +10 -10
  27. package/skills/qualia-build/SKILL.md +20 -14
  28. package/skills/qualia-debug/SKILL.md +16 -8
  29. package/skills/qualia-discuss/SKILL.md +10 -10
  30. package/skills/qualia-doctor/SKILL.md +140 -0
  31. package/skills/qualia-feature/SKILL.md +23 -21
  32. package/skills/qualia-fix/SKILL.md +216 -0
  33. package/skills/qualia-flush/SKILL.md +9 -9
  34. package/skills/qualia-handoff/SKILL.md +9 -9
  35. package/skills/qualia-help/SKILL.md +3 -3
  36. package/skills/qualia-hook-gen/SKILL.md +1 -1
  37. package/skills/qualia-idk/SKILL.md +4 -4
  38. package/skills/qualia-issues/SKILL.md +2 -2
  39. package/skills/qualia-learn/SKILL.md +10 -10
  40. package/skills/qualia-map/SKILL.md +2 -2
  41. package/skills/qualia-milestone/SKILL.md +15 -15
  42. package/skills/qualia-new/REFERENCE.md +9 -9
  43. package/skills/qualia-new/SKILL.md +14 -14
  44. package/skills/qualia-optimize/REFERENCE.md +1 -1
  45. package/skills/qualia-optimize/SKILL.md +23 -16
  46. package/skills/qualia-pause/SKILL.md +2 -2
  47. package/skills/qualia-plan/SKILL.md +23 -13
  48. package/skills/qualia-polish/REFERENCE.md +14 -14
  49. package/skills/qualia-polish/SKILL.md +64 -19
  50. package/skills/qualia-polish/scripts/loop.mjs +3 -3
  51. package/skills/qualia-polish/scripts/score.mjs +9 -3
  52. package/skills/qualia-postmortem/SKILL.md +9 -9
  53. package/skills/qualia-report/SKILL.md +23 -23
  54. package/skills/qualia-research/SKILL.md +5 -5
  55. package/skills/qualia-resume/SKILL.md +4 -4
  56. package/skills/qualia-review/SKILL.md +28 -12
  57. package/skills/qualia-road/SKILL.md +18 -5
  58. package/skills/qualia-ship/SKILL.md +22 -22
  59. package/skills/qualia-skill-new/SKILL.md +13 -13
  60. package/skills/qualia-test/SKILL.md +5 -5
  61. package/skills/qualia-triage/SKILL.md +1 -1
  62. package/skills/qualia-verify/SKILL.md +37 -23
  63. package/skills/qualia-vibe/SKILL.md +13 -10
  64. package/skills/qualia-vibe/scripts/extract.mjs +1 -1
  65. package/skills/zoho-workflow/SKILL.md +1 -1
  66. package/templates/help.html +12 -10
  67. package/tests/bin.test.sh +34 -4
  68. package/tests/install-smoke.test.sh +22 -2
  69. package/tests/lib.test.sh +290 -0
  70. package/tests/runner.js +3 -0
  71. package/tests/skills.test.sh +4 -4
  72. package/tests/state.test.sh +65 -3
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env node
2
+ // Qualia trust score — compact harness health summary.
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+ const pc = require("./plan-contract.js");
8
+ const ledger = require("./state-ledger.js");
9
+ const { binFiles } = require("./runtime-manifest.js");
10
+
11
+ const HOMES = [
12
+ { name: "Claude", dir: path.join(os.homedir(), ".claude") },
13
+ { name: "Codex", dir: path.join(os.homedir(), ".codex") },
14
+ ];
15
+
16
+ const REQUIRED_BIN = binFiles();
17
+
18
+ const REQUIRED_HOOKS = [
19
+ "session-start.js", "auto-update.js", "branch-guard.js", "pre-push.js",
20
+ "pre-deploy-gate.js", "migration-guard.js", "git-guardrails.js",
21
+ "stop-session-log.js", "vercel-account-guard.js", "env-empty-guard.js",
22
+ "supabase-destructive-guard.js",
23
+ ];
24
+
25
+ const REQUIRED_DESIGN_FILES = [
26
+ "design-laws.md",
27
+ "design-rubric.md",
28
+ "design-brand.md",
29
+ "design-product.md",
30
+ "design-reference.md",
31
+ "frontend.md",
32
+ "graphics.md",
33
+ ];
34
+
35
+ const REQUIRED_EMPLOYEE_SKILLS = [
36
+ "qualia-doctor",
37
+ "qualia-road",
38
+ "qualia-resume",
39
+ "qualia-pause",
40
+ "qualia-report",
41
+ ];
42
+
43
+ function exists(p) {
44
+ try { return fs.existsSync(p); } catch { return false; }
45
+ }
46
+ function readJson(p) {
47
+ try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
48
+ }
49
+ function readText(p) {
50
+ try { return fs.readFileSync(p, "utf8"); } catch { return ""; }
51
+ }
52
+
53
+ function installedHomes() {
54
+ return HOMES.filter((h) => exists(path.join(h.dir, ".qualia-config.json")));
55
+ }
56
+
57
+ function inspectInstall(homes) {
58
+ if (homes.length === 0) {
59
+ return { status: "fail", score: 0, issues: ["no Qualia install config found"], targets: [] };
60
+ }
61
+ const issues = [];
62
+ for (const home of homes) {
63
+ for (const f of REQUIRED_BIN) {
64
+ if (!exists(path.join(home.dir, "bin", f))) issues.push(`${home.name}: missing bin/${f}`);
65
+ }
66
+ if (home.name === "Claude") {
67
+ if (!exists(path.join(home.dir, "CLAUDE.md"))) issues.push("Claude: missing CLAUDE.md");
68
+ if (!exists(path.join(home.dir, "settings.json"))) issues.push("Claude: missing settings.json");
69
+ } else {
70
+ if (!exists(path.join(home.dir, "AGENTS.md"))) issues.push("Codex: missing AGENTS.md");
71
+ if (!exists(path.join(home.dir, "hooks.json"))) issues.push("Codex: missing hooks.json");
72
+ if (!exists(path.join(home.dir, "config.toml"))) issues.push("Codex: missing config.toml");
73
+ }
74
+ }
75
+ return {
76
+ status: issues.length ? "degraded" : "pass",
77
+ score: issues.length ? Math.max(6, 20 - issues.length * 2) : 20,
78
+ issues,
79
+ targets: homes.map((h) => h.name),
80
+ };
81
+ }
82
+
83
+ function inspectHooks(homes) {
84
+ if (homes.length === 0) return { status: "fail", score: 0, issues: ["no install targets"] };
85
+ const issues = [];
86
+ for (const home of homes) {
87
+ for (const h of REQUIRED_HOOKS) {
88
+ if (!exists(path.join(home.dir, "hooks", h))) issues.push(`${home.name}: missing hooks/${h}`);
89
+ }
90
+ }
91
+ return {
92
+ status: issues.length ? "degraded" : "pass",
93
+ score: issues.length ? Math.max(4, 12 - issues.length) : 12,
94
+ issues,
95
+ };
96
+ }
97
+
98
+ function inspectProject(cwd) {
99
+ const planning = path.join(cwd, ".planning");
100
+ if (!exists(planning)) return { status: "not_applicable", score: 12, issues: [], phase: null };
101
+ const tracking = readJson(path.join(planning, "tracking.json"));
102
+ const stateText = readText(path.join(planning, "STATE.md"));
103
+ const issues = [];
104
+ if (!tracking) issues.push("tracking.json missing or invalid");
105
+ if (!stateText) issues.push("STATE.md missing");
106
+ const phaseMatch = stateText.match(/^Phase:\s*(\d+)/m);
107
+ const statusMatch = stateText.match(/^Status:\s*(.+)$/m);
108
+ const phase = phaseMatch ? Number(phaseMatch[1]) : (tracking && Number(tracking.phase)) || 1;
109
+ if (!phaseMatch && stateText) issues.push("STATE.md phase header missing");
110
+ if (!statusMatch && stateText) issues.push("STATE.md status missing");
111
+ return {
112
+ status: issues.length ? "degraded" : "pass",
113
+ score: issues.length ? Math.max(4, 12 - issues.length * 4) : 12,
114
+ issues,
115
+ phase,
116
+ project_status: statusMatch ? statusMatch[1].trim() : tracking?.status || "",
117
+ };
118
+ }
119
+
120
+ function inspectContracts(cwd, phase) {
121
+ const planning = path.join(cwd, ".planning");
122
+ if (!exists(planning) || !phase) return { status: "not_applicable", score: 18, issues: [] };
123
+ const planPath = path.join(planning, `phase-${phase}-plan.md`);
124
+ const contractPath = path.join(planning, `phase-${phase}-contract.json`);
125
+ if (!exists(planPath)) return { status: "not_applicable", score: 18, issues: [] };
126
+ if (!exists(contractPath)) return { status: "degraded", score: 6, issues: [`phase ${phase}: JSON contract missing`] };
127
+ const loaded = pc.readContractFile(contractPath);
128
+ if (!loaded.ok) return { status: "fail", score: 0, issues: [`phase ${phase}: contract unreadable (${loaded.message || loaded.error})`] };
129
+ const errors = pc.validate(loaded.contract);
130
+ if (errors.length) return { status: "fail", score: 0, issues: [`phase ${phase}: contract invalid (${errors.length} issue(s))`] };
131
+ const drift = pc.checkDrift(contractPath, planPath);
132
+ if (drift.ok && drift.drift) return { status: "fail", score: 0, issues: [`phase ${phase}: contract drifted from plan`] };
133
+ return { status: "pass", score: 18, issues: [], contract: path.relative(cwd, contractPath) };
134
+ }
135
+
136
+ function inspectLedger(cwd) {
137
+ const planning = path.join(cwd, ".planning");
138
+ if (!exists(planning)) return { status: "not_applicable", score: 10, issues: [] };
139
+ const file = ledger.ledgerPath(cwd);
140
+ if (!exists(file)) return { status: "degraded", score: 4, issues: ["state ledger missing"] };
141
+ const result = ledger.validate(cwd);
142
+ if (!result.ok) return { status: "fail", score: 0, issues: result.errors };
143
+ if (result.count === 0) return { status: "degraded", score: 4, issues: ["state ledger empty"] };
144
+ return { status: "pass", score: 10, issues: [], events: result.count };
145
+ }
146
+
147
+ function inspectMemory(homes) {
148
+ if (homes.length === 0) return { status: "fail", score: 0, issues: ["no install targets"] };
149
+ const issues = [];
150
+ for (const home of homes) {
151
+ if (!exists(path.join(home.dir, "knowledge", "index.md"))) issues.push(`${home.name}: missing knowledge/index.md`);
152
+ if (!exists(path.join(home.dir, "knowledge", "agents.md"))) issues.push(`${home.name}: missing knowledge/agents.md`);
153
+ if (!exists(path.join(home.dir, "knowledge", "daily-log"))) issues.push(`${home.name}: missing knowledge/daily-log`);
154
+ }
155
+ return {
156
+ status: issues.length ? "degraded" : "pass",
157
+ score: issues.length ? Math.max(2, 5 - issues.length) : 5,
158
+ issues,
159
+ };
160
+ }
161
+
162
+ function inspectDesign(homes) {
163
+ if (homes.length === 0) return { status: "fail", score: 0, issues: ["no install targets"] };
164
+ const issues = [];
165
+ for (const home of homes) {
166
+ for (const f of REQUIRED_DESIGN_FILES) {
167
+ if (!exists(path.join(home.dir, "qualia-design", f))) issues.push(`${home.name}: missing qualia-design/${f}`);
168
+ }
169
+ if (!exists(path.join(home.dir, "skills", "qualia-polish", "SKILL.md"))) {
170
+ issues.push(`${home.name}: missing qualia-polish skill`);
171
+ }
172
+ if (!exists(path.join(home.dir, "skills", "qualia-vibe", "SKILL.md"))) {
173
+ issues.push(`${home.name}: missing qualia-vibe skill`);
174
+ }
175
+ if (!exists(path.join(home.dir, "agents", home.name === "Codex" ? "visual-evaluator.toml" : "visual-evaluator.md"))) {
176
+ issues.push(`${home.name}: missing visual-evaluator agent`);
177
+ }
178
+ if (!exists(path.join(home.dir, "bin", "slop-detect.mjs"))) {
179
+ issues.push(`${home.name}: missing slop-detect.mjs`);
180
+ }
181
+ }
182
+ return {
183
+ status: issues.length ? "degraded" : "pass",
184
+ score: issues.length ? Math.max(2, 8 - issues.length) : 8,
185
+ issues,
186
+ };
187
+ }
188
+
189
+ function inspectEmployeeExperience(homes) {
190
+ if (homes.length === 0) return { status: "fail", score: 0, issues: ["no install targets"] };
191
+ const issues = [];
192
+ for (const home of homes) {
193
+ for (const skill of REQUIRED_EMPLOYEE_SKILLS) {
194
+ if (!exists(path.join(home.dir, "skills", skill, "SKILL.md"))) issues.push(`${home.name}: missing ${skill} skill`);
195
+ }
196
+ if (!exists(path.join(home.dir, "qualia-guide.md"))) issues.push(`${home.name}: missing qualia-guide.md`);
197
+ if (!exists(path.join(home.dir, "qualia-templates", "help.html"))) issues.push(`${home.name}: missing help.html`);
198
+ }
199
+ return {
200
+ status: issues.length ? "degraded" : "pass",
201
+ score: issues.length ? Math.max(1, 5 - issues.length) : 5,
202
+ issues,
203
+ };
204
+ }
205
+
206
+ function inspectErp(homes) {
207
+ if (homes.length === 0) return { status: "not_applicable", score: 10, issues: [] };
208
+ const issues = [];
209
+ let enabled = false;
210
+ let queueCount = 0;
211
+ for (const home of homes) {
212
+ const cfg = readJson(path.join(home.dir, ".qualia-config.json")) || {};
213
+ if (cfg.erp && cfg.erp.enabled !== false) {
214
+ enabled = true;
215
+ const keyFile = path.join(home.dir, cfg.erp.api_key_file || ".erp-api-key");
216
+ if (!exists(keyFile)) issues.push(`${home.name}: ERP enabled but API key missing`);
217
+ }
218
+ const queue = readJson(path.join(home.dir, ".erp-retry-queue.json"));
219
+ if (Array.isArray(queue)) queueCount += queue.length;
220
+ }
221
+ if (!enabled) return { status: "not_applicable", score: 10, issues: [], queue_count: queueCount };
222
+ if (queueCount > 0) issues.push(`ERP retry queue has ${queueCount} item(s)`);
223
+ return {
224
+ status: issues.length ? "degraded" : "pass",
225
+ score: issues.length ? Math.max(3, 10 - issues.length * 3) : 10,
226
+ issues,
227
+ queue_count: queueCount,
228
+ };
229
+ }
230
+
231
+ function buildTrustScore(cwd = process.cwd()) {
232
+ const homes = installedHomes();
233
+ const install = inspectInstall(homes);
234
+ const hooks = inspectHooks(homes);
235
+ const project = inspectProject(cwd);
236
+ const contracts = inspectContracts(cwd, project.phase);
237
+ const state_ledger = inspectLedger(cwd);
238
+ const memory = inspectMemory(homes);
239
+ const erp = inspectErp(homes);
240
+ const design = inspectDesign(homes);
241
+ const employee_experience = inspectEmployeeExperience(homes);
242
+ const checks = { install, hooks, project, contracts, state_ledger, memory, erp, design, employee_experience };
243
+ const score = Math.max(0, Math.min(100, Object.values(checks).reduce((n, c) => n + (c.score || 0), 0)));
244
+ const failCount = Object.values(checks).filter((c) => c.status === "fail").length;
245
+ const degradedCount = Object.values(checks).filter((c) => c.status === "degraded").length;
246
+ const status = failCount ? "FAIL" : degradedCount ? "DEGRADED" : "PASS";
247
+ return { ok: failCount === 0, status, score, generated_at: new Date().toISOString(), checks };
248
+ }
249
+
250
+ function printHuman(result) {
251
+ console.log(`Trust score: ${result.score}/100 (${result.status})`);
252
+ for (const [name, check] of Object.entries(result.checks)) {
253
+ const label = `${name[0].toUpperCase()}${name.slice(1)}`;
254
+ console.log(`${label}: ${check.status} (${check.score})`);
255
+ for (const issue of check.issues || []) console.log(` - ${issue}`);
256
+ }
257
+ }
258
+
259
+ function main(argv) {
260
+ const result = buildTrustScore(process.cwd());
261
+ if (argv.includes("--json")) console.log(JSON.stringify(result, null, 2));
262
+ else printHuman(result);
263
+ return result.status === "FAIL" ? 1 : 0;
264
+ }
265
+
266
+ module.exports = {
267
+ buildTrustScore,
268
+ inspectInstall,
269
+ inspectProject,
270
+ inspectContracts,
271
+ inspectLedger,
272
+ inspectDesign,
273
+ inspectEmployeeExperience,
274
+ };
275
+
276
+ if (require.main === module) process.exit(main(process.argv));
@@ -496,9 +496,10 @@
496
496
  <div class="kit-group">
497
497
  <h4>Diagnose &amp; fix</h4>
498
498
  <dl>
499
- <dt>/qualia-debug</dt><dd>Investigative debugging. Finds root cause, applies a minimal fix, writes a debug report.</dd>
500
- <dt>/qualia-review</dt><dd>Production audit with severity-scored findings. Run before a big deploy.</dd>
501
- <dt>/qualia-optimize</dt><dd>Deep pass for performance, design, architecture. Spawns parallel specialists.</dd>
499
+ <dt>/qualia-fix</dt><dd>Repair lane. Finds root cause, applies a minimal fix, verifies it, writes a fix report.</dd>
500
+ <dt>/qualia-debug</dt><dd>Investigative debugging. Use when the failure is unclear and you need evidence before repair.</dd>
501
+ <dt>/qualia-review</dt><dd>Read-only production audit with severity-scored findings. Run before a big deploy.</dd>
502
+ <dt>/qualia-optimize</dt><dd>Deep improvement map for performance, design, alignment, and architecture. Spawns parallel specialists.</dd>
502
503
  <dt>/qualia-postmortem</dt><dd>Self-healing layer. After a failed verify, identifies which agent or rule should have caught it and proposes a delta.</dd>
503
504
  </dl>
504
505
  </div>
@@ -521,7 +522,7 @@
521
522
  <div class="kit-group">
522
523
  <h4>Build something small</h4>
523
524
  <dl>
524
- <dt>/qualia-feature</dt><dd>Auto-scoped: inline for trivia (typo, config tweak), fresh builder spawn for 1-5 file features. Refuses and routes to /qualia-plan when scope is phase-sized.</dd>
525
+ <dt>/qualia-feature</dt><dd>Auto-scoped: inline for trivia (copy, config tweak), fresh builder spawn for 1-5 file features. Broken existing behavior routes to /qualia-fix.</dd>
525
526
  </dl>
526
527
  </div>
527
528
  <div class="kit-group">
package/guide.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  **v6.2.2 carries forward.** Framework builds, Memory remembers, ERP operates. ERP work packets can seed Claude/Codex sessions, `/qualia-report` can carry ERP-native IDs, and release verification now has a public `@latest` install smoke.
15
15
 
16
- **v6.2.1 carries forward.** Active docs match the v6.2 no-bot-commit model, the explicit `/qualia-report` ERP contract, the current 33-skill surface, and fail-closed `INSUFFICIENT EVIDENCE` behavior. `tests/refs.test.sh` guards those claims.
16
+ **v6.2.1 carries forward.** Active docs match the v6.2 no-bot-commit model, the explicit `/qualia-report` ERP contract, the current skill surface, and fail-closed `INSUFFICIENT EVIDENCE` behavior. `tests/refs.test.sh` guards those claims.
17
17
 
18
18
  **v6.1.0 ships the design-pivot path you were missing.** New `/qualia-vibe` is fast aesthetic pivot (~3 min): swap design tokens, keep layout. Default proposes ONE direction per `rules/one-opinion.md` (the EventMaster discipline — never give the user a menu). Sub-modes: `--variants N` for the opt-in menu, `--extract URL` reverse-engineers DESIGN.md from a reference site, `--sync` shows code↔DESIGN.md drift and can patch DESIGN.md from code. Slop-detect grew banned fonts (Montserrat/Poppins/Lato/Open Sans) and a `--watch` flag for proactive single-file mode. Several design-surface bugs from v6.0 audit are fixed too (viewport mismatch, slop-detect path resolution in the polish loop, dead `/qualia-design` references, the bounce-easing token that contradicted design-laws).
19
19
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  **v5.9.0 carries forward.** `tests/refs.test.sh` catches dead command references in user-facing surfaces on every release. `bin/erp-retry.js` is a real persistent retry queue for ERP report uploads. Four structured agents (verifier, plan-checker, roadmapper, qa-browser) run on Sonnet for ~40% per-phase cost cut, while builder/planner/researcher/visual-evaluator stay on Opus where the architectural and vision reasoning lives. The verifier downgrades to FAIL on any `INSUFFICIENT EVIDENCE` line.
27
27
 
28
- **Surface is 33 skills.** Use `/qualia-feature` for single-feature work and `/qualia-discuss` in PROJECT MODE for kickoff capture; `/qualia-polish --loop` for the autonomous visual loop; `/qualia-vibe` for fast layout-preserving aesthetic pivots.
28
+ **Surface is 35 installed skills.** Use `/qualia-fix` for broken existing behavior, `/qualia-feature` for new single-feature work, and `/qualia-discuss` in PROJECT MODE for kickoff capture; `/qualia-polish --loop` for the autonomous visual loop; `/qualia-vibe` for fast layout-preserving aesthetic pivots.
29
29
 
30
30
  ## The Road
31
31
 
@@ -91,6 +91,7 @@ Append `--auto` to `/qualia-new` and the framework chains every step:
91
91
  | Issues | `/qualia-issues` | Break a phase plan into vertical-slice GitHub issues |
92
92
  | Triage | `/qualia-triage` | Triage open issues through the ready-for-agent state machine |
93
93
  | Optimize | `/qualia-optimize --deepen` | Find shallow modules; v5.3+ spawns 3 parallel interface-design variants per candidate |
94
+ | Planning hygiene | `qualia-framework planning-hygiene scan` | Detect loose `.planning/` reports/assets before they turn into folder bloat |
94
95
  | Hook gen | `/qualia-hook-gen` | Convert a CLAUDE.md/rules instruction into a deterministic hook (v5.3+) |
95
96
  | Road view | `/qualia-road` | View and navigate journey/milestone/phase status |
96
97
  | Lost? | `/qualia` | Mechanical next-command router |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qualia-framework",
3
- "version": "6.2.9",
3
+ "version": "6.2.10",
4
4
  "description": "Claude Code and Codex workflow framework by Qualia Solutions. Plan, build, verify, ship.",
5
5
  "bin": {
6
6
  "qualia-framework": "./bin/cli.js"
@@ -4,7 +4,7 @@ globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.vue", "*.svelte"]
4
4
 
5
5
  # Design Rubric
6
6
 
7
- Anchored 1-5 scoring across 8 dimensions. Used by the verifier agent to score frontend phases. Used by `/qualia-polish --critique` for read-only audits.
7
+ Anchored 1-5 scoring across 9 dimensions. Used by the verifier agent to score frontend phases. Used by `/qualia-polish --critique` for read-only audits.
8
8
 
9
9
  ## How to score
10
10
 
@@ -25,9 +25,9 @@ If a vision model is critiquing screenshots, it must score against this rubric.
25
25
 
26
26
  Score only what is scope-relevant. A `/qualia-polish src/components/Button.tsx` review scores Typography, Color, States, Motion, Microcopy. Skip Layout Originality and Container Depth — they're component-internal concerns at most.
27
27
 
28
- A whole-app `/qualia-polish` scores all 8 dimensions across multiple representative routes.
28
+ A whole-app `/qualia-polish` scores all 9 dimensions across multiple representative routes.
29
29
 
30
- ## The 8 dimensions
30
+ ## The 9 dimensions
31
31
 
32
32
  ### 1. Typography
33
33
 
@@ -125,10 +125,22 @@ Evidence: grep for banned phrases, sample of empty/error states
125
125
 
126
126
  Evidence: max DOM nesting depth on cards/panels, grep for `border-left` decorative usage
127
127
 
128
+ ### 9. Visual system & graphics
129
+
130
+ | Score | Criteria |
131
+ |---|---|
132
+ | 1 | Page is only generic cards/text where product, state, data, or brand object should be visible. Or visual is decorative noise unrelated to meaning. |
133
+ | 2 | Simple icon/illustration exists but could fit any project. No product/domain specificity. |
134
+ | 3 | Visual supports the page: screenshot, product image, simple diagram, chart, or meaningful icon system. |
135
+ | 4 | Visual system is custom to the product/domain: annotated diagram, varied chart, bespoke hero composition, real media, or meaningful motion. |
136
+ | 5 | Complex visual is memorable and useful: product-in-context scene, interactive graph/map/timeline, 3D/canvas/WebGL, or data visualization with clear insight and accessible fallback. |
137
+
138
+ Evidence: visual type, file:line references, relationship to PRODUCT.md/CONTEXT.md, reduced-motion or static fallback
139
+
128
140
  ## Aggregate score
129
141
 
130
142
  ```
131
- total = sum of dimension scores (max 40)
143
+ total = sum of dimension scores (max 45)
132
144
  average = total / count_of_scored_dimensions
133
145
  phase_pass = ALL scored dimensions ≥ 3
134
146
  phase_fail = ANY scored dimension < 3
@@ -149,7 +161,7 @@ A phase fails if any dimension is below 3. Same gate as functional verification.
149
161
  | Layout originality | 2 | `app/page.tsx:42-78` is a three-column feature grid in section 2 — first-order slop. Rework. |
150
162
  | ... | ... | ... |
151
163
 
152
- **Aggregate:** 28/40 (avg 3.5)
164
+ **Aggregate:** 32/45 (avg 3.56)
153
165
  **Phase verdict:** FAIL — Layout Originality at 2 blocks the phase.
154
166
  **Fix priority:** Rework section 2. Replace three-column grid with varied-height layout per `design-brand.md` §Layout.
155
167
  ```
@@ -109,10 +109,14 @@ These are Qualia brand standards — mandatory for every frontend component. Not
109
109
  If `.planning/DESIGN.md` exists in the project, it takes precedence over these defaults.
110
110
  Read it before any frontend work. It contains project-specific: palette, typography, spacing, component patterns.
111
111
 
112
+ For redesigns and full-app polish, also read:
113
+ - `qualia-design/design-reference.md` for motion, accessibility, responsive, and performance depth
114
+ - `qualia-design/graphics.md` for complex graphics, charts, canvas, 3D, visual systems, and media rules
115
+
112
116
  ## Qualia design commands
113
117
  - `/qualia-polish` — design pass, scope-adaptive (component / section / app / redesign / critique / quick / loop)
114
118
  - `/qualia-vibe` — fast aesthetic pivot (swap tokens, keep layout) + reverse-engineer from URL + code↔DESIGN.md sync
115
119
  - `/qualia-review` — scored production audit
116
120
 
117
121
  ### Recommended workflow
118
- 1. Build feature → 2. `/qualia-polish` to polish within the current vibe → 3. `/qualia-vibe` when the vibe itself needs to change → 4. `/qualia-polish --loop` for autonomous visual QA → ship.
122
+ 1. Build feature → 2. `/qualia-polish` to polish within the current vibe → 3. `/qualia-vibe` when the vibe itself needs to change → 4. `/qualia-polish --redesign` when the whole surface needs stronger visual concept/graphics → 5. `/qualia-polish --loop` for autonomous visual QA → ship.
@@ -0,0 +1,47 @@
1
+ ---
2
+ globs: ["*.tsx", "*.jsx", "*.css", "*.scss", "*.html", "*.svg", "*.canvas", "*.webgl"]
3
+ ---
4
+
5
+ # Complex Graphics & Visual Systems
6
+
7
+ Loaded by `/qualia-polish --redesign`, `/qualia-polish --loop`, and any polish pass where the page needs stronger visual identity than typography and spacing alone can provide.
8
+
9
+ ## When To Add Complex Visuals
10
+
11
+ Use richer graphics when they clarify the product, reveal state, or create a memorable first-viewport signal:
12
+
13
+ - Product object, venue, person, or service that needs immediate recognition
14
+ - Operational dashboard where relationships, flow, status, or hierarchy matter
15
+ - Brand site whose current page is only text blocks and cards
16
+ - Demo or portfolio page where the work itself should be visible
17
+ - Game, spatial tool, voice/AI workflow, analytics surface, map, timeline, graph, or process flow
18
+
19
+ Do not add complex visuals as decoration. A visual must either explain, orient, compare, or make the brand/product unforgettable.
20
+
21
+ ## Visual Types
22
+
23
+ Pick the strongest type for the job:
24
+
25
+ | Need | Prefer |
26
+ |---|---|
27
+ | Show real product/place/person | Photo, screenshot, generated bitmap, or image search asset |
28
+ | Show relationships | Node graph, map, flow field, timeline, Sankey, chord, matrix |
29
+ | Show data | Custom chart with annotations, small multiples, sparklines, heatmap |
30
+ | Show process | Step path, layered diagram, animated system map |
31
+ | Create brand memory | Bespoke hero composition, editorial collage, product-in-context scene |
32
+ | Need depth/spatiality | Three.js full-bleed scene, CSS 3D, canvas/WebGL |
33
+ | Need icons | One icon family, ideally lucide if available |
34
+
35
+ ## Quality Rules
36
+
37
+ 1. Use real or generated bitmap imagery when the user needs to inspect a real thing. Do not substitute abstract SVG blobs.
38
+ 2. For 3D, use Three.js and verify the canvas is nonblank at desktop and mobile viewports.
39
+ 3. For charts, annotate the insight. A chart without labels or a takeaway is decoration.
40
+ 4. For diagrams, keep text short and legible at 375px width.
41
+ 5. For generated assets, save them under project assets, not `.planning/`.
42
+ 6. For motion, connect animation to meaning: reveal sequence, state change, data transition, or spatial relationship.
43
+ 7. Respect reduced motion. Complex visuals must have a static fallback.
44
+
45
+ ## Redesign Expectation
46
+
47
+ `/qualia-polish --redesign` must consider at least one complex-visual concept for the first viewport. It may reject the concept only if it explains why typography/layout alone is stronger for that product.
@@ -0,0 +1,35 @@
1
+ ---
2
+ alwaysApply: true
3
+ ---
4
+
5
+ # Command Output Contract
6
+
7
+ Every Qualia command must be transparent before, during, and after work. Users should never wonder what the command is doing, what it can change, or what the next step is.
8
+
9
+ ## Required Shape
10
+
11
+ Every command should expose these seven lines or their equivalent:
12
+
13
+ ```text
14
+ Command: /qualia-{name} {mode}
15
+ Scope: {files/routes/project area}
16
+ Intent: {audit|investigate|repair|build|polish|ship|report}
17
+ Mutation: none | planned | active | blocked
18
+ Evidence: {files read, commands run, sources checked}
19
+ Output: {files/reports/commits produced}
20
+ Next: {next command or done}
21
+ ```
22
+
23
+ ## Rules
24
+
25
+ 1. Start with a `qualia-ui.js banner` whenever the command has an existing banner mode.
26
+ 2. State whether the command is read-only or mutating before editing.
27
+ 3. For mutating commands, name the exact files or route family before writing.
28
+ 4. For audit or investigation commands, name the evidence commands and report path.
29
+ 5. For design commands, name the design substrate loaded from `qualia-design/` and `.planning/DESIGN.md`.
30
+ 6. End with a `qualia-ui.js end` next command. If there is no next command, say `done`.
31
+ 7. If the command blocks, say why and route to the smallest useful next command.
32
+
33
+ ## Anti-Pattern
34
+
35
+ Do not answer with only a prose paragraph like "Done, I fixed it." The command must tell the user what happened and where the evidence lives.
@@ -17,7 +17,7 @@ Read project state. Classify your situation. Tell you the exact next command.
17
17
  ### 1. Get State
18
18
 
19
19
  ```bash
20
- node ~/.claude/bin/state.js check 2>/dev/null
20
+ node ${QUALIA_BIN}/state.js check 2>/dev/null
21
21
  ```
22
22
 
23
23
  Also gather context:
@@ -50,8 +50,8 @@ Use the state.js JSON output plus gathered context:
50
50
  | `polished` | status == "polished" | → `/qualia-ship` |
51
51
  | `shipped` | status == "shipped" | → `/qualia-handoff` |
52
52
  | `handed-off` | status == "handed_off" | → `/qualia-report` then done |
53
- | `blocked` | STATE.md lists blockers or same error 3+ times | → Analyze, suggest `/qualia-debug` |
54
- | `bug-loop` | Same files edited 3+ times, user frustrated | → Different approach, `/qualia-debug` |
53
+ | `blocked` | STATE.md lists blockers or same error 3+ times | → Analyze; `/qualia-fix` if expected behavior is known, `/qualia-debug` if not |
54
+ | `bug-loop` | Same files edited 3+ times, user frustrated | → Different approach, `/qualia-fix` if actionable, `/qualia-debug` if evidence is missing |
55
55
  | `need-tests` | User mentions "tests", "coverage", "test this" | → `/qualia-test` |
56
56
 
57
57
  **Employee escalation:** If role is EMPLOYEE and situation is `gap-limit` or `bug-loop`, suggest: "Want to flag this for Fawzi?"
@@ -60,16 +60,16 @@ Use the state.js JSON output plus gathered context:
60
60
 
61
61
  **Clear next step** (use the UI helper — it reads state.js itself):
62
62
  ```bash
63
- node ~/.claude/bin/qualia-ui.js banner router
63
+ node ${QUALIA_BIN}/qualia-ui.js banner router
64
64
  # If a project is loaded, show the journey position first (one-glance orientation)
65
- test -f .planning/JOURNEY.md && node ~/.claude/bin/qualia-ui.js journey-tree .planning/JOURNEY.md
66
- node ~/.claude/bin/qualia-ui.js next "{next_command from state.js}"
65
+ test -f .planning/JOURNEY.md && node ${QUALIA_BIN}/qualia-ui.js journey-tree .planning/JOURNEY.md
66
+ node ${QUALIA_BIN}/qualia-ui.js next "{next_command from state.js}"
67
67
  ```
68
68
 
69
69
  **Ambiguous situation (multiple options):**
70
70
  Print the banner first, then use plain markdown for the options:
71
71
  ```bash
72
- node ~/.claude/bin/qualia-ui.js banner router
72
+ node ${QUALIA_BIN}/qualia-ui.js banner router
73
73
  ```
74
74
  ```
75
75
  ## Where You Are
@@ -87,9 +87,9 @@ node ~/.claude/bin/qualia-ui.js banner router
87
87
 
88
88
  **Blocker detected** (gap-limit, bug-loop, employee escalation):
89
89
  ```bash
90
- node ~/.claude/bin/qualia-ui.js banner router
91
- node ~/.claude/bin/qualia-ui.js fail "{blocker description}"
92
- node ~/.claude/bin/qualia-ui.js warn "Escalate to Fawzi or re-plan from scratch"
90
+ node ${QUALIA_BIN}/qualia-ui.js banner router
91
+ node ${QUALIA_BIN}/qualia-ui.js fail "{blocker description}"
92
+ node ${QUALIA_BIN}/qualia-ui.js warn "Escalate to Fawzi or re-plan from scratch"
93
93
  ```
94
94
 
95
95
  User can respond with a number, "just do it", or natural language.
@@ -32,9 +32,11 @@ Per `rules/codex-goal.md` — set the thread goal at phase start with scope `pha
32
32
 
33
33
  ```bash
34
34
  cat .planning/phase-{N}-plan.md
35
+ cat .planning/phase-{N}-contract.json
36
+ node ${QUALIA_BIN}/plan-contract.js validate .planning/phase-{N}-contract.json
35
37
  ```
36
38
 
37
- Parse tasks, waves, file refs.
39
+ Parse tasks, waves, file refs. Prefer the JSON contract for task ids, dependencies, file lists, and verification checks; use the Markdown plan as the human-readable context.
38
40
 
39
41
  ### 1b. Recovery Reference
40
42
 
@@ -45,7 +47,7 @@ git tag -f "pre-build-phase-{N}" HEAD 2>/dev/null
45
47
  ```
46
48
 
47
49
  ```bash
48
- node ~/.claude/bin/qualia-ui.js info "Recovery point: pre-build-phase-{N}"
50
+ node ${QUALIA_BIN}/qualia-ui.js info "Recovery point: pre-build-phase-{N}"
49
51
  ```
50
52
 
51
53
  Wave fail → stop, inspect status + output. Preserve work; fix forward or ask before reverting.
@@ -57,19 +59,19 @@ git diff --stat
57
59
  ### 2. Execute Waves
58
60
 
59
61
  ```bash
60
- node ~/.claude/bin/qualia-ui.js banner build {N} "{phase name}"
62
+ node ${QUALIA_BIN}/qualia-ui.js banner build {N} "{phase name}"
61
63
  ```
62
64
 
63
65
  **For each wave (sequential):**
64
66
 
65
67
  ```bash
66
- node ~/.claude/bin/qualia-ui.js wave {W} {total_waves} {tasks_in_wave}
68
+ node ${QUALIA_BIN}/qualia-ui.js wave {W} {total_waves} {tasks_in_wave}
67
69
  ```
68
70
 
69
71
  **Per task in wave: spawn ALL as separate `Agent()` calls in SAME turn (concurrent). Do NOT await one before spawning next.**
70
72
 
71
73
  ```bash
72
- node ~/.claude/bin/qualia-ui.js task {task_num} "{task title}"
74
+ node ${QUALIA_BIN}/qualia-ui.js task {task_num} "{task title}"
73
75
  ```
74
76
 
75
77
  **Pre-inline context** (saves 3-5 Read calls per builder):
@@ -84,7 +86,7 @@ Spawn builder:
84
86
 
85
87
  ```
86
88
  Agent(prompt="
87
- Role: @~/.claude/agents/builder.md
89
+ Role: @${QUALIA_AGENTS}/builder.md
88
90
 
89
91
  <phase_context>
90
92
  # PROJECT.md
@@ -110,6 +112,10 @@ Parallel tasks Wave {W} (do NOT touch their files):
110
112
  {task block from plan: title, wave, persona, files, depends-on, why, AC, action, validation, context}
111
113
  </task>
112
114
 
115
+ <task_contract>
116
+ {matching task object from .planning/phase-{N}-contract.json}
117
+ </task_contract>
118
+
113
119
  Context tags already loaded. Only Read project code you modify.
114
120
  Execute. Commit. Return DONE/BLOCKED/PARTIAL.
115
121
  ", subagent_type="qualia-builder", description="Task {N}: {title}")
@@ -121,7 +127,7 @@ Execute. Commit. Return DONE/BLOCKED/PARTIAL.
121
127
  - Verify commit: `git log --oneline -1`
122
128
  - Show:
123
129
  ```bash
124
- node ~/.claude/bin/qualia-ui.js done {task_num} "{title}" {commit_hash}
130
+ node ${QUALIA_BIN}/qualia-ui.js done {task_num} "{title}" {commit_hash}
125
131
  ```
126
132
 
127
133
  **After each wave:** move to next, show summary.
@@ -131,10 +137,10 @@ node ~/.claude/bin/qualia-ui.js done {task_num} "{title}" {commit_hash}
131
137
  All waves done:
132
138
 
133
139
  ```bash
134
- node ~/.claude/bin/qualia-ui.js divider
135
- node ~/.claude/bin/qualia-ui.js ok "Tasks: {done}/{total}"
136
- node ~/.claude/bin/qualia-ui.js ok "Commits: {count}"
137
- node ~/.claude/bin/qualia-ui.js ok "Waves: {count}"
140
+ node ${QUALIA_BIN}/qualia-ui.js divider
141
+ node ${QUALIA_BIN}/qualia-ui.js ok "Tasks: {done}/{total}"
142
+ node ${QUALIA_BIN}/qualia-ui.js ok "Commits: {count}"
143
+ node ${QUALIA_BIN}/qualia-ui.js ok "Waves: {count}"
138
144
  ```
139
145
 
140
146
  ### 4. Handle Failures
@@ -147,7 +153,7 @@ Builder returns deviation/blocker:
147
153
  ### 5. Update State
148
154
 
149
155
  ```bash
150
- node ~/.claude/bin/state.js transition --to built --phase {N} --tasks-done {done} --tasks-total {total} --wave {wave}
156
+ node ${QUALIA_BIN}/state.js transition --to built --phase {N} --tasks-done {done} --tasks-total {total} --wave {wave}
151
157
  ```
152
158
  Error → show, stop.
153
159
  Do NOT edit STATE.md or tracking.json manually; state.js handles both.
@@ -157,7 +163,7 @@ Do NOT edit STATE.md or tracking.json manually; state.js handles both.
157
163
  **`--auto`:** invoke `/qualia-verify {N} --auto` inline. No pause.
158
164
 
159
165
  ```bash
160
- node ~/.claude/bin/qualia-ui.js info "Auto mode — chaining into /qualia-verify {N}"
166
+ node ${QUALIA_BIN}/qualia-ui.js info "Auto mode — chaining into /qualia-verify {N}"
161
167
  ```
162
168
 
163
169
  Then invoke `qualia-verify` inline with `--auto`.
@@ -165,5 +171,5 @@ Then invoke `qualia-verify` inline with `--auto`.
165
171
  **Guided mode:** stop, show next step:
166
172
 
167
173
  ```bash
168
- node ~/.claude/bin/qualia-ui.js end "PHASE {N} BUILT" "/qualia-verify {N}"
174
+ node ${QUALIA_BIN}/qualia-ui.js end "PHASE {N} BUILT" "/qualia-verify {N}"
169
175
  ```