@rafinery/cli 0.8.16 → 0.10.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 (71) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/bin/rafa.mjs +11 -0
  3. package/blueprint/.claude/agents/atlas.md +21 -3
  4. package/blueprint/.claude/agents/bloom.md +2 -2
  5. package/blueprint/.claude/agents/prism.md +42 -3
  6. package/blueprint/.claude/agents/sage.md +1 -1
  7. package/blueprint/.claude/commands/rafa.md +24 -13
  8. package/blueprint/.claude/rafa/contract.md +6 -0
  9. package/blueprint/.claude/rafa/hooks/brain-commit.mjs +4 -0
  10. package/blueprint/.claude/rafa/hooks/session-start.mjs +66 -0
  11. package/blueprint/.claude/skills/rafa-build/SKILL.md +111 -20
  12. package/blueprint/.claude/skills/rafa-improve/SKILL.md +22 -0
  13. package/blueprint/.claude/skills/rafa-leverage/SKILL.md +6 -1
  14. package/blueprint/.claude/skills/rafa-plan/SKILL.md +36 -1
  15. package/blueprint/.claude/skills/rafa-review/SKILL.md +16 -2
  16. package/blueprint/.claude/skills/rafa-sage/SKILL.md +8 -2
  17. package/blueprint/.claude/skills/rafa-scan/SKILL.md +4 -1
  18. package/lib/brain-repo.mjs +1 -1
  19. package/lib/checkpoint.mjs +12 -10
  20. package/lib/distiller/doctrine.mjs +5 -16
  21. package/lib/doctor.mjs +19 -1
  22. package/lib/facts.mjs +130 -0
  23. package/lib/gate/compile.mjs +12 -0
  24. package/lib/gate/verify-citations.mjs +78 -1
  25. package/lib/hydrate.mjs +45 -0
  26. package/lib/init.mjs +9 -1
  27. package/lib/leverage/engine.mjs +32 -3
  28. package/lib/leverage.mjs +11 -2
  29. package/lib/loop-cache.mjs +67 -0
  30. package/lib/mcp-client.mjs +12 -0
  31. package/lib/push.mjs +9 -1
  32. package/lib/reflex.mjs +5 -1
  33. package/lib/releases.mjs +51 -0
  34. package/lib/review.mjs +32 -6
  35. package/lib/session-facts.mjs +108 -0
  36. package/lib/skill-deps.mjs +377 -0
  37. package/lib/update.mjs +9 -0
  38. package/package.json +3 -2
  39. package/skills-bundle/frontend-design/LICENSE.txt +177 -0
  40. package/skills-bundle/frontend-design/SKILL.md +55 -0
  41. package/{LICENSE → skills-bundle/grill-me/LICENSE} +6 -6
  42. package/skills-bundle/grill-me/SKILL.md +7 -0
  43. package/skills-bundle/grill-me/agents/openai.yaml +5 -0
  44. package/skills-bundle/grilling/SKILL.md +53 -0
  45. package/skills-bundle/improve-codebase-architecture/HTML-REPORT.md +123 -0
  46. package/skills-bundle/improve-codebase-architecture/LICENSE +21 -0
  47. package/skills-bundle/improve-codebase-architecture/SKILL.md +71 -0
  48. package/skills-bundle/improve-codebase-architecture/agents/openai.yaml +5 -0
  49. package/skills-bundle/requesting-code-review/LICENSE +21 -0
  50. package/skills-bundle/requesting-code-review/SKILL.md +95 -0
  51. package/skills-bundle/requesting-code-review/code-reviewer.md +172 -0
  52. package/skills-bundle/skills-manifest.json +72 -0
  53. package/skills-bundle/tdd/LICENSE +21 -0
  54. package/skills-bundle/tdd/SKILL.md +36 -0
  55. package/skills-bundle/tdd/agents/openai.yaml +3 -0
  56. package/skills-bundle/tdd/mocking.md +59 -0
  57. package/skills-bundle/tdd/tests.md +77 -0
  58. package/skills-bundle/vercel-composition-patterns/AGENTS.md +946 -0
  59. package/skills-bundle/vercel-composition-patterns/README.md +60 -0
  60. package/skills-bundle/vercel-composition-patterns/SKILL.md +89 -0
  61. package/skills-bundle/vercel-composition-patterns/metadata.json +11 -0
  62. package/skills-bundle/vercel-composition-patterns/rules/_sections.md +29 -0
  63. package/skills-bundle/vercel-composition-patterns/rules/_template.md +24 -0
  64. package/skills-bundle/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  65. package/skills-bundle/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  66. package/skills-bundle/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  67. package/skills-bundle/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  68. package/skills-bundle/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
  69. package/skills-bundle/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  70. package/skills-bundle/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  71. package/skills-bundle/vercel-composition-patterns/rules/state-lift-state.md +125 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,87 @@ The machine-readable version of this lives in [`lib/releases.mjs`](lib/releases.
4
4
  CLI reads it to tell a client, on `rafa update`, exactly what an upgrade requires (nothing,
5
5
  a re-scan, or a `rafa migrate`).
6
6
 
7
+ ## 0.10.0 — wave 6: the capability arc (skill dependencies + agent upgrades)
8
+
9
+ rafa's agents get materially better at engineering — and the mechanism is the
10
+ owner's design: rafa **declares** third-party skill dependencies and installs
11
+ them **with your consent**, package-manager style, so the agents adopt YOUR
12
+ toolbox instead of hardcoding one.
13
+
14
+ - **The skill installer.** `rafa update`/`init` offers 7 declared skills (tdd,
15
+ frontend-design, vercel-composition-patterns, requesting-code-review,
16
+ improve-codebase-architecture, grill-me, and rafa's own grilling procedure)
17
+ with one-line reasons; on yes it installs them one at a time into the
18
+ **harness-neutral `.agents/skills/`** (one convention Claude Code, Codex,
19
+ and Cursor all read — Codex manifests ride along), verifies each
20
+ (frontmatter parses · sha256s match the tarball's own manifest · license
21
+ files present), and stamps `rafa.json`. Declining is remembered — re-offer
22
+ with `rafa update --skills`; `--yes` for CI; non-TTY without `--yes` skips
23
+ and stamps nothing. A declined set degrades gracefully, never breaks:
24
+ vendored cards never reference `.agents/` (pinned by test).
25
+ - **TDD by default** (atlas 4.0.0): eligible code tasks (tdd installed + a
26
+ harness detected) build red→green — the Done-check IS a failing test
27
+ authored first at a seam the dev approved at plan time; evidence (red
28
+ exit≠0 before green exit 0) rides the structured return; prism re-runs
29
+ green live and verifies red per validation tier. Refactor defers to review.
30
+ - **The design method**: UI tasks invoke frontend-design (token system,
31
+ signature element, critique-before-code) and consult Vercel's composition
32
+ rules (React-19-gated) — with the brain's design-system notes as the brief.
33
+ - **The grill gate** (prism 0.9.0): plans get a tier-bounded adversarial
34
+ interview before approval — grounding, unstated dependencies, VACUOUS
35
+ Done-checks (tautological tests caught), YAGNI. Devs also get `/grill-me`.
36
+ - **Severity-labeled findings** everywhere prism/review rule: Critical ·
37
+ Important · Minor — Minor never flips a verdict.
38
+ - **bloom's architecture method** (0.9.0): git-log heat scoping, the deletion
39
+ test, Confidence-lined candidates, first-slice-or-backlog — never big-bang.
40
+ - **Toolbox surfaces**: conductor 2.2.0 recalls `.agents/skills` in every
41
+ spawn slice; `rafa leverage --json` carries top-level `agentSkills` +
42
+ `skillDeps`; `rafa doctor` verifies installed skills against the bundle.
43
+
44
+ Adopt with `rafa update` — the skills prompt runs first, then the blueprint
45
+ sync. All licenses confirmed and vendored (MIT ×5, Apache-2.0 frontend-design).
46
+
47
+ ## 0.9.0 — wave 5: trust plane + transport economics
48
+
49
+ The research-canvas QA dogfood session (2026-07-25) produced a P0–P3 bottleneck
50
+ backlog; its root causes converged with sage's first two learnings on the same
51
+ seam. This release lands the whole wave:
52
+
53
+ - **Strict loop events, no legacy path.** `report_loop_event` requires the
54
+ actor envelope (`model` · `agent` · `runner ∈ sandbox|ci|session`; mechanical
55
+ CLI emits stamp `model:"mechanical"`) and `verification {method: static|live,
56
+ tool?, evidence?}` on verdict/gate categories — the static-vs-executed
57
+ distinction is structural, so a fabricated "tests passed" can't survive the
58
+ schema. `dedupeKey` makes every emit idempotent; `tier` records the
59
+ validation tier the ruling ran at. Pre-0.9.0 emits are rejected server-side
60
+ (pre-launch: old CLIs discarded). Both sage learnings flipped accepted.
61
+ - **One beat per task boundary.** `patch_plan_item` patches ONE item's fields
62
+ (structure stays `push_plan`-only); `checkpoint_task` lands the item patch +
63
+ the prism-verdict loop event + decision records in ONE transaction,
64
+ idempotent on retry. A task boundary went from ~6 platform calls + a
65
+ full-tree resend to 2 beats.
66
+ - **Self-healing citations.** `verify-citations --fix` rewrites a drifted cite
67
+ when its token still lives at exactly ONE grep site (gone/ambiguous stay
68
+ failing — the checker never guesses), then re-runs clean. `rafa hydrate`
69
+ re-bases served cites against the local checkout on arrival.
70
+ - **`rafa facts` — the session-facts plane.** Bank a verified fact once (claim
71
+ + the command/exit that proved it + dependsOn paths, sha-anchored); agents
72
+ read first and cite `SF-n unchanged`. Staleness is mechanical (dependsOn ∩
73
+ git diff since the verifying sha, working tree included).
74
+ - **The sage-due bridge.** `rafa checkpoint` caches loop-event shapes locally;
75
+ the SessionStart digest prints "sage due: N" with zero added platform calls,
76
+ and the conductor card makes honoring it a MUST — sage's trigger is
77
+ mechanical instead of discretionary.
78
+ - **`validation_tier` on plan tasks** (light|standard|full): bounds prism's
79
+ re-derivation depth only — the Done-check gate never relaxes.
80
+ - Cards/SOPs re-vendored: prism 0.8.0 (tool-authenticity rule · structured
81
+ return · bounded invariant citation) · atlas 3.9.0 (structured return ·
82
+ facts duty) · sage 0.4.0 (verification/actor/tier analysis dimensions) ·
83
+ conductor 2.1.0 (mechanical sage trigger). Review summary counts dedupe by
84
+ note id.
85
+
86
+ Adopt with `rafa update` (re-vendors hooks, cards, SOPs, contract).
87
+
7
88
  ## 0.8.16 — the session tail never strands (both planes, one beat)
8
89
 
9
90
  Live-catch (2026-07-24, research-canvas): brain notes authored AFTER the last code
package/bin/rafa.mjs CHANGED
@@ -46,6 +46,7 @@ const COMMANDS = [
46
46
  "hydrate",
47
47
  "dirty",
48
48
  "reflex",
49
+ "facts",
49
50
  "status",
50
51
  "doctor",
51
52
  "fold",
@@ -73,6 +74,8 @@ Commands:
73
74
  Flags (init/update): --overwrite replace changed owned files without asking
74
75
  --keep keep all changed owned files without asking
75
76
  (default: ask per file; in a non-TTY it keeps them)
77
+ --skills re-offer declared skill dependencies (incl. declined)
78
+ --yes install all skill dependencies unprompted (CI)
76
79
  compile Validate the brain against the contract → .rafa/manifest.json.
77
80
  manifest The BRANCH manifest (lenient snapshot — WIP files land under
78
81
  drafts, never fail). You never need to run this: the post-commit
@@ -113,6 +116,14 @@ Commands:
113
116
  detected. List/--json; --consume <id> [verdict] AFTER banking it
114
117
  through the gates (or judging it session-only). Corrections are
115
118
  the highest-value knowledge signal — they must never evaporate.
119
+ facts The session-facts plane (wave 5.4 — memoized verification): bank a
120
+ VERIFIED fact once (claim + the command/exit that proved it +
121
+ dependsOn paths, sha-anchored); agents read first and cite
122
+ "SF-n unchanged" instead of re-deriving. Staleness is mechanical
123
+ (dependsOn ∩ git diff since the verifying sha) and NOT transitive —
124
+ declare the enclosing directory for subsystem-spanning facts
125
+ (breadth is the conservative move). add · invalidate ·
126
+ prune --stale · --json.
116
127
  status Where the loop stands, from local state only: active plan +
117
128
  task progress · stale count · corrections · conflicts.
118
129
  --line = the one-line ambient form (statuslines/prompts) ·
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: atlas
3
- version: 3.8.1
3
+ version: 4.0.0
4
4
  model: opus # authoring is correctness-critical — a hallucinated note poisons the brain; best model, never cheap
5
5
  groundTruth: code-at-sha
6
6
  description: >-
@@ -16,7 +16,7 @@ duties:
16
16
  - "scan :: .claude/skills/rafa-scan/SKILL.md :: comprehensive breadth-first cited brain · verify-citations exits 0 · coverage honest (thin/gap stated, never hidden)"
17
17
  - "repair :: .claude/skills/rafa-scan/SKILL.md :: every blocker + major in checklist.md fixed against the code · checker re-run to exit 0 · never weaken a check to pass it"
18
18
  - "plan-drafting :: .claude/skills/rafa-plan/SKILL.md :: recall-grounded decomposition (coverage → search → notes) · blast radius named · contract §7 files · every child carries a Done-check"
19
- - "build-execution :: .claude/skills/rafa-build/SKILL.md :: implement per recalled knowledge · never hand-edit brain files around the gate · return what changed, cited"
19
+ - "build-execution :: .claude/skills/rafa-build/SKILL.md :: implement per recalled knowledge · TDD-DEFAULT when the tdd skill + a harness are present (the Done-check is a failing test FIRST at the plan-named seam; red→green evidence in the return) · UI tasks run the installed design skills per SOP (brain conventions win) · read session-facts FIRST and bank new expensive verifications (rafa facts add) · never hand-edit brain files around the gate · return what changed, cited"
20
20
  - "scoped-refresh :: .claude/skills/rafa-scan/SKILL.md :: re-derive ONLY the dirty-cited notes against current code (input: rafa dirty --json) · same gates as scan (verify-citations exit 0) · on main: compile+push; on a branch: working-set edit + checkpoint · queue consumed only after the refresh ships"
21
21
  - "okf-surface :: .claude/skills/rafa-okf/SKILL.md :: authored files pass rafa okf check as written — body links are markdown (never wikilinks); emit-owned sections untouched"
22
22
  ---
@@ -62,6 +62,22 @@ whole-codebase read happens in *your* window, not the conductor's, so the main s
62
62
  stays lean. You **never spawn** other agents — subagents can't nest, so the conductor owns
63
63
  the loop. Return summaries + on-disk artifacts, not the raw reads.
64
64
 
65
+ **Structured return (wave 5).** A build-execution report ends with the machine
66
+ block the conductor assembles Logs from — never re-paraphrased prose:
67
+
68
+ ```json
69
+ { "filesChanged": ["path", "…"], "commitSha": "…",
70
+ "notes": "one-paragraph what/why",
71
+ "factsDiscovered": ["SF-n banked: <claim>", "…"] }
72
+ ```
73
+
74
+ **Session facts (wave 5 — read first, bank once).** Before re-deriving any
75
+ environment/tooling fact, read `.rafa/session-facts.json` (`rafa facts`) and
76
+ cite `SF-<n> unchanged` when it holds. When YOU verify something expensive
77
+ (a harness exists, an install works, an env constraint), bank it once:
78
+ `rafa facts add --claim="…" --command="…" --exit=N [--depends=a,b]` — the next
79
+ spawn inherits it instead of re-learning it.
80
+
65
81
  ## The brain is your index, once it exists
66
82
  Generic at provision-time, repo-aware after the scan: **if a brain exists at
67
83
  `.rafa/brain/`, consult it as your index before re-reading code** — route via its cited
@@ -91,7 +107,9 @@ Never act cold; never over-load.
91
107
  *name* is a contract; its *value* is a secret. If a note genuinely needs a value, stop and ask
92
108
  the dev — don't harvest it. (Enforced in [the scan skill](../skills/rafa-scan/SKILL.md) step 4.)
93
109
  - **Toolbox-first execution (automatic — never an offer).** Before implementing any
94
- task step, CHECK the repo's installed toolbox — committed `.claude/skills/`,
110
+ task step, CHECK the repo's installed toolbox — committed `.claude/skills/`, the
111
+ **harness-neutral `.agents/skills/`** (consent-installed skill dependencies —
112
+ tdd, frontend-design, vercel-composition-patterns are yours to INVOKE),
95
113
  `.mcp.json` servers, commands — for a capability that already does it, and INVOKE
96
114
  it (Skill tool / MCP) instead of hand-rolling. The conductor passes the matching
97
115
  inventory slice in your spawn prompt; consult it first. Only what is actually
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bloom
3
- version: 0.8.1
3
+ version: 0.9.0
4
4
  model: opus # a false flag mutes the whole ledger — best model, never cheap
5
5
  groundTruth: code-trend
6
6
  description: >-
@@ -13,7 +13,7 @@ description: >-
13
13
  tools: Read, Grep, Glob, Bash, Skill, Write, mcp__rafinery
14
14
  color: green
15
15
  duties:
16
- - "improve-pass :: .claude/skills/rafa-improve/SKILL.md :: cited · prioritized P0–P3 · leverage-ranked · cite-checked (unresolved dropped) · ledger + debt trend regenerated"
16
+ - "improve-pass :: .claude/skills/rafa-improve/SKILL.md :: cited · prioritized P0–P3 · the architecture lens applies the deletion-test/depth method with its vocabulary (Confidence-lined candidates, first-slice-or-backlog, never big-bang) · leverage-ranked · cite-checked (unresolved dropped) · ledger + debt trend regenerated"
17
17
  - "plan-pull :: .claude/skills/rafa-plan/SKILL.md :: top-leverage OPEN improvements in the blast radius surfaced as optional child tasks — dismissible, never blocking"
18
18
  - "build-sweep :: .claude/skills/rafa-build/SKILL.md :: newly spotted → ledger files · fixed-in-passing → status fixed · at most ONE opt-in nudge per task"
19
19
  - "staleness-watch :: .claude/skills/rafa-improve/SKILL.md :: median open-item age stays minimal — every sweep re-validates or closes aged items; no improvement ages silently"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: prism
3
- version: 0.7.1
3
+ version: 0.9.0
4
4
  model: opus # the trust anchor — a hallucinated verdict/finding is the worst failure; best model, never cheap
5
5
  groundTruth: code-vs-claim
6
6
  description: >-
@@ -14,8 +14,8 @@ tools: Read, Grep, Glob, Bash, Write, mcp__rafinery
14
14
  color: orange
15
15
  duties:
16
16
  - "scan-validation :: .claude/skills/rafa-validate/SKILL.md :: PASS = hard gates pass · score ≥ 85 · 0 blockers · majors ≤ 2 · no unflagged salient-but-wrong exemplar (5b override → auto-ITERATE regardless of score)"
17
- - "plan-gate :: .claude/skills/rafa-plan/SKILL.md :: PASS = every task grounded in brain + code (none hallucinated) · every child carries a Done-check · blast radius named from coverage; else REJECT with cited reasons"
18
- - "execution-validation :: .claude/skills/rafa-build/SKILL.md :: done only when the child's Done-check demonstrably holds — run it yourself against the working tree; FAIL returns cited reasons to the producer"
17
+ - "plan-gate :: .claude/skills/rafa-plan/SKILL.md :: PASS = every task grounded in brain + code (none hallucinated) · every child's Done-check present AND non-vacuous (grilled per validation_tier — light = invariants only · standard = bounded probes · full = the whole interview) · TDD-eligible code tasks name their seam in the Done-check · blast radius named from coverage; else REJECT with cited reasons"
18
+ - "execution-validation :: .claude/skills/rafa-build/SKILL.md :: done only when the child's Done-check demonstrably holds — run it yourself against the working tree · TDD tasks: green re-run live, red per tier (static-attested at standard, worktree re-run at full) · findings labeled Critical/Important/Minor — ITERATE iff any Critical or Important, Minor never flips the verdict; FAIL returns cited reasons to the producer"
19
19
  - "distillation-validation :: .claude/skills/rafa-distill/SKILL.md :: every working-set file judged against MERGED MAIN (never the fork point) with a confirming/refuting file:line — survivors enter the org brain only through compile; refutations go back to their author, cited"
20
20
  - "okf-surface :: .claude/skills/rafa-okf/SKILL.md :: every checker LINKS warn judged (unwritten knowledge vs typo) — never ignored, never auto-failed"
21
21
  ---
@@ -37,6 +37,45 @@ brain as a trusted index, whatever you are validating is the thing on trial —
37
37
  against the code (ground truth) and the stated acceptance criteria. Trust nothing in
38
38
  it until you've confirmed it.
39
39
 
40
+ **Tool authenticity (wave 5 — the npx-placeholder trap).** A tool result is
41
+ evidence ONLY if the tool actually ran: name the binary (path or resolved
42
+ command) and the REAL exit code. `npx <name>` can silently resolve a joke/
43
+ placeholder package and "pass" — a class that already produced one fabricated
44
+ verdict in the field. If you cannot execute the check (no keys, no deps, no
45
+ env), the claim is `method: "static"` — say so in the same breath, never
46
+ smooth it into an unqualified PASS. Fabricating a `live` result is the one
47
+ unforgivable failure: the whole trust model prices from it.
48
+
49
+ **Structured return (wave 5).** Your report ends with the machine block the
50
+ conductor assembles Logs and loop events from — never re-paraphrased prose:
51
+
52
+ ```json
53
+ { "verdict": "PASS|ITERATE",
54
+ "evidence": [{ "claim": "…", "method": "static|live", "tool": "…", "exitCode": 0 }],
55
+ "residualRisk": "what was NOT proven, named — or the explicit empty string" }
56
+ ```
57
+
58
+ The `evidence` entries feed `report_loop_event.verification` verbatim; the
59
+ static-vs-live split is structural, not prose discipline.
60
+
61
+ **Bounded invariant citation (wave 5 — no-redundant-reverification).** An
62
+ invariant YOU (or a prior prism pass this plan) verified once is citable as
63
+ `SF-<n> unchanged` **iff** `.rafa/session-facts.json` shows its `dependsOn`
64
+ untouched since its verifying sha (the session-start digest surfaces staleness;
65
+ `rafa facts` lists it). Re-deriving an unchanged, evidence-backed fact is waste,
66
+ not rigor. The bound is hard: anything NOT in the facts file — and everything
67
+ about the artifact under test itself — stays trust-nothing. **Invalidation is
68
+ NOT transitive** (dependsOn is intersected with the diff, imports are never
69
+ walked — pinned by test): trust a fact only as far as its declared breadth, and
70
+ when a fact you rely on spans a subsystem whose dependsOn names single files,
71
+ treat it as under-declared — re-verify instead of citing it.
72
+
73
+ **Validation tier (wave 5).** A task's `validation_tier` (plan §7) bounds your
74
+ RE-DERIVATION DEPTH — how much surrounding context you re-read and re-prove:
75
+ light = the Done-check itself + direct evidence · standard = + the touched
76
+ files' immediate seams · full = unbounded re-derivation. The Done-check gate
77
+ NEVER relaxes at any tier, and your emitted loop event carries the tier it ran at.
78
+
40
79
  ## Duties (each duty's bar is in the frontmatter; the SOP carries the procedure)
41
80
  1. **Scan validation** — judge the brain in `.rafa/brain/` against the repo per
42
81
  [the validate skill](../skills/rafa-validate/SKILL.md). Verdict + score + severity-
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sage
3
- version: 0.3.0
3
+ version: 0.4.0
4
4
  model: opus # a wrong learning re-shapes an agent for every repo — best model, never cheap
5
5
  groundTruth: sessions-over-time
6
6
  description: >-
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 2.0.2
2
+ version: 2.2.0
3
3
  description: rafa — the repo's engineering SOP. Use whenever the dev wants to plan a feature or change, build/execute against the active plan, improve code health, or ground work in the repo's brain — intent counts, the dev does NOT need to type /rafa. Also explicit: /rafa <init|scan|improve|plan|build|push|leverage|migrate|update|help> [--brain-only]. Admin verbs (init/scan/push/migrate/update) run ONLY when explicitly invoked.
4
4
  ---
5
5
 
@@ -59,10 +59,15 @@ MCP tools, checkpoints, pushes, decisions logging — is AGENT-INTERNAL machiner
59
59
  dev speaks intent; YOU run the machinery. Never instruct the dev to run a command you
60
60
  can run yourself. The only human acts: init (provision) · `npx rafa pull` (once per
61
61
  clone) · ci-setup approval · npm publish. **Toolbox is a recall surface:** at bootstrap,
62
- load the committed toolbox (`.claude/skills/`, `.mcp.json`, commands) alongside brain
63
- status; pass the matching slice into every atlas/bloom spawn prompt agents USE the fit
64
- capability instead of hand-rolling (only what's installed, never guessed; personal
65
- `~/.claude/` stays live-recommend-only). **Actor envelope:** stamp `actorMeta {model,
62
+ load the committed toolbox `.claude/skills/` (this harness) **and
63
+ `.agents/skills/` (the HARNESS-NEUTRAL third-party skillsone convention
64
+ Claude Code · Codex · Cursor all read; installed by `rafa update` with
65
+ consent, wave 6)** plus `.mcp.json` and commands, alongside brain status
66
+ (`rafa leverage --json` is the deterministic read); pass the matching slice
67
+ into every atlas/bloom spawn prompt — agents USE the fit capability instead of
68
+ hand-rolling (only what's installed, never guessed; a DECLINED skill dep means
69
+ the SOP's fallback path, never a nag; personal `~/.claude/` stays
70
+ live-recommend-only). **Actor envelope:** stamp `actorMeta {model,
66
71
  agent, runner: "session"}` on every state-plane write (checkpoints, plan pushes,
67
72
  decisions, reports) and export `RAFA_ACTOR_MODEL` before CLI calls — the platform records
68
73
  WHO/WHAT executed; "unreported" when unknown, never guessed.
@@ -240,14 +245,20 @@ explicitly typed; an ACCEPTED boundary offer counts as the explicit invocation.
240
245
 
241
246
  ## sage — the silent L5 observer (runs IMPLICITLY, zero-command; owner 2026-07-13)
242
247
 
243
- Devs never type `/rafa sage`. **Trigger:** at completion boundaries build final-verify ·
244
- distill close · bootstrap when new loop events exist since the learnings ledger's newest
245
- entry, spawn sage per [rafa-sage](../skills/rafa-sage/SKILL.md); announce ONE line, proceed;
246
- its proposals ride the NEXT bootstrap digest. Writing proposals is the just-do rung; the
247
- consent moment is ACCEPTING one (accepted = a versioned, MR-reviewed card/SOP edit proposals
248
- never self-apply). `/rafa sage` is the explicit override. Full mechanics (≥10-event threshold,
249
- `get_loop_events` shape read, scrub step, ledger schema, the person-free/asset-free creed) live
250
- in the skill.
248
+ Devs never type `/rafa sage`. **Trigger MECHANICAL as of wave 5 (owner
249
+ 2026-07-25):** the SessionStart digest computes it a `[rafa · sage] sage due:
250
+ N loop events…` line (the checkpoint-written loop-event cache vs the learnings
251
+ ledger, compared locally). **When that line is present, spawning sage at the
252
+ next completion boundary build final-verify · distill close · bootstrapis
253
+ a MUST, not a judgment call**: the line IS the trigger condition already
254
+ evaluated; a discretionary trigger is how sage never fired organically. Spawn
255
+ per [rafa-sage](../skills/rafa-sage/SKILL.md); announce ONE line, proceed; its
256
+ proposals ride the NEXT bootstrap digest. No digest line = not due (skip
257
+ silently). Writing proposals is the just-do rung; the consent moment is
258
+ ACCEPTING one (accepted = a versioned, MR-reviewed card/SOP edit — proposals
259
+ never self-apply). `/rafa sage` is the explicit override. Full mechanics
260
+ (≥10-event threshold, `get_loop_events` shape read, scrub step, ledger schema,
261
+ the person-free/asset-free creed) live in the skill.
251
262
 
252
263
  ## migrate · update — brain-side schema migration (no skill; the intelligence-only half)
253
264
 
@@ -382,6 +382,7 @@ blocked_by: [payments-c2] # optional · item ids this waits on — a dependency
382
382
  blocked_reason: "vendor sandbox access" # optional · external blocks (no dependency id to point at)
383
383
  priority: 2 # optional · 0 none · 1 urgent · 2 high · 3 medium · 4 low (Linear scale)
384
384
  estimate: 3 # optional · points (Linear estimate / Jira story points)
385
+ validation_tier: standard # optional · light|standard|full — bounds prism's re-derivation DEPTH only (wave 5); the Done-check gate itself never relaxes. Planner assigns from blast radius: contract/state/graph surface → full · types/docs/leaf-UI → light · default standard
385
386
  branch: feat/webhooks # optional · ↔ Linear branchName
386
387
  baseSha: abc123 # optional · commit it was cut from
387
388
  domains: [payments] # optional · EPIC only · the blast radius — the brain link
@@ -573,12 +574,17 @@ state**, marked by `envelope.plane: "state"`:
573
574
  | `resolve_working_file` | branch working set | `distilled` / `refuted` (+ cited note) at merge-to-main distillation; `keep-current` for adjudication decisions; CAS on status — a racing distill fails loudly |
574
575
  | `fold_working_set` | branch working set | branch→parent-branch MECHANICAL fold (no LLM, no prism — rigor only at main): absent → re-keyed · identical → merged · divergent → `needs-adjudication` on the parent row |
575
576
  | `push_plan` / `list_plans` / `update_plan_status` / `set_active_plan` / `log_decision` | **plans** — (repo) work objects | the push-on-approval channel (§7 v2): approval pushes the whole work-item TREE (items[], bodies stored); `list_plans` = names only; statuses push back (5-value enum — blocked is derived); `log_decision` records the deliberation trail (paraphrase + pivotal quotes); the active pointer is explicit, never inferred |
577
+ | `patch_plan_item` | plans (one item) | wave 5.2 — field-level patch on ONE item (no full-tree resend): provided fields replace, absent stay; CLEARING a field and any STRUCTURE change (kind/parent/add/remove/domains/external/merged) remain `push_plan`-only, so "a vanished optional field must not survive" keeps holding at tree granularity; snapshot-before-change rides noteRevisions like a re-push |
578
+ | `checkpoint_task` | plans + loopEvents + planDecisions (**ONE transaction**) | wave 5.2 — the ONE-beat boundary write: optional item patch + the loop-event ruling + decision records land atomically (all-or-nothing; `mcp.call` is an action, so composing the separate tools can never be atomic — this is one internalMutation). Idempotent end-to-end (patch no-ops on same values · loop event dedupes on `dedupeKey` · decisions dedupe on client-stable ids). The embedded loopEvent obeys `report_loop_event`'s exact rules — one rulebook, no side door. `checkpoint_sync` stays separate: per-file CAS partial-accept is a different failure model than an atomic beat |
576
579
  | `report_improvement_status` | **events only** — no store | a LIVE session signal (bloom's fixed-in-passing during build). The improvement ledger row is untouched — it has ONE writer, the ingest (K1); the platform overlays the report as pending-reconciliation until the next brain push confirms it |
577
580
  | `reconcile_claim` | **reconciliations queue** — (repo) run rows | executor claim (`plane:"state"`): ACID no-running-sibling assert → returns the attempt token; delegates to `reconciliations.claim`; stamps the actor envelope `{model, agent, runner}` (runner ∈ `sandbox`\|`ci`\|`session`, enum-checked at the boundary) on the row |
578
581
  | `reconcile_heartbeat` | reconciliations queue | extends the wall-clock lease + sets the phase; **attempt-fenced** (a non-current attempt is rejected, nothing written); actor envelope re-stamped |
579
582
  | `reconcile_log_append` | **reconciliationLogs** — the run log tail | batched chunks, **secret-screened PER LINE** (the same `looksLikeSecret` screen — a credential chunk is rejected by chunk/line index, nothing stored or echoed) + **attempt-fenced**; `seq`-ordered; UNLOGGED (emits its own `reconcile.log` event, counts only) |
580
583
  | `reconcile_report` | reconciliations queue (+ knowledge node on success) | terminal outcome + node delta + meter; **attempt-fenced**; success COMPOSES the pointer advance atomically (insert node + move pointer, one mutation); refutation/deletion ride the success outcomes; actor envelope stamped |
581
584
  | `list_working_sets` | branch working set (**READ**) | enumerates branches with LIVE candidate rows + counts only (no bodies) — closes the 07-14 sensor gap (`get_working_set` needs a branch arg; nothing listed them); feeds the SessionStart digest pending line |
585
+ | `report_loop_event` | **loopEvents** — the loop-outcome ledger (sage's evidence) | ONE structured outcome per ruling, shapes only (enum category + per-category enum outcome + `subject` shape ref, secret-screened). Wave 5 trust plane, STRICT (no legacy path): **required actor envelope** `{model, agent, runner ∈ sandbox\|ci\|session}` (mechanical CLI emits use `model:"mechanical"`); **required `verification {method: static\|live, tool?, evidence?}`** on `prism-verdict`/`gate-result`/`review-verdict` — the static-vs-executed distinction is structural, never prose; optional `dedupeKey` (≤120) makes the emit **idempotent** (same `(repo, key)` never lands twice — response says `deduped`) and `tier ∈ light\|standard\|full` records the §7 `validation_tier` the ruling ran at |
586
+ | `get_loop_events` | loopEvents (**READ**) | newest-first shape read (optional category filter, limit ≤500): category/outcome/subject + actorMeta/verification/dedupeKey/tier + at — no bodies, no code; sage splits miss patterns by verification method, actor, and tier |
587
+ | `report_learning` | **agentLearnings** — sage's summary-row mirror | sage ONLY (wave 4.1): mirrors the SUMMARY ROW of one committed ledger entry (`.claude/rafa/learnings/<id>.md` stays the single truth, MR-gated, never self-applied). Asset-free ENFORCED server-side: enum status/leverage/categories, `.claude/`-only diff target, length caps, code-fence + secret screens; upsert by id |
582
588
 
583
589
  State tools work with or without an ingested brain (a working set can exist —
584
590
  and a plan can be pushed — before the first scan) and are exempt from the
@@ -181,6 +181,10 @@ try {
181
181
  // rafa review's scored-ranges artifact (wave 2.3) — regenerated per
182
182
  // review, machine state like distill-verdicts; never brain knowledge.
183
183
  "review.json",
184
+ // wave 5: session-scoped machine state — memoized verification + the
185
+ // checkpoint-written loop-event cache the sage-due digest reads.
186
+ "session-facts.json",
187
+ "loop-events-tail.json",
184
188
  ];
185
189
  const gi = join(rafa, ".gitignore");
186
190
  const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
@@ -300,6 +300,72 @@ try {
300
300
  }
301
301
  }
302
302
 
303
+ // Session facts (wave 5.4 — memoized verification): banked verified facts +
304
+ // mechanical staleness (dependsOn ∩ git diff since the verifying sha, working
305
+ // tree included). Spawned agents read the file FIRST and cite "SF-n unchanged"
306
+ // instead of re-deriving. Local reads only — no platform call.
307
+ {
308
+ const doc = readJson(join(rafaDir, "session-facts.json"));
309
+ if (doc?.schemaVersion === 1 && Array.isArray(doc.facts) && doc.facts.length) {
310
+ let stale = 0;
311
+ for (const f of doc.facts) {
312
+ try {
313
+ const changed = execSync(`git diff --name-only ${f.verifiedAtSha}`, {
314
+ cwd: root,
315
+ encoding: "utf8",
316
+ stdio: ["ignore", "pipe", "ignore"],
317
+ })
318
+ .split("\n")
319
+ .filter(Boolean);
320
+ if (
321
+ (f.dependsOn ?? []).some((p) =>
322
+ changed.some((c) => c === p || c.startsWith(p.endsWith("/") ? p : p + "/")),
323
+ )
324
+ )
325
+ stale++;
326
+ } catch {
327
+ stale++; // unknown sha — fail-honest, count it stale
328
+ }
329
+ }
330
+ lines.push(
331
+ `[rafa · facts] ${doc.facts.length} session fact(s) banked (.rafa/session-facts.json — spawned agents read it FIRST and cite "SF-n unchanged" instead of re-deriving)` +
332
+ (stale ? ` · ${stale} STALE — re-verify or \`rafa facts prune --stale\`` : ""),
333
+ );
334
+ }
335
+ }
336
+
337
+ // Sage-due bridge (wave 5.5 — mechanical detection): the checkpoint-written
338
+ // loop-event cache vs the learnings ledger, compared LOCALLY (zero platform
339
+ // calls). Sage's trigger was a discretionary end-of-session SOP step and
340
+ // never fired organically — detection now lands front-of-session where
341
+ // reminders get obeyed; only the SPAWN stays with the conductor.
342
+ {
343
+ const cache = readJson(join(rafaDir, "loop-events-tail.json"));
344
+ if (cache?.schemaVersion === 1 && Array.isArray(cache.events)) {
345
+ let ledgerAt = null;
346
+ const ledgerPath = join(root, ".claude", "rafa", "learnings", "ledger.md");
347
+ if (existsSync(ledgerPath)) {
348
+ const m = readFileSync(ledgerPath, "utf8").match(
349
+ /^-\s*Generated:\s*([0-9]{4}-[0-9]{2}-[0-9]{2}(?:[T ][0-9:.Z+-]+)?)/m,
350
+ );
351
+ if (m) {
352
+ const t = Date.parse(m[1]);
353
+ if (Number.isFinite(t)) ledgerAt = t;
354
+ }
355
+ }
356
+ const fresh =
357
+ ledgerAt == null
358
+ ? cache.events
359
+ : cache.events.filter((e) => Number.isFinite(e.at) && e.at > ledgerAt);
360
+ if (fresh.length >= 10)
361
+ lines.push(
362
+ `[rafa · sage] sage due: ${fresh.length} loop events since the last learnings pass` +
363
+ (ledgerAt == null ? " (no ledger yet — sage has never run here)" : "") +
364
+ ` — spawn sage at the next completion boundary (zero-command; this line is the trigger, not a suggestion).`,
365
+ );
366
+ }
367
+ }
368
+
303
369
  // Benchmark liveness (harness-arc: the proof engine is workflow-woven, never a
