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.
Files changed (93) hide show
  1. package/AGENTS.md +1 -0
  2. package/CLAUDE.md +1 -0
  3. package/README.md +26 -30
  4. package/agents/builder.md +7 -7
  5. package/agents/planner.md +39 -3
  6. package/agents/research-synthesizer.md +1 -1
  7. package/agents/researcher.md +3 -3
  8. package/agents/roadmapper.md +7 -7
  9. package/agents/verifier.md +18 -6
  10. package/agents/visual-evaluator.md +8 -7
  11. package/bin/cli.js +160 -16
  12. package/bin/command-surface.js +71 -0
  13. package/bin/contract-runner.js +219 -0
  14. package/bin/harness-eval.js +296 -0
  15. package/bin/host-adapters.js +66 -0
  16. package/bin/install.js +116 -172
  17. package/bin/knowledge-flush.js +21 -10
  18. package/bin/knowledge.js +1 -1
  19. package/bin/plan-contract.js +99 -2
  20. package/bin/planning-hygiene.js +262 -0
  21. package/bin/project-snapshot.js +20 -0
  22. package/bin/report-payload.js +18 -0
  23. package/bin/runtime-manifest.js +35 -0
  24. package/bin/state-ledger.js +184 -0
  25. package/bin/state.js +330 -20
  26. package/bin/trust-score.js +268 -0
  27. package/bin/work-packet.js +228 -0
  28. package/docs/erp-contract.md +81 -1
  29. package/docs/onboarding.html +4 -14
  30. package/guide.md +16 -16
  31. package/hooks/fawzi-approval-guard.js +143 -0
  32. package/hooks/pre-deploy-gate.js +74 -1
  33. package/hooks/session-start.js +29 -1
  34. package/package.json +1 -1
  35. package/qualia-design/design-rubric.md +17 -5
  36. package/qualia-design/frontend.md +6 -2
  37. package/qualia-design/graphics.md +47 -0
  38. package/rules/codex-goal.md +1 -1
  39. package/rules/command-output.md +35 -0
  40. package/rules/one-opinion.md +2 -2
  41. package/rules/speed.md +0 -1
  42. package/skills/qualia/SKILL.md +12 -12
  43. package/skills/qualia-build/SKILL.md +20 -14
  44. package/skills/qualia-discuss/SKILL.md +10 -10
  45. package/skills/qualia-doctor/SKILL.md +140 -0
  46. package/skills/qualia-feature/SKILL.md +24 -22
  47. package/skills/qualia-fix/SKILL.md +216 -0
  48. package/skills/qualia-handoff/SKILL.md +9 -9
  49. package/skills/qualia-learn/SKILL.md +11 -11
  50. package/skills/qualia-map/SKILL.md +2 -2
  51. package/skills/qualia-milestone/SKILL.md +15 -15
  52. package/skills/qualia-new/REFERENCE.md +9 -9
  53. package/skills/qualia-new/SKILL.md +14 -14
  54. package/skills/qualia-optimize/REFERENCE.md +1 -1
  55. package/skills/qualia-optimize/SKILL.md +23 -16
  56. package/skills/qualia-plan/SKILL.md +23 -13
  57. package/skills/qualia-polish/REFERENCE.md +15 -15
  58. package/skills/qualia-polish/SKILL.md +81 -21
  59. package/skills/qualia-polish/scripts/loop.mjs +3 -3
  60. package/skills/qualia-polish/scripts/score.mjs +9 -3
  61. package/skills/{qualia-vibe/scripts/extract.mjs → qualia-polish/scripts/vibe-extract.mjs} +5 -5
  62. package/skills/{qualia-vibe/scripts/tokens.mjs → qualia-polish/scripts/vibe-tokens.mjs} +6 -6
  63. package/skills/qualia-postmortem/SKILL.md +9 -9
  64. package/skills/qualia-report/SKILL.md +23 -23
  65. package/skills/qualia-research/SKILL.md +5 -5
  66. package/skills/qualia-review/SKILL.md +28 -12
  67. package/skills/qualia-road/SKILL.md +30 -22
  68. package/skills/qualia-ship/SKILL.md +31 -24
  69. package/skills/qualia-test/SKILL.md +5 -5
  70. package/skills/qualia-verify/SKILL.md +45 -23
  71. package/skills/zoho-workflow/SKILL.md +1 -1
  72. package/templates/help.html +11 -20
  73. package/tests/bin.test.sh +178 -76
  74. package/tests/hooks.test.sh +81 -1
  75. package/tests/install-smoke.test.sh +35 -5
  76. package/tests/lib.test.sh +432 -0
  77. package/tests/published-install-smoke.test.sh +4 -3
  78. package/tests/refs.test.sh +9 -4
  79. package/tests/runner.js +32 -28
  80. package/tests/skills.test.sh +4 -4
  81. package/tests/state.test.sh +133 -3
  82. package/skills/qualia-debug/SKILL.md +0 -185
  83. package/skills/qualia-flush/SKILL.md +0 -198
  84. package/skills/qualia-help/SKILL.md +0 -74
  85. package/skills/qualia-hook-gen/SKILL.md +0 -206
  86. package/skills/qualia-idk/SKILL.md +0 -166
  87. package/skills/qualia-issues/SKILL.md +0 -151
  88. package/skills/qualia-pause/SKILL.md +0 -68
  89. package/skills/qualia-resume/SKILL.md +0 -52
  90. package/skills/qualia-skill-new/SKILL.md +0 -173
  91. package/skills/qualia-triage/SKILL.md +0 -152
  92. package/skills/qualia-vibe/SKILL.md +0 -226
  93. package/skills/qualia-zoom/SKILL.md +0 -51
