qualia-framework 6.9.2 → 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 (64) hide show
  1. package/AGENTS.md +8 -5
  2. package/CHANGELOG.md +208 -0
  3. package/CLAUDE.md +3 -1
  4. package/agents/roadmapper.md +16 -14
  5. package/agents/verifier.md +1 -1
  6. package/bin/agent-status.js +264 -0
  7. package/bin/analyze-gate.js +318 -0
  8. package/bin/branch-hygiene.js +135 -0
  9. package/bin/command-surface.js +2 -0
  10. package/bin/compile-instructions.js +82 -0
  11. package/bin/eval-runner.js +218 -0
  12. package/bin/host-adapters.js +72 -12
  13. package/bin/install.js +27 -17
  14. package/bin/last-report.js +207 -0
  15. package/bin/project-sync.js +315 -0
  16. package/bin/report-payload.js +7 -0
  17. package/bin/runtime-manifest.js +8 -0
  18. package/bin/state.js +257 -12
  19. package/bin/verify-panel.js +294 -0
  20. package/bin/wave-plan.js +211 -0
  21. package/docs/EMPLOYEE-QUICKSTART.md +3 -3
  22. package/docs/erp-contract.md +168 -0
  23. package/docs/qualia-manual.html +5 -5
  24. package/hooks/branch-guard.js +133 -63
  25. package/hooks/pre-deploy-gate.js +38 -0
  26. package/hooks/task-write-guard.js +165 -0
  27. package/package.json +3 -2
  28. package/rules/codex-goal.md +28 -26
  29. package/rules/infrastructure.md +1 -1
  30. package/skills/qualia/SKILL.md +6 -0
  31. package/skills/qualia-build/SKILL.md +39 -7
  32. package/skills/qualia-eval/SKILL.md +83 -0
  33. package/skills/qualia-feature/SKILL.md +20 -4
  34. package/skills/qualia-fix/SKILL.md +13 -1
  35. package/skills/qualia-milestone/SKILL.md +12 -6
  36. package/skills/qualia-new/REFERENCE.md +6 -4
  37. package/skills/qualia-new/SKILL.md +27 -15
  38. package/skills/qualia-plan/SKILL.md +2 -2
  39. package/skills/qualia-report/SKILL.md +10 -0
  40. package/skills/qualia-scope/SKILL.md +3 -3
  41. package/skills/qualia-ship/SKILL.md +37 -4
  42. package/skills/qualia-update/SKILL.md +100 -0
  43. package/skills/qualia-verify/SKILL.md +51 -24
  44. package/templates/instructions.md +32 -0
  45. package/templates/journey.md +2 -2
  46. package/templates/project-discovery.md +30 -23
  47. package/templates/requirements.md +7 -7
  48. package/tests/agent-status.test.sh +153 -0
  49. package/tests/analyze-gate.test.sh +170 -0
  50. package/tests/bin.test.sh +5 -4
  51. package/tests/branch-hygiene.test.sh +93 -0
  52. package/tests/eval-runner.test.sh +147 -0
  53. package/tests/hooks.test.sh +218 -17
  54. package/tests/install-smoke.test.sh +4 -3
  55. package/tests/instructions.test.sh +109 -0
  56. package/tests/last-report.test.sh +156 -0
  57. package/tests/lib.test.sh +2 -2
  58. package/tests/project-sync.test.sh +175 -0
  59. package/tests/run-all.sh +9 -0
  60. package/tests/runner.js +3 -2
  61. package/tests/state.test.sh +187 -0
  62. package/tests/verify-panel.test.sh +162 -0
  63. package/tests/wave-plan.test.sh +153 -0
  64. package/skills/qualia-discuss/SKILL.md +0 -222