304
370
  // dev-typed command) — a scanned brain with no MEASURED benchmark is a gap the
305
371
  // scan SOP's Prove-it step closes. Absence sensor only; never a nag mid-flow.
@@ -17,7 +17,7 @@ platform MCP (one read path — the same surface any third-party agent uses).
17
17
  | Role | Agent | Job per task |
18
18
  |---|---|---|
19
19
  | **Executor** | atlas | RECALL the task's brain slice via MCP (`search_knowledge` + `get_rule`/`get_playbook`; honor non-exemplars) → implement, convention-adherent |
20
- | **Validator** | prism | validate the execution against the child's `## Done-check` — strict, unbiased, against code + brain, never against atlas's claims. **`status: done` only on prism PASS**; FAIL → atlas corrects (validate-and-correct at work time). Plan-done adds one line to the verdict: **working set reviewed — captured, or clean-with-reason** (a build that learned nothing SAYS so; a build that learned something SHOWS the files) |
20
+ | **Validator** | prism | validate the execution against the child's `## Done-check` — strict, unbiased, against code + brain, never against atlas's claims. **`status: done` only on prism PASS**; FAIL → atlas corrects (validate-and-correct at work time). **TDD tasks (wave 6): green re-run LIVE; red per tier (standard = attested `method:"static"` · full = re-run at the red commit in a throwaway worktree). Findings carry `Critical (Must Fix) · Important (Should Fix) · Minor (Nice to Have)` — ITERATE iff any Critical/Important; Minor NEVER flips the verdict.** Plan-done adds one line to the verdict: **working set reviewed — captured, or clean-with-reason** (a build that learned nothing SAYS so; a build that learned something SHOWS the files) |
21
21
  | **Improver** | bloom | **push**: new improvement opportunities spotted during execution → new ledger files. **close**: improvements fixed in passing → `status: fixed` in the ledger file + `report_improvement_status(id, fixed)` so the platform shows it LIVE as pending-reconciliation (the ledger row itself changes only at the next brain push — K1). **nudge**: top-leverage open item in the task's blast radius — opt-in, never blocking |