@@ -496,9 +496,9 @@
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-review</dt><dd>Read-only production audit with severity-scored findings. Run before a big deploy.</dd>
501
+ <dt>/qualia-optimize</dt><dd>Deep improvement map for performance, design, alignment, and architecture. Spawns parallel specialists.</dd>
502
502
  <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
503
  </dl>
504
504
  </div>
@@ -521,18 +521,14 @@
521
521
  <div class="kit-group">
522
522
  <h4>Build something small</h4>
523
523
  <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>
524
+ <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
525
  </dl>
526
526
  </div>
527
527
  <div class="kit-group">
528
528
  <h4>Navigate the session</h4>
529
529
  <dl>
530
530
  <dt>/qualia</dt><dd>State router. Tells you the exact next command.</dd>
531
- <dt>/qualia-idk</dt><dd>"Something feels off." Diagnoses confusion across .planning + codebase.</dd>
532
- <dt>/qualia-pause</dt><dd>Save context for handoff to a future session.</dd>
533
- <dt>/qualia-resume</dt><dd>Restore context and routing from a paused session.</dd>
534
531
  <dt>/qualia-road</dt><dd>Terminal map of the workflow. Headless-friendly.</dd>
535
- <dt>/qualia-help</dt><dd>Open this kind of reference in the browser.</dd>
536
532
  </dl>
537
533
  </div>
538
534
  </div>
@@ -558,7 +554,6 @@
558
554
  <h4>Code archaeology</h4>
559
555
  <dl>
560
556
  <dt>/qualia-map</dt><dd>Brownfield onboarding. Map an existing codebase before adding to it.</dd>
561
- <dt>/qualia-zoom</dt><dd>Map a small, unfamiliar code area in domain-glossary terms.</dd>
562
557
  </dl>
563
558
  </div>
564
559
  </div>
@@ -573,21 +568,16 @@
573
568
  <h4>Knowledge</h4>
574
569
  <dl>
575
570
  <dt>/qualia-learn</dt><dd>Save a learning, pattern, fix, or client preference. Persists across projects and sessions.</dd>
576
- <dt>/qualia-flush</dt><dd>Promote daily-log raw entries to the curated knowledge tier.</dd>
577
571
  </dl>
578
572
  </div>
579
573
  <div class="kit-group">
580
574
  <h4>Issue queue</h4>
581
575
  <dl>
