qualia-framework 5.5.0 → 5.9.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 (45) hide show
  1. package/README.md +17 -13
  2. package/agents/plan-checker.md +8 -0
  3. package/agents/qa-browser.md +7 -0
  4. package/agents/research-synthesizer.md +4 -1
  5. package/agents/researcher.md +6 -1
  6. package/agents/roadmapper.md +8 -0
  7. package/agents/verifier.md +14 -1
  8. package/agents/visual-evaluator.md +1 -1
  9. package/bin/cli.js +30 -1
  10. package/bin/erp-retry.js +289 -0
  11. package/bin/install.js +12 -6
  12. package/bin/slop-detect.mjs +1 -1
  13. package/bin/state.js +10 -1
  14. package/docs/onboarding.html +621 -0
  15. package/docs/playwright-loop-pilot-results.md +7 -5
  16. package/docs/research/2026-05-11-deep-research.md +189 -0
  17. package/guide.md +5 -6
  18. package/hooks/session-start.js +19 -1
  19. package/package.json +3 -2
  20. package/rules/speed.md +1 -2
  21. package/skills/qualia-discuss/SKILL.md +106 -6
  22. package/skills/qualia-feature/SKILL.md +216 -0
  23. package/skills/qualia-milestone/SKILL.md +73 -1
  24. package/skills/qualia-new/SKILL.md +52 -25
  25. package/skills/qualia-optimize/SKILL.md +1 -1
  26. package/skills/{qualia-polish-loop → qualia-polish}/REFERENCE.md +5 -5
  27. package/skills/qualia-polish/SKILL.md +13 -4
  28. package/skills/{qualia-polish-loop → qualia-polish}/scripts/loop.mjs +2 -2
  29. package/skills/{qualia-polish-loop → qualia-polish}/scripts/playwright-capture.mjs +1 -1
  30. package/skills/qualia-report/SKILL.md +64 -2
  31. package/skills/qualia-road/SKILL.md +10 -11
  32. package/skills/qualia-verify/SKILL.md +16 -0
  33. package/templates/help.html +2 -3
  34. package/templates/project-discovery.md +83 -0
  35. package/templates/project.md +7 -0
  36. package/tests/bin.test.sh +97 -67
  37. package/tests/refs.test.sh +146 -0
  38. package/tests/slop-detect.test.sh +2 -2
  39. package/skills/qualia-polish-loop/SKILL.md +0 -201
  40. package/skills/qualia-prd/SKILL.md +0 -199
  41. package/skills/qualia-quick/SKILL.md +0 -44
  42. package/skills/qualia-task/SKILL.md +0 -98
  43. /package/skills/{qualia-polish-loop → qualia-polish}/fixtures/broken.html +0 -0
  44. /package/skills/{qualia-polish-loop → qualia-polish}/fixtures/clean.html +0 -0
  45. /package/skills/{qualia-polish-loop → qualia-polish}/scripts/score.mjs +0 -0
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: qualia-feature
3
+ description: "Auto-scoped single feature build. Picks inline (≤1 trivial file, no spawn) or fresh builder spawn (1-5 logic files, atomic commit) automatically from the task description. Refuses and routes to /qualia-plan for 5+ files or full phases. Replaces /qualia-quick and /qualia-task. Flags: --force-spawn forces a builder, --force-inline forces inline. Trigger phrases: 'build this one thing', 'add a component', 'implement this feature', 'quick fix', 'small change', 'tweak', 'hot fix', 'one-line fix', 'typo', 'config tweak', 'qualia-feature'."
4
+ allowed-tools:
5
+ - Bash
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Grep
10
+ - Glob
11
+ - Agent
12
+ - AskUserQuestion
13
+ ---
14
+
15
+ # /qualia-feature — Auto-scoped Single Feature
16
+
17
+ One command for everything between a typo and a phase. Auto-detects scope from the task description and picks the right execution path. No more guessing whether to use `/qualia-quick` or `/qualia-task`.
18
+
19
+ ## Usage
20
+
21
+ `/qualia-feature` — describe interactively
22
+ `/qualia-feature {description}` — run with description directly
23
+ `/qualia-feature --force-spawn {description}` — force a fresh builder spawn (even if auto would inline)
24
+ `/qualia-feature --force-inline {description}` — force inline path (even if auto would spawn)
25
+
26
+ ## When to use
27
+
28
+ - One feature outside a planned phase
29
+ - A bug fix, typo, config tweak, or one-line change
30
+ - A 1-5 file feature, component, API route, or integration
31
+ - A refactor of a single module
32
+
33
+ ## When NOT to use
34
+
35
+ - 5+ files or multiple subsystems touched → `/qualia-plan`
36
+ - Part of a planned phase → `/qualia-build`
37
+ - Investigating a symptom you can't name yet → `/qualia-debug`
38
+ - Optimization or polish pass → `/qualia-optimize` or `/qualia-polish`
39
+
40
+ ## Process
41
+
42
+ ### 1. Capture description
43
+
44
+ If invoked without args, ask: **"What do you want to build?"**
45
+
46
+ Wait for free-text answer. Don't paraphrase back. Capture the user's exact phrasing — it feeds both the auto-scope classifier and the eventual commit message.
47
+
48
+ ### 2. Auto-detect scope
49
+
50
+ Classify the description into one of three buckets:
51
+
52
+ | Bucket | Heuristic | Path |
53
+ |--------|-----------|------|
54
+ | **inline** | Typo, comment edit, 1-line config change, single-word rename, dead-code removal, copy edit, link fix, version bump, single emoji/icon swap | Direct work, no spawn |
55
+ | **spawn** | Add a component, add an API route, add a small migration + UI, implement a single feature, fix a non-trivial bug, refactor a module under 5 files | Fresh builder spawn |
56
+ | **refuse** | "Build the dashboard", "implement payments", "ship the auth system", "add a checkout flow", any description that names a multi-step workflow or 5+ subsystems | Refuse, route to `/qualia-plan` |
57
+
58
+ **Inline signals** (any one is sufficient):
59
+ - "fix typo", "fix the comment", "rename X to Y", "remove unused", "update copy", "change the link", "bump version"
60
+ - Description fits in one short sentence and names one specific micro-change
61
+ - The action is reversible by a single `git revert`
62
+
63
+ **Spawn signals** (any one is sufficient):
64
+ - "add a", "implement", "build", "create" + a single noun (component, route, page, table, form, modal, hook, util, migration)
65
+ - Description names a single feature with multiple touch points (e.g. "add a feedback form" → migration + API + UI = 3-4 files, but still one cohesive thing)
66
+ - The work needs a fresh context to avoid contaminating the current conversation
67
+
68
+ **Refuse signals** (any one is sufficient):
69
+ - "build the X system", "implement the entire Y", "add complete Z support"
70
+ - Description names a multi-milestone capability (auth, payments, search, admin panel) without a tight slice
71
+ - Estimated touch ≥ 5 files OR ≥ 1 day
72
+
73
+ ### 3. Announce the route + escape hatch
74
+
75
+ Show the user what was detected. Always offer the escape hatch:
76
+
77
+ ```bash
78
+ node ~/.claude/bin/qualia-ui.js banner feature
79
+ node ~/.claude/bin/qualia-ui.js info "Detected scope: {inline|spawn|refuse}"
80
+ node ~/.claude/bin/qualia-ui.js info "Reason: {one-line rationale}"
81
+ ```
82
+
83
+ Then:
84
+
85
+ - header: "Scope detected"
86
+ - question: "Auto-scope says {bucket}. Proceed, or override?"
87
+ - options:
88
+ - "Proceed with {bucket}"
89
+ - "Force inline" (only shown if bucket != inline)
90
+ - "Force spawn" (only shown if bucket != spawn)
91
+ - "Route to /qualia-plan" (only shown if bucket != refuse)
92
+ - "Re-describe"
93
+
94
+ If `--force-spawn` or `--force-inline` was passed on the command line, skip this gate entirely and execute the forced path.
95
+
96
+ ### 4. Execute the inline path
97
+
98
+ For inline scope:
99
+
100
+ 1. Read the file(s) that need to change (one read per file, no spawning).
101
+ 2. Make the edit directly using Edit/Write.
102
+ 3. Run `npx tsc --noEmit` if TypeScript and the change touches typed code; skip otherwise.
103
+ 4. Atomic commit with a clear conventional-commit message:
104
+
105
+ ```bash
106
+ git add {specific files}
107
+ git commit -m "fix: {description}"
108
+ ```
109
+
110
+ 5. Record in state:
111
+
112
+ ```bash
113
+ node ~/.claude/bin/state.js transition --to note --notes "{brief description}" --tasks-done 1
114
+ ```
115
+
116
+ 6. End with:
117
+
118
+ ```bash
119
+ node ~/.claude/bin/qualia-ui.js end "FEATURE SHIPPED (inline)"
120
+ ```
121
+
122
+ ### 5. Execute the spawn path
123
+
124
+ For spawn scope:
125
+
126
+ 1. Build a quick task spec (don't write to a file, just confirm with user):
127
+
128
+ ```bash
129
+ node ~/.claude/bin/qualia-ui.js info "What: {what to build}"
130
+ node ~/.claude/bin/qualia-ui.js info "Files: {files to create/modify, comma list}"
131
+ node ~/.claude/bin/qualia-ui.js info "Done: {observable acceptance criteria, 1-3 bullets}"
132
+ ```
133
+
134
+ Ask: **"Good to build?"** Wait for confirmation.
135
+
136
+ 2. Spawn the builder:
137
+
138
+ ```
139
+ Agent(subagent_type="qualia-builder", description="Feature: {short title}")
140
+ prompt: |
141
+ Read your role: @~/.claude/agents/builder.md
142
+
143
+ <task>
144
+ {task description verbatim from user}
145
+ </task>
146
+
147
+ <files>
148
+ {files to create/modify}
149
+ </files>
150
+
151
+ <acceptance_criteria>
152
+ {1-3 observable bullets}
153
+ </acceptance_criteria>
154
+
155
+ <context>
156
+ Read .planning/PROJECT.md if it exists.
157
+ Read .planning/CONTEXT.md if it exists (domain glossary).
158
+ Follow rules/security.md, rules/frontend.md, rules/deployment.md as applicable.
159
+ Atomic commit. Run npx tsc --noEmit before commit.
160
+ </context>
161
+ ```
162
+
163
+ 3. After the builder returns, verify:
164
+ - `npx tsc --noEmit` exits 0 (rerun the builder up to 2x if it failed self-verify, per the v5.5.0 auto-heal pattern).
165
+ - Quick smoke test if applicable.
166
+ - Confirm the commit was made: `git log --oneline -1` shows the feature.
167
+
168
+ 4. Report:
169
+
170
+ ```bash
171
+ node ~/.claude/bin/qualia-ui.js divider
172
+ node ~/.claude/bin/qualia-ui.js ok "Feature: {description}"
173
+ node ~/.claude/bin/qualia-ui.js ok "Files: {files changed}"
174
+ node ~/.claude/bin/qualia-ui.js ok "Commit: {commit hash}"
175
+ node ~/.claude/bin/qualia-ui.js end "FEATURE SHIPPED (spawn)"
176
+ ```
177
+
178
+ 5. Record in state:
179
+
180
+ ```bash
181
+ node ~/.claude/bin/state.js transition --to note --notes "{description}" --tasks-done 1
182
+ ```
183
+
184
+ ### 6. Execute the refuse path
185
+
186
+ For refuse scope, do NOT build. Explain why and route:
187
+
188
+ ```bash
189
+ node ~/.claude/bin/qualia-ui.js warn "This is too big for /qualia-feature (5+ files or multi-step workflow)."
190
+ node ~/.claude/bin/qualia-ui.js info "Reason: {one-line — e.g. 'description names the entire auth system, not a slice'}"
191
+ node ~/.claude/bin/qualia-ui.js end "ROUTED" "/qualia-plan"
192
+ ```
193
+
194
+ If the user is sure it's small and the classifier got it wrong, they can re-invoke with `--force-spawn`.
195
+
196
+ ## Auto-scope examples
197
+
198
+ | Description | Detected | Why |
199
+ |-------------|----------|-----|
200
+ | "fix the typo in the hero copy" | inline | typo + 1 file |
201
+ | "rename `userId` to `user_id` in auth.ts" | inline | rename in 1 file |
202
+ | "bump next from 16.0.3 to 16.0.4" | inline | version bump, 1 file |
203
+ | "add a Feedback button that opens a modal" | spawn | single feature, 2-4 files |
204
+ | "add a GET /api/health route returning ok" | spawn | 1 route, but worth a fresh context for test pass |
205
+ | "implement Stripe subscription checkout" | refuse | multi-file + webhooks + admin = phase |
206
+ | "build the dashboard" | refuse | names a multi-page surface |
207
+ | "add the admin user-management page" | refuse | usually 5+ files (table, modals, API, RLS, types) |
208
+
209
+ ## Rules
210
+
211
+ 1. **Auto-scope is confident by default, with two escape hatches.** Trust the classifier; use `--force-spawn` or `--force-inline` when it guesses wrong. Don't waste a question on "which path?" — propose and let the user override.
212
+ 2. **Inline is genuinely fresh-context-free.** No subagent spawn for inline work — the current Claude does it directly. That's the whole speed advantage over `/qualia-task`.
213
+ 3. **Spawn means atomic.** One feature, one commit, one acceptance criteria block. No grab-bag commits that touch four unrelated things.
214
+ 4. **Refuse means refuse.** When a description is phase-sized, route to `/qualia-plan`, don't try to fit it in. The auto-classifier is the gate that protects the work from sprawling.
215
+ 5. **STATE.md via state.js only.** Never edit STATE.md or tracking.json by hand. Both paths record through `state.js transition`.
216
+ 6. **Follow rules/* on every path.** Security, frontend, deployment rules apply equally to inline and spawn. Read-before-write is non-negotiable.
@@ -38,6 +38,77 @@ node ~/.claude/bin/state.js check
38
38
 
39
39
  If either fires (without `--force`), stop and show the error. The user must verify remaining phases first (or add `--force` for explicit bypass on a preview/demo milestone).
40
40
 
41
+ ### 1b. Demo-Extension Branch (v5.6)
42
+
43
+ Detect if this is a demo project closing its only milestone:
44
+
45
+ ```bash
46
+ PROJECT_TYPE=$(grep -E "^project_type:" .planning/PROJECT.md | head -1 | awk '{print $2}')
47
+ MILESTONE_COUNT=$(grep -cE "^## Milestone " .planning/JOURNEY.md 2>/dev/null || echo 0)
48
+ ```
49
+
50
+ If `PROJECT_TYPE=demo` AND `MILESTONE_COUNT=1`, the demo's one milestone is closing — there is no "next milestone" in the file. Ask the conversion question:
51
+
52
+ - header: "Demo shipped — what next?"
53
+ - question: "The demo milestone is done. Did the client sign? If yes, extend this into a full project (regenerate JOURNEY.md as a multi-milestone arc to Handoff). If no, this project closes here."
54
+ - options:
55
+ - "Client signed — extend to full project"
56
+ - "Demo only — close here, project is done"
57
+ - "Pause" — don't close yet, decide later
58
+
59
+ **If "Client signed — extend to full project":**
60
+
61
+ 1. Update `.planning/PROJECT.md` frontmatter: `project_type: full`.
62
+ 2. Run a brief discovery top-up — invoke `/qualia-discuss` in PROJECT MODE, but only ask §9-§14 (the full-project-only questions). This adds the milestone arc, compliance, integrations, content ownership, handoff team, and budget shape.
63
+ 3. Spawn the roadmapper in `extend-to-full` mode (see prompt below). It reads the existing single milestone (now M1), the updated discovery, and produces a full JOURNEY.md with M2..M{N-1} sketches plus the Handoff milestone.
64
+ 4. Then proceed with the standard close-milestone flow (Steps 2-9) — M1 closes, M2 opens, the user is asked to continue.
65
+
66
+ Roadmapper prompt for the extension:
67
+
68
+ ```
69
+ Agent(prompt="
70
+ Read your role: @~/.claude/agents/roadmapper.md
71
+
72
+ <mode>extend-demo-to-full</mode>
73
+ <existing_journey>.planning/JOURNEY.md</existing_journey>
74
+ <discovery>.planning/project-discovery.md</discovery>
75
+
76
+ <task>
77
+ The existing JOURNEY.md has 1 milestone (the demo, now M1 and shipped). Extend it
78
+ into a 2-5 milestone arc to Handoff:
79
+
80
+ - Keep M1 exactly as-is (it shipped).
81
+ - Add M2..M{N-1} based on §9 of project-discovery.md (the milestone-arc question
82
+ the user answered when converting from demo).
83
+ - Append a Handoff milestone (fixed 4 phases: Polish, Content + SEO, Final QA,
84
+ Handoff).
85
+ - Update REQUIREMENTS.md to add REQ-IDs for the new milestones.
86
+ - Do NOT regenerate ROADMAP.md here — that happens in Step 7 of /qualia-milestone
87
+ when M2 opens.
88
+
89
+ Mark M2..M{N-1} as sketched, not fully detailed. The user gets full detail per
90
+ milestone when /qualia-milestone opens each one.
91
+ </task>
92
+ ", subagent_type=\"qualia-roadmapper\", description=\"Extend demo to full project\")
93
+ ```
94
+
95
+ **If "Demo only — close here":** treat the demo as a shipped-and-done project. Skip Steps 2-9 of the normal flow. Just run:
96
+
97
+ ```bash
98
+ node ~/.claude/bin/state.js close-milestone --force
99
+ node ~/.claude/bin/qualia-ui.js end "DEMO SHIPPED" "/qualia-report"
100
+ ```
101
+
102
+ In `--auto` mode, inline-invoke `/qualia-report` and stop.
103
+
104
+ **If "Pause":** stop, show:
105
+
106
+ ```bash
107
+ node ~/.claude/bin/qualia-ui.js info "Demo milestone left open. Run /qualia-milestone again when you decide."
108
+ ```
109
+
110
+ If the demo branch did NOT fire (full project, or demo with multiple milestones already after extension), continue to Step 2.
111
+
41
112
  ### 2. Banner + Confirm
42
113
 
43
114
  ```bash
@@ -206,4 +277,5 @@ node ~/.claude/bin/qualia-ui.js end "M{N} CLOSED · M{N+1} OPEN" "/qualia-plan 1
206
277
  3. **Archive, don't delete.** Old phase work stays accessible via `.planning/archive/`.
207
278
  4. **New milestone = fresh phase numbering.** First phase of the new milestone is Phase 1, not Phase {N+1}.
208
279
  5. **ERP sync aware.** tracking.json milestones[] gets a summary entry on close — the ERP reads this to render the tree.
209
- 6. **Handoff is the final milestone.** If the current milestone IS Handoff, there is no "next" — route to `/qualia-report` and the project is done.
280
+ 6. **Handoff is the final milestone (full projects).** If the current milestone IS Handoff, there is no "next" — route to `/qualia-report` and the project is done.
281
+ 7. **Demo closes via the demo-extension branch (Step 1b).** A demo's single milestone closing triggers the "client signed?" question. The framework never silently auto-extends a demo without asking — the conversion is a real business decision.
@@ -58,27 +58,41 @@ test -d .git && echo "HAS_GIT"
58
58
  test -f .planning/codebase/README.md && echo "ALREADY_MAPPED"
59
59
  ```
60
60
 
61
- If existing code is detected AND not already mapped, ask the user whether to run `/qualia-map` inline first. If yes, invoke the `qualia-map` skill inline, wait for completion, then continue to Step 1.
61
+ If existing code is detected AND not already mapped, ask the user whether to run `/qualia-map` inline first. If yes, invoke the `qualia-map` skill inline, wait for completion, then continue to Step 0.6.
62
62
 
63
- ### Step 1. Deep Questioning
63
+ ### Step 0.6. Project Type Gate (Demo vs Full)
64
64
 
65
- Load the questioning methodology:
65
+ The single most important fork in the workflow. Demo and Full produce different journeys, different research depth, different milestone counts.
66
66
 
67
- ```bash
68
- cat ~/.claude/qualia-references/questioning.md 2>/dev/null
69
- ```
67
+ - header: "Project shape"
68
+ - question: "Is this a demo (single shippable milestone, sales conversation) or a full project (multi-milestone arc to Handoff)?"
69
+ - options:
70
+ - "Demo" — one shippable milestone, real backend, no mocks. Built to win a client conversation, extensible via `/qualia-milestone` if they sign.
71
+ - "Full project" — the full multi-milestone arc to Handoff. 2-5 milestones planned upfront.
72
+
73
+ Store the answer as `PROJECT_TYPE=demo` or `PROJECT_TYPE=full`. It drives Steps 1, 8, and 10.
70
74
 
71
- Follow the thread. Challenge vagueness. Make abstract concrete. Check the 4-item mental checklist (what, why, who, done).
75
+ **Demo design philosophy is non-negotiable:** real backend always, DESIGN.md mandatory, slop-detect hard-block. Speed comes from skipping multi-milestone planning, NEVER from skipping design quality or mocking the backend.
72
76
 
73
- Use `AskUserQuestion` for forks with 2-4 concrete interpretations. Use free text when you want them to think freely.
77
+ ### Step 1. Mandatory Discovery Interview (PROJECT MODE)
74
78
 
75
- **Decision gate**when you could write a clear PROJECT.md:
79
+ Invoke `/qualia-discuss` inline in PROJECT MODE non-technical kickoff interview. 8 questions for demo, 14 for full project. Pass `PROJECT_TYPE` so the discuss skill skips the type question.
80
+
81
+ This step REPLACES the old free-form "deep questioning" loop. The discuss skill captures answers verbatim into `.planning/project-discovery.md`, which seeds PROJECT.md, PRODUCT.md, CONTEXT.md, and (for full projects) JOURNEY.md milestone names.
82
+
83
+ ```bash
84
+ # Inline-invoke qualia-discuss in PROJECT MODE
85
+ # (no phase number arg = PROJECT MODE; PROJECT_TYPE env tells it which question set)
86
+ PROJECT_TYPE=$PROJECT_TYPE qualia-discuss
87
+ ```
88
+
89
+ After the interview returns, `.planning/project-discovery.md` exists with the user's answers. Read it. Confirm understanding:
76
90
 
77
91
  - header: "Ready?"
78
- - question: "I understand what you're building. Create PROJECT.md and move forward?"
79
- - options: ["Create PROJECT.md", "Keep exploring"]
92
+ - question: "I have what I need to write PROJECT.md and move forward. Continue?"
93
+ - options: ["Continue", "More questions"]
80
94
 
81
- Loop until "Create PROJECT.md".
95
+ If "More questions": re-invoke `/qualia-discuss` for additional rounds. Otherwise continue.
82
96
 
83
97
  ### Step 2. Detect Project Type
84
98
 
@@ -216,11 +230,16 @@ node ~/.claude/bin/qualia-ui.js banner research
216
230
  mkdir -p .planning/research
217
231
  ```
218
232
 
219
- Say: **"Running 4 parallel research agents (stack, features, architecture, pitfalls)..."**
233
+ **Research scope branches on `PROJECT_TYPE`:**
234
+
235
+ - **Demo** (`PROJECT_TYPE=demo`): pass `<scope>quick</scope>` to all 4 researchers AND the synthesizer. Each researcher gets a 3-call budget instead of 8. The synthesizer produces a single-milestone suggestion (no Handoff, no multi-milestone arc). Total research time drops roughly 3x with no loss of correctness on the demo path — the depth was wasted when there's only one milestone to ship.
236
+ - **Full project** (`PROJECT_TYPE=full`): standard scope, 8-call budget per researcher, full milestone-arc synthesis.
220
237
 
221
- Spawn 4 researchers in parallel (single message, 4 Agent tool calls), with multi-milestone scope. See REFERENCE.md section "Researcher prompts (4 dimensions)" for the verbatim prompt templates.
238
+ Say: **"Running 4 parallel research agents (stack, features, architecture, pitfalls)..."** (demo: append "— quick scope").
222
239
 
223
- **After all 4 complete, spawn synthesizer.** See REFERENCE.md section "Synthesizer prompt" for the verbatim prompt template.
240
+ Spawn 4 researchers in parallel (single message, 4 Agent tool calls). See REFERENCE.md section "Researcher prompts (4 dimensions)" for the verbatim prompt templates. Include `<scope>quick</scope>` in each prompt when `PROJECT_TYPE=demo`.
241
+
242
+ **After all 4 complete, spawn synthesizer.** See REFERENCE.md section "Synthesizer prompt" for the verbatim prompt template. Include `<scope>quick</scope>` when `PROJECT_TYPE=demo`.
224
243
 
225
244
  **Commit:**
226
245
  ```bash
@@ -258,7 +277,12 @@ Gather any additional requirements the user wants that research missed.
258
277
  node ~/.claude/bin/qualia-ui.js banner roadmap
259
278
  ```
260
279
 
261
- Spawn the roadmapper with full-journey mandate. If the user passed `--full-detail`, include `<full_detail>true</full_detail>` in the prompt so the roadmapper writes complete phase detail for ALL milestones. See REFERENCE.md section "Roadmapper prompt" for the verbatim prompt template.
280
+ **Roadmapper output branches on `PROJECT_TYPE`:**
281
+
282
+ - **Demo** (`PROJECT_TYPE=demo`): roadmapper produces a 1-milestone JOURNEY.md (the demo milestone, 2-4 phases) plus a matching REQUIREMENTS.md and a fully-detailed ROADMAP.md. No "Handoff" milestone is appended — the demo is its own complete artifact. The journey-tree at Step 11 shows a single rung; the "extend to full project" branch is handled later by `/qualia-milestone` if the client signs.
283
+ - **Full project** (`PROJECT_TYPE=full`): roadmapper produces the standard 2-5 milestone arc ending in Handoff. Milestone 1 fully detailed, M2..M{N-1} sketched (unless `--full-detail`).
284
+
285
+ Spawn the roadmapper with `<project_type>$PROJECT_TYPE</project_type>` in the prompt. If the user passed `--full-detail`, include `<full_detail>true</full_detail>` so the roadmapper writes complete phase detail for ALL milestones (full project only; demo always has full detail because there's only one milestone). See REFERENCE.md section "Roadmapper prompt" for the verbatim prompt template.
262
286
 
263
287
  ### Step 11. Present the Journey (single view)
264
288
 
@@ -366,12 +390,15 @@ Do NOT use `--quick` for: client projects, anything with compliance stakes, anyt
366
390
 
367
391
  ## Rules
368
392
 
369
- 1. **Research runs automatically.** No permission ask. Only `--quick` skips it. This is a v4 invariant.
370
- 2. **The journey includes Handoff.** Every project's final milestone is literally named "Handoff" with 4 standard phases. The roadmapper enforces this.
371
- 3. **Single approval gate.** One gate for the whole journey. Not per-milestone, not per-phase.
372
- 4. **Milestone count: 2-5.** Hard floor 2, hard ceiling 5. Bigger projects defer remainder to post-handoff v2.
373
- 5. **Milestone 1 is fully detailed.** M2..M{N-1} are sketched. Detail fills in when each milestone opens.
374
- 6. **STATE.md through state.js.** Never edit STATE.md or tracking.json by hand.
375
- 7. **Inline skill invocation.** When Step 0.5 offers `/qualia-map`, invoke it inline don't exit.
376
- 8. **CONTEXT.md is mandatory (v5.0).** Every project gets a domain glossary at `.planning/CONTEXT.md`. Seeded from questioning answers. Loaded by every road agent. Kept terse.
377
- 9. **ADRs are scarce.** `.planning/decisions/` exists from day one but only fills with hard-to-reverse, surprising-without-context, real-tradeoff decisions. Cargo-culting ADRs ruins the signal.
393
+ 1. **Project type is the first fork.** Step 0.6 asks Demo vs Full Project. Every downstream step branches on this. Don't skip it, don't infer it.
394
+ 2. **Discovery interview is mandatory (v5.6).** Step 1 always invokes `/qualia-discuss` in PROJECT MODE. No free-form questioning loop, no "I'll just sketch PROJECT.md from the user's first message." The interview is 8 questions for demo, 14 for full project.
395
+ 3. **Research runs automatically.** No permission ask. Only `--quick` skips it. Demo path uses `<scope>quick</scope>` (3-call budget per researcher); full project uses standard 8-call budget.
396
+ 4. **Demo design philosophy is non-negotiable.** Real backend always, DESIGN.md mandatory, slop-detect hard-block. Speed comes from skipping multi-milestone planning, never from skipping design quality or mocking the backend. A demo that uses mock data is not a Qualia demo.
397
+ 5. **Demos are 1 milestone, full projects are 2-5.** Demo journeys have no "Handoff" — the demo IS the artifact. Full projects always end in Handoff (fixed 4 phases). The journey-tree adapts to both shapes.
398
+ 6. **The full-project journey includes Handoff.** Every full project's final milestone is literally named "Handoff" with 4 standard phases. The roadmapper enforces this.
399
+ 7. **Single approval gate.** One gate for the whole journey. Not per-milestone, not per-phase.
400
+ 8. **Milestone 1 is fully detailed (full projects).** M2..M{N-1} are sketched. Detail fills in when each milestone opens. Demos are always fully detailed because they're 1 milestone.
401
+ 9. **STATE.md through state.js.** Never edit STATE.md or tracking.json by hand.
402
+ 10. **Inline skill invocation.** When Step 0.5 offers `/qualia-map` or Step 1 invokes `/qualia-discuss`, invoke it inline — don't exit.
403
+ 11. **CONTEXT.md is mandatory.** Every project gets a domain glossary at `.planning/CONTEXT.md`. Seeded from the discovery interview answers. Loaded by every road agent. Kept terse.
404
+ 12. **ADRs are scarce.** `.planning/decisions/` exists from day one but only fills with hard-to-reverse, surprising-without-context, real-tradeoff decisions. Cargo-culting ADRs ruins the signal.
@@ -146,7 +146,7 @@ Collect all 3 proposals. Present to the user as a side-by-side table:
146
146
  | 2 | ... | ... | ... | ... |
147
147
  | 3 | ... | ... | ... | ... |
148
148
 
149
- User picks `1`, `2`, `3`, or `hybrid` (with notes on which elements from which proposals to combine). The synthesizer then writes a "Refactor RFC" to `.planning/REFACTOR-{slug}.md` and optionally opens a GH issue (mirrors `/qualia-prd` flow).
149
+ User picks `1`, `2`, `3`, or `hybrid` (with notes on which elements from which proposals to combine). The synthesizer then writes a "Refactor RFC" to `.planning/REFACTOR-{slug}.md` and optionally opens a GH issue.
150
150
 
151
151
  **Why parallel + radically different**: a single deepening proposal anchors on the first idea the LLM has. Three parallel proposals with diverse design constraints surface trade-offs the user can see at a glance — and the human's "taste" dominates the choice rather than the agent's first instinct. Empirically (Matt Pocock + Qualia internal testing) this produces dramatically better refactor RFCs than a single-pass proposal.
152
152
 
@@ -1,4 +1,4 @@
1
- # REFERENCE — /qualia-polish-loop
1
+ # REFERENCE — /qualia-polish --loop
2
2
 
3
3
  Verbatim agent prompts and operational details. Loaded on demand by SKILL.md, not carried in the system prompt. Per progressive-disclosure discipline (Matt Pocock): the agent reads SKILL.md first, then this file when it needs the spawn templates.
4
4
 
@@ -104,7 +104,7 @@ Agent({
104
104
  Role: @~/.claude/agents/builder.md
105
105
 
106
106
  <phase_context>
107
- You are inside /qualia-polish-loop iteration {N}. The vision evaluator scored
107
+ You are inside /qualia-polish --loop iteration {N}. The vision evaluator scored
108
108
  the {dim} dimension at {score}. Your single task: fix that one dimension.
109
109
 
110
110
  <design>
@@ -133,7 +133,7 @@ the {dim} dimension at {score}. Your single task: fix that one dimension.
133
133
  2. Make the MINIMUM edit to fix this one dimension. Do not refactor. Do not change logic. Do not touch state management. Do not change copy unless this is a microcopy issue.
134
134
  3. Use design tokens from DESIGN.md. Do not invent new color values, font names, or spacing.
135
135
  4. After the edit, commit via the orchestrator (slop-detect-gated):
136
- node ~/.claude/skills/qualia-polish-loop/scripts/loop.mjs commit-fix --state {STATE} --file {file} --slug {dim}-{short-keyword}
136
+ node ~/.claude/skills/qualia-polish/scripts/loop.mjs commit-fix --state {STATE} --file {file} --slug {dim}-{short-keyword}
137
137
  If slop-detect blocks (exit 2), READ the slop output and re-edit. If you cannot fix without violating slop-detect, return BLOCKED with the conflict.
138
138
  5. Return DONE with: file modified, lines changed, slop-detect: pass, commit: {sha}.
139
139
  </task>
@@ -260,6 +260,6 @@ This is intentional. Most visual regressions Fawzi has documented in `/insights`
260
260
 
261
261
  - Cross-browser rendering checks (Firefox / WebKit) — Chromium-only, per `qualia-polish` Stage 4 precedent
262
262
  - Accessibility audits beyond what the rubric scores — use `/qualia-polish` Stage 3 (Lighthouse + axe) for that
263
- - Performance regressions — use `/qualia-polish-loop` only after Lighthouse score passes
263
+ - Performance regressions — use `/qualia-polish --loop` only after Lighthouse score passes
264
264
  - Reference-image-only mode (compare to a target screenshot without a brief) — currently the brief is required; reference is supplemental
265
- - Multi-page sweeps — one URL per invocation; chain `/qualia-polish-loop` per route for site-wide passes
265
+ - Multi-page sweeps — one URL per invocation; chain `/qualia-polish --loop` per route for site-wide passes
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: qualia-polish
3
- description: "Scope-adaptive design pass works on a single component, a route, the whole app, or a ground-up redesign. Trigger on 'polish', 'design pass', 'fix the design', 'redesign', 'critique', 'audit design', 'looks ugly', 'make it look better'. Replaces both /qualia-polish and /qualia-design from earlier versions."
3
+ description: "Scope-adaptive design pass. Works on a single component, a route, the whole app, a ground-up redesign, or an autonomous visual loop. Trigger on 'polish', 'design pass', 'fix the design', 'redesign', 'critique', 'audit design', 'looks ugly', 'make it look better', 'polish loop', 'visual loop', 'fix what I see', 'iterate on the design until it's correct'. Replaces /qualia-polish-loop (now /qualia-polish --loop) and /qualia-design from earlier versions."
4
4
  allowed-tools:
5
5
  - Bash
6
6
  - Read
@@ -9,12 +9,12 @@ allowed-tools:
9
9
  - Grep
10
10
  - Glob
11
11
  - Agent
12
- argument-hint: "[file|route|--redesign|--critique|--quick] [--register=brand|product]"
12
+ argument-hint: "[file|route|--redesign|--critique|--quick|--loop] [--register=brand|product] [--brief PATH] [--max 8] [--viewports 375,768,1440]"
13
13
  ---
14
14
 
15
15
  # /qualia-polish — Scope-Adaptive Design Pass
16
16
 
17
- One command. Six scopes. Use it whenever you need design work from a 30-second component touch-up to a 30-minute ground-up redesign.
17
+ One command. Seven scopes. Use it whenever you need design work, from a 30-second component touch-up to a 30-minute ground-up redesign to a fully autonomous see-fix-verify loop.
18
18
 
19
19
  ## Scopes
20
20
 
@@ -28,8 +28,17 @@ The first argument selects the scope. Stage selection follows from scope.
28
28
  | `/qualia-polish --redesign` | **Redesign** | ~30m | all + Stage 1 mandatory + 2 vision iterations |
29
29
  | `/qualia-polish --critique` | **Critique** | read-only | 0, 4, 5 (no edits) |
30
30
  | `/qualia-polish --quick` | **Quick** | ~1m | 0, 2, 7 (gates only, no vision loop) |
31
+ | `/qualia-polish --loop {url}` | **Loop** | ~5-15m | autonomous see/fix/verify, max 8 iterations |
31
32
 
32
- Other flags: `--register=brand|product` to override register inference.
33
+ Other flags: `--register=brand|product` overrides register inference. Loop-specific flags: `--brief PATH`, `--max N`, `--viewports 375,768,1440`, `--ref PATH`, `--budget 100000`.
34
+
35
+ ## --loop mode (autonomous visual loop)
36
+
37
+ When `--loop` is the first flag, the polish run is fully autonomous: screenshot a live URL at three viewports, score 8 design dimensions of `qualia-design/design-rubric.md` against the brief using vision, fix top issues in the codebase, re-screenshot, repeat until every dimension scores ≥ 3 or the kill-switch fires (regression, budget cap, max iterations).
38
+
39
+ This is the surface formerly known as `/qualia-polish-loop` (consolidated into a flag in v5.8.0). The scripts ship at `skills/qualia-polish/scripts/{loop,playwright-capture,score}.mjs`; vision evaluator is `agents/visual-evaluator.md`; full loop spec lives in this skill's `REFERENCE.md`.
40
+
41
+ When `--loop` is detected on entry, route to the loop process documented in `REFERENCE.md` and stop executing the standard stages below. The two paths share Stage 0 substrate gates and the rubric, but diverge from Stage 1 onward.
33
42
 
34
43
  ## Setup gates (non-optional, every scope)
35
44
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * loop.mjs — orchestrator state-machine for /qualia-polish-loop.
3
+ * loop.mjs — orchestrator state-machine for /qualia-polish --loop.
4
4
  *
5
5
  * Claude (the parent session) drives the loop by issuing CLI commands. This
6
6
  * script keeps the deterministic state — iteration counter, regression
@@ -294,7 +294,7 @@ switch (cmd) {
294
294
  case "--help":
295
295
  case "-h":
296
296
  case undefined:
297
- console.log(`loop.mjs — orchestrator for /qualia-polish-loop
297
+ console.log(`loop.mjs — orchestrator for /qualia-polish --loop
298
298
 
299
299
  Commands:
300
300
  init --state PATH (--url URL | --routes URL1,URL2,...) [--brief PATH] [--ref PATH] [--max 8] [--budget 100000] [--reduced-motion]
@@ -36,7 +36,7 @@ function parseArgs() {
36
36
  } else if (a === "--wait" && argv[i + 1]) args.wait = parseInt(argv[++i], 10);
37
37
  else if (a === "--reduced-motion") args.reducedMotion = true;
38
38
  else if (a === "--help" || a === "-h") {
39
- console.log(`playwright-capture.mjs — Screenshot capture for /qualia-polish-loop
39
+ console.log(`playwright-capture.mjs — Screenshot capture for /qualia-polish --loop
40
40
 
41
41
  Usage:
42
42
  node playwright-capture.mjs --url <url> --out <dir> [--viewports 375,768,1440] [--wait 1500] [--reduced-motion]
@@ -168,6 +168,17 @@ REPORT_FILE=".planning/reports/report-{date}.md"
168
168
  SUBMITTED_BY=$(git config user.name || echo "unknown")
169
169
  SUBMITTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
170
170
 
171
+ # Idempotency key — deterministic per (client_report_id, submitted_at). A retry
172
+ # of the same shift report carries the same key, so the ERP can dedupe at the
173
+ # header level in addition to the UPSERT on (project_id, client_report_id).
174
+ IDEMPOTENCY_KEY=$(node -e "
175
+ const c=require('crypto');
176
+ const seed='$CLIENT_REPORT_ID|$SUBMITTED_AT|'+require('path').basename(process.cwd());
177
+ // RFC 4122 v5-style: deterministic UUID from sha1 of the seed
178
+ const h=c.createHash('sha1').update(seed).digest('hex');
179
+ console.log([h.slice(0,8),h.slice(8,12),'5'+h.slice(13,16),'8'+h.slice(17,20),h.slice(20,32)].join('-'));
180
+ ")
181
+
171
182
  # Guard: API key required for upload (otherwise curl posts an empty bearer)
172
183
  if [ "$ERP_ENABLED" = "true" ] && [ -z "$API_KEY" ] && [ "$DRY_RUN" != "true" ]; then
173
184
  node ~/.claude/bin/qualia-ui.js warn "ERP API key missing (~/.claude/.erp-api-key). Run: qualia-framework set-erp-key <key>"
@@ -189,6 +200,17 @@ PAYLOAD=$(
189
200
  const commits=[];try{const r=spawnSync('git',['log','--oneline','--since=8 hours ago','--format=%h'],{encoding:'utf8',timeout:3000});if(r.stdout)commits.push(...r.stdout.trim().split('\n').filter(Boolean));}catch{}
190
201
  const gitRemote=t.git_remote||git(['config','--get','remote.origin.url']);
191
202
  const projectKey=t.project_id||repoSlug(gitRemote)||require('path').basename(process.cwd());
203
+ // Session duration: minutes from session_started_at to submitted_at. The ERP's
204
+ // example payload (docs/erp-contract.md:93) includes this; without it the ERP
205
+ // can't compute shift-length analytics without parsing notes.
206
+ let sessionDurationMinutes=0;
207
+ if(t.session_started_at){
208
+ const startMs=Date.parse(t.session_started_at);
209
+ const endMs=Date.parse(process.env.SUBMITTED_AT)||Date.now();
210
+ if(!Number.isNaN(startMs)&&endMs>startMs){
211
+ sessionDurationMinutes=Math.round((endMs-startMs)/60000);
212
+ }
213
+ }
192
214
  console.log(JSON.stringify({
193
215
  project:t.project||require('path').basename(process.cwd()),
194
216
  project_id:projectKey,team_id:t.team_id||'qualia-solutions',git_remote:gitRemote,
@@ -200,6 +222,7 @@ PAYLOAD=$(
200
222
  gap_cycles:(t.gap_cycles||{})[String(t.phase)]||0,build_count:t.build_count||0,
201
223
  deploy_count:t.deploy_count||0,deployed_url:t.deployed_url||'',
202
224
  session_started_at:t.session_started_at||'',last_pushed_at:t.last_pushed_at||'',
225
+ session_duration_minutes:sessionDurationMinutes,
203
226
  lifetime:t.lifetime||{},commits:commits,notes:notes,
204
227
  submitted_by:process.env.SUBMITTED_BY||'unknown',submitted_at:process.env.SUBMITTED_AT
205
228
  }));
@@ -214,11 +237,15 @@ if [ "$DRY_RUN" = "true" ]; then
214
237
  exit 0
215
238
  fi
216
239
 
217
- # Upload — 3 attempts with 1s/3s/9s backoff
240
+ # Upload — 3 attempts with 1s/3s/9s backoff.
241
+ # Idempotency-Key header carries a deterministic UUID per (client_report_id, submitted_at)
242
+ # so the ERP can dedupe at the request level in addition to the UPSERT key on the body.
243
+ # Documented in docs/erp-contract.md:42-49 with a 24h replay window.
218
244
  if [ "$ERP_ENABLED" = "true" ]; then
219
245
  for ATTEMPT in 1 2 3; do
220
246
  RESPONSE=$(curl -sS -X POST "$ERP_URL/api/v1/reports" \
221
247
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
248
+ -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
222
249
  -d "$PAYLOAD" --max-time 10 -w "\n__HTTP__%{http_code}" 2>&1)
223
250
  HTTP_CODE=$(echo "$RESPONSE" | grep -o "__HTTP__[0-9]*" | sed 's/__HTTP__//')
224
251
  BODY=$(echo "$RESPONSE" | sed 's/__HTTP__[0-9]*//g')
@@ -235,7 +262,42 @@ if [ "$ERP_ENABLED" = "true" ]; then
235
262
  fi
236
263
  [ $ATTEMPT -lt 3 ] && { SLEEP=$((1 * 3 ** (ATTEMPT - 1))); node ~/.claude/bin/qualia-ui.js warn "Attempt $ATTEMPT failed (HTTP ${HTTP_CODE:-timeout}), retrying in ${SLEEP}s..."; sleep $SLEEP; }
237
264
  done
238
- [ "$ATTEMPT" = "3" ] && [ "$HTTP_CODE" != "200" ] && node ~/.claude/bin/qualia-ui.js warn "ERP upload failed after 3 attempts. $CLIENT_REPORT_ID is committed locally; will appear in ERP after retry."
265
+
266
+ # If all 3 in-process attempts failed, enqueue the report into the persistent
267
+ # retry queue (~/.claude/.erp-retry-queue.json). session-start.js drains it on
268
+ # the next Claude Code launch; `qualia-framework erp-flush` drains it on demand.
269
+ # This replaces the prior "will appear after retry" message which was a lie —
270
+ # no retry mechanism existed before v5.9.
271
+ if [ "$ATTEMPT" = "3" ] && [ "$HTTP_CODE" != "200" ]; then
272
+ LAST_ERR="HTTP ${HTTP_CODE:-timeout}"
273
+ if [ -n "$BODY" ]; then LAST_ERR="$LAST_ERR: $(echo "$BODY" | head -c 200)"; fi
274
+ PAYLOAD="$PAYLOAD" \
275
+ CLIENT_REPORT_ID="$CLIENT_REPORT_ID" \
276
+ IDEMPOTENCY_KEY="$IDEMPOTENCY_KEY" \
277
+ ERP_URL="$ERP_URL" \
278
+ LAST_ERR="$LAST_ERR" \
279
+ node -e "
280
+ try {
281
+ const {enqueue} = require(require('os').homedir() + '/.claude/bin/erp-retry.js');
282
+ enqueue({
283
+ client_report_id: process.env.CLIENT_REPORT_ID,
284
+ idempotency_key: process.env.IDEMPOTENCY_KEY,
285
+ url: process.env.ERP_URL + '/api/v1/reports',
286
+ payload: process.env.PAYLOAD,
287
+ last_error: process.env.LAST_ERR,
288
+ });
289
+ process.stdout.write('enqueued');
290
+ } catch (e) {
291
+ process.stderr.write('enqueue failed: ' + (e.message || e));
292
+ process.exit(1);
293
+ }
294
+ " 2>/dev/null && {
295
+ node ~/.claude/bin/qualia-ui.js warn "ERP upload failed after 3 attempts — $CLIENT_REPORT_ID enqueued for auto-retry on next session"
296
+ node ~/.claude/bin/qualia-ui.js info "Drain manually with: qualia-framework erp-flush"
297
+ } || {
298
+ node ~/.claude/bin/qualia-ui.js warn "ERP upload failed after 3 attempts AND queue enqueue failed. $CLIENT_REPORT_ID is committed locally — re-run /qualia-report later to retry."
299
+ }
300
+ fi
239
301
  fi
240
302
 
241
303
  [ "$ERP_ENABLED" != "true" ] && node ~/.claude/bin/qualia-ui.js info "ERP upload skipped (disabled). $CLIENT_REPORT_ID committed locally."