22
22
  | **Coach** | compass | **sitback** (harness-arc): after each task's verdict + sweep, one beat of reflection — did THIS task reveal something about how this DEV works (a preference, a recurring friction, a steering pattern)? Repo knowledge goes to the working set, never here. A genuine dev-level observation becomes its OWN opt-in offer (consent doctrine: insights are NEVER under session consent) → `put_dev_insight` on yes. No observation = no offer — silence is the honest default |
23
23
 
@@ -47,22 +47,33 @@ platform MCP (one read path — the same surface any third-party agent uses).
47
47
  **and append a dated entry to the child's `## Log`** (body links: markdown,
48
48
  per [rafa-okf](../rafa-okf/SKILL.md)) — what was done, what was
49
49
  decided, what surprised (body prose: displayed verbatim on the platform, never
50
- parsed; the plan files at `.rafa/plans/<plan>/` ARE the local cache) → at the
51
- task-done CHECKPOINT (under the session consent): `push_plan` /
52
- `update_plan_status` (the plans channel — statuses + Log journals) +
53
- **`log_decision` for each deliberation since the last checkpoint** what
54
- came up, what was considered, what the DEV chose, why (actor = the dev for
55
- steering, the agent for its own proposals; PARAPHRASE + short verbatim
56
- quotes only where the wording carries the decision — transcripts never land
57
- in shared stores; mirror each into the item's `## Decisions` section) +
58
- **`report_loop_event(category: "prism-verdict", outcome: PASS|ITERATE,
59
- subject: <task id>)`** at the moment prism rules on the Done-check (sage's
60
- evidence — shapes only: the verdict + the task id, never code) +
61
- `rafa checkpoint` (the branch working set), so the platform and every MCP
62
- consumer reflect live progress. Checkpoint moments: task done · plan
50
+ parsed; the plan files at `.rafa/plans/<plan>/` ARE the local cache) → the
51
+ task-done CHECKPOINT is **ONE beat (wave 5), two calls total**, under the
52
+ session consent:
53
+ **(a) `checkpoint_task`** the atomic boundary write (all-or-nothing,
54
+ idempotent on retry) carrying together: `patch: {status, body}` (the item's
55
+ new status + its full body with the fresh `## Log` entry — never a
56
+ whole-tree `push_plan` for one task); `loopEvent: {category:
57
+ "prism-verdict", outcome: PASS|ITERATE, subject: <task id>, tier: <the
58
+ task's validation_tier>, verification: <prism's evidence entry — method
59
+ static|live + tool + exit code, straight from its structured return>,
60
+ actorMeta: {model: <ruling model>, agent: "prism@<card ver>", runner:
61
+ "session"}, dedupeKey: "<task-id>·v<attempt>"}` (the emit rides the ruling's
62
+ OWN moment, never a session-end sweep; the dedupeKey makes a retried beat
63
+ count ONCE — the non-idempotent-emit learning, closed); and `decisions:
64
+ [...]` — each deliberation since the last beat with a CLIENT-STABLE id
65
+ (`<task-id>·d1`, `·d2` …): what came up, what was considered, what the DEV
66
+ chose, why (actor = the dev for steering, the agent for its own proposals;
67
+ PARAPHRASE + short verbatim quotes only where the wording carries the
68
+ decision — transcripts never land in shared stores; mirror each into the
69
+ item's `## Decisions` section).
70
+ **(b) `rafa checkpoint`** — the branch working set + sensor heartbeat +
71
+ brain mirror (the CLI beat; it also refreshes the loop-event cache the
72
+ sage-due digest reads).
73
+ `push_plan` remains for CREATION and structure changes only;
74
+ `update_plan_status`/`log_decision` stay valid but the one-beat call
75
+ replaces them at task boundaries. Checkpoint moments: task done · plan
63
76
  approved · explicit ask · cadence · git push/pull — never session-end. The
64
- loop-event emits ride the SAME checkpoint beats — one per outcome as it
65
- occurs, monotonic, NEVER a session-end sweep. The
66
77
  git-push boundary is MECHANICAL (M5): the pre-push hook runs `rafa checkpoint`
67
78
  itself, non-blocking — the session still owns the task-done/plan-approved
68
79
  moments. A checkpoint CONFLICT (a teammate's newer copy of the same file) is
@@ -71,10 +82,11 @@ platform MCP (one read path — the same surface any third-party agent uses).
71
82
  3. **Brain changes mid-build — WHERE you are decides WHERE it goes.**
72
83
  - **On the default branch (main):** run a full `/rafa scan` (regenerate →
73
84
  prism → compile → push); the brain re-stamps at the new sha, so
74
- `brain = f(code@sha)` stays exact. When the gate runs, emit
75
- **`report_loop_event(category: "gate-result", outcome: exit0|failed,
76
- subject: <compile|verify-citations>)`** at that checkpoint beat the gate's
77
- own moment, never deferred to session-end.
85
+ `brain = f(code@sha)` stays exact. gate-result loop events are
86
+ **MECHANICAL** (wave 5): the push gates emit them CLI-stamped actor
87
+ envelope, `method: "live"` verification with the real exit code, and a
88
+ `(gate·sha·outcome)` dedupeKey. The session never hand-emits gate-result;
89
+ the double-emit class died with the non-idempotent-emit learning.
78
90
  - **On any other branch:** the org brain is NEVER written from a branch —
79
91
  it describes main, and a branch-state scan would poison it for everyone.
80
92
  Invalidated/learned knowledge → the branch **working set**: hydrate the
@@ -106,6 +118,85 @@ platform MCP (one read path — the same surface any third-party agent uses).
106
118
  scoped-refresh offer NOW (on main: refresh → gates → push; on a branch:
107
119
  working-set edit → checkpoint), and `rafa dirty --consume` only after it ships.
108
120
 
121
+ ## TDD-default (wave 6 — code tasks build red → green)
122
+
123
+ **Eligibility gate (deterministic, decided once per session):** the `tdd` skill
124
+ is installed (`.agents/skills/tdd/` — the toolbox slice says so) AND the repo
125
+ has a test harness — checklist, first hit wins: `package.json` `test` script ·
126
+ `vitest.config.*`/`jest.config.*` · `pytest.ini`/`pyproject [tool.pytest]` ·
127
+ `go.mod` with `*_test.go` · `Cargo.toml`. Bank the answer as a session fact
128
+ (`rafa facts add --claim="test harness: <runner>" --command="<detect>" --exit=0
129
+ --depends=package.json`) so no later spawn re-derives it. Not eligible → the
130
+ pre-wave path below, unchanged, plus a `factsDiscovered: ["tdd unavailable:
131
+ <reason>"]` note in atlas's return — bloom may later surface a tooling-fit
132
+ improvement; NEVER a mid-build nag.
133
+
134
+ **The loop (per the installed tdd skill — atlas INVOKES it; its anti-patterns
135
+ are prism's criteria):**
136
+ 1. **Red** — atlas authors the failing test FIRST, at the seam the plan's
137
+ Done-check names (the seam was dev-confirmed at approval — never invented
138
+ here). Run it; it must FAIL for the right reason. Commit
139
+ `[<task-id>] test: red — <seam>` (full tier prefers the split commit; at
140
+ standard tier the evidence object below is the durable proof and red+green
141
+ may land as one commit).
142
+ 2. **Green** — implement the minimal slice to exit 0. One seam, one test, one
143
+ vertical slice per cycle — never all-tests-then-all-code.
144
+ 3. **Refactor is NOT part of the loop** — it belongs to the review stage
145
+ (tdd SKILL.md's own rule + the [rafa-review](../rafa-review/SKILL.md) gate;
146
+ bloom's no-big-bang directive is the same law).
147
+ Evidence rides atlas's structured return —
148
+ `evidence: [{claim: "red", tool: "<runner>", exitCode: 1}, {claim: "green",
149
+ tool: "<runner>", exitCode: 0}]` + `skillsUsed: ["tdd"]` — and
150
+ `how: via the tdd skill` lands in the item's `approach`.
151
+
152
+ **prism's verification, tier-scaled:** green is ALWAYS re-run live (tool
153
+ authenticity: binary + real exit code). Red at **standard** = attested from
154
+ atlas's evidence, labeled `method: "static"`; at **full** = re-run at the red
155
+ commit in a throwaway worktree (`git worktree add`), labeled `method: "live"`.
156
+ Mock discipline is a finding class: mocks only at system boundaries, never the
157
+ repo's own collaborators (the tdd skill's mocking reference is the criteria).
158
+
159
+ ## UI tasks — the design method (wave 6)
160
+
161
+ **Trigger:** the task's epic `domains:` include a UI domain, or its target
162
+ files land in the app's component/route surfaces — the BRAIN's domain map
163
+ decides, never a hardcoded path list.
164
+
165
+ **Method (when the skills are installed — the toolbox slice says):** atlas
166
+ **INVOKES** `frontend-design` (Skill tool) for the two-pass plan — token
167
+ system (Color 4–6 named values · Type roles · Layout · the ONE signature
168
+ element), then self-critique against the brief before any code — and CONSULTS
169
+ `vercel-composition-patterns` for component structure (no boolean-prop
170
+ sprawl · compound components · state/actions/meta context · children over
171
+ render props), gating the `react19-*` rules on the repo's actual React version
172
+ (a React ≤18 repo skips them, stated in the return).
173
+
174
+ **Adherence vs distinctive — the brain decides:** when the brain documents a
175
+ design system (theme tokens, component conventions), those notes ARE the
176
+ brief and the skill's own "the brief's own words always win" rule applies —
177
+ adherence mode, distinctiveness spent within the system. No design-system
178
+ notes (greenfield/marketing surface) → distinctive mode, full method. Record
179
+ `how: via the frontend-design skill` (+ vercel-composition-patterns when
180
+ consulted). Skills declined → the pre-wave path + a `factsDiscovered` note.
181
+
182
+ ## Spawn-prompt discipline (wave 5 — pay once, cite after)
183
+
184
+ - **Session facts first.** Every atlas/prism spawn prompt begins by pointing at
185
+ `.rafa/session-facts.json` (read first; cite `SF-n unchanged` where it holds;
186
+ bank new expensive verifications via `rafa facts add`). Six agents
187
+ re-deriving one environment fact was the QA session's headline waste.
188
+ - **Never restate card doctrine.** The card already binds the agent's creed
189
+ (prism's skepticism, atlas's toolbox-first) — a spawn prompt carries ONLY the
190
+ task-specific grounding: the task file, the exact target files/patterns, the
191
+ `validation_tier`, the facts pointer. Re-explaining the card in every prompt
192
+ is conductor inefficiency, not rigor.
193
+ - **Compact agent-to-agent register.** A subagent's primary reader is the
194
+ CONDUCTOR: terse-with-citations by default, ending in the structured return
195
+ (prism: `{verdict, evidence[], residualRisk}` · atlas: `{filesChanged,
196
+ commitSha, notes, factsDiscovered}`) — Logs and loop events assemble from
197
+ FIELDS, never re-paraphrased prose. Expand to full prose only on ITERATE or
198
+ when the human will read it directly.
199
+
109
200
  **Lite plans** (single-child, from plan-lite) run the same per-task loop — the
110
201
  Done-check gate never relaxes; only the ceremony around it shrinks (no bloom nudge,
111
202
  single checkpoint + plan push at the end).