582
- <dt>/qualia-issues</dt><dd>Break a phase plan into independent vertical-slice GitHub issues.</dd>
583
- <dt>/qualia-triage</dt><dd>Route open issues into needs-info, ready-for-agent, ready-for-human.</dd>
584
576
  </dl>
585
577
  </div>
586
578
  <div class="kit-group">
587
579
  <h4>Extend the framework</h4>
588
580
  <dl>
589
- <dt>/qualia-skill-new</dt><dd>Author a new Qualia skill or agent.</dd>
590
- <dt>/qualia-hook-gen</dt><dd>Convert an instruction into a deterministic Claude Code hook.</dd>
591
581
  </dl>
592
582
  </div>
593
583
  <div class="kit-group">
package/guide.md CHANGED
@@ -1,9 +1,11 @@
1
- # Qualia Developer Guide (v6.2.7)
1
+ # Qualia Developer Guide (v6.3.0)
2
2
 
3
3
  > Follow the road. Type the commands. The framework handles the rest.
4
4
  > `--auto` chains the whole road end-to-end with only two human checkpoints per project.
5
5
 
6
- **v6.2.7 is the Codex runtime compatibility patch.** Codex installs now get native `~/.codex/hooks.json`, TOML agents, bin scripts, rules, skills, templates, knowledge, guide, and role config in addition to `AGENTS.md`.
6
+ **v6.3.0 is the harness hardening patch.** The default command surface is 23 active skills, retired helper sources are removed and pruned from older installs, `/qualia-polish --vibe` absorbs the old vibe command, `qualia-framework eval --run --write` produces scored eval artifacts, and PASS transitions now require clean machine evidence when a phase contract exists.
7
+
8
+ **v6.2.7 carries forward.** Codex installs now get native `~/.codex/hooks.json`, TOML agents, bin scripts, rules, skills, templates, knowledge, guide, and role config in addition to `AGENTS.md`.
7
9
 
8
10
  **v6.2.5 carries forward.** `qualia-framework project-snapshot --write` creates a single `.planning/snapshots/project-snapshot-*.json` artifact with project identifiers, current milestone/phase, closed milestones, lifetime counters, and a project progress percentage for explicit ERP/admin import.
9
11
 
@@ -13,9 +15,9 @@
13
15
 
14
16
  **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
17
 
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.
18
+ **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
19
 
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).
20
+ **v6.1.0 ships the design-pivot path you were missing.** New `/qualia-polish --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
21
 
20
22
  **v6.2.0 removes hook-created bot commits.** `pre-push.js` stamps local `tracking.json` telemetry but no longer commits it. `pre-compact.js` is gone because `state.js` already gives stronger crash safety with atomic writes plus a journal. ERP sync remains explicit through `/qualia-report` POSTs, not passive git scraping.
21
23
 
@@ -25,7 +27,7 @@
25
27
 
26
28
  **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
29
 
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.
30
+ **Surface is 23 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-polish --vibe` for fast layout-preserving aesthetic pivots.
29
31
 
30
32
  ## The Road
31
33
 
@@ -81,20 +83,18 @@ Append `--auto` to `/qualia-new` and the framework chains every step:
81
83
  | Single feature | `/qualia-feature` | Auto-scoped: inline for trivia, fresh spawn for 1-5 files |
82
84
  | Finishing | `/qualia-polish` | Design and UX pass (scope-adaptive: component / route / app / redesign / critique / quick / loop) |
83
85
  | | `/qualia-polish --loop` | Autonomous visual-polish loop: screenshot, vision-eval, fix, repeat |
84
- | Pivot the vibe | `/qualia-vibe` | Fast aesthetic pivot (~3 min): swap tokens, keep layout. Propose ONE direction. |
85
- | | `/qualia-vibe --extract <URL>` | Reverse-engineer DESIGN.md from a reference site |
86
- | | `/qualia-vibe --sync` | Show / patch drift between code (CSS vars + Tailwind) and DESIGN.md |
86
+ | Pivot the vibe | `/qualia-polish --vibe` | Fast aesthetic pivot (~3 min): swap tokens, keep layout. Propose ONE direction. |
87
+ | | `/qualia-polish --vibe --extract <URL>` | Reverse-engineer DESIGN.md from a reference site |
88
+ | | `/qualia-polish --vibe --sync` | Show / patch drift between code (CSS vars + Tailwind) and DESIGN.md |
87
89
  | | `/qualia-ship` | Deploy to production |
