qualia-framework 6.14.0 → 6.22.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 +8 -5
- package/CHANGELOG.md +130 -0
- package/CLAUDE.md +3 -1
- package/agents/roadmapper.md +16 -14
- package/bin/agent-status.js +24 -11
- package/bin/branch-hygiene.js +135 -0
- package/bin/command-surface.js +1 -0
- package/bin/compile-instructions.js +82 -0
- package/bin/eval-runner.js +218 -0
- package/bin/host-adapters.js +72 -12
- package/bin/install.js +21 -13
- package/bin/last-report.js +207 -0
- package/bin/project-sync.js +315 -0
- package/bin/runtime-manifest.js +6 -0
- package/bin/state.js +112 -1
- package/bin/verify-panel.js +294 -0
- package/bin/wave-plan.js +211 -0
- package/docs/erp-contract.md +145 -0
- package/package.json +3 -2
- package/rules/codex-goal.md +28 -26
- package/rules/infrastructure.md +1 -1
- package/skills/qualia/SKILL.md +6 -0
- package/skills/qualia-build/SKILL.md +12 -9
- package/skills/qualia-eval/SKILL.md +83 -0
- package/skills/qualia-feature/SKILL.md +20 -4
- package/skills/qualia-fix/SKILL.md +13 -1
- package/skills/qualia-milestone/SKILL.md +12 -6
- package/skills/qualia-new/REFERENCE.md +6 -4
- package/skills/qualia-new/SKILL.md +27 -15
- package/skills/qualia-plan/SKILL.md +2 -2
- package/skills/qualia-report/SKILL.md +10 -0
- package/skills/qualia-scope/SKILL.md +3 -3
- package/skills/qualia-ship/SKILL.md +34 -4
- package/skills/qualia-update/SKILL.md +4 -0
- package/skills/qualia-verify/SKILL.md +45 -24
- package/templates/instructions.md +32 -0
- package/templates/journey.md +1 -1
- package/templates/project-discovery.md +30 -23
- package/templates/requirements.md +7 -7
- package/tests/agent-status.test.sh +15 -0
- package/tests/branch-hygiene.test.sh +93 -0
- package/tests/eval-runner.test.sh +147 -0
- package/tests/instructions.test.sh +109 -0
- package/tests/last-report.test.sh +156 -0
- package/tests/lib.test.sh +2 -2
- package/tests/project-sync.test.sh +175 -0
- package/tests/run-all.sh +7 -0
- package/tests/state.test.sh +92 -0
- package/tests/verify-panel.test.sh +162 -0
- package/tests/wave-plan.test.sh +153 -0
|
@@ -121,17 +121,37 @@ if [ $SEC_FAIL -ne 0 ]; then
|
|
|
121
121
|
fi
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
### 3.
|
|
124
|
+
### 3. Integrate to main (ship IS the merge point)
|
|
125
|
+
|
|
126
|
+
Ship is the one place feature work lands on `main`, so `main` is always exactly what's in production and no branch lingers. Commit, then fast-forward-integrate into `main` and push. `branch-guard` records the main push (accountability, not a block — see `rules/infrastructure.md`).
|
|
125
127
|
|
|
126
128
|
```bash
|
|
127
129
|
git add {specific changed files}
|
|
128
130
|
git commit -m "ship: {project name} production deploy"
|
|
129
|
-
|
|
131
|
+
|
|
132
|
+
BR=$(git branch --show-current)
|
|
133
|
+
if [ "$BR" != "main" ] && [ "$BR" != "master" ]; then
|
|
134
|
+
git checkout main
|
|
135
|
+
git pull --ff-only origin main 2>/dev/null || true # sync with remote first
|
|
136
|
+
if git merge --ff-only "$BR"; then
|
|
137
|
+
: # clean fast-forward
|
|
138
|
+
else
|
|
139
|
+
# main moved since the branch started — rebase the feature, then ff.
|
|
140
|
+
git checkout "$BR" && git rebase main || {
|
|
141
|
+
node ${QUALIA_BIN}/qualia-ui.js fail "Rebase conflict integrating $BR → main. Resolve, then re-run /qualia-ship."
|
|
142
|
+
exit 1
|
|
143
|
+
}
|
|
144
|
+
git checkout main && git merge --ff-only "$BR"
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
git push origin HEAD
|
|
130
148
|
```
|
|
131
149
|
|
|
132
|
-
|
|
150
|
+
If anything in this block fails (conflict, push rejected), STOP and surface it — do not deploy a half-integrated tree.
|
|
151
|
+
|
|
152
|
+
### 4. Deploy (from main)
|
|
133
153
|
|
|
134
|
-
|
|
154
|
+
Deploy runs on `main` HEAD — what you just integrated — so the deployed artifact and `main` are byte-identical.
|
|
135
155
|
|
|
136
156
|
```bash
|
|
137
157
|
vercel --prod # Website/AI agent
|
|
@@ -141,6 +161,16 @@ supabase functions deploy # Edge functions
|
|
|
141
161
|
wrangler deploy # Cloudflare Workers
|
|
142
162
|
```
|
|
143
163
|
|
|
164
|
+
### 4b. Close the branch
|
|
165
|
+
|
|
166
|
+
On a verified successful deploy, delete the integrated feature branch so nothing lingers (skip if you shipped directly from `main`):
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
if [ -n "$BR" ] && [ "$BR" != "main" ] && [ "$BR" != "master" ]; then
|
|
170
|
+
git branch -d "$BR" 2>/dev/null && git push origin --delete "$BR" 2>/dev/null || true
|
|
171
|
+
fi
|
|
172
|
+
```
|
|
173
|
+
|
|
144
174
|
### 5. Post-Deploy Verification
|
|
145
175
|
|
|
146
176
|
Read the deployed URL from `tracking.json.deployed_url` or from an explicit user-provided URL. Do NOT use a `{domain}` placeholder — that expects the LLM to hallucinate the URL, which is exactly the kind of silent fail the state guard above prevents.
|
|
@@ -54,6 +54,10 @@ Keep it small and shippable. A bug fix, a copy change, a new section, a single
|
|
|
54
54
|
feature. If it's larger than ~5 files or needs its own milestone arc, it's a new
|
|
55
55
|
`build`-mode milestone — use `/qualia-milestone`, not an update.
|
|
56
56
|
|
|
57
|
+
### 2b. Set the work-unit goal
|
|
58
|
+
|
|
59
|
+
Per `rules/codex-goal.md` — set the work-unit goal with scope `feature` (Codex `/goal`; on Claude Code, a tracked task + budget). An update runs its own lean loop without `/qualia-plan`, so set the objective + budget here so the change stays one coherent, bounded unit.
|
|
60
|
+
|
|
57
61
|
### 3. Run the lean loop
|
|
58
62
|
|
|
59
63
|
Reuse the real lifecycle skills, scoped to this one change:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qualia-verify
|
|
3
|
-
description: "Goal-backward verification of a built phase —
|
|
3
|
+
description: "Goal-backward verification of a built phase — a parallel verifier PANEL (one lens each) greps code against acceptance criteria, per-finding adversarial skeptics vote majority-survives, and verify-panel.js aggregates a deterministic verdict. Triggers: 'verify this phase', 'check if it works', 'run verification', 'did the build pass'."
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- Bash
|
|
6
6
|
- Read
|
|
@@ -19,7 +19,7 @@ Spawn verifier to check phase goal. Does NOT trust build summaries; greps codeba
|
|
|
19
19
|
`/qualia-verify` — verify current built phase
|
|
20
20
|
`/qualia-verify {N}` — verify specific phase
|
|
21
21
|
`/qualia-verify {N} --auto` — verify + auto-chain: PASS → next phase/milestone; FAIL → gap closure; gap limit → halt
|
|
22
|
-
`/qualia-verify {N} --adversarial` —
|
|
22
|
+
`/qualia-verify {N} --adversarial` — deepen the panel: 5 skeptics per finding instead of 3. Recommended for high-stakes phases (Handoff, payment/auth/migration).
|
|
23
23
|
|
|
24
24
|
## Process
|
|
25
25
|
|
|
@@ -44,25 +44,39 @@ node ${QUALIA_BIN}/contract-runner.js .planning/phase-{N}-contract.json
|
|
|
44
44
|
|
|
45
45
|
If it fails, the phase cannot PASS. Still spawn the verifier to explain the failure and identify the smallest gap-closure tasks. If it passes, pass the evidence file into the verifier prompt.
|
|
46
46
|
|
|
47
|
-
### 3. Spawn Verifier (
|
|
47
|
+
### 3. Spawn Verifier Panel (parallel lenses, fresh context each)
|
|
48
|
+
|
|
49
|
+
A single LLM judge is adversarially fragile (a lone stray token flips ~35% of verdicts). Replace single-pass review with a **panel**: one verifier per *relevant* lens, spawned in parallel (separate `Agent()` calls in the SAME turn), each scoped to one concern and anchored on the SAME machine evidence (contract-run + harness-eval) as shared ground truth.
|
|
48
50
|
|
|
49
51
|
```bash
|
|
50
52
|
node ${QUALIA_BIN}/qualia-ui.js banner verify {N} "{phase name}"
|
|
51
|
-
node ${QUALIA_BIN}/qualia-ui.js spawn verifier "Goal-backward check..."
|
|
52
53
|
```
|
|
53
54
|
|
|
55
|
+
Pick lenses by what the phase touches (scale cost to risk — don't run all four on a one-file change):
|
|
56
|
+
- **correctness** — always.
|
|
57
|
+
- **security** — if the plan touches `auth|payment|rls|service_role|migration|secret|upload`.
|
|
58
|
+
- **performance** — if it touches data fetching, network-in-loop, large lists, or `--perf` is set.
|
|
59
|
+
- **design** — if it touches `.tsx/.jsx/.css/.scss` or `app/|components/|pages/`.
|
|
60
|
+
|
|
61
|
+
For each chosen lens `L`, spawn (all in one turn, then wait for all):
|
|
62
|
+
|
|
54
63
|
```
|
|
55
64
|
Agent(prompt="
|
|
56
65
|
Role: @${QUALIA_AGENTS}/verifier.md
|
|
57
66
|
|
|
67
|
+
LENS: {L}. Review ONLY through the {L} lens — concerns owned by other lenses have their own reviewer; do not duplicate them.
|
|
68
|
+
|
|
58
69
|
Project: @.planning/PROJECT.md
|
|
59
70
|
Plan + AC + validation: @.planning/phase-{N}-plan.md
|
|
60
71
|
Machine contract: @.planning/phase-{N}-contract.json
|
|
61
|
-
Contract evidence: @.planning/evidence/phase-{N}-contract-run.json
|
|
72
|
+
Contract evidence (shared ground truth — do not re-derive): @.planning/evidence/phase-{N}-contract-run.json
|
|
62
73
|
{Re-verify → previous gaps: @.planning/phase-{N}-verification.md}
|
|
63
74
|
|
|
64
|
-
Verify phase. Every finding needs file:line evidence
|
|
65
|
-
|
|
75
|
+
Verify the {L} concerns of this phase. Every finding needs file:line evidence and a Severity Rubric label.
|
|
76
|
+
Write your findings as a JSON array to .planning/phase-{N}-panel-{L}.json:
|
|
77
|
+
[{\"file\":\"path\",\"line\":N,\"severity\":\"CRITICAL|HIGH|MEDIUM|LOW\",\"title\":\"one-line claim\"}]
|
|
78
|
+
Empty array [] if the {L} lens is clean. Also append a human '## {L} lens' section to .planning/phase-{N}-verification.md.
|
|
79
|
+
", subagent_type="qualia-verifier", description="Verify phase {N} — {L} lens")
|
|
66
80
|
```
|
|
67
81
|
|
|
68
82
|
### 3b. Browser QA (if phase touched frontend)
|
|
@@ -89,34 +103,41 @@ Drive dev server, test routes phase touched. Append '## Browser QA' to verificat
|
|
|
89
103
|
|
|
90
104
|
Wait for both verifier + QA before step 3. Playwright MCP unavailable → QA returns BLOCKED (note, not phase failure).
|
|
91
105
|
|
|
92
|
-
### 3c.
|
|
106
|
+
### 3c. Skeptic Pass + Deterministic Aggregation
|
|
93
107
|
|
|
94
|
-
|
|
108
|
+
The panel FINDS; skeptics decide what's REAL; `verify-panel.js` decides the verdict — math, not a vibe.
|
|
109
|
+
|
|
110
|
+
**1. Assemble** the per-lens finding files into one panel skeleton (votes zeroed):
|
|
95
111
|
|
|
96
112
|
```bash
|
|
97
|
-
node ${QUALIA_BIN}/
|
|
113
|
+
node ${QUALIA_BIN}/verify-panel.js assemble {N} # → .planning/phase-{N}-panel.json
|
|
98
114
|
```
|
|
99
115
|
|
|
116
|
+
**2. Skeptic vote** on each CRITICAL/HIGH finding (MEDIUM/LOW auto-survive — they don't flip the C/H verdict; skipping skeptics on them is the documented cost bound, not a silent cap). For each such finding spawn **3 skeptics** (5 if `--adversarial`, Handoff milestone, or the finding's lens is `security`), each in fresh context, each prompted to REFUTE:
|
|
117
|
+
|
|
100
118
|
```
|
|
101
119
|
Agent(prompt="
|
|
102
120
|
Role: @${QUALIA_AGENTS}/verifier.md
|
|
103
121
|
|
|
104
|
-
|
|
105
|
-
- Untested error paths?
|
|
106
|
-
- Crash-inducing input?
|
|
107
|
-
- Unhandled concurrent access?
|
|
108
|
-
- Downstream breaks if contract changes?
|
|
109
|
-
- Security assumption (auth, RLS, secrets) implicit not enforced?
|
|
122
|
+
SKEPTIC. A panel verifier claims this finding. Try to REFUTE it: is it actually reachable on a real path, or already handled elsewhere, or a false positive? Default to refuted ONLY with evidence — uncertainty is NOT refutation.
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
Finding: {severity} — {title}
|
|
125
|
+
Location: {file}:{line}
|
|
126
|
+
Evidence to re-check yourself: @{file}
|
|
127
|
+
|
|
128
|
+
Return exactly one line: REAL — {file:line reason} OR NOT_REAL — {file:line reason}
|
|
129
|
+
", subagent_type="qualia-verifier", description="Skeptic {i}/3 — {title}")
|
|
130
|
+
```
|
|
114
131
|
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
Tally each finding's votes into `.planning/phase-{N}-panel.json` (`votes.real` / `votes.notReal`).
|
|
133
|
+
|
|
134
|
+
**3. Aggregate** deterministically:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
node ${QUALIA_BIN}/verify-panel.js .planning/phase-{N}-panel.json --write
|
|
117
138
|
```
|
|
118
139
|
|
|
119
|
-
|
|
140
|
+
`verify-panel.js` dedupes findings across lenses, applies **majority-survives** (a finding is killed only when skeptics are a strict majority calling it not-real; ties and unvoted findings survive), computes category scores via the `rules/grounding.md` formula, and exits **0 = PASS / 1 = FAIL** (any surviving CRITICAL/HIGH → FAIL). That exit code IS the panel verdict — carry it into step 4. Artifacts: `.planning/phase-{N}-verification-panel.{json,md}`.
|
|
120
141
|
|
|
121
142
|
### 3d. INSUFFICIENT EVIDENCE downgrade (mandatory)
|
|
122
143
|
|
|
@@ -132,7 +153,7 @@ if [ "$IE_COUNT" -gt 0 ]; then
|
|
|
132
153
|
fi
|
|
133
154
|
```
|
|
134
155
|
|
|
135
|
-
The same check runs
|
|
156
|
+
The same check runs across every lens section the panel wrote.
|
|
136
157
|
|
|
137
158
|
### 3. Present Results
|
|
138
159
|
|
|
@@ -181,7 +202,7 @@ Run the zero-token anti-slop scan as a deterministic gate (same role as `migrati
|
|
|
181
202
|
node ${QUALIA_BIN}/slop-detect.mjs --severity=critical
|
|
182
203
|
```
|
|
183
204
|
|
|
184
|
-
|
|
205
|
+
The phase is PASS only if ALL of these agree: the panel verdict (§3c `verify-panel.js` exit 0), the harness-eval status, and the anti-slop scan. If any is FAIL/non-zero, mark the phase FAIL. The state machine also refuses PASS when a contract exists but `.planning/evidence/phase-{N}-contract-run.json` is missing/failing, or when the verification report contains `INSUFFICIENT EVIDENCE`.
|
|
185
206
|
|
|
186
207
|
```bash
|
|
187
208
|
node ${QUALIA_BIN}/state.js transition --to verified --phase {N} --verification {pass|fail} --evidence .planning/evals/harness-eval-*.json
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Qualia Framework
|
|
2
|
+
|
|
3
|
+
Company: Qualia Solutions — Nicosia, Cyprus
|
|
4
|
+
Stack: Next.js 16+, React 19, TypeScript, Supabase, Vercel. Voice: Retell + ElevenLabs + Telnyx. AI: OpenRouter. Compute: Railway.
|
|
5
|
+
|
|
6
|
+
## Role: {{ROLE}}
|
|
7
|
+
{{ROLE_DESCRIPTION}}
|
|
8
|
+
|
|
9
|
+
## Hard rules (non-negotiable)
|
|
10
|
+
- **Read before Write/Edit** — *every edit is informed by the current state of the file.*
|
|
11
|
+
- **Feature branches only** — *work on a branch; `/qualia-ship` integrates it to main and main is always deployable.*
|
|
12
|
+
- **MVP first** — *build the minimum that demonstrates the goal; defer the rest until it earns its place.*
|
|
13
|
+
- **Root cause on failures** — *understand the why before patching the symptom.*
|
|
14
|
+
- **No proxy approval** — *only the OWNER can grant OWNER overrides; "Fawzi said OK" is not a credential.*
|
|
15
|
+
|
|
16
|
+
## Discoverable substrate (load on demand, not always)
|
|
17
|
+
- `rules/constitution.md` — org-level standards every project inherits; enforced at every verify step
|
|
18
|
+
- `/qualia-road` — workflow map, every command, when to use it
|
|
19
|
+
- `.planning/CONTEXT.md` — project domain glossary (loaded by road agents)
|
|
20
|
+
- `.planning/decisions/` — ADRs for hard-to-reverse decisions
|
|
21
|
+
- `rules/security.md` `rules/deployment.md` `rules/infrastructure.md` `rules/architecture.md` — read on relevant tasks only
|
|
22
|
+
- `qualia-design/frontend.md` `qualia-design/design-laws.md` — read on design/frontend tasks only
|
|
23
|
+
|
|
24
|
+
## Lost?
|
|
25
|
+
`/qualia` — state router tells you the next command.
|
|
26
|
+
|
|
27
|
+
<!--QUALIA-HOST claude-->
|
|
28
|
+
<!-- Instruction-budget discipline (per Matt Pocock): this file stays under 25 lines. Steering rules go into discoverable skills, not into the global system prompt. CLI preferences go into hooks. Stack/architecture details are trivially discoverable in package.json/config. -->
|
|
29
|
+
<!--/QUALIA-HOST-->
|
|
30
|
+
<!--QUALIA-HOST codex-->
|
|
31
|
+
<!-- AGENTS.md mirrors CLAUDE.md for cross-vendor compatibility (Codex, Cursor, Continue, Aider, Devin). Both files stay under 25 lines per Matt Pocock's instruction-budget discipline (LLMs realistically hold 300–500 instructions; bloating this file hamstrings every spawn). -->
|
|
32
|
+
<!--/QUALIA-HOST-->
|
package/templates/journey.md
CHANGED
|
@@ -100,7 +100,7 @@ M1 ─── M2 ─── M3 ─── ... ─── M{N} (Handoff)
|
|
|
100
100
|
|
|
101
101
|
## Rules for This Journey
|
|
102
102
|
|
|
103
|
-
1. **
|
|
103
|
+
1. **No milestone ceiling — the arc spans the whole agreed scope.** Plan as many milestones as the capability inventory (discovery §9) needs to reach the §10 done-state. Do NOT compress real work to hit a number, and do NOT defer agreed work to a "v2" — the only deferrals are what the client explicitly marked Out of Scope (discovery §8). An arc that stops short of the agreed scope is the root cause of teams improvising off-plan later.
|
|
104
104
|
2. **Hard floor: 2 milestones.** Anything smaller should use `/qualia-new --quick` instead.
|
|
105
105
|
3. **In BUILD mode, the final milestone is Handoff.** This is the convention for a one-shot client build that ends with a handoff. It is **not** a universal law: once a project launches and enters the `operate` lifecycle (`state.js launch`), it becomes an *update stream* with no forced Handoff — it ships updates indefinitely. Don't author a Handoff milestone for a product/retainer that will keep shipping; launch it instead.
|
|
106
106
|
4. **Milestones ≥ 2 phases OR are a shipped release gate.** A 1-phase milestone is a phase, not a milestone.
|
|
@@ -6,9 +6,9 @@ discovery_mode: project
|
|
|
6
6
|
|
|
7
7
|
# Project Discovery, {Project Name}
|
|
8
8
|
|
|
9
|
-
The non-technical kickoff interview output. `/qualia-scope` writes this in PROJECT MODE before `/qualia-new` generates JOURNEY.md. Captures intent, audience, brand, and
|
|
9
|
+
The non-technical kickoff interview output. `/qualia-scope` writes this in PROJECT MODE before `/qualia-new` generates JOURNEY.md. Captures intent, audience, brand, constraints — and, for full projects, the **complete capability set that defines the project as DONE** so the roadmap can span the whole arc, not a v1 slice.
|
|
10
10
|
|
|
11
|
-
Demo path: 8 questions. Full-project path:
|
|
11
|
+
Demo path: §1–§8 (8 questions). Full-project path: §1–§15 (adds the completeness pass + delivery questions).
|
|
12
12
|
|
|
13
13
|
## 1. The one-line pitch
|
|
14
14
|
|
|
@@ -36,48 +36,55 @@ Demo path: 8 questions. Full-project path: 14 questions.
|
|
|
36
36
|
|
|
37
37
|
## 7. Hard constraints
|
|
38
38
|
|
|
39
|
-
> {Anything
|
|
39
|
+
> {Anything non-negotiable: stack, deadline, compliance, integrations, budget.}
|
|
40
40
|
|
|
41
|
-
## 8. Out of scope
|
|
41
|
+
## 8. Out of scope (explicitly deferred)
|
|
42
42
|
|
|
43
|
-
> {What is intentionally NOT in this project, even if
|
|
43
|
+
> {What is intentionally NOT in this project — work the client has consciously deferred, even if obvious to add. This is the ONLY place work leaves the arc; anything not listed here is in-scope and must land in a milestone.}
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
47
|
-
The remaining
|
|
47
|
+
The remaining questions only run for `project_type: full`. Demo mode stops here.
|
|
48
48
|
|
|
49
|
-
## 9.
|
|
49
|
+
## 9. Capability inventory — the WHOLE project
|
|
50
50
|
|
|
51
|
-
> {
|
|
51
|
+
> {List EVERY capability this project must have to be considered finished — not a v1 slice, the whole thing. One bullet per capability, plain language. Push for completeness: "what else does it need before you'd call it done?" Keep asking until the client says "that's everything." This list becomes the REQ-IDs and milestones — anything missing here is what the team is later forced to improvise, so it is the most important answer in the interview.}
|
|
52
52
|
|
|
53
|
-
## 10.
|
|
53
|
+
## 10. Definition of done — for the entire project
|
|
54
|
+
|
|
55
|
+
> {Describe the end state where there is no more agreed work. What is true when the project is finished and the team stops? This anchors the final milestone (Handoff for client projects) and tells the roadmapper how far the arc must reach.}
|
|
56
|
+
|
|
57
|
+
## 11. Shipping order
|
|
58
|
+
|
|
59
|
+
> {Roughly what order should the capabilities in §9 ship in? Group them into chapters if natural. We shape milestones from this — as many as the scope needs, no artificial cap. For client projects the last chapter is Handoff; for an internal or ongoing product it may have no Handoff.}
|
|
60
|
+
|
|
61
|
+
## 12. Compliance and legal
|
|
54
62
|
|
|
55
63
|
> {Anything regulated: payments, medical, legal, finance, accessibility commitments, data residency.}
|
|
56
64
|
|
|
57
|
-
##
|
|
65
|
+
## 13. Integrations
|
|
58
66
|
|
|
59
67
|
> {Third-party systems this must talk to, in priority order.}
|
|
60
68
|
|
|
61
|
-
##
|
|
69
|
+
## 14. Content and copy
|
|
62
70
|
|
|
63
71
|
> {Who writes the copy and where does it live, today and after handoff?}
|
|
64
72
|
|
|
65
|
-
##
|
|
73
|
+
## 15. Ownership, team, and delivery shape
|
|
66
74
|
|
|
67
|
-
> {Who maintains this after we ship
|
|
68
|
-
|
|
69
|
-
## 14. Budget and timeline shape
|
|
70
|
-
|
|
71
|
-
> {Fixed deadline, fixed scope, or fixed budget? Pick one — the other two flex.}
|
|
75
|
+
> {Who maintains this after we ship — what can they do, what can't they? And: fixed deadline, fixed scope, or fixed budget? Pick one — the other two flex.}
|
|
72
76
|
|
|
73
77
|
---
|
|
74
78
|
|
|
75
79
|
## How this feeds `/qualia-new`
|
|
76
80
|
|
|
77
|
-
- §1
|
|
81
|
+
- §1–§5 seed PROJECT.md (one-line pitch, what we're building) and PRODUCT.md (users, register, voice, anti-references).
|
|
78
82
|
- §6 becomes the first row of the success-criteria table in ROADMAP.md.
|
|
79
|
-
- §7
|
|
80
|
-
- §
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
- §7 populates PROJECT.md's constraints section.
|
|
84
|
+
- §8 populates PROJECT.md's "Out of Scope" — the explicit, client-agreed deferrals (the roadmapper must NOT defer anything else here).
|
|
85
|
+
- **§9 (full only) is the capability inventory → every item becomes a REQ-ID in REQUIREMENTS.md, assigned to exactly one milestone. The roadmapper must cover ALL of §9 across the arc — no overflow into an unplanned "v2".**
|
|
86
|
+
- **§10 (full only) defines the arc's endpoint → the roadmapper builds milestones until the whole capability set reaches this done-state.**
|
|
87
|
+
- §11 (full only) seeds JOURNEY.md milestone order + "why now" lines (no milestone cap — the arc is as long as §9 requires).
|
|
88
|
+
- §12–§15 (full only) feed research scoping, integrations, and the Handoff milestone checklist.
|
|
89
|
+
|
|
90
|
+
Demo projects skip §9–§15 because they ARE one milestone — the journey is just that milestone plus an implicit "client signs, we extend" branch handled by `/qualia-milestone`.
|
|
@@ -75,17 +75,17 @@ Fixed scope for every project. Do not reassign these elsewhere.
|
|
|
75
75
|
|
|
76
76
|
## Post-Handoff (v2)
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
**Only** capabilities the client EXPLICITLY deferred in discovery §8 (Out of Scope) belong here. This is NOT an overflow bucket for a milestone cap — there is no cap; everything in the capability inventory (discovery §9) must be a REQ-ID mapped to a milestone above. If a capability is agreed but absent from the arc, that's a roadmap bug, not a v2 item.
|
|
79
79
|
|
|
80
80
|
### {Category}
|
|
81
81
|
|
|
82
|
-
- **{CAT}-XX**: {capability}
|
|
82
|
+
- **{CAT}-XX**: {capability} — deferred by client (discovery §8)
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
|
|
86
86
|
## Out of Scope
|
|
87
87
|
|
|
88
|
-
Explicit exclusions with reasoning. Prevents scope creep.
|
|
88
|
+
Explicit exclusions with reasoning, drawn from discovery §8. Prevents scope creep.
|
|
89
89
|
|
|
90
90
|
| Feature | Reason |
|
|
91
91
|
|---------|--------|
|
|
@@ -95,16 +95,16 @@ Explicit exclusions with reasoning. Prevents scope creep.
|
|
|
95
95
|
|
|
96
96
|
## Traceability
|
|
97
97
|
|
|
98
|
-
Populated during roadmap creation. Every
|
|
98
|
+
Populated during roadmap creation. Every capability from discovery §9 maps to exactly one milestone + phase. Coverage of the full inventory — not a v1 slice — is the gate.
|
|
99
99
|
|
|
100
100
|
| Requirement | Milestone | Phase | Status |
|
|
101
101
|
|-------------|-----------|-------|--------|
|
|
102
102
|
| {CAT}-01 | M1: {name} | Phase {N} | Pending |
|
|
103
103
|
|
|
104
|
-
**Coverage:**
|
|
105
|
-
-
|
|
104
|
+
**Coverage (must be 100% of the §9 capability inventory):**
|
|
105
|
+
- Agreed capabilities (discovery §9, whole project): {X} total
|
|
106
106
|
- Mapped to milestones + phases: {Y}
|
|
107
|
-
- Unmapped: {Z}
|
|
107
|
+
- Unmapped: {Z} ← MUST be 0 before the journey-approval gate passes
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
@@ -114,6 +114,21 @@ assert_exit "BLOCKED task holds barrier" 1 $RC
|
|
|
114
114
|
assert_contains "barrier json counts blocked" "$OUT" '"blocked": 1'
|
|
115
115
|
rm -rf "$TMP"
|
|
116
116
|
|
|
117
|
+
# --- barrier --tasks (explicit batch gate, no contract needed; R16 wave-plan) ---
|
|
118
|
+
TMP=$(mktemp -d)
|
|
119
|
+
$NODE "$AS" write T1 DONE --commit a --cwd "$TMP" >/dev/null 2>&1
|
|
120
|
+
$NODE "$AS" write T2 RUNNING --cwd "$TMP" >/dev/null 2>&1
|
|
121
|
+
$NODE "$AS" write T5 DONE --commit b --cwd "$TMP" >/dev/null 2>&1
|
|
122
|
+
# batch {T1,T5} both DONE → pass, with no contract file present
|
|
123
|
+
$NODE "$AS" barrier --tasks T1,T5 --cwd "$TMP" >/dev/null 2>&1
|
|
124
|
+
assert_exit "barrier --tasks all DONE → pass (no contract)" 0 $?
|
|
125
|
+
# batch {T1,T2} → T2 RUNNING → hold
|
|
126
|
+
$NODE "$AS" barrier --tasks T1,T2 --cwd "$TMP" >/dev/null 2>&1
|
|
127
|
+
assert_exit "barrier --tasks with a RUNNING member → hold" 1 $?
|
|
128
|
+
OUT=$($NODE "$AS" barrier --tasks T1,T2 --cwd "$TMP" 2>&1)
|
|
129
|
+
assert_contains "barrier --tasks scope label" "$OUT" "batch T1,T2"
|
|
130
|
+
rm -rf "$TMP"
|
|
131
|
+
|
|
117
132
|
# --- list + clear ---
|
|
118
133
|
TMP=$(mktemp -d)
|
|
119
134
|
$NODE "$AS" write T1 DONE --cwd "$TMP" >/dev/null 2>&1
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# branch-hygiene.test.sh — bin/branch-hygiene.js (clock-out stranded-branch sweep)
|
|
3
|
+
# Run: bash tests/branch-hygiene.test.sh
|
|
4
|
+
|
|
5
|
+
PASS=0
|
|
6
|
+
FAIL=0
|
|
7
|
+
BIN_DIR="$(cd "$(dirname "$0")/../bin" && pwd)"
|
|
8
|
+
NODE="${NODE:-node}"
|
|
9
|
+
BH="$BIN_DIR/branch-hygiene.js"
|
|
10
|
+
|
|
11
|
+
assert_exit() {
|
|
12
|
+
local name="$1" expected="$2" actual="$3"
|
|
13
|
+
if [ "$expected" = "$actual" ]; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
14
|
+
else echo " ✗ $name (expected exit $expected, got $actual)"; FAIL=$((FAIL+1)); fi
|
|
15
|
+
}
|
|
16
|
+
assert_contains() {
|
|
17
|
+
local name="$1" hay="$2" needle="$3"
|
|
18
|
+
if echo "$hay" | grep -qF "$needle"; then echo " ✓ $name"; PASS=$((PASS+1));
|
|
19
|
+
else echo " ✗ $name (missing '$needle' in: $hay)"; FAIL=$((FAIL+1)); fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# fresh repo on main with one commit; prints the dir (caller rm -rf)
|
|
23
|
+
setup_repo() {
|
|
24
|
+
local tmp
|
|
25
|
+
tmp=$(mktemp -d)
|
|
26
|
+
(cd "$tmp" \
|
|
27
|
+
&& git init -q \
|
|
28
|
+
&& git checkout -q -b main 2>/dev/null \
|
|
29
|
+
&& git config user.email t@t.com && git config user.name T \
|
|
30
|
+
&& echo seed > seed.txt && git add seed.txt && git commit -q -m seed)
|
|
31
|
+
echo "$tmp"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
echo "branch-hygiene.test.sh — bin/branch-hygiene.js"
|
|
35
|
+
echo ""
|
|
36
|
+
|
|
37
|
+
$NODE -c "$BH" 2>/dev/null && { echo " ✓ syntax valid"; PASS=$((PASS+1)); } || { echo " ✗ syntax invalid"; FAIL=$((FAIL+1)); }
|
|
38
|
+
|
|
39
|
+
# --- not a git repo → exit 2 ---
|
|
40
|
+
TMP=$(mktemp -d)
|
|
41
|
+
(cd "$TMP" && $NODE "$BH" >/dev/null 2>&1)
|
|
42
|
+
assert_exit "not a git repo → exit 2" 2 $?
|
|
43
|
+
rm -rf "$TMP"
|
|
44
|
+
|
|
45
|
+
# --- clean: only main → exit 0 ---
|
|
46
|
+
TMP=$(setup_repo)
|
|
47
|
+
(cd "$TMP" && $NODE "$BH" >/dev/null 2>&1)
|
|
48
|
+
assert_exit "clean repo (main only) → exit 0" 0 $?
|
|
49
|
+
OUT=$(cd "$TMP" && $NODE "$BH" 2>&1)
|
|
50
|
+
assert_contains "reports clean" "$OUT" "clean"
|
|
51
|
+
rm -rf "$TMP"
|
|
52
|
+
|
|
53
|
+
# --- stranded feature branch ahead of main → exit 1, listed ---
|
|
54
|
+
TMP=$(setup_repo)
|
|
55
|
+
(cd "$TMP" && git checkout -q -b feat/stranded && echo work > w.txt && git add w.txt && git commit -q -m "wip work")
|
|
56
|
+
(cd "$TMP" && $NODE "$BH" >/dev/null 2>&1)
|
|
57
|
+
assert_exit "branch ahead of main → exit 1" 1 $?
|
|
58
|
+
OUT=$(cd "$TMP" && $NODE "$BH" 2>&1)
|
|
59
|
+
assert_contains "lists the stranded branch" "$OUT" "feat/stranded"
|
|
60
|
+
assert_contains "shows commits ahead" "$OUT" "+1 commit"
|
|
61
|
+
# json shape
|
|
62
|
+
OUT=$(cd "$TMP" && $NODE "$BH" --json 2>&1)
|
|
63
|
+
assert_contains "json stranded entry" "$OUT" '"branch": "feat/stranded"'
|
|
64
|
+
assert_contains "json ahead count" "$OUT" '"ahead": 1'
|
|
65
|
+
rm -rf "$TMP"
|
|
66
|
+
|
|
67
|
+
# --- once integrated (ff-merged) into main → no longer stranded → exit 0 ---
|
|
68
|
+
TMP=$(setup_repo)
|
|
69
|
+
(cd "$TMP" && git checkout -q -b feat/done && echo x > x.txt && git add x.txt && git commit -q -m "done work")
|
|
70
|
+
(cd "$TMP" && git checkout -q main && git merge -q --ff-only feat/done)
|
|
71
|
+
(cd "$TMP" && $NODE "$BH" >/dev/null 2>&1)
|
|
72
|
+
assert_exit "ff-merged branch no longer stranded → exit 0" 0 $?
|
|
73
|
+
rm -rf "$TMP"
|
|
74
|
+
|
|
75
|
+
# --- master as the base branch is detected ---
|
|
76
|
+
TMP=$(mktemp -d)
|
|
77
|
+
(cd "$TMP" && git init -q && git checkout -q -b master 2>/dev/null && git config user.email t@t.com && git config user.name T && echo s > s.txt && git add s.txt && git commit -q -m s)
|
|
78
|
+
(cd "$TMP" && git checkout -q -b feature && echo y > y.txt && git add y.txt && git commit -q -m y)
|
|
79
|
+
OUT=$(cd "$TMP" && $NODE "$BH" --json 2>&1)
|
|
80
|
+
assert_contains "detects master as base" "$OUT" '"base": "master"'
|
|
81
|
+
assert_contains "stranded vs master" "$OUT" '"branch": "feature"'
|
|
82
|
+
rm -rf "$TMP"
|
|
83
|
+
|
|
84
|
+
# --- library: analyze() returns structured result ---
|
|
85
|
+
TMP=$(setup_repo)
|
|
86
|
+
(cd "$TMP" && git checkout -q -b b1 && echo a>a && git add a && git commit -q -m a)
|
|
87
|
+
RES=$($NODE -e "console.log(JSON.stringify(require('$BH').analyze('$TMP').stranded.length))" 2>&1)
|
|
88
|
+
assert_contains "analyze() finds 1 stranded" "$RES" "1"
|
|
89
|
+
rm -rf "$TMP"
|
|
90
|
+
|
|
91
|
+
echo ""
|
|
92
|
+
echo "=== Results: $PASS passed, $FAIL failed ==="
|
|
93
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|