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.
Files changed (50) hide show
  1. package/AGENTS.md +8 -5
  2. package/CHANGELOG.md +130 -0
  3. package/CLAUDE.md +3 -1
  4. package/agents/roadmapper.md +16 -14
  5. package/bin/agent-status.js +24 -11
  6. package/bin/branch-hygiene.js +135 -0
  7. package/bin/command-surface.js +1 -0
  8. package/bin/compile-instructions.js +82 -0
  9. package/bin/eval-runner.js +218 -0
  10. package/bin/host-adapters.js +72 -12
  11. package/bin/install.js +21 -13
  12. package/bin/last-report.js +207 -0
  13. package/bin/project-sync.js +315 -0
  14. package/bin/runtime-manifest.js +6 -0
  15. package/bin/state.js +112 -1
  16. package/bin/verify-panel.js +294 -0
  17. package/bin/wave-plan.js +211 -0
  18. package/docs/erp-contract.md +145 -0
  19. package/package.json +3 -2
  20. package/rules/codex-goal.md +28 -26
  21. package/rules/infrastructure.md +1 -1
  22. package/skills/qualia/SKILL.md +6 -0
  23. package/skills/qualia-build/SKILL.md +12 -9
  24. package/skills/qualia-eval/SKILL.md +83 -0
  25. package/skills/qualia-feature/SKILL.md +20 -4
  26. package/skills/qualia-fix/SKILL.md +13 -1
  27. package/skills/qualia-milestone/SKILL.md +12 -6
  28. package/skills/qualia-new/REFERENCE.md +6 -4
  29. package/skills/qualia-new/SKILL.md +27 -15
  30. package/skills/qualia-plan/SKILL.md +2 -2
  31. package/skills/qualia-report/SKILL.md +10 -0
  32. package/skills/qualia-scope/SKILL.md +3 -3
  33. package/skills/qualia-ship/SKILL.md +34 -4
  34. package/skills/qualia-update/SKILL.md +4 -0
  35. package/skills/qualia-verify/SKILL.md +45 -24
  36. package/templates/instructions.md +32 -0
  37. package/templates/journey.md +1 -1
  38. package/templates/project-discovery.md +30 -23
  39. package/templates/requirements.md +7 -7
  40. package/tests/agent-status.test.sh +15 -0
  41. package/tests/branch-hygiene.test.sh +93 -0
  42. package/tests/eval-runner.test.sh +147 -0
  43. package/tests/instructions.test.sh +109 -0
  44. package/tests/last-report.test.sh +156 -0
  45. package/tests/lib.test.sh +2 -2
  46. package/tests/project-sync.test.sh +175 -0
  47. package/tests/run-all.sh +7 -0
  48. package/tests/state.test.sh +92 -0
  49. package/tests/verify-panel.test.sh +162 -0
  50. 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. Git
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
- git push
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
- Employee stays on feature branch. Never push to main.
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
- ### 4. Deploy
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 — fresh verifier agent greps code against acceptance criteria, scores design rubric, optional adversarial second pass. Triggers: 'verify this phase', 'check if it works', 'run verification', 'did the build pass'."
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` — second verifier in fresh context with adversarial prompt. Union findings. Recommended for high-stakes phases (Handoff, payment/auth/migration).
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 (Fresh Context)
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. Severity Rubric for all labels. Output: .planning/phase-{N}-verification.md
65
- ", subagent_type="qualia-verifier", description="Verify phase {N}")
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. Adversarial Second Opinion (--adversarial flag, optional)
106
+ ### 3c. Skeptic Pass + Deterministic Aggregation
93
107
 
94
- `--adversarial` in args, OR milestone is `Handoff`, OR plan touches `auth|payment|migration|rls|service_role` spawn SECOND verifier in fresh context. Mitigates self-grading bias (~70% fewer findings without adversarial pass).
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}/qualia-ui.js spawn verifier "Adversarial pass — find what's wrong"
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
- ADVERSARIAL reviewer. Find what's WRONG. Assume cooperative verifier missed something. Same Severity Rubric + evidence-citation req. Bias toward edge cases:
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
- Project: @.planning/PROJECT.md
112
- Plan: @.planning/phase-{N}-plan.md
113
- Cooperative report (find what they MISSED): @.planning/phase-{N}-verification.md
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
- Append '## Adversarial Findings' to verification file. Empty section fine if nothing found.
116
- ", subagent_type="qualia-verifier", description="Adversarial verify phase {N}")
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
- Findings merge into main report. Union PASS/FAIL: either pass found CRITICAL/HIGH → phase FAIL.
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 after the adversarial pass if it executed.
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
- If the eval status is `FAIL` or anti-slop exits non-zero, do not mark the phase PASS. 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`.
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-->
@@ -100,7 +100,7 @@ M1 ─── M2 ─── M3 ─── ... ─── M{N} (Handoff)
100
100
 
101
101
  ## Rules for This Journey
102
102
 
103
- 1. **Hard ceiling: 5 milestones.** If the project needs more, defer to a v2 release after handoff.
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 constraints in the user's own words.
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: 14 questions.
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 that is non-negotiable: stack, deadline, compliance, integrations, budget.}
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 it would be obvious to add.}
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 six questions only run for `project_type: full`. Demo mode stops here.
47
+ The remaining questions only run for `project_type: full`. Demo mode stops here.
48
48
 
49
- ## 9. Milestone arc, in the client's words
49
+ ## 9. Capability inventory the WHOLE project
50
50
 
51
- > {After the demo, what's the next chapter? After that, what's the chapter after? Stop at three to five chapters total. The last chapter is always Handoff.}
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. Compliance and legal
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
- ## 11. Integrations
65
+ ## 13. Integrations
58
66
 
59
67
  > {Third-party systems this must talk to, in priority order.}
60
68
 
61
- ## 12. Content and copy
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
- ## 13. Team and roles after handoff
73
+ ## 15. Ownership, team, and delivery shape
66
74
 
67
- > {Who maintains this after we ship? What can they do, what can't they do?}
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
- - §15 seed PROJECT.md (one-line pitch, what we're building) and PRODUCT.md (users, register, voice, anti-references).
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-§8 populate PROJECT.md's "Out of Scope" and the constraints section.
80
- - §9 (full only) seeds JOURNEY.md milestone names + "why now" lines.
81
- - §10-§14 (full only) feed research scoping and the Handoff milestone checklist.
82
-
83
- Demo projects skip §9-§14 because they ARE one milestone the journey is just that milestone plus an implicit "client signs, we extend" branch handled by `/qualia-milestone`.
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
- Features acknowledged but deferred past initial handoff. Not in current journey.
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 v1 requirement maps to exactly one milestone + phase.
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
- - v1 requirements (all feature milestones + Handoff): {X} total
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