88
90
  | | `/qualia-handoff` | Deliver to client (4 mandatory deliverables) |
89
91
  | Reporting | `/qualia-report` | Log what you did (mandatory before clock-out) |
90
- | Zooming in | `/qualia-zoom` | Focus on a single file or function with full context |
91
- | Issues | `/qualia-issues` | Break a phase plan into vertical-slice GitHub issues |
92
- | Triage | `/qualia-triage` | Triage open issues through the ready-for-agent state machine |
93
92
  | Optimize | `/qualia-optimize --deepen` | Find shallow modules; v5.3+ spawns 3 parallel interface-design variants per candidate |
94
- | Hook gen | `/qualia-hook-gen` | Convert a CLAUDE.md/rules instruction into a deterministic hook (v5.3+) |
93
+ | Harness eval | `qualia-framework eval --run --write` | Run machine contract checks and write scored eval artifacts for ERP/reporting |
94
+ | Planning hygiene | `qualia-framework planning-hygiene scan` | Detect loose `.planning/` reports/assets before they turn into folder bloat |
95
95
  | Road view | `/qualia-road` | View and navigate journey/milestone/phase status |
96
96
  | Lost? | `/qualia` | Mechanical next-command router |
97
- | Confused? | `/qualia-idk` | Diagnostic — scans planning + code, explains what's going on |
97
+ | Confused? | `/qualia` | Diagnostic — scans planning + code, explains what's going on |
98
98
 
99
99
  ## Full Journey Hierarchy
100
100
 
@@ -119,13 +119,13 @@ Hard rules (enforced by `state.js` and the roadmapper):
119
119
  3. **MVP first** — build what's asked, nothing extra
120
120
  4. **Every task has a `Why`** (story-file format) — if you can't explain why a task matters in one sentence, it probably shouldn't exist
121
121
  5. **`/qualia` is your friend** — lost? type it
122
- 6. **`/qualia-idk` is your deeper friend** — not lost on "what command", but confused about the *situation*? Type `idk`.
122
+ 6. **`/qualia` is your deeper friend** — not lost on "what command", but confused about the *situation*? Type `idk`.
123
123
 
124
124
  ## When You're Stuck
125
125
 
126
126
  ```
127
127
  /qualia ← "what command should I run next?" (state-driven, instant)
128
- /qualia-idk ← "what's actually going on here?" (diagnostic, scans planning + code, ~30s)
128
+ /qualia ← "what's actually going on here?" (diagnostic, scans planning + code, ~30s)
129
129
  ```
130
130
 
131
131
  If neither helps, paste the error and ask Claude directly. If Claude can't fix it, tell Fawzi.
@@ -154,7 +154,7 @@ If neither helps, paste the error and ask Claude directly. If Claude can't fix i
154
154
  | Starting a quick throwaway | `/qualia-new --quick` |
155
155
  | Brownfield project | `/qualia-map` first, then `/qualia-new` |
156
156
  | Stuck picking next command | `/qualia` |
157
- | Confused about the situation | `/qualia-idk` |
157
+ | Confused about the situation | `/qualia` |
158
158
  | Finished the last phase of a milestone | `/qualia-milestone` |
159
159
  | About to ship | `/qualia-ship` |
