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.
- package/README.md +17 -13
- package/agents/plan-checker.md +8 -0
- package/agents/qa-browser.md +7 -0
- package/agents/research-synthesizer.md +4 -1
- package/agents/researcher.md +6 -1
- package/agents/roadmapper.md +8 -0
- package/agents/verifier.md +14 -1
- package/agents/visual-evaluator.md +1 -1
- package/bin/cli.js +30 -1
- package/bin/erp-retry.js +289 -0
- package/bin/install.js +12 -6
- package/bin/slop-detect.mjs +1 -1
- package/bin/state.js +10 -1
- package/docs/onboarding.html +621 -0
- package/docs/playwright-loop-pilot-results.md +7 -5
- package/docs/research/2026-05-11-deep-research.md +189 -0
- package/guide.md +5 -6
- package/hooks/session-start.js +19 -1
- package/package.json +3 -2
- package/rules/speed.md +1 -2
- package/skills/qualia-discuss/SKILL.md +106 -6
- package/skills/qualia-feature/SKILL.md +216 -0
- package/skills/qualia-milestone/SKILL.md +73 -1
- package/skills/qualia-new/SKILL.md +52 -25
- package/skills/qualia-optimize/SKILL.md +1 -1
- package/skills/{qualia-polish-loop → qualia-polish}/REFERENCE.md +5 -5
- package/skills/qualia-polish/SKILL.md +13 -4
- package/skills/{qualia-polish-loop → qualia-polish}/scripts/loop.mjs +2 -2
- package/skills/{qualia-polish-loop → qualia-polish}/scripts/playwright-capture.mjs +1 -1
- package/skills/qualia-report/SKILL.md +64 -2
- package/skills/qualia-road/SKILL.md +10 -11
- package/skills/qualia-verify/SKILL.md +16 -0
- package/templates/help.html +2 -3
- package/templates/project-discovery.md +83 -0
- package/templates/project.md +7 -0
- package/tests/bin.test.sh +97 -67
- package/tests/refs.test.sh +146 -0
- package/tests/slop-detect.test.sh +2 -2
- package/skills/qualia-polish-loop/SKILL.md +0 -201
- package/skills/qualia-prd/SKILL.md +0 -199
- package/skills/qualia-quick/SKILL.md +0 -44
- package/skills/qualia-task/SKILL.md +0 -98
- /package/skills/{qualia-polish-loop → qualia-polish}/fixtures/broken.html +0 -0
- /package/skills/{qualia-polish-loop → qualia-polish}/fixtures/clean.html +0 -0
- /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
|
|
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
|
|
63
|
+
### Step 0.6. Project Type Gate (Demo vs Full)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
The single most important fork in the workflow. Demo and Full produce different journeys, different research depth, different milestone counts.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
77
|
+
### Step 1. Mandatory Discovery Interview (PROJECT MODE)
|
|
74
78
|
|
|
75
|
-
|
|
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
|
|
79
|
-
- options: ["
|
|
92
|
+
- question: "I have what I need to write PROJECT.md and move forward. Continue?"
|
|
93
|
+
- options: ["Continue", "More questions"]
|
|
80
94
|
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
+
Say: **"Running 4 parallel research agents (stack, features, architecture, pitfalls)..."** (demo: append "— quick scope").
|
|
222
239
|
|
|
223
|
-
|
|
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
|
-
|
|
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. **
|
|
370
|
-
2. **
|
|
371
|
-
3. **
|
|
372
|
-
4. **
|
|
373
|
-
5. **
|
|
374
|
-
6. **
|
|
375
|
-
7. **
|
|
376
|
-
8. **
|
|
377
|
-
9. **
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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."
|