qualia-framework 4.5.0 → 5.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.
- package/AGENTS.md +24 -0
- package/CLAUDE.md +12 -75
- package/README.md +23 -16
- package/agents/builder.md +9 -21
- package/agents/planner.md +8 -0
- package/agents/verifier.md +8 -0
- package/agents/visual-evaluator.md +132 -0
- package/bin/cli.js +54 -18
- package/bin/install.js +369 -29
- package/bin/qualia-ui.js +208 -1
- package/bin/slop-detect.mjs +5 -0
- package/bin/state.js +34 -1
- package/docs/install-redesign-builder-prompt.md +290 -0
- package/docs/install-redesign-pilot.md +234 -0
- package/docs/playwright-loop-builder-prompt.md +185 -0
- package/docs/playwright-loop-design-notes.md +108 -0
- package/docs/playwright-loop-pilot-results.md +170 -0
- package/docs/playwright-loop-tester-prompt.md +213 -0
- package/docs/polish-loop-supervised-run.md +111 -0
- package/docs/reviews/matt-pocock-skills-analysis.md +300 -0
- package/guide.md +9 -5
- package/hooks/env-empty-guard.js +74 -0
- package/hooks/pre-compact.js +19 -9
- package/hooks/pre-deploy-gate.js +8 -2
- package/hooks/pre-push.js +26 -12
- package/hooks/supabase-destructive-guard.js +62 -0
- package/hooks/vercel-account-guard.js +91 -0
- package/package.json +2 -1
- package/rules/design-brand.md +4 -0
- package/rules/design-laws.md +4 -0
- package/rules/design-product.md +4 -0
- package/rules/design-rubric.md +4 -0
- package/rules/grounding.md +4 -0
- package/skills/qualia-build/SKILL.md +40 -46
- package/skills/qualia-discuss/SKILL.md +51 -68
- package/skills/qualia-handoff/SKILL.md +1 -0
- package/skills/qualia-hook-gen/SKILL.md +206 -0
- package/skills/qualia-issues/SKILL.md +151 -0
- package/skills/qualia-map/SKILL.md +78 -35
- package/skills/qualia-new/REFERENCE.md +139 -0
- package/skills/qualia-new/SKILL.md +45 -121
- package/skills/qualia-optimize/REFERENCE.md +265 -0
- package/skills/qualia-optimize/SKILL.md +92 -232
- package/skills/qualia-plan/SKILL.md +58 -65
- package/skills/qualia-polish-loop/REFERENCE.md +265 -0
- package/skills/qualia-polish-loop/SKILL.md +201 -0
- package/skills/qualia-polish-loop/fixtures/broken.html +117 -0
- package/skills/qualia-polish-loop/fixtures/clean.html +196 -0
- package/skills/qualia-polish-loop/scripts/loop.mjs +323 -0
- package/skills/qualia-polish-loop/scripts/playwright-capture.mjs +206 -0
- package/skills/qualia-polish-loop/scripts/score.mjs +176 -0
- package/skills/qualia-prd/SKILL.md +199 -0
- package/skills/qualia-report/SKILL.md +141 -200
- package/skills/qualia-research/SKILL.md +28 -33
- package/skills/qualia-road/SKILL.md +103 -0
- package/skills/qualia-ship/SKILL.md +1 -0
- package/skills/qualia-task/SKILL.md +1 -1
- package/skills/qualia-test/SKILL.md +50 -2
- package/skills/qualia-triage/SKILL.md +152 -0
- package/skills/qualia-verify/SKILL.md +63 -104
- package/skills/qualia-zoom/SKILL.md +51 -0
- package/skills/zoho-workflow/SKILL.md +1 -1
- package/templates/CONTEXT.md +36 -0
- package/templates/decisions/ADR-template.md +30 -0
- package/tests/bin.test.sh +598 -7
- package/tests/state.test.sh +58 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* score.mjs -- Qualia visual-polish loop scoring utility.
|
|
4
|
+
*
|
|
5
|
+
* Takes a JSON object with 8 dimension scores and computes pass/fail
|
|
6
|
+
* per the design rubric formula from rules/design-rubric.md.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* echo '{"typography":3,"color":2,"spatial":3,"layout":3,"shadow":3,"motion":3,"microcopy":3,"container":3}' | node score.mjs
|
|
10
|
+
* node score.mjs --file scores.json
|
|
11
|
+
* node score.mjs --scores '{"typography":4,"color":3,...}'
|
|
12
|
+
*
|
|
13
|
+
* Output (JSON):
|
|
14
|
+
* { total, avg, pass, failing: [{dim, score}], verdict }
|
|
15
|
+
*
|
|
16
|
+
* Exit codes:
|
|
17
|
+
* 0 all dimensions >= 3 (pass)
|
|
18
|
+
* 1 one or more dimensions < 3 (fail)
|
|
19
|
+
* 2 invocation error
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
import { argv, stdin, exit } from "node:process";
|
|
24
|
+
|
|
25
|
+
const DIMENSIONS = [
|
|
26
|
+
"typography",
|
|
27
|
+
"color",
|
|
28
|
+
"spatial",
|
|
29
|
+
"layout",
|
|
30
|
+
"shadow",
|
|
31
|
+
"motion",
|
|
32
|
+
"microcopy",
|
|
33
|
+
"container",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const DIMENSION_ALIASES = {
|
|
37
|
+
"color cohesion": "color",
|
|
38
|
+
"color_cohesion": "color",
|
|
39
|
+
"spatial rhythm": "spatial",
|
|
40
|
+
"spatial_rhythm": "spatial",
|
|
41
|
+
"layout originality": "layout",
|
|
42
|
+
"layout_originality": "layout",
|
|
43
|
+
"shadow & depth": "shadow",
|
|
44
|
+
"shadow_depth": "shadow",
|
|
45
|
+
"shadow & depth hierarchy": "shadow",
|
|
46
|
+
"motion intent": "motion",
|
|
47
|
+
"motion_intent": "motion",
|
|
48
|
+
"microcopy specificity": "microcopy",
|
|
49
|
+
"microcopy_specificity": "microcopy",
|
|
50
|
+
"container depth": "container",
|
|
51
|
+
"container_depth": "container",
|
|
52
|
+
"container depth & nesting": "container",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function normalizeKey(key) {
|
|
56
|
+
const lower = key.toLowerCase().trim();
|
|
57
|
+
return DIMENSION_ALIASES[lower] || lower;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseScores(raw) {
|
|
61
|
+
if (typeof raw === "string") {
|
|
62
|
+
try {
|
|
63
|
+
raw = JSON.parse(raw);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error(`Invalid JSON: ${e.message}`);
|
|
66
|
+
exit(2);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const scores = {};
|
|
71
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
72
|
+
const normalized = normalizeKey(key);
|
|
73
|
+
if (DIMENSIONS.includes(normalized)) {
|
|
74
|
+
const num = parseInt(value, 10);
|
|
75
|
+
if (num < 1 || num > 5 || isNaN(num)) {
|
|
76
|
+
console.error(`Invalid score for ${key}: ${value} (must be 1-5)`);
|
|
77
|
+
exit(2);
|
|
78
|
+
}
|
|
79
|
+
scores[normalized] = num;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Validate all dimensions present
|
|
84
|
+
const missing = DIMENSIONS.filter((d) => !(d in scores));
|
|
85
|
+
if (missing.length > 0) {
|
|
86
|
+
console.error(`Missing dimensions: ${missing.join(", ")}`);
|
|
87
|
+
exit(2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return scores;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function computeResult(scores) {
|
|
94
|
+
const dims = DIMENSIONS.map((d) => scores[d]);
|
|
95
|
+
const total = dims.reduce((a, b) => a + b, 0);
|
|
96
|
+
const avg = +(total / dims.length).toFixed(2);
|
|
97
|
+
const failing = DIMENSIONS.filter((d) => scores[d] < 3).map((d) => ({
|
|
98
|
+
dim: d,
|
|
99
|
+
score: scores[d],
|
|
100
|
+
}));
|
|
101
|
+
const pass = failing.length === 0;
|
|
102
|
+
|
|
103
|
+
let verdict;
|
|
104
|
+
if (pass) {
|
|
105
|
+
verdict = "SUCCESS";
|
|
106
|
+
} else if (failing.some((f) => f.score === 1)) {
|
|
107
|
+
verdict = "CRITICAL_FAIL";
|
|
108
|
+
} else {
|
|
109
|
+
verdict = "FAIL";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
scores,
|
|
114
|
+
total,
|
|
115
|
+
max: 40,
|
|
116
|
+
avg,
|
|
117
|
+
pass,
|
|
118
|
+
failing,
|
|
119
|
+
verdict,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// -- CLI entry point --
|
|
124
|
+
|
|
125
|
+
async function main() {
|
|
126
|
+
let input = null;
|
|
127
|
+
|
|
128
|
+
// Parse args
|
|
129
|
+
for (let i = 2; i < argv.length; i++) {
|
|
130
|
+
if (argv[i] === "--file" && argv[i + 1]) {
|
|
131
|
+
try {
|
|
132
|
+
input = readFileSync(argv[i + 1], "utf8");
|
|
133
|
+
} catch (e) {
|
|
134
|
+
console.error(`Cannot read file: ${e.message}`);
|
|
135
|
+
exit(2);
|
|
136
|
+
}
|
|
137
|
+
i++;
|
|
138
|
+
} else if (argv[i] === "--scores" && argv[i + 1]) {
|
|
139
|
+
input = argv[i + 1];
|
|
140
|
+
i++;
|
|
141
|
+
} else if (argv[i] === "--help" || argv[i] === "-h") {
|
|
142
|
+
console.log(`score.mjs -- Qualia visual-polish loop scoring utility
|
|
143
|
+
|
|
144
|
+
Usage:
|
|
145
|
+
echo '{"typography":3,...}' | node score.mjs
|
|
146
|
+
node score.mjs --file scores.json
|
|
147
|
+
node score.mjs --scores '{"typography":4,...}'
|
|
148
|
+
|
|
149
|
+
Dimensions: ${DIMENSIONS.join(", ")}
|
|
150
|
+
Each scored 1-5. All must be >= 3 to pass.`);
|
|
151
|
+
exit(0);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Read from stdin if no explicit input
|
|
156
|
+
if (!input) {
|
|
157
|
+
const chunks = [];
|
|
158
|
+
for await (const chunk of stdin) {
|
|
159
|
+
chunks.push(chunk);
|
|
160
|
+
}
|
|
161
|
+
input = Buffer.concat(chunks).toString("utf8").trim();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!input) {
|
|
165
|
+
console.error("No input provided. Use --file, --scores, or pipe JSON to stdin.");
|
|
166
|
+
exit(2);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const scores = parseScores(input);
|
|
170
|
+
const result = computeResult(scores);
|
|
171
|
+
|
|
172
|
+
console.log(JSON.stringify(result, null, 2));
|
|
173
|
+
exit(result.pass ? 0 : 1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
main();
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qualia-prd
|
|
3
|
+
description: "Synthesize the current conversation into a durable Product Requirements Document (PRD) at .planning/PRD-{slug}.md. Optionally opens a parent GitHub issue. No interview — synthesizes what's already been discussed. Trigger on 'qualia-prd', 'turn this into a PRD', 'write this up as a feature spec', 'make a spec from this discussion', 'PRD this', 'externalize this idea', 'capture this as a spec'. Pairs with /qualia-issues to break the PRD into vertical-slice issues. Distinct from /qualia-plan (phase-operational) — /qualia-prd is feature-durable. v5.3 flagship from Matt Pocock's /to-prd pattern."
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Grep
|
|
10
|
+
- Glob
|
|
11
|
+
- Agent
|
|
12
|
+
argument-hint: "[slug] [--issue] [--no-issue] [--update PATH]"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# /qualia-prd — Conversation → durable feature spec
|
|
16
|
+
|
|
17
|
+
You've been discussing a feature in chat. `/qualia-prd` synthesizes that conversation into a durable PRD on disk so the spec lives outside the chat context. From there, `/qualia-issues` can split it into vertical-slice GH issues, and `/qualia-plan` can plan a phase against it.
|
|
18
|
+
|
|
19
|
+
**Distinct from `/qualia-new`:** `/qualia-new` is project setup (one-shot — JOURNEY.md, PRODUCT.md, CONTEXT.md). `/qualia-prd` is mid-project feature spec capture. You'll run it dozens of times across a project's life; you run `/qualia-new` once.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- Mid-project, when a feature has been discussed enough that you want it durable
|
|
24
|
+
- Before `/qualia-issues` so the issues link back to a real spec
|
|
25
|
+
- Before `/qualia-plan` if the phase needs a feature spec upstream of it
|
|
26
|
+
- When the conversation is about to compact and the PRD context would be lost
|
|
27
|
+
|
|
28
|
+
## Pre-flight
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
node ~/.claude/bin/qualia-ui.js banner plan
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Gate | Check | If fail |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| In a project | `.planning/` exists | HALT — "Run `/qualia-new` first or use `/qualia-quick` for one-off work" |
|
|
37
|
+
| PRODUCT.md present | `.planning/PRODUCT.md` readable | NUDGE — proceed but recommend running `/qualia-new` to seed PRODUCT.md |
|
|
38
|
+
| CONTEXT.md present | `.planning/CONTEXT.md` readable | NUDGE — PRD will use ad-hoc terminology instead of the glossary |
|
|
39
|
+
| Working tree | `git status --porcelain` empty | OK to be dirty — PRD is additive, no logic changes |
|
|
40
|
+
| Slug | First arg or auto-derive from first heading | Auto-derive: kebab-case the conversation's main feature topic |
|
|
41
|
+
|
|
42
|
+
## Process
|
|
43
|
+
|
|
44
|
+
### 1. Slug + path
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
SLUG="${1:-$(date +%Y%m%d)-feature}" # or auto-derive from conversation
|
|
48
|
+
PRD_PATH=".planning/PRD-${SLUG}.md"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If `--update PATH` is provided, update an existing PRD instead of writing a new one. The synthesis preserves existing sections that haven't been touched in conversation; only changed sections get rewritten.
|
|
52
|
+
|
|
53
|
+
### 2. Spawn synthesizer (forked subagent — preserves taste, cheap on context)
|
|
54
|
+
|
|
55
|
+
The synthesis runs in a **forked subagent**. Forks inherit the full conversation history + share the prompt cache, so the agent can pull the design discussion, decisions, ADR-worthy moments, and user voice without re-loading anything. The fork writes the PRD file and returns just the path + 1-line summary — keeps the parent session lean.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Agent(
|
|
59
|
+
subagent_type="general-purpose",
|
|
60
|
+
description="Synthesize conversation → PRD",
|
|
61
|
+
prompt=`
|
|
62
|
+
You are synthesizing this conversation's feature discussion into a durable PRD.
|
|
63
|
+
|
|
64
|
+
# Output location
|
|
65
|
+
Write to: ${PRD_PATH}
|
|
66
|
+
|
|
67
|
+
# Inputs you have (from forked context)
|
|
68
|
+
- The full conversation up to this point
|
|
69
|
+
- @.planning/PRODUCT.md (project register, voice, anti-references)
|
|
70
|
+
- @.planning/CONTEXT.md (domain glossary — USE these terms, do not invent synonyms)
|
|
71
|
+
- @.planning/decisions/*.md (ADRs constraining the design space)
|
|
72
|
+
|
|
73
|
+
# PRD structure (copy this skeleton; fill from conversation)
|
|
74
|
+
|
|
75
|
+
\`\`\`markdown
|
|
76
|
+
---
|
|
77
|
+
name: {feature name}
|
|
78
|
+
slug: ${SLUG}
|
|
79
|
+
created: ${date}
|
|
80
|
+
status: draft | accepted | shipped | abandoned
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# {Feature name}
|
|
84
|
+
|
|
85
|
+
## Why this exists
|
|
86
|
+
{1-3 sentences: what problem does this solve, for whom, why now}
|
|
87
|
+
|
|
88
|
+
## User stories
|
|
89
|
+
- As a {persona}, I want to {do X} so that {outcome}.
|
|
90
|
+
- ...
|
|
91
|
+
|
|
92
|
+
## Acceptance criteria
|
|
93
|
+
Observable, testable behaviors. Each must be verifiable post-build.
|
|
94
|
+
- [ ] {criterion 1}
|
|
95
|
+
- [ ] {criterion 2}
|
|
96
|
+
|
|
97
|
+
## Out of scope (mandatory)
|
|
98
|
+
What this feature is NOT. Pin this down so scope creep is detectable.
|
|
99
|
+
- {explicit non-goal 1}
|
|
100
|
+
- {explicit non-goal 2}
|
|
101
|
+
|
|
102
|
+
## Modules touched
|
|
103
|
+
List the deep modules / files / packages this PRD will modify or create.
|
|
104
|
+
Use CONTEXT.md domain language. Surface interface changes explicitly.
|
|
105
|
+
|
|
106
|
+
| Module | Interface change | Rationale |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| {module} | {new method / removed export / signature change} | {why} |
|
|
109
|
+
|
|
110
|
+
## Testing decisions
|
|
111
|
+
Which behaviors get tests, what kind (unit / integration / e2e), where the
|
|
112
|
+
seams are. /qualia-test --tdd will read this.
|
|
113
|
+
|
|
114
|
+
## Open questions
|
|
115
|
+
Things the conversation flagged but didn't resolve. /qualia-discuss can
|
|
116
|
+
walk these down before /qualia-plan.
|
|
117
|
+
|
|
118
|
+
## References
|
|
119
|
+
- Conversation timestamp: {ISO}
|
|
120
|
+
- Related ADRs: docs/adr/...
|
|
121
|
+
- Related PRDs: .planning/PRD-...
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
# Discipline
|
|
125
|
+
- NO interview. Synthesize what was DISCUSSED. If a section has nothing in
|
|
126
|
+
the conversation, leave a TODO marker — do NOT invent.
|
|
127
|
+
- Use CONTEXT.md domain terms. If the user said "lesson" but CONTEXT.md says
|
|
128
|
+
"module", normalize to "module" and note the alias.
|
|
129
|
+
- Voice = PRODUCT.md voice. No "Welcome to" / "Get Started" / em-dashes in
|
|
130
|
+
user-facing copy snippets.
|
|
131
|
+
- Output: write the file, then return ONLY \`{ "path": "...", "summary": "1 sentence" }\`.
|
|
132
|
+
`
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 3. Optional GH issue
|
|
137
|
+
|
|
138
|
+
If `--issue` (or default when `gh` is configured AND `.planning/agents/tracker.md` exists), open a parent issue linking to the PRD path:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
PRD_BODY=$(cat "${PRD_PATH}")
|
|
142
|
+
gh issue create \
|
|
143
|
+
--title "PRD: {feature name}" \
|
|
144
|
+
--body-file "${PRD_PATH}" \
|
|
145
|
+
--label "prd,needs-triage"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Pass `--no-issue` to skip. The PRD itself is the source of truth — the GH issue is a notification surface.
|
|
149
|
+
|
|
150
|
+
### 4. Commit
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
git add "${PRD_PATH}"
|
|
154
|
+
git -c user.name="Qualia Solutions" -c user.email="info@qualiasolutions.net" \
|
|
155
|
+
commit -m "prd(${SLUG}): {feature name}"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 5. End-card + next-command hint
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
node ~/.claude/bin/qualia-ui.js divider
|
|
162
|
+
node ~/.claude/bin/qualia-ui.js ok "PRD: ${PRD_PATH}"
|
|
163
|
+
node ~/.claude/bin/qualia-ui.js ok "Issue: {url or 'skipped'}"
|
|
164
|
+
node ~/.claude/bin/qualia-ui.js end "PRD CAPTURED" "/qualia-issues # break into vertical slices"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Token discipline (mandatory)
|
|
168
|
+
|
|
169
|
+
This skill is the v5.3 reply to Matt's instruction-budget thesis. Three rules:
|
|
170
|
+
|
|
171
|
+
1. **Forked subagent, file output.** The synthesis runs in a fork; main session never sees the full PRD body. Only `{path, summary}` flows back.
|
|
172
|
+
2. **No re-summarization.** When the parent session needs the PRD later, it Reads the file (or a later skill does). Never paste the PRD body into chat to "confirm."
|
|
173
|
+
3. **Fork prefix is stable.** Role + PRD skeleton are stable across spawns — Anthropic prompt caching applies. Per-invocation cost is ~5K tokens for the fork + ~2K for the synthesis output.
|
|
174
|
+
|
|
175
|
+
## Failure modes
|
|
176
|
+
|
|
177
|
+
| Symptom | Cause | Action |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `.planning/ not found` | Not in a Qualia project | HALT; recommend `/qualia-new` or `/qualia-quick` |
|
|
180
|
+
| Conversation has no clear feature topic | Skill invoked too early | NUDGE — ask the user "what feature are we PRD-ing? Pick a slug or paste a 1-line summary" |
|
|
181
|
+
| `gh` not configured | No GitHub auth | Skip issue creation silently; print PRD path |
|
|
182
|
+
| PRD path collision | Slug already exists | Append `-2`, `-3` to slug; surface to user |
|
|
183
|
+
| Empty conversation context | Fresh session | HALT — "/qualia-prd needs a discussion to synthesize. Discuss first, then run." |
|
|
184
|
+
|
|
185
|
+
## Rules
|
|
186
|
+
|
|
187
|
+
1. **Don't interview.** This is synthesis, not a deep-dive. If gaps exist, mark TODO and recommend `/qualia-discuss`.
|
|
188
|
+
2. **CONTEXT.md is law.** Use the project's terms. Don't invent.
|
|
189
|
+
3. **One PRD per feature.** If two features got conflated in conversation, write two PRDs and link them.
|
|
190
|
+
4. **Voice match.** PRODUCT.md voice. No generic SaaS copy.
|
|
191
|
+
5. **Forked context, file output.** Token discipline above.
|
|
192
|
+
6. **Commit immediately.** PRDs are durable artifacts — they ship in git.
|
|
193
|
+
|
|
194
|
+
## Pairs with
|
|
195
|
+
|
|
196
|
+
- `/qualia-discuss` — run BEFORE if the conversation has open questions
|
|
197
|
+
- `/qualia-issues` — run AFTER to break PRD into vertical-slice GH issues
|
|
198
|
+
- `/qualia-plan` — run AFTER `/qualia-issues` if you want a phase plan
|
|
199
|
+
- `/qualia-new` — runs ONCE at project setup; `/qualia-prd` is the per-feature equivalent
|