160
160
  | Client is ready to take over | `/qualia-handoff` |
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ // Silently records EMPLOYEE attempts to use "Fawzi said OK" style proxy
3
+ // approval. This hook never blocks and never prints: the policy text teaches
4
+ // the agent not to do it; this hook records when it still appears in tool input.
5
+
6
+ const fs = require("fs");
7
+ const path = require("path");
8
+ const os = require("os");
9
+ const crypto = require("crypto");
10
+
11
+ function qualiaHome() {
12
+ if (process.env.QUALIA_HOME) return process.env.QUALIA_HOME;
13
+ const parent = path.basename(path.dirname(__dirname));
14
+ if (parent === ".codex" || parent === ".claude") return path.dirname(__dirname);
15
+ return path.join(os.homedir(), ".claude");
16
+ }
17
+
18
+ const QUALIA_HOME = qualiaHome();
19
+ const CONFIG = path.join(QUALIA_HOME, ".qualia-config.json");
20
+ const EVENT_FILE = path.join(QUALIA_HOME, ".approval-policy-events.json");
21
+
22
+ function readJson(file, fallback) {
23
+ try {
24
+ return JSON.parse(fs.readFileSync(file, "utf8"));
25
+ } catch {
26
+ return fallback;
27
+ }
28
+ }
29
+
30
+ function writeJson(file, data) {
31
+ try {
32
+ const dir = path.dirname(file);
33
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
34
+ const tmp = `${file}.tmp.${process.pid}`;
35
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", { mode: 0o600 });
36
+ try { fs.chmodSync(tmp, 0o600); } catch {}
37
+ fs.renameSync(tmp, file);
38
+ } catch {}
39
+ }
40
+
41
+ function collectStrings(value, out = []) {
42
+ if (typeof value === "string") out.push(value);
43
+ else if (Array.isArray(value)) value.forEach((v) => collectStrings(v, out));
44
+ else if (value && typeof value === "object") {
45
+ for (const v of Object.values(value)) collectStrings(v, out);
46
+ }
47
+ return out;
48
+ }
49
+
50
+ function hookInputText() {
51
+ try {
52
+ if (process.stdin.isTTY) return "";
53
+ const raw = fs.readFileSync(0, "utf8");
54
+ if (!raw) return "";
55
+ try {
56
+ const parsed = JSON.parse(raw);
57
+ return collectStrings(parsed.tool_input || parsed).join("\n");
58
+ } catch {
59
+ return raw;
60
+ }
61
+ } catch {
62
+ return "";
63
+ }
64
+ }
65
+
66
+ function approvalClaim(text) {
67
+ const normalized = String(text || "").replace(/\s+/g, " ").trim();
68
+ if (!normalized) return "";
69
+ const patterns = [
70
+ /\bfawzi\b.{0,80}\b(said|says|told|approved|approves|okayed|ok'd|allowed|allows|authorized|authorizes|confirmed)\b.{0,80}\b(ok|okay|fine|yes|ship|deploy|go ahead|do it|allowed|approved)?/i,
71
+ /\bfawzi\s+is\s+(here|with\s+me|beside\s+me)\b.{0,80}\b(ok|okay|fine|yes|approved|approves|allow|allows|ship|deploy|go ahead|do it)\b/i,
72
+ /\b(owner|boss)\b.{0,40}\b(said|says|approved|approves|okayed|ok'd)\b.{0,60}\b(ok|okay|fine|yes|ship|deploy|go ahead|do it)\b/i,
73
+ ];
74
+ for (const re of patterns) {
75
+ const m = normalized.match(re);
76
+ if (m) return m[0].slice(0, 220);
77
+ }
78
+ return "";
79
+ }
80
+
81
+ function recordLocal(config, sample) {
82
+ const data = readJson(EVENT_FILE, { counts: {}, events: [] });
83
+ if (!data.counts || typeof data.counts !== "object") data.counts = {};
84
+ if (!Array.isArray(data.events)) data.events = [];
85
+
86
+ const key = config.code || config.installed_by || "unknown";
87
+ const prev = data.counts[key] || {};
88
+ const count = (prev.total || 0) + 1;
89
+ const event = {
90
+ type: "proxy_owner_approval_claim",
91
+ actor_code: config.code || "",
92
+ actor_name: config.installed_by || "",
93
+ actor_role: config.role || "",
94
+ count,
95
+ sample,
96
+ project: path.basename(process.cwd()),
97
+ cwd: process.cwd(),
98
+ recorded_at: new Date().toISOString(),
99
+ };
100
+
101
+ data.counts[key] = {
102
+ actor_code: event.actor_code,
103
+ actor_name: event.actor_name,
104
+ actor_role: event.actor_role,
105
+ total: count,
106
+ last_seen_at: event.recorded_at,
107
+ };
108
+ data.events.push(event);
109
+ data.events = data.events.slice(-200);
110
+ writeJson(EVENT_FILE, data);
111
+ return event;
112
+ }
113
+
114
+ function enqueueErp(config, event) {
115
+ try {
116
+ const retryPath = path.join(QUALIA_HOME, "bin", "erp-retry.js");
117
+ if (!fs.existsSync(retryPath)) return;
118
+ if (config.erp && config.erp.enabled === false) return;
119
+ const erpUrl = (config.erp && config.erp.url) || "https://portal.qualiasolutions.net";
120
+ const { enqueue } = require(retryPath);
121
+ enqueue({
122
+ client_report_id: `QS-POLICY-${(event.actor_code || "UNKNOWN").replace(/[^A-Z0-9-]/gi, "")}-${event.count}`,
123
+ idempotency_key: crypto.randomUUID ? crypto.randomUUID() : "",
124
+ url: `${erpUrl.replace(/\/$/, "")}/api/v1/policy-events`,
125
+ payload: JSON.stringify(event),
126
+ last_error: "",
127
+ });
128
+ } catch {}
129
+ }
130
+
131
+ try {
132
+ const config = readJson(CONFIG, {});
133
+ if ((config.role || "").toUpperCase() === "OWNER") process.exit(0);
134
+ if ((config.role || "").toUpperCase() !== "EMPLOYEE") process.exit(0);
135
+
136
+ const sample = approvalClaim(hookInputText());
137
+ if (!sample) process.exit(0);
138
+
139
+ const event = recordLocal(config, sample);
140
+ enqueueErp(config, event);
141
+ } catch {}
142
+
143
+ process.exit(0);
@@ -21,6 +21,8 @@ function qualiaHome() {
21
21
  }
22
22
 
23
23
  const QUALIA_HOME = qualiaHome();
24
+ const CONFIG = path.join(QUALIA_HOME, ".qualia-config.json");
25
+ let HOOK_COMMAND = null;
24
26
 
25
27
  // Self-filter on the proposed bash command — only act when the user is
26
28
  // actually trying to deploy. Claude Code's `if: "Bash(vercel --prod*)"` does
@@ -44,7 +46,8 @@ const QUALIA_HOME = qualiaHome();
44
46
  }
45
47
  } catch {}