@@ -0,0 +1,153 @@
1
+ #!/bin/bash
2
+ # wave-plan.test.sh — bin/wave-plan.js (dependency-derived build schedule, R16)
3
+ # Run: bash tests/wave-plan.test.sh
4
+
5
+ PASS=0
6
+ FAIL=0
7
+ BIN_DIR="$(cd "$(dirname "$0")/../bin" && pwd)"
8
+ NODE="${NODE:-node}"
9
+ WP="$BIN_DIR/wave-plan.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
+ assert_eq() {
22
+ local name="$1" expected="$2" actual="$3"
23
+ if [ "$expected" = "$actual" ]; then echo " ✓ $name"; PASS=$((PASS+1));
24
+ else echo " ✗ $name (expected '$expected', got '$actual')"; FAIL=$((FAIL+1)); fi
25
+ }
26
+
27
+ echo "wave-plan.test.sh — bin/wave-plan.js"
28
+ echo ""
29
+
30
+ $NODE -c "$WP" 2>/dev/null && { echo " ✓ syntax valid"; PASS=$((PASS+1)); } || { echo " ✗ syntax invalid"; FAIL=$((FAIL+1)); }
31
+
32
+ # --- chain DAG: T1→T2→T3 → 3 sequential waves of 1 ---
33
+ TMP=$(mktemp -d)
34
+ cat > "$TMP/c.json" <<'EOF'
35
+ { "phase": 1, "tasks": [
36
+ {"id":"T1","wave":1,"depends_on":[]},
37
+ {"id":"T2","wave":2,"depends_on":["T1"]},
38
+ {"id":"T3","wave":3,"depends_on":["T2"]}
39
+ ] }
40
+ EOF
41
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
42
+ assert_exit "chain → exit 0" 0 $?
43
+ assert_contains "chain has 3 batches" "$OUT" '"batch_count": 3'
44
+ rm -rf "$TMP"
45
+
46
+ # --- 6 independent tasks, auto cap 5 → 2 batches (5 + 1), all wave 1 ---
47
+ TMP=$(mktemp -d)
48
+ cat > "$TMP/c.json" <<'EOF'
49
+ { "phase": 2, "tasks": [
50
+ {"id":"T1","wave":1,"depends_on":[]},
51
+ {"id":"T2","wave":1,"depends_on":[]},
52
+ {"id":"T3","wave":1,"depends_on":[]},
53
+ {"id":"T4","wave":1,"depends_on":[]},
54
+ {"id":"T5","wave":1,"depends_on":[]},
55
+ {"id":"T6","wave":1,"depends_on":[]}
56
+ ] }
57
+ EOF
58
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
59
+ assert_contains "6 independent → max_concurrency 5" "$OUT" '"max_concurrency": 5'
60
+ assert_contains "6 independent → 2 batches" "$OUT" '"batch_count": 2'
61
+ assert_contains "6 independent → 1 derived level" "$OUT" '"derived_levels": 1'
62
+ assert_contains "wide-level note emitted" "$OUT" "capped to batches of 5"
63
+ rm -rf "$TMP"
64
+
65
+ # --- tiny phase (<3 tasks) → auto sequential (cap 1) ---
66
+ TMP=$(mktemp -d)
67
+ cat > "$TMP/c.json" <<'EOF'
68
+ { "phase": 3, "tasks": [
69
+ {"id":"T1","wave":1,"depends_on":[]},
70
+ {"id":"T2","wave":1,"depends_on":[]}
71
+ ] }
72
+ EOF
73
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
74
+ assert_contains "2 tasks → cap 1 (sequential)" "$OUT" '"max_concurrency": 1'
75
+ assert_contains "2 tasks → 2 batches" "$OUT" '"batch_count": 2'
76
+ rm -rf "$TMP"
77
+
78
+ # --- --parallel override ---
79
+ TMP=$(mktemp -d)
80
+ cat > "$TMP/c.json" <<'EOF'
81
+ { "phase": 4, "tasks": [
82
+ {"id":"T1","wave":1,"depends_on":[]},
83
+ {"id":"T2","wave":1,"depends_on":[]},
84
+ {"id":"T3","wave":1,"depends_on":[]},
85
+ {"id":"T4","wave":1,"depends_on":[]}
86
+ ] }
87
+ EOF
88
+ OUT=$($NODE "$WP" "$TMP/c.json" --parallel 2 --json 2>&1)
89
+ assert_contains "--parallel 2 sets cap" "$OUT" '"max_concurrency": 2'
90
+ assert_contains "--parallel 2 → 2 batches of 2" "$OUT" '"batch_count": 2'
91
+ # invalid --parallel
92
+ $NODE "$WP" "$TMP/c.json" --parallel 0 >/dev/null 2>&1
93
+ assert_exit "--parallel 0 → invocation error (exit 2)" 2 $?
94
+ rm -rf "$TMP"
95
+
96
+ # --- over-serialization: independent task declared in a deeper wave ---
97
+ TMP=$(mktemp -d)
98
+ cat > "$TMP/c.json" <<'EOF'
99
+ { "phase": 5, "tasks": [
100
+ {"id":"T1","wave":1,"depends_on":[]},
101
+ {"id":"T2","wave":2,"depends_on":[]}
102
+ ] }
103
+ EOF
104
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
105
+ assert_contains "flags over-serialization" "$OUT" "deeper wave than the dependency graph requires"
106
+ # both are actually level 1 (no deps) → 1 derived level
107
+ assert_contains "both tasks derive to one level" "$OUT" '"derived_levels": 1'
108
+ rm -rf "$TMP"
109
+
110
+ # --- diamond DAG: T1; T2,T3 dep T1; T4 dep T2,T3 → 3 levels ---
111
+ TMP=$(mktemp -d)
112
+ cat > "$TMP/c.json" <<'EOF'
113
+ { "phase": 6, "tasks": [
114
+ {"id":"T1","wave":1,"depends_on":[]},
115
+ {"id":"T2","wave":2,"depends_on":["T1"]},
116
+ {"id":"T3","wave":2,"depends_on":["T1"]},
117
+ {"id":"T4","wave":3,"depends_on":["T2","T3"]}
118
+ ] }
119
+ EOF
120
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
121
+ assert_contains "diamond → 3 derived levels" "$OUT" '"derived_levels": 3'
122
+ # middle level T2,T3 share a batch
123
+ assert_contains "diamond middle batch has T2+T3" "$OUT" '"T2",'
124
+ rm -rf "$TMP"
125
+
126
+ # --- cycle → exit 1 ---
127
+ TMP=$(mktemp -d)
128
+ cat > "$TMP/c.json" <<'EOF'
129
+ { "phase": 7, "tasks": [
130
+ {"id":"T1","wave":1,"depends_on":["T2"]},
131
+ {"id":"T2","wave":1,"depends_on":["T1"]}
132
+ ] }
133
+ EOF
134
+ $NODE "$WP" "$TMP/c.json" >/dev/null 2>&1
135
+ assert_exit "cycle → exit 1" 1 $?
136
+ OUT=$($NODE "$WP" "$TMP/c.json" --json 2>&1)
137
+ assert_contains "cycle reported" "$OUT" '"error": "CYCLE"'
138
+ rm -rf "$TMP"
139
+
140
+ # --- library: resolveConcurrency + deriveLevels units ---
141
+ assert_eq "resolveConcurrency(2,auto)=1" "1" "$($NODE -e "console.log(require('$WP').resolveConcurrency(2))")"
142
+ assert_eq "resolveConcurrency(5,auto)=5" "5" "$($NODE -e "console.log(require('$WP').resolveConcurrency(5))")"
143
+ assert_eq "resolveConcurrency(99,parallel=3)=3" "3" "$($NODE -e "console.log(require('$WP').resolveConcurrency(99,3))")"
144
+ LV=$($NODE -e "const w=require('$WP'); const r=w.deriveLevels([{id:'T1',depends_on:[]},{id:'T2',depends_on:['T1']}]); console.log(r.levels.get('T2'))" 2>&1)
145
+ assert_eq "deriveLevels T2 after T1 = 2" "2" "$LV"
146
+
147
+ # --- missing contract → exit 2 ---
148
+ $NODE "$WP" /nonexistent.json >/dev/null 2>&1
149
+ assert_exit "missing contract → exit 2" 2 $?
150
+
151
+ echo ""
152
+ echo "=== Results: $PASS passed, $FAIL failed ==="
153
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1
@@ -1,222 +0,0 @@
1
- ---
2
- name: qualia-discuss
3
- description: "Alignment interview — PROJECT MODE before /qualia-new, PHASE MODE before /qualia-plan N. Triggers: 'discuss', 'kickoff interview', 'grill me', 'stress test this plan', 'I'm not sure how to approach this'."
4
- allowed-tools:
5
- - Bash
6
- - Read
7
- - Write
8
- - Edit
9
- - Grep
10
- - Glob
11
- - AskUserQuestion
12
- ---
13
-
14
- > **RETIRED (v6.7, A4 collapse).** Folded into `/qualia-scope`: PROJECT MODE = this kickoff interview, PHASE MODE = the pre-plan grill, picked by qualia-scope's mode router. This file is no longer installed as a slash command. Kept for history.
15
-
16
- # /qualia-discuss — Alignment Interview
17
-
18
- Two modes. The skill picks one based on whether an arg is passed.
19
-
20
- | Arg | Mode | When | Output |
21
- |-----|------|------|--------|
22
- | (none) | **PROJECT MODE** | Kickoff, before `/qualia-new`. Non-technical, audience-facing. | `.planning/project-discovery.md` |
23
- | `N` (e.g. `2`) | **PHASE MODE** | Before `/qualia-plan N`. Technical, aggressive grilling. | `.planning/phase-{N}-context.md` |
24
-
25
- ## Mode routing
26
-
27
- ```bash
28
- if [ -z "$1" ] || ! [[ "$1" =~ ^[0-9]+$ ]]; then
29
- MODE="project"
30
- else
31
- MODE="phase"
32
- PHASE_N="$1"
33
- fi
34
- ```
35
-
36
- If `MODE=project` → jump to **PROJECT MODE** section below.
37
- If `MODE=phase` → jump to **PHASE MODE** section below.
38
-
39
- ---
40
-
41
- # PROJECT MODE — Kickoff Interview (no args)
42
-
43
- The non-technical conversation that runs at the very start of `/qualia-new`, BEFORE any roadmapping or research. Captures what the client wants, who it's for, brand voice, and constraints — in the client's own words.
44
-
45
- Hard rule: **never go technical here.** No "Should we use Supabase or Postgres?". The technical decisions happen in PHASE MODE, per phase. This mode is for the human shape of the project.
46
-
47
- ## When PROJECT MODE runs
48
-
49
- - Triggered automatically by `/qualia-new` after Step 0 banner, BEFORE journey generation.
50
- - Or invoked manually (e.g. on a brownfield project that already has code but no discovery doc).
51
-
52
- ## Process — PROJECT MODE
53
-
54
- ### P1. Detect project type (or accept it from `/qualia-new`)
55
-
56
- If `/qualia-new` already asked the Demo vs Full gate (it does this as Step 1, the literal first question), it passes the type in as `PROJECT_TYPE=demo` or `PROJECT_TYPE=full` via env or arg — **skip the gate, do not re-ask.**
57
-
58
- Only ask the gate yourself when invoked standalone (not via `/qualia-new`). When you do, use **AskUserQuestion** (interactive UI — never plain text):
59
-
60
- - header: "Project shape"
61
- - question: "Is this a demo (single shippable milestone, sales conversation) or a full project (multi-milestone arc to Handoff)?"
62
- - options: ["Demo", "Full project"]
63
-
64
- This is the only fork. Demo runs §1-§8 of the discovery template. Full project runs all 14 questions.
65
-
66
- ### P2. Banner and open
67
-
68
- ```bash
69
- node ${QUALIA_BIN}/qualia-ui.js banner discuss-project
70
- ```
71
-
72
- Say: **"Eight quick questions for the demo path"** or **"Fourteen questions to shape the full project — we'll move fast"** depending on type.
73
-
74
- ### P3. One question at a time, copy from `templates/project-discovery.md`
75
-
76
- For each question §1..§8 (demo) or §1..§14 (full), ask in plain language. Format:
77
-
78
- ```
79
- **Question {N}/{total}:** {question text from template}
80
-
81
- ({one-line clarifier showing the kind of answer that helps)
82
- ```
83
-
84
- NO "my recommendation" line in PROJECT MODE — this is open discovery, not technical grilling. Wait for the user's answer. Don't paraphrase back; capture verbatim. If the answer is too thin, ask one follow-up max, then move on.
85
-
86
- Allowed `[Enter]` defaults exist on §2, §3, §5 only (inferred from project type). All other questions require an answer.
87
-
88
- ### P4. Write `.planning/project-discovery.md`
89
-
90
- Fill the template at `${QUALIA_TEMPLATES}/project-discovery.md` with the user's verbatim answers. Set frontmatter `project_type` and `discovered_at`.
91
-
92
- ### P5. Hand back to `/qualia-new`
93
-
94
- ```bash
95
- git add .planning/project-discovery.md
96
- git commit -m "docs: project discovery interview ($PROJECT_TYPE)"
97
- node ${QUALIA_BIN}/qualia-ui.js ok "Discovery captured — back to /qualia-new"
98
- ```
99
-
100
- If invoked standalone (not from `/qualia-new`), end with:
101
-
102
- ```bash
103
- node ${QUALIA_BIN}/qualia-ui.js end "DISCOVERY CAPTURED" "/qualia-new"
104
- ```
105
-
106
- If invoked inline by `/qualia-new`, return control silently — `/qualia-new` continues from Step 2.
107
-
108
- ## Rules — PROJECT MODE
109
-
110
- 1. **Never technical.** No stack questions, no architecture forks. Those are PHASE MODE.
111
- 2. **Verbatim capture.** Don't translate the client's words into framework-speak. The client's exact phrasing is the input to PRODUCT.md voice and CONTEXT.md glossary.
112
- 3. **One question, one answer, move on.** No batching. No drilling deeper than one follow-up.
113
- 4. **Demo stops at §8.** Don't ask demo clients milestone-arc questions — they don't have an arc yet.
114
- 5. **Anti-references are required.** If the user can't name three sites this should NOT look like, the design will end up generic. Push back once, then capture whatever they give you.
115
-
116
- ---
117
-
118
- # PHASE MODE — Pre-Plan Grilling (`/qualia-discuss N`)
119
-
120
- Surface and lock the decisions, trade-offs, and constraints that must inform a phase plan. Output: `.planning/phase-{N}-context.md` (locked input). Side effects: `.planning/CONTEXT.md` gains new terms; `.planning/decisions/` may gain ADRs.
121
-
122
- ## When PHASE MODE runs
123
- - Regulated domains (legal, medical, financial) where wrong choices have legal cost
124
- - Phases with architectural forks ("auth via middleware or RLS?")
125
- - Phases with external dependencies you want to lock first
126
- - User says "wait, let's think about this one"
127
-
128
- ## The four grilling rules (PHASE MODE)
129
-
130
- 1. **One question at a time.** Wait for the answer before asking the next. Never batch.
131
- 2. **Propose your recommended answer first.** Format every question as `Question / Recommendation / Trade-offs`. The user accepts, edits, or rejects — way faster than open interview.
132
- 3. **If the codebase can answer, explore instead of asking.** Don't make the user say what `git grep` could tell you.
133
- 4. **Walk every branch.** When the user picks A over B, the next question is the one that A makes load-bearing. Resolve dependencies one-by-one until the tree is fully traversed.
134
-
135
- ## Process — PHASE MODE
136
-
137
- ### 1. Load substrate
138
-
139
- ```bash
140
- node ${QUALIA_BIN}/state.js check 2>/dev/null
141
- cat .planning/PROJECT.md .planning/ROADMAP.md .planning/CONTEXT.md 2>/dev/null
142
- ls .planning/decisions/ 2>/dev/null
143
- cat .planning/research/SUMMARY.md 2>/dev/null
144
- ```
145
-
146
- If `.planning/CONTEXT.md` is missing, copy `${QUALIA_TEMPLATES}/CONTEXT.md` to `.planning/CONTEXT.md` first.
147
- If `.planning/decisions/` is missing, create it. Copy `${QUALIA_TEMPLATES}/decisions/ADR-template.md` next to it for reference.
148
-
149
- ### 2. Open the conversation
150
-
151
- ```bash
152
- node ${QUALIA_BIN}/qualia-ui.js banner discuss {N} "{phase name from ROADMAP.md}"
153
- ```
154
-
155
- Then state the goal in one sentence and the open questions you found in priority order (highest-stakes / hardest-to-reverse first).
156
-
157
- ### 3. Grill, one question at a time
158
-
159
- Format:
160
-
161
- ```
162
- **Question {N}/{total}:** {the question}
163
-
164
- **My recommendation:** {your proposed answer + 1-sentence why}
165
-
166
- **Trade-offs:** {what gets harder if we go this way}
167
- ```
168
-
169
- Wait for response. Then:
170
- - Update `.planning/CONTEXT.md` inline if a term crystallized (add definition + `Avoid:` line for rejected synonyms)
171
- - Write an ADR in `.planning/decisions/ADR-{NNNN}-{slug}.md` ONLY when the decision is hard-to-reverse, surprising-without-context, AND involves real trade-offs (use the template — keep it scarce)
172
- - Drill deeper if the answer opens new branches
173
-
174
- ### 4. Build the locked-decisions list (with IDs — machine-parseable downstream)
175
-
176
- For each resolved decision, capture as a row with a stable ID `D-NN` (zero-padded, sequential within the phase). The planner's Decision Coverage Audit checks every `D-NN` is implemented; the plan-checker BLOCKS if any is missing.
177
-
178
- **Locked Decision row format:**
179
-
180
- | ID | Decision | Rationale | Source |
181
- |----|----------|-----------|--------|
182
- | D-01 | Use Supabase RLS for authorization, not middleware | Compliance requires database-level checks | discuss session 2026-05-09 |
183
- | D-02 | Store session tokens server-side, not in JWT | OWASP guidance + revocation requirement | ADR-0007 |
184
-
185
- Also track:
186
- - **Discretion items** — planner decides based on best practice (no IDs needed).
187
- - **Deferred ideas** — explicitly NOT this phase. The plan-checker will REVISE if any of these appears in a task.
188
- - **Risk flags** — watch during build (no IDs).
189
- - **Open questions** — still unresolved. Cannot lock until resolved.
190
-
191
- When you reference a decision later (in commit messages, task Why fields, ADRs), use its ID — `D-03` is shorter than the full decision text and cross-checkable.
192
-
193
- ### 5. Decision gate
194
-
195
- When the tree is fully traversed:
196
-
197
- - header: "Ready to lock?"
198
- - question: "Lock these decisions and move to /qualia-plan {N}?"
199
- - options: "Lock it in" / "Keep exploring"
200
-
201
- Loop until "Lock it in".
202
-
203
- ### 6. Write phase-{N}-context.md
204
-
205
- Fill `${QUALIA_TEMPLATES}/phase-context.md` with concrete content. Reference any ADRs written, any CONTEXT.md terms added.
206
-
207
- ### 7. Commit and route
208
-
209
- ```bash
210
- git add .planning/phase-{N}-context.md .planning/CONTEXT.md .planning/decisions/
211
- git commit -m "docs(phase-{N}): lock context, glossary terms, ADRs"
212
- node ${QUALIA_BIN}/qualia-ui.js end "PHASE {N} CONTEXT LOCKED" "/qualia-plan {N}"
213
- ```
214
-
215
- ## Rules — PHASE MODE
216
-
217
- 1. **One session, one phase.** Don't discuss phases 1 and 2 in the same invocation.
218
- 2. **Locked decisions are NON-NEGOTIABLE.** The planner honors them exactly. Don't lock what you're unsure of — defer it instead.
219
- 3. **Don't redo research.** If a question requires research you don't have, suggest `/qualia-research {N}` and pause.
220
- 4. **CONTEXT.md is precious — keep entries terse.** One sentence each. It's loaded into every spawn; bloat costs tokens.
221
- 5. **ADRs are scarce on purpose.** Three criteria all required. If any is missing, put it in CONTEXT.md or the phase-context.md instead.
222
- 6. **Short context files are fine.** A 30-line context for a simple phase beats a forced 200-line one.