@rafinery/cli 0.8.15 → 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 +104 -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 +2 -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 +62 -16
  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 +21 -4
  17. package/blueprint/.claude/skills/rafa-scan/SKILL.md +4 -1
  18. package/lib/brain-repo.mjs +1 -1
  19. package/lib/checkpoint.mjs +76 -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 +87 -18
  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
@@ -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).
@@ -91,6 +91,24 @@ convention — see [RSC client boundary](/brain/rules/rsc-client-boundary.md)).
91
91
  performance · architecture · product · ops**. Weight the **silent** issues (directive 2):
92
92
  convention violations the brain documents, framework boundary breaches (e.g. RSC), missing
93
93
  authz, drifting contracts. The brain's conventions are your oracle for "what's a violation here."
94
+
95
+ **The architecture lens has a METHOD (wave 6)** — criteria adapted from the installed
96
+ `improve-codebase-architecture` skill (READ as criteria, never invoked — it is
97
+ `disable-model-invocation`; the method below stands whether or not the skill is installed):
98
+ - **Scope by heat, not by sweep** (YAGNI): unless the dev named a direction, walk
99
+ `git log --oneline` for the hot spots — churn is where depth pays; cold code waits.
100
+ - **Deep vs shallow judgment**: a module earns its interface when the interface is smaller
101
+ than what it hides. Bouncing between many small modules to follow one behavior, pure
102
+ functions extracted only for testability, and leaky seams are the friction signals.
103
+ - **The deletion test**: would deleting this module CONCENTRATE complexity somewhere honest,
104
+ or just smear it around? "Concentrates" marks a candidate.
105
+ - **Candidates are normal §3 improvement files** (zero schema change): before/after as body
106
+ prose + a `Confidence: Strong | Worth exploring | Speculative` body line; inside these
107
+ notes use the depth vocabulary — module · interface · implementation · seam · adapter ·
108
+ depth · leverage · locality — and keep it INSIDE the architecture lens (the rest of the
109
+ ledger speaks the repo's own language).
110
+ - **The big-bang guard is sovereign** (directive 1): a `Strong` candidate lands as its first
111
+ 10-minute slice or a P2 backlog item — never a grand refactor plan.
94
112
  3. **Delegate security.** Run `pnpm audit` (dep CVEs). Recommend/run the **`security-review`**
95
113
  skill and semgrep/CodeQL if available. Record *tool* results as improvements; the LLM pass is
96
114
  observational only — never present it as an authoritative audit.
@@ -166,3 +184,7 @@ the ledger the nudge will read.
166
184
  beyond the deterministic cite-check.
167
185
  - Debt-score weighting calibrated from real runs.
168
186
  - Per-lens diverse validators (a security lens checked by a security-minded validator).
187
+ - A visual before/after artifact for architecture candidates (the
188
+ improve-codebase-architecture skill's HTML report) — deliberately SKIPPED for now: its
189
+ CDN-loaded Tailwind/Mermaid + temp-file-and-open mechanics violate the deterministic,
190
+ no-network posture; the ledger IS the report.
@@ -44,7 +44,12 @@ at the fix. This is platform DNA: **leverage what's present; never reinvent it.*
44
44
 
45
45
  The engine ([`lib/leverage/engine.mjs`](../../../packages/cli/lib/leverage/engine.mjs)) gathers
46
46
  light, tool-agnostic repo signals (`repoContext`: stack deps, `.env` files + whether they're
47
- ignored, declared MCP servers), then runs every **detected** adapter and ranks the tips.
47
+ ignored, declared MCP servers, **and the harness-neutral `.agents/skills/` inventory +
48
+ skill-dependency status (wave 6)** — `.agents/` is ONE convention Claude Code · Codex ·
49
+ Cursor all read, so it lives in the engine, never inside a per-toolchain adapter), then runs
50
+ every **detected** adapter and ranks the tips. The engine emits its own rafa-level tip —
51
+ `skill-deps-missing` (`P2`, fix = `rafa update --skills`) — because rafa's declared skill
52
+ dependencies are harness-neutral by definition.
48
53
 
49
54
  Each adapter implements one contract ([`lib/leverage/adapters/index.mjs`](../../../packages/cli/lib/leverage/adapters/index.mjs)):
50
55
 
@@ -41,6 +41,34 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
41
41
  A plan whose children lack Done-checks is REJECTED here — compile never parses
42
42
  bodies (invariant #3); this gate is prism's.
43
43
 
44
+ **The grill pass (wave 6 — tier-bounded adversarial interview).** On top of
45
+ the two invariants, prism GRILLS the draft — the same procedure the installed
46
+ `grilling` skill carries (`.agents/skills/grilling/SKILL.md`; prism READS it
47
+ as criteria when installed, and this SOP carries the method regardless —
48
+ never invoked mid-validation, per its card). Per task, up the probe ladder:
49
+ **grounding** (which claims rest on assumption? name it, demand its
50
+ evidence — a session-fact id counts, vibes don't) · **unstated dependencies**
51
+ (what must exist for this task that no `blocked_by` declares?) · **vacuous
52
+ Done-check** (can it pass while the feature is broken? a check that
53
+ recomputes its expectation the way the code will, or that a no-op satisfies,
54
+ is TAUTOLOGICAL — criteria per the tdd skill's anti-patterns when installed)
55
+ · **YAGNI** (does the INTENT need this task, or did thoroughness invent it?).
56
+ Budget scales with the task's `validation_tier`: **light** = the two
57
+ invariants only (exactly today) · **standard** = ≤2 probes per task, ≤10 per
58
+ plan · **full** = the complete grilling procedure (one probe at a time,
59
+ survives-three-consecutive ends it). Every probe finding cites the plan line
60
+ it hits — an uncited probe is dropped, and findings feed the existing
61
+ REJECT-with-cited-reasons loop. Plan-lite drafts grill at light.
62
+
63
+ **The seam ritual (wave 6 — TDD's consent moment, no new prompt).** For code
64
+ tasks in TDD-eligible repos (the tdd skill installed + a test harness
65
+ detected — the build SOP's gate), every LEAF's `## Done-check` NAMES the seam
66
+ under test: *"`<runner>` on `<test-file>` at seam `<the public interface>`
67
+ exits 0"*. Seams come from recall (the brain's contracts/flows) — never
68
+ invented at build time. The plan-approval summary lists the seams as their
69
+ own line, so **the dev approving the plan IS the tdd skill's "confirm seams
70
+ with the user" step** — consent rides the existing approval gate.
71
+
44
72
  ## Procedure
45
73
 
46
74
  1. **Staleness check** — compare the platform envelope's `brainForSha` against the
@@ -58,7 +86,14 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
58
86
  carries the glimpse fields — `title` (what) · `description` (why) ·
59
87
  `approach` (how, one line) · `assignee` when known · `blocked_by` for
60
88
  intra-plan dependencies (a dependency IS a blocker; blocked is DERIVED,
61
- never a status) · optional `priority` 0–4 / `estimate`. Every LEAF carries a
89
+ never a status) · optional `priority` 0–4 / `estimate` ·
90
+ **`validation_tier` (wave 5 — rigor scales with blast radius, not with
91
+ process): assign per LEAF from what the task touches — contract/state-shape/
92
+ graph surface → `full` · pure types/docs/leaf-UI → `light` · everything else
93
+ → `standard` (the default when omitted). The tier bounds prism's
94
+ RE-DERIVATION DEPTH only; the Done-check gate itself never relaxes, and the
95
+ tier rides the task's loop events so sage can audit the tiering against
96
+ escaped-bug reality instead of anyone guessing.** Every LEAF carries a
62
97
  `## Done-check`. The blast radius goes on the EPIC's `domains:` — it rides
63
98
  `push_plan` and the platform renders the plan's brain slice beside it.
64
99
  ADR material (alternatives, risks, non-goals) lives in the epic body AND
@@ -104,8 +104,16 @@ its OWN question:
104
104
  7. **The active task's Done-check** (from `get_active_plan`): does the diff
105
105
  satisfy what the plan said done means?
106
106
  8. Verdict: **PASS** or **ITERATE** with per-finding citations (note id +
107
- file:line). Findings feed atlas (validate-and-correctatlas fixes, the
108
- judge re-checks; the reviewer never edits).
107
+ file:line). **Severity rides every finding (wave 6 — the
108
+ requesting-code-review taxonomy): `Critical (Must Fix)` · `Important
109
+ (Should Fix)` · `Minor (Nice to Have)`. ITERATE iff ≥1 Critical or
110
+ Important; Minor NEVER flips the verdict** (advisory noise must not
111
+ masquerade as a gate — and "not everything is Critical" is the taxonomy's
112
+ own first rule). Findings feed atlas (validate-and-correct — atlas fixes,
113
+ the judge re-checks; the reviewer never edits). The fresh-context reviewer
114
+ discipline the installed requesting-code-review skill prescribes
115
+ (precisely-crafted context, never the session's history) IS rafa's spawn
116
+ model already — confirmation, not new machinery.
109
117
  9. Emit **`report_loop_event(category: "review-verdict", outcome:
110
118
  PASS|ITERATE, subject: <task id or branch>)`** at the ruling — sage's
111
119
  evidence, monotonic, never a session-end sweep.
@@ -124,3 +132,9 @@ its OWN question:
124
132
  - Trusting the diff author's summary — judge code against rules.
125
133
  - Blocking on the gate — it is advisory; the merge reconciler is the hard gate.
126
134
  - Findings without citations (rule id + file:line) — uncited = unactionable.
135
+ - **Mutating anything while judging** (wave 6, from the code-reviewer's
136
+ read-only rule): the review never touches the working tree, the index, HEAD,
137
+ or branch state — inspect another revision via `git show`/a throwaway
138
+ worktree, never a checkout on this one.
139
+ - **Severity inflation** — a verdict where everything is Critical has stopped
140
+ discriminating; that is itself a review failure.
@@ -58,8 +58,14 @@ boundary; its product is a ledger reviewed *later*, never a live nudge. No mid-f
58
58
  ### 2. evidence-cited — SHAPES only, via `get_loop_events`
59
59
  Every learning cites its evidence, and evidence is **loop-event shapes and categories only**:
60
60
  verdict TYPES, gap CLASSES, miss TAXONOMIES, and the aggregate counts/rates over them — read via
61
- `get_loop_events` (category ∈ `prism-verdict | gate-result | reflex-outcome | distill-refutation`;
62
- outcome enum per category; `subject` is a shape reference, never content). A learning with no
61
+ `get_loop_events` (category ∈ `prism-verdict | gate-result | reflex-outcome | distill-refutation |
62
+ review-verdict`; outcome enum per category; `subject` is a shape reference, never content). Wave 5
63
+ enriched every event with THREE new analysis dimensions — split every rate by them: **verification
64
+ method** (`static` vs `live` — a PASS-rate that holds only on static rulings is a coverage gap, not
65
+ health), **actor envelope** (model · agent@version · runner — attribute a miss class to the agent
66
+ structure that produced it), and **tier** (`light|standard|full` — audit the tiering itself: a
67
+ light-tier task class that later produced escaped bugs is evidence the planner's tier rule needs a
68
+ diff; a full-tier class whose findings-density is ~zero is evidence of ceremony). A learning with no
63
69
  event-shape evidence is a hunch — drop it. sage never cites a customer artifact (no `file:line`
64
70
  into customer code; that field does not exist in the schema below).
65
71
 
@@ -101,7 +107,9 @@ human.
101
107
  The ledger is a **committed, human-reviewed governance artifact** — diffs for MR review. It is
102
108
  **NOT** a Convex table and **NOT** inside any customer `.rafa/brain/` (asset-free: learnings are
103
109
  about OUR agents; they never mix with customer knowledge). It lives beside the contract at
104
- `.claude/rafa/learnings/` (governance, versioned with the blueprint, MR-reviewed):
110
+ `.claude/rafa/learnings/` (governance, versioned with the blueprint, MR-reviewed). The platform's
111
+ "agent learnings" card renders a **projection** of it (summary rows via `report_learning`, wave
112
+ 4.1, owner-ratified 2026-07-24) — a display overlay, never a second truth and never the store:
105
113
 
106
114
  - `learnings/<id>.md` — one file per learning (one proposed card/SOP diff, cited to event shapes).
107
115
  - `ledger.md` — generated index: counts by category / target / status, and the top-leverage few.
@@ -169,7 +177,16 @@ enforced by sage and by MR review.
169
177
  7. **Ledger + leverage.** Regenerate `ledger.md`: counts by category / target / status, and the
170
178
  **top-leverage few** (impact × ease) — the single card/SOP change that would cover the widest
171
179
  class of misses, first.
172
- 8. **Present, don't nag, don't apply.** Surface the top few high-leverage learnings succinctly as
180
+ 8. **Mirror the summary rows (wave 4.1 the platform "agent learnings" card).** For each entry
181
+ written or status-changed in step 6, call `report_learning` with the entry's SUMMARY fields
182
+ (`id · pattern · categories · evidence_shape · proposed_diff_target · proposed_change ·
183
+ status · leverage`) — AFTER the scrub step and the file write, never before. This is a
184
+ **projection, not a second truth** (owner 2026-07-24): the committed `.md` stays the single
185
+ truth; the rows only render the Activity card. Upsert by id — re-runs refresh `status`,
186
+ never duplicate. The server re-screens every field (enum checks, `.claude/`-only target,
187
+ length caps, code-fence/secret rejection) — a rejection means the entry failed the asset-free
188
+ gate: fix the ENTRY, don't re-word past the screen.
189
+ 9. **Present, don't nag, don't apply.** Surface the top few high-leverage learnings succinctly as
173
190
  PROPOSALS. sage never applies a diff; a human reviews the ledger, and any accepted change lands
174
191
  as a versioned, MR-reviewed card/SOP edit (bump `version:`, record the why). Silent otherwise.
175
192
 
@@ -155,7 +155,10 @@ retrieval index. Bodies read like a senior engineer explaining that one concept
155
155
  3b. **Toolbox inventory** [deterministic] — ratified 2026-07-12: the REPO toolbox is a
156
156
  first-class brain domain (`toolbox`). Run `npx @rafinery/cli leverage --json` (the
157
157
  deterministic extractor) and author cited notes for the committed toolbox — skills
158
- (`.claude/skills/*/SKILL.md` name+description), commands, `.mcp.json` servers,
158
+ (`.claude/skills/*/SKILL.md` name+description), **the harness-neutral
159
+ `.agents/skills/*/SKILL.md` (wave 6 — the `agentSkills` block in the JSON: one
160
+ convention Claude Code · Codex · Cursor all read; consent-installed by
161
+ `rafa update`)**, commands, `.mcp.json` servers,
159
162
  granted permissions — cites into the config files themselves (contract §2; they are
160
163
  citable file:line). This makes the toolbox recallable through the SAME MCP surface
161
164
  as all knowledge and refreshable like any note. Personal `~/.claude/` is NEVER
@@ -77,7 +77,7 @@ export function ensureBrainRepo(cwd, { requireRemote = true } = {}) {
77
77
  // (not knowledge). MERGED line-by-line so clones ignored under an older CLI
78
78
  // still gain new exclusions.
79
79
  const ignore = join(rafaDir, ".gitignore");
80
- const IGNORES = ["hydration.json", "*.theirs.md", "distill-incoming/", "distill-verdicts.json", "dirty.jsonl", "reflex.jsonl", "sensor-errors.jsonl", "benchmark.demo.json"];
80
+ const IGNORES = ["hydration.json", "*.theirs.md", "distill-incoming/", "distill-verdicts.json", "dirty.jsonl", "reflex.jsonl", "sensor-errors.jsonl", "benchmark.demo.json", "session-facts.json", "loop-events-tail.json"];
81
81
  const cur = existsSync(ignore) ? readFileSync(ignore, "utf8") : "";
82
82
  const have = new Set(cur.split("\n").map((l) => l.trim()).filter(Boolean));
83
83
  const missing = IGNORES.filter((l) => !have.has(l));
@@ -27,6 +27,62 @@ const die = (m) => {
27
27
  process.exit(1);
28
28
  };
29
29
 
30
+ // The GIT-PLANE tail beat (0.8.16, live-catch 2026-07-24): brain deltas written
31
+ // AFTER the last code commit (verify flips · late-authored notes · regenerated
32
+ // manifest) have no post-commit mirror to ride, and the pre-push ceremony may
33
+ // already have run — they strand uncommitted until the next code commit. The
34
+ // checkpoint beat is the boundary that CREATES that tail, so it fires the SAME
35
+ // vendored worker the git hooks run (single implementation, tail-aware since
36
+ // 0.8.16) whenever the brain tree is dirty. One beat, both planes: "clean"
37
+ // from this command means capture AND mirror. Best-effort — a mirror problem
38
+ // never fails a checkpoint.
39
+ function mirrorTail(ROOT, branch) {
40
+ const rafa = join(ROOT, ".rafa");
41
+ const worker = join(ROOT, ".claude", "rafa", "hooks", "brain-commit.mjs");
42
+ if (!existsSync(join(rafa, ".git")) || !existsSync(worker)) return;
43
+ const shR = (cmd) =>
44
+ execSync(cmd, { cwd: rafa, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
45
+ let dirty = "";
46
+ try {
47
+ dirty = shR("git status --porcelain");
48
+ } catch {
49
+ return; // unreadable brain repo — the hooks' own paths will surface it
50
+ }
51
+ if (dirty === "") return; // git plane already clean — nothing stranded
52
+ let before = "";
53
+ try {
54
+ before = shR("git rev-parse HEAD");
55
+ } catch {
56
+ /* unborn HEAD — the worker handles it */
57
+ }
58
+ try {
59
+ execSync(`node "${worker}"`, { cwd: ROOT, stdio: ["ignore", "ignore", "ignore"], timeout: 60000 });
60
+ } catch {
61
+ /* worker is silent + non-blocking by design; fall through to the report */
62
+ }
63
+ try {
64
+ const after = shR("git rev-parse HEAD");
65
+ if (after !== before) {
66
+ let pushed = false;
67
+ try {
68
+ pushed = shR(`git rev-parse "origin/${branch}"`) === after;
69
+ } catch {
70
+ /* no remote tracking yet */
71
+ }
72
+ console.log(
73
+ `✓ brain mirror: session tail committed (${after.slice(0, 12)})` +
74
+ (pushed ? " + pushed" : " — push rides the next pre-push ceremony"),
75
+ );
76
+ } else {
77
+ console.log(
78
+ "• brain tree dirt is disposable hydration cache — nothing real to mirror",
79
+ );
80
+ }
81
+ } catch {
82
+ /* report is best-effort */
83
+ }
84
+ }
85
+
30
86
  export default async function checkpoint() {
31
87
  const ROOT = process.cwd();
32
88
 
@@ -101,6 +157,9 @@ export default async function checkpoint() {
101
157
 
102
158
  if (candidates.length === 0) {
103
159
  console.log(`✓ working set clean on ${branch} — nothing to checkpoint`);
160
+ // A clean CAPTURE plane can still hide a dirty GIT plane (the live catch:
161
+ // "checkpoint said clean" while the session tail sat uncommitted).
162
+ mirrorTail(ROOT, branch);
104
163
  // Knowledge-absence nudge (live-catch 2026-07-17: a session can do real
105
164
  // work and bank ZERO knowledge, silently — no sensor fires on absence).
106
165
  // Deterministic signal only: code edits recorded, no brain delta on this
@@ -184,22 +243,29 @@ export default async function checkpoint() {
184
243
  (conflicts ? ` · ${conflicts} conflict(s) need YOUR decision (see above)` : ""),
185
244
  );
186
245
 
187
- // prism-verdict backstop (harness-arc wave 0): gate-result/reflex-outcome are
188
- // emitted mechanically by the CLI, but prism verdicts are LLM judgments only
189
- // the session can report. The checkpoint beat — the SOP's own emit moment
190
- // checks the store and reminds while it's still empty. Best-effort, one line.
246
+ // Both planes, one beat: the capture sync above, the brain mirror here.
247
+ // (Runs before the conflict exit a contested working set is still worth a
248
+ // durable mirror of the dev's current copies; .theirs.md files are ignored.)
249
+ mirrorTail(ROOT, branch);
250
+
251
+ // prism-verdict backstop (harness-arc wave 0) + the sage-due cache (wave
252
+ // 5.5): the SAME single best-effort call, enriched. It now fetches recent
253
+ // events (no category filter) and caches their SHAPES locally
254
+ // (.rafa/loop-events-tail.json — category/at only, subjects excluded) so the
255
+ // SessionStart digest computes "sage due" with ZERO added platform calls.
256
+ // The old prism-verdict reminder derives from the same response.
191
257
  try {
192
- const evts = await callTool(ROOT, "get_loop_events", {
193
- category: "prism-verdict",
194
- limit: 1,
195
- });
196
- if ((evts.events ?? []).length === 0)
258
+ const { writeLoopCache } = await import("./loop-cache.mjs");
259
+ const evts = await callTool(ROOT, "get_loop_events", { limit: 200 });
260
+ const events = evts.events ?? [];
261
+ writeLoopCache(ROOT, { fetchedAt: new Date().toISOString(), events });
262
+ if (events.filter((e) => e.category === "prism-verdict").length === 0)
197
263
  console.log(
198
264
  " ℹ no prism-verdict loop events recorded yet — the build SOP emits " +
199
265
  "report_loop_event(prism-verdict, PASS|ITERATE, <task id>) at each Done-check ruling.",
200
266
  );
201
267
  } catch {
202
- /* unprovisioned/unreachable — the reminder is best-effort */
268
+ /* unprovisioned/unreachable — the reminder + cache are best-effort */
203
269
  }
204
270
  if (conflicts) process.exit(2);
205
271
  }
@@ -164,22 +164,11 @@ export function citesGround(content, cwd = process.cwd()) {
164
164
  }
165
165
 
166
166
  // Rewrite a survivor's frontmatter `cites:` DSL so a re-grounded citation points
167
- // at where the token NOW lives on main otherwise banking it as authored would
168
- // fail the checker (the stale line no longer resolves). Mechanical string
169
- // substitution on the exact `file:line :: token` locator; untouched lines pass
170
- // through. Returns the rewritten content.
171
- export function rewriteCites(content, regroundings) {
172
- let out = content;
173
- for (const r of regroundings ?? []) {
174
- const fromLoc = r.cite.loc; // file:line[-end]
175
- const toLoc = r.to.loc; // file:line
176
- // Match the DSL line ` - <fromLoc> :: <token>` and swap the locator only.
177
- const esc = fromLoc.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
178
- const re = new RegExp(`(-\\s+)${esc}(\\s*::\\s*)`, "g");
179
- out = out.replace(re, `$1${toLoc}$2`);
180
- }
181
- return out;
182
- }
167
+ // at where the token NOW lives on main. MOVED to the gate (wave 5.3) so
168
+ // `verify-citations --fix` can use it without an import cycle (this module
169
+ // imports the gate's primitives, never the reverse); re-exported here so the
170
+ // doctrine's callers keep their import path.
171
+ export { rewriteCites } from "../gate/verify-citations.mjs";
183
172
 
184
173
  // ── folding N branch working sets into ONE run (case 1 · case 5) ────────────────
185
174
  // Input: [{ branch, capturedAtSha, mergedAt, files: [{ path, content }] }].
package/lib/doctor.mjs CHANGED
@@ -79,7 +79,25 @@ export async function runDoctor(ROOT = process.cwd()) {
79
79
  }
80
80
  }
81
81
 
82
- // 3 · the round-trip the heartbeat must LAND, not just send. A recorded
82
+ // 3 · capabilitiesevery STAMPED-installed skill dependency re-verified
83
+ // against disk (wave 6): dir present, frontmatter parses, hashes match the
84
+ // bundle, license file where required. Declined/never-offered = silent.
85
+ {
86
+ const { verifyInstalled, neededSkills } = await import("./skill-deps.mjs");
87
+ const results = verifyInstalled(ROOT);
88
+ const pending = neededSkills(ROOT).length;
89
+ if (results.length || pending) {
90
+ console.log("• capabilities (skill dependencies)");
91
+ for (const r of results) {
92
+ if (r.ok) ok(`${r.name} installed + verified`);
93
+ else bad(`${r.name} ${r.reason}`, "re-install: `rafa update --skills`");
94
+ }
95
+ if (pending)
96
+ console.log(` · ${pending} declared dep(s) not installed — offer: \`rafa update --skills\``);
97
+ }
98
+ }
99
+
100
+ // 4 · the round-trip — the heartbeat must LAND, not just send. A recorded
83
101
  // response proves key + url + repo scoping + the platform handler, end to end.
84
102
  console.log("• platform round-trip");
85
103
  if (provisioned) {