46
48
  if (command === null) return; // malformed or empty stdin — run full gate
47
- if (!/^\s*(npx\s+)?vercel\s+(--prod|deploy\s+--prod)\b/.test(command)) {
49
+ HOOK_COMMAND = command;
50
+ if (!/^\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*(npx\s+)?vercel\s+(--prod|deploy\s+--prod)\b/.test(command)) {
48
51
  process.exit(0);
49
52
  }
50
53
  })();
@@ -107,6 +110,76 @@ function hasScript(name) {
107
110
  }
108
111
  }
109
112
 
113
+ function readConfig() {
114
+ try {
115
+ return JSON.parse(fs.readFileSync(CONFIG, "utf8"));
116
+ } catch {
117
+ return {};
118
+ }
119
+ }
120
+
121
+ function readTrackingState() {
122
+ try {
123
+ const tracking = JSON.parse(fs.readFileSync(path.join(process.cwd(), ".planning", "tracking.json"), "utf8"));
124
+ return {
125
+ status: tracking.status || "",
126
+ verification: tracking.verification || "",
127
+ next_command: tracking.next_command || "",
128
+ };
129
+ } catch {
130
+ return null;
131
+ }
132
+ }
133
+
134
+ function readState() {
135
+ const stateJs = path.join(QUALIA_HOME, "bin", "state.js");
136
+ if (fs.existsSync(stateJs)) {
137
+ try {
138
+ const r = spawnSync(process.execPath, [stateJs, "check"], {
139
+ encoding: "utf8",
140
+ timeout: 3000,
141
+ stdio: ["ignore", "pipe", "ignore"],
142
+ });
143
+ if (r.status === 0 && r.stdout) return JSON.parse(r.stdout);
144
+ } catch {}
145
+ }
146
+ return readTrackingState();
147
+ }
148
+
149
+ function blockDeploy(reason, nextCommand) {
150
+ console.error(`BLOCKED: ${reason}`);
151
+ if (nextCommand) console.error(`Run: ${nextCommand}`);
152
+ _trace("pre-deploy-gate", "block", { reason, next_command: nextCommand || "" });
153
+ process.exit(2);
154
+ }
155
+
156
+ function enforceShipPolicy() {
157
+ if (!HOOK_COMMAND) return;
158
+
159
+ const config = readConfig();
160
+ const role = String(config.role || "").toUpperCase();
161
+ const force = process.env.QUALIA_SHIP_FORCE === "1" || /\bQUALIA_SHIP_FORCE=1\b/.test(HOOK_COMMAND);
162
+ const state = readState();
163
+ const status = state && state.status ? String(state.status) : "";
164
+ const verification = state && state.verification ? String(state.verification) : "";
165
+ const nextCommand = (state && state.next_command) || "/qualia";
166
+
167
+ if (force && role !== "OWNER") {
168
+ blockDeploy("QUALIA_SHIP_FORCE is OWNER-only.", nextCommand);
169
+ }
170
+
171
+ // If this is not a Qualia-managed project, keep the legacy behavior: run the
172
+ // quality/security gates but do not invent state.
173
+ if (!state || !status) return;
174
+
175
+ const shippable = status === "polished" || (status === "verified" && verification === "pass");
176
+ if (!shippable && !force) {
177
+ blockDeploy(`Ship refused from state '${status}' (verification: ${verification || "none"}).`, nextCommand);
178
+ }
179
+ }
180
+
181
+ enforceShipPolicy();
182
+
110
183
  // Directories that should never be walked (build outputs, deps, caches).
111
184
  const EXCLUDED_DIRS = new Set([
112
185
  "node_modules",
@@ -25,6 +25,9 @@ function qualiaHome() {
25
25
  if (process.env.QUALIA_HOME) return process.env.QUALIA_HOME;
26
26
  const parent = path.basename(path.dirname(__dirname));
27
27
  if (parent === ".codex" || parent === ".claude") return path.dirname(__dirname);
28
+ if (fs.existsSync(path.join(path.dirname(__dirname), "bin", "qualia-ui.js"))) {
29
+ return path.dirname(__dirname);
30
+ }
28
31
  return path.join(HOME, ".claude");
29
32
  }
30
33
 
@@ -36,6 +39,7 @@ const NOTIF_FILE = path.join(QUALIA_HOME, ".qualia-update-available.json");
36
39
  const HEALTH_FILE = path.join(QUALIA_HOME, ".qualia-install-health.json");
37
40
  const ERP_RETRY = path.join(QUALIA_HOME, "bin", "erp-retry.js");
38
41
  const ERP_QUEUE = path.join(QUALIA_HOME, ".erp-retry-queue.json");
42
+ const WORK_PACKET_BIN = path.join(QUALIA_HOME, "bin", "work-packet.js");
39
43
 
40
44
  // Critical files referenced by skills via @-import. If any are missing, skills
41
45
  // silently get empty context and produce ungrounded output. We spot-check these
@@ -97,6 +101,29 @@ function getNextCommand() {
97
101
  }
98
102
  }
99
103
 
104
+ function readWorkPacket() {
105
+ try {
106
+ if (!fs.existsSync(WORK_PACKET_BIN)) return null;
107
+ const mod = require(WORK_PACKET_BIN);
108
+ if (!mod || typeof mod.readLocalWorkPacket !== "function") return null;
109
+ return mod.readLocalWorkPacket(process.cwd());
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+
115
+ function renderWorkPacketContext() {
116
+ const packet = readWorkPacket();
117
+ if (!packet) return;
118
+ const project = packet.project && packet.project.name ? packet.project.name : "ERP mission";
119
+ const deadline = packet.deadline_date || "no deadline";
120
+ const next = packet.next_command || "/qualia";
121
+ const employee = packet.employee && packet.employee.name ? ` · ${packet.employee.name}` : "";
122
+ const text = `${project}: due ${deadline} · next ${next}${employee}`;
123
+ if (fs.existsSync(UI)) runUi("info", text);
124
+ else console.log(`QUALIA: ${text}`);
125
+ }
126
+
100
127
  function readConfig() {
101
128
  try {
102
129
  return JSON.parse(fs.readFileSync(path.join(QUALIA_HOME, ".qualia-config.json"), "utf8"));
@@ -178,6 +205,7 @@ try {
178
205
  fallbackText();
179
206
  } else if (fs.existsSync(STATE_FILE)) {
180
207
  runUi("banner", "router");
208
+ renderWorkPacketContext();
181
209
  const next = getNextCommand();
182
210
  if (next) {
183
211
  console.log("");
@@ -185,7 +213,7 @@ try {
185
213
  }
186
214
  } else if (fs.existsSync(CONTINUE_HERE)) {
187
215
  runUi("banner", "resume");
188
- runUi("warn", "Previous session found — type /qualia-resume to pick up where you left off");
216
+ runUi("warn", "Previous session found — type /qualia to pick up where you left off");
189
217
  console.log("");
190
218
  } else {
191
219
  // No project — show a welcoming first-run experience
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qualia-framework",
3
- "version": "6.2.9",
3
+ "version": "6.3.0",
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
- - `/qualia-vibe` — fast aesthetic pivot (swap tokens, keep layout) + reverse-engineer from URL + code↔DESIGN.md sync
118
+ - `/qualia-polish --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-polish --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.
@@ -39,7 +39,7 @@ If the answer is `claude`, **skip this entire rule** — Claude Code has no equi
39
39
 
40
40
  - The user is on Claude Code (no `/goal` surface).
41
41
  - A goal is already active for this thread (Codex rejects `update_goal` when one exists — call `thread/goal/get` first if you're using the tool API directly).
42
- - The work is open-ended exploration with no clear objective (e.g. `/qualia-idk`, `/qualia-discuss`). Goals are for executing a defined scope.
42
+ - The work is open-ended exploration with no clear objective (e.g. `/qualia`, `/qualia-discuss`). Goals are for executing a defined scope.
43
43
 
44
44
  ## Why
45
45
 
@@ -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.
@@ -1,6 +1,6 @@
1
1
  # One Opinion (design-decision discipline)
2
2
 
3
- Loaded on demand by design-adjacent skills: `/qualia-vibe`, `/qualia-polish`, `/qualia-new` (DESIGN.md creation step), `/qualia-discuss` PROJECT MODE. Not always-on — most skills don't need it.
3
+ Loaded on demand by design-adjacent skills: `/qualia-polish --vibe`, `/qualia-polish`, `/qualia-new` (DESIGN.md creation step), `/qualia-discuss` PROJECT MODE. Not always-on — most skills don't need it.
4
4
 
5
5
  ## The rule
6
6
 
@@ -34,7 +34,7 @@ Owners and clients have a strong sense of what they DON'T want and a weaker sens
34
34
  - The user explicitly asked for options ("show me 3 directions", "give me a menu").
35
35
  - The decision is technical, not aesthetic (e.g. "use Postgres or MongoDB?" — those have objectively different tradeoffs and should be discussed, not opinionated).
36
36
  - A locked decision in `.planning/decisions/*.md` already exists — surface it, don't re-decide.
37
- - The framework command explicitly supports a `--variants` mode (e.g. `/qualia-vibe --variants 3`), which is the user opting into the menu surface.
37
+ - The framework command explicitly supports a `--variants` mode (e.g. `/qualia-polish --vibe --variants 3`), which is the user opting into the menu surface.
38
38
 
39
39
  ## Output shape
40
40