@rafinery/cli 0.3.0 → 0.4.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 (43) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +17 -7
  3. package/bin/rafa.mjs +68 -20
  4. package/blueprint/.claude/agents/atlas.md +28 -20
  5. package/blueprint/.claude/agents/bloom.md +15 -8
  6. package/blueprint/.claude/agents/compass.md +46 -0
  7. package/blueprint/.claude/agents/prism.md +42 -25
  8. package/blueprint/.claude/commands/rafa.md +151 -26
  9. package/blueprint/{.rafa → .claude/rafa}/contract.md +108 -22
  10. package/blueprint/.claude/skills/rafa-build/SKILL.md +76 -0
  11. package/blueprint/.claude/skills/rafa-distill/SKILL.md +87 -0
  12. package/blueprint/{.rafa/capabilities/improve.md → .claude/skills/rafa-improve/SKILL.md} +10 -5
  13. package/blueprint/.claude/skills/rafa-insights/SKILL.md +71 -0
  14. package/blueprint/{.rafa/capabilities/leverage.md → .claude/skills/rafa-leverage/SKILL.md} +9 -4
  15. package/blueprint/{.rafa/capabilities/plan.md → .claude/skills/rafa-plan/SKILL.md} +30 -3
  16. package/blueprint/{.rafa/capabilities/scan.md → .claude/skills/rafa-scan/SKILL.md} +14 -9
  17. package/blueprint/{.rafa/capabilities/validate.md → .claude/skills/rafa-validate/SKILL.md} +14 -5
  18. package/lib/blueprint.mjs +22 -12
  19. package/lib/brain-repo.mjs +100 -0
  20. package/lib/checkpoint.mjs +138 -0
  21. package/lib/ci-setup.mjs +109 -0
  22. package/lib/claude-config.mjs +5 -5
  23. package/lib/compile.mjs +6 -21
  24. package/lib/distill.mjs +237 -0
  25. package/lib/fold.mjs +53 -0
  26. package/lib/gate/compile.mjs +549 -0
  27. package/lib/gate/verify-citations.mjs +156 -0
  28. package/lib/hydrate.mjs +96 -0
  29. package/lib/init.mjs +15 -30
  30. package/lib/leverage/adapters/claude-code.mjs +5 -4
  31. package/lib/mcp-client.mjs +97 -0
  32. package/lib/mcp-config.mjs +1 -1
  33. package/lib/migrations/index.mjs +39 -3
  34. package/lib/pull.mjs +129 -0
  35. package/lib/push.mjs +93 -14
  36. package/lib/releases.mjs +23 -0
  37. package/lib/verify-citations.mjs +9 -0
  38. package/lib/working-set.mjs +113 -0
  39. package/package.json +2 -2
  40. package/blueprint/.rafa/bin/rafa-compile.mjs +0 -478
  41. package/blueprint/.rafa/bin/rafa-push.mjs +0 -75
  42. package/blueprint/.rafa/bin/verify-citations.mjs +0 -153
  43. package/blueprint/.rafa/capabilities/build.md +0 -39
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: rafa-distill
3
+ description: "rafa SOP — merge-time reconciliation: validate a merged branch's WORKING SET against merged MAIN (prism), author survivors into the org brain through verify-citations + compile + push (atlas), refute loudly with citations; contested items flagged needs-adjudication, never guessed. Loaded on /rafa distill or the merge offer; CI runs the same SOP headlessly (rafa distill --headless)."
4
+ ---
5
+
6
+ # distill — merge-time reconciliation of a branch working set into the org brain
7
+
8
+ > Status: **active.** Two runners, ONE SOP: the org's CI (`rafa distill
9
+ > --headless`, installed by `rafa ci-setup`, driven by the ORG'S OWN
10
+ > `ANTHROPIC_API_KEY` — never stored on the platform) and the dev session
11
+ > (offer-driven at bootstrap, or explicit `/rafa distill <branch>`) as the
12
+ > fallback when CI isn't wired or failed. Depends on: brain (#1), the branch
13
+ > working set (synced via `rafa checkpoint`).
14
+
15
+ The rigor gradient this enforces (ratified 2026-07-10): **dev↔dev = CAS + a
16
+ session prompt · branch↔branch = free mechanical fold (no LLM, no prism) ·
17
+ branch→main = full distillation + gates.** Cost tracks consequence. Working-set
18
+ files are candidate-grade — attributed, loose, never served as org truth. The
19
+ merge to main is the one moment rigor fires: what survives validation against
20
+ merged main enters the org brain through the normal gates; what doesn't is
21
+ refuted loudly back to its author; what can't be decided is flagged
22
+ `needs-adjudication` — NEVER guessed. Knowledge propagates exactly like the
23
+ code it describes.
24
+
25
+ ## Trigger
26
+
27
+ - **CI (normal path):** the reconcile workflow fires on the PR-merged EVENT
28
+ (squash/rebase-safe — ancestry is never consulted): merge to the default
29
+ branch → `rafa distill --headless <branch>` · branch→parent merge →
30
+ `rafa fold --from <branch> --to <parent>` (mechanical, no LLM).
31
+ - **Offer (session fallback):** at bootstrap the conductor checks
32
+ `get_working_set` for active files on branches whose code reached main —
33
+ *"branch <x> merged with N working-set files — distill now?"* Part of the
34
+ ONE bootstrap digest. Boundary consent; accepted offer = invocation.
35
+ - **Explicit:** `/rafa distill <branch>`.
36
+
37
+ ## Procedure (the trio, distillation roles)
38
+
39
+ 1. **Collect** — `get_working_set(repo, branch, status: active)`. Zero files →
40
+ nothing to do, say so, stop. Rows already `needs-adjudication` are a HUMAN's
41
+ to resolve — surface them in the digest, never fold them in silently.
42
+ 2. **Validate (prism, context-isolated)** — the target is **merged MAIN as of
43
+ NOW, never the fork point and never a stale checkout**: `git fetch origin`
44
+ first; judge every claim against the fetched trunk. Confirm every citation
45
+ resolves (the cited line contains its token — grep it yourself). A claim
46
+ that can't be confirmed with a `file:line` is REFUTED (cited reason).
47
+ Contested/low-confidence → `needs-adjudication`, never a guess. Files about
48
+ code the merge did NOT touch are judged on their own merits, same bar.
49
+ 3. **Author (atlas)** — for each survivor: write/update the org-brain file
50
+ (contract §2) with real cites into main; `anchor:` on contracts (hydrated
51
+ files lost it — re-declare); fold into an existing note when one covers the
52
+ topic (supersede, never duplicate).
53
+ 4. **Gate + ship** — `rafa verify-citations` AND `rafa compile` to exit 0 →
54
+ `rafa push`. Only now has anything entered the org brain. A failed gate
55
+ aborts EVERYTHING: nothing resolved, nothing pushed, working set intact.
56
+ 5. **Resolve** — `resolve_working_file(path, distilled)` for survivors —
57
+ CAS: only a live row resolves; a failure means ANOTHER runner already
58
+ distilled this branch — STOP and reconcile against what it shipped.
59
+ `resolve_working_file(path, refuted, note)` for failures — TELL the author
60
+ which files died and why (cited); refutation is feedback, not silence.
61
+ `resolve_working_file(path, needs-adjudication, note)` for the undecidable —
62
+ the next session's digest offers the decision.
63
+
64
+ ## Branch→branch merges (sub-feature → feature): FOLD, don't distill
65
+
66
+ Distillation targets ONLY the default branch. When a sub-feature merges into
67
+ its parent branch, the working set is **folded forward mechanically** —
68
+ `rafa fold --from <branch> --to <parent>` (or the CI fold job): absent on the
69
+ parent → re-keyed · identical content → merged silently · true same-file
70
+ divergence → `needs-adjudication` on the parent row (incoming preserved,
71
+ attributed). No LLM, no prism — the knowledge keeps riding with the code until
72
+ the code reaches main.
73
+
74
+ Sub-feature CONTEXT: a branch's working knowledge = org brain (via MCP) + its
75
+ own working set + unmerged ANCESTOR branches' working sets (the conductor
76
+ derives the ancestor chain from git and calls `get_working_set` per ancestor —
77
+ the platform stores no lineage).
78
+
79
+ ## Rules
80
+
81
+ - Validation target is main at distillation time, never the fork point.
82
+ - An abandoned branch's working set is never distilled — it dies with the
83
+ branch (propagation mirrors code).
84
+ - No file skips steps 2–4; there is no fast path into the org brain.
85
+ - CI can detect and flag; only a dev session resolves a human's divergence.
86
+ - Dev-level observations found among the files route to `put_dev_insight`
87
+ (the author's user brain), never to the org brain.
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: rafa-improve
3
+ description: "rafa SOP — bloom's continuous-improvement pass: multi-lens, cited, prioritized (P0–P3) ledger of the silent rot no gate catches; leverage-ranked, advisory, never nagging. Loaded on /rafa improve."
4
+ ---
5
+
1
6
  # improve — continuous improvement (capability #2)
2
7
 
3
8
  rafa's second mission: **drive the codebase worst → well-managed, compounding via work
@@ -42,12 +47,12 @@ Invoke via `/rafa improve`. The brain (#1) must exist — bloom *uses* it as the
42
47
  - `improvements/<id>.md` — one file per improvement (cited, prioritized, status-tracked).
43
48
  - `ledger.md` — generated index: counts by priority/lens/status + the **health/debt trend**.
44
49
  Its **frontmatter carries the machine-read `open` · `debt_score` · `by_priority`** (per
45
- [`.rafa/contract.md`](../contract.md) §5) — the platform reads *that*, never the body tables,
50
+ [`.claude/rafa/contract.md`](../../rafa/contract.md) §5) — the platform reads *that*, never the body tables,
46
51
  and `rafa compile` cross-checks `open`/`by_priority` against the actual improvement rows.
47
52
  - `manifest.json` — regenerated by `rafa compile`; the JSON the platform ingests. Never hand-edit.
48
53
  - `citation-check.md` — generated by the checker (improvement cites resolve).
49
54
 
50
- **Improvement schema.** The **strict contract is [`.rafa/contract.md`](../contract.md) §3** —
55
+ **Improvement schema.** The **strict contract is [`.claude/rafa/contract.md`](../../rafa/contract.md) §3** —
51
56
  `rafa compile` rejects violations. `title`, `summary`, `fix` are **required frontmatter** (the
52
57
  platform shows them; nothing is scraped from prose). Example *illustrative — from the rafa repo*:
53
58
  ```yaml
@@ -89,13 +94,13 @@ convention — see [[rsc-client-boundary]]). Compiles + runs, so no gate catches
89
94
  whose code is now clean (`status: fixed`); **preserve** the dev's prior triage
90
95
  (backlog/wontfix stay).
91
96
  5. **Cite-check (the fidelity gate).** Run
92
- `node .rafa/bin/verify-citations.mjs --root=.rafa/improve --dirs=improvements`. Every cite
97
+ `npx @rafinery/cli verify-citations --root=.rafa/improve --dirs=improvements`. Every cite
93
98
  must resolve; **drop or fix any that don't** — a hallucinated improvement is the cardinal sin.
94
99
  6. **Ledger + trend.** Regenerate `ledger.md`: counts by priority/lens/status + a **debt
95
100
  score** (weighted open improvements) and its trend vs the last run — the compounding made
96
101
  visible. Write the machine-read **frontmatter** `open` · `debt_score` · `by_priority`
97
- ([contract §5](../contract.md)) — the platform reads that.
98
- 7. **Contract gate.** Run `node .rafa/bin/rafa-compile.mjs`. Fix every `path · field · rule`
102
+ ([contract §5](../../rafa/contract.md)) — the platform reads that.
103
+ 7. **Contract gate.** Run `npx @rafinery/cli compile`. Fix every `path · field · rule`
99
104
  violation (missing `title`/`summary`/`fix`, bad enum, ledger counts not matching rows) and
100
105
  re-run until it **exits 0** and writes `manifest.json`. Validate-and-correct: a
101
106
  schema-invalid brain never ships.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: rafa-insights
3
+ description: "rafa SOP — compass's user brain: bootstrap from the dev's native /insights report, refine with consent, steer back at boundaries; account-scoped, cross-repo, private. Loaded on /rafa insights and routed capture moments."
4
+ ---
5
+
6
+ # insights — the user brain: bootstrap, refine, steer (compass's SOP)
7
+
8
+ > Status: **active (v1 — conductor-mediated).** Invoke via `/rafa insights`
9
+ > (bootstrap/refresh) — capture during work is the conductor's job (rafa.md
10
+ > §capture routes dev-level observations here). Store: `put_dev_insight` /
11
+ > `list_dev_insights` / `remove_dev_insight` (account-scoped, cross-repo).
12
+
13
+ ## The creed (owner, verbatim — non-negotiable)
14
+ **"We are not stealing their knowledge; we are working with them to refine
15
+ their knowledge to do bigger things in better way."** Partnership, not
16
+ extraction. The dev is the beneficiary AND co-author of their own refinement.
17
+ The measure of this work is the dev doing bigger things better — never the dev
18
+ becoming legible to anyone else.
19
+
20
+ ## Honesty about tenancy (pre-orgs)
21
+ Until org accounts land, teammates' keys live under the repo owner's platform
22
+ account: MCP scoping keeps one KEY from reading another's insights, but the
23
+ ACCOUNT owner can see data stored under their account on the platform. When
24
+ the key isn't the account owner's, the offer says so: *"banked privately to
25
+ your key — note: stored under this repo owner's account until org accounts
26
+ ship."* Consent means informed.
27
+
28
+ ## The boundary (both directions, no exceptions)
29
+ - Nothing **code-cited** enters the user brain (code facts → the branch working set →
30
+ org brain via distillation).
31
+ - Nothing **person-scoped** leaves it — not to teammates, not to repo activity
32
+ feeds, not to aggregates without explicit standing consent. This floor is
33
+ enforced in the platform dispatch, not just here.
34
+
35
+ ## Procedure
36
+
37
+ ### 1. Bootstrap (first run, or on request)
38
+ Read the dev's native usage report (Claude Code `/insights` — ask the dev to
39
+ run it if absent; its report lands at `~/.claude/usage-data/report.html`).
40
+ **Distill with judgment — never parse the HTML mechanically** (its shape is
41
+ unversioned; the model reads it like a document). Extract dev-level signals
42
+ only: asking/decomposition patterns, repeated instructions (each is a candidate
43
+ preference), friction loops, unused leverage. For each candidate: show the dev,
44
+ **offer** — banked only on yes (`put_dev_insight`, kind pattern|preference|
45
+ constraint).
46
+
47
+ ### 2. Continuous refinement (every session, conductor-routed)
48
+ **Before ANY put: `list_dev_insights` first.** Update the matching existing
49
+ insight (its id) instead of creating a near-copy — and NEVER re-offer an
50
+ insight the dev deleted (deleted rows persist as content-erased tombstones in
51
+ the list precisely so you can honor the refusal: "you removed this before; not
52
+ re-suggesting"). **Never bank a secret**: a stated constraint quoting a
53
+ credential is rephrased value-free before the offer (the platform screens too —
54
+ don't rely on it).
55
+ Capture moments: a correction repeated across sessions · a stated preference ·
56
+ a named constraint · a friction loop. Route: dev-level → offer → bank;
57
+ code-level → the branch working set (not yours — rafa.md §capture routes it). Update beats duplicate: refine the
58
+ existing insight (`put_dev_insight` with its id) rather than accreting near-
59
+ copies.
60
+
61
+ ### 3. Steering (the payoff — propose, the dev disposes)
62
+ At natural boundaries only, at most one nudge: surface the insight that changes
63
+ what the dev does next — "you've corrected X three times: want it banked as a
64
+ rule?" · "this decomposition worked before, reuse it?" · "you never use plan
65
+ mode on big changes; it would have caught this." Dismissal is final for the
66
+ session. Never rank, never compare devs, never nag.
67
+
68
+ ### 4. Legibility (consent's other half)
69
+ On request, show everything (`list_dev_insights`), correct anything, delete
70
+ anything (`remove_dev_insight`) — no residue, no argument. An insight the dev
71
+ says is wrong IS wrong.
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: rafa-leverage
3
+ description: "rafa SOP — tune the dev's agent toolbox: detect missing/misconfigured/unused permissions, skills, MCP servers; on approval apply exact merges. Loaded on /rafa leverage."
4
+ ---
5
+
1
6
  # leverage — get more out of the toolbox you already have (capability #3)
2
7
 
3
8
  rafa's third mission: **make the dev's agent toolchain pull its full weight.** rafa already
@@ -37,11 +42,11 @@ at the fix. This is platform DNA: **leverage what's present; never reinvent it.*
37
42
 
38
43
  ## Architecture — the ToolchainAdapter seam
39
44
 
40
- The engine ([`lib/leverage/engine.mjs`](../../packages/cli/lib/leverage/engine.mjs)) gathers
45
+ The engine ([`lib/leverage/engine.mjs`](../../../packages/cli/lib/leverage/engine.mjs)) gathers
41
46
  light, tool-agnostic repo signals (`repoContext`: stack deps, `.env` files + whether they're
42
47
  ignored, declared MCP servers), then runs every **detected** adapter and ranks the tips.
43
48
 
44
- Each adapter implements one contract ([`lib/leverage/adapters/index.mjs`](../../packages/cli/lib/leverage/adapters/index.mjs)):
49
+ Each adapter implements one contract ([`lib/leverage/adapters/index.mjs`](../../../packages/cli/lib/leverage/adapters/index.mjs)):
45
50
 
46
51
  | Member | Shape | Job |
47
52
  |---|---|---|
@@ -57,7 +62,7 @@ list) — the seam is real, the adapters are added when a user actually runs on
57
62
 
58
63
  ### What the Claude Code adapter checks today
59
64
  - **Secrets** — `.env*` present but not gitignored → `P1` (protect the keys before a scan or a commit).
60
- - **Permissions** — `.rafa/bin` vendored but its tools aren't allow-listed / no `settings.json`
65
+ - **Permissions** — rafa provisioned but its CLI commands are not allow-listed / no `settings.json`
61
66
  → `P2`, fix = `rafa init`/`update` (which *merge*, never clobber — see the CLI).
62
67
  - **MCP** — a stack with no matching server wired (e.g. Convex repo, no `convex` MCP) → `P2`.
63
68
  - **Skills** — no project skills captured → `P3`.
@@ -70,7 +75,7 @@ behind the same contract. The engine and the `rafa leverage` command never chang
70
75
  ## Boundaries
71
76
 
72
77
  - **Committed config only.** Read the repo's `.claude/` (org config). The dev's personal
73
- `~/.claude/` is private — never inspected. (Mirrors [scan](scan.md)'s privacy line.)
78
+ `~/.claude/` is private — never inspected. (Mirrors [scan](../rafa-scan/SKILL.md)'s privacy line.)
74
79
  - **Secrets are off-limits.** Detect that an `.env` is unprotected; **never open it or read a
75
80
  value.** Same hard line as the scan guardrail.
76
81
  - **Read-only.** leverage itself mutates nothing. The only writes are `rafa init`/`update`
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: rafa-plan
3
+ description: "rafa SOP — brain-grounded, prism-validated decomposition of an intent into contract §7 plan files (each child with a Done-check); plan-lite for small blast radius. Loaded on /rafa plan or plan-shaped intent."
4
+ ---
5
+
1
6
  # plan — brain-grounded, prism-validated decomposition (capability #3)
2
7
 
3
8
  > Status: **active.** The first *consumer* of the stores — where the brain starts to
@@ -14,7 +19,10 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
14
19
  - **atlas drafts** — RECALL the brain slice for the intent's domains through the
15
20
  knowledge MCP (`get_coverage` to navigate → `search_knowledge` → `get_rule` /
16
21
  `get_playbook`, including non-exemplars), name the BLAST RADIUS from coverage,
17
- decompose into parent + child plan files.
22
+ decompose into parent + child plan files. *Recall is automatic — the SOP calls
23
+ the tools; the dev never asks for it. Repo not platform-connected (no `rafinery`
24
+ MCP in the session)? Fall back to reading `.rafa/brain/` files directly — same
25
+ knowledge, just unserved.*
18
26
  - **bloom pulls** — `list_improvements` in the blast radius; surface the
19
27
  top-leverage open items as optional *"while-you're-here"* child tasks
20
28
  (leverage-ranked, dismissible, never blocking).
@@ -38,8 +46,27 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
38
46
  5. **prism plan-validation** → REJECT/fix loop until clean.
39
47
  6. **Approval gate** (owner). Then materialize `plans/<plan>/*.md` per contract §7
40
48
  (parent + child-owned files, globally-unique prefixed ids) + `active.md` pointer
41
- → `rafa compile` `rafa push` (the plan is now resumable from ANY session,
42
- machine, or teammate via `get_active_plan`).
49
+ → `rafa compile` (validate the files) **`push_plan` + `set_active_plan`
50
+ immediately, no second prompt — plan approval IS the push trigger** (the
51
+ dedicated plans channel; plans never ride the brain manifest). The dev just
52
+ approved this exact content; a connected repo renders it on the platform
53
+ within moments, resumable from ANY session, machine, or teammate — *"list
54
+ plans"* shows names only; *"load plan X"* (`get_plan`) materializes the full
55
+ plan (bodies included) back into `.rafa/plans/`.
56
+
57
+ ## plan-lite — the light path (a mode of THIS SOP, not a bypass)
58
+ The full choreography earns its weight on cross-cutting work; a one-file change routed
59
+ through five steps teaches devs to route around rafa — and a route-around is a product
60
+ failure. So the conductor weighs the **blast radius** (from coverage at recall time):
61
+
62
+ - **Lite** (radius ≤ 2 domains and no contract/schema surface touched): ONE parent +
63
+ ONE child file, recall scoped to the touched domains, bloom pull skipped, prism gate
64
+ collapses to the two invariants that never relax — every task grounded, the child
65
+ carries a `## Done-check`. Same contract §7 files, same compile, same approval.
66
+ - **Full** (anything wider): the complete procedure above.
67
+
68
+ The dev can force either: `/rafa plan` always offers the choice when the radius is
69
+ borderline; "skip" bypasses planning entirely (their call — note it, never police it).
43
70
 
44
71
  ## Deferred / open
45
72
  - Capture-back of plan-time decisions → brain (needs the capture engine).
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: rafa-scan
3
+ description: "rafa SOP — atlas's founding scan: map the whole codebase into cited rules/playbooks + coverage, breadth before depth, every citation mechanically verified. Loaded when the /rafa conductor runs scan or init; atlas follows it verbatim."
4
+ ---
5
+
1
6
  # Scan — capability
2
7
 
3
8
  atlas's founding contribution to the brain: read the **whole** codebase and write its
@@ -63,7 +68,7 @@ A well-formed note with a wrong citation is worse than no note — a dev greps t
63
68
  file, finds nothing, and stops trusting the brain. So fidelity is non-negotiable:
64
69
 
65
70
  - **Every citation is mechanically checked — by script, all of them.** Write each cite as
66
- `file:line :: token`, then run `node .rafa/bin/verify-citations.mjs`, which asserts
71
+ `file:line :: token`, then run `npx @rafinery/cli verify-citations`, which asserts
67
72
  every cited line contains its token and exits non-zero on any miss. Fix every ✗ until it
68
73
  exits 0. A "spot-check" does NOT satisfy this. Off-by-N or wrong-file → fix or drop.
69
74
  - **Contract sites come from grep, not memory — and the checker enforces it.** Every
@@ -87,10 +92,10 @@ file, finds nothing, and stops trusting the brain. So fidelity is non-negotiable
87
92
  - `.rafa/brain/rules/*.md` — `convention` + `contract` notes ("what not to break")
88
93
  - `.rafa/brain/playbooks/*.md` — `flow` + `how-to` notes ("how it works / how to add X")
89
94
  - `.rafa/brain/coverage.md` — the coverage report. **Machine-read frontmatter** per
90
- [`.rafa/contract.md`](../contract.md) §6: `domains: { <domain>: mapped|thin|stubbed|empty, … }`
95
+ [`.claude/rafa/contract.md`](../../rafa/contract.md) §6: `domains: { <domain>: mapped|thin|stubbed|empty, … }`
91
96
  — one entry per domain from Step 1. The body keeps the per-criterion PASS/FAIL narrative.
92
97
 
93
- **Note format.** The **strict contract is [`.rafa/contract.md`](../contract.md) §2** — every
98
+ **Note format.** The **strict contract is [`.claude/rafa/contract.md`](../../rafa/contract.md) §2** — every
94
99
  required field there is mandatory and `rafa compile` (Step 7) **rejects** any violation with a
95
100
  `path · field · rule` error you must fix. Machine-read fields live in frontmatter; the body is
96
101
  prose (never parsed). The example is *illustrative — a contract from the rafa reference repo*:
@@ -173,7 +178,7 @@ retrieval index. Bodies read like a senior engineer explaining that one concept
173
178
 
174
179
  7. **Verification & synthesis** — apply the 100× filter (a note earns a file only if it
175
180
  answers a work-time question AND eliminates a recurring tax), then write the notes. Run
176
- the **deterministic citation checker** — `node .rafa/bin/verify-citations.mjs` — which
181
+ the **deterministic citation checker** — `npx @rafinery/cli verify-citations` — which
177
182
  verifies every `file:line :: token` cite resolves (B1) AND that every contract `anchor:`'s
178
183
  code occurrences are all cited (B2); fix every ✗ and re-run until it **exits 0**. It
179
184
  writes `.rafa/brain/citation-check.md` — **reference that file** from `coverage.md`,
@@ -182,8 +187,8 @@ retrieval index. Bodies read like a senior engineer explaining that one concept
182
187
  Step-1 checklist — every domain has ≥1 note or is flagged thin/stubbed; no silent
183
188
  truncation). Finalize `coverage.md` with per-criterion PASS/FAIL.
184
189
 
185
- Finally, run the **contract gate** — `node .rafa/bin/rafa-compile.mjs` — which validates
186
- every file against [`.rafa/contract.md`](../contract.md) and emits `.rafa/manifest.json`
190
+ Finally, run the **contract gate** — `npx @rafinery/cli compile` — which validates
191
+ every file against [`.claude/rafa/contract.md`](../../rafa/contract.md) and emits `.rafa/manifest.json`
187
192
  (the JSON the platform ingests). It fails loudly with `path · field · rule` errors; **fix
188
193
  every one and re-run until it exits 0.** This is the validate-and-correct loop: a
189
194
  schema-invalid brain never ships, and the platform never guesses a value. Do not hand-edit
@@ -211,7 +216,7 @@ A scan **PASSES only if every box below is true.** Any single failure → `itera
211
216
  - [ ] **A4** Every `thin`/`stubbed`/`empty` domain states why. (a silent gap = FAIL.)
212
217
 
213
218
  **B · Fidelity — verify, don't infer (hard gate)**
214
- - [ ] **B1** `node .rafa/bin/verify-citations.mjs` **exits 0** — every `file:line :: token`
219
+ - [ ] **B1** `npx @rafinery/cli verify-citations` **exits 0** — every `file:line :: token`
215
220
  cite mechanically verified (all, not a sample); its table pasted in `coverage.md`.
216
221
  A spot-check, or a PASS without the checker exiting 0, = FAIL.
217
222
  - [ ] **B2** Every `type: contract` declares `anchor:` — a token, or `anchor: none` (with
@@ -238,9 +243,9 @@ A scan **PASSES only if every box below is true.** Any single failure → `itera
238
243
  **D · Format & contract**
239
244
  - [ ] **D1** Output is exactly `.rafa/brain/rules/` + `.rafa/brain/playbooks/` +
240
245
  `.rafa/brain/coverage.md`. **No `graph.json`.**
241
- - [ ] **D2** Every note has valid frontmatter per [`.rafa/contract.md`](../contract.md) §2
246
+ - [ ] **D2** Every note has valid frontmatter per [`.claude/rafa/contract.md`](../../rafa/contract.md) §2
242
247
  (incl. required `schemaVersion`, `title`, `summary`).
243
- - [ ] **D3** `node .rafa/bin/rafa-compile.mjs` **exits 0** and writes `.rafa/manifest.json`.
248
+ - [ ] **D3** `npx @rafinery/cli compile` **exits 0** and writes `.rafa/manifest.json`.
244
249
  Any `path · field · rule` violation = FAIL — fix and re-run (validate-and-correct).
245
250
 
246
251
  The report must state PASS/FAIL **per criterion** (not a summary verdict), so iteration
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: rafa-validate
3
+ description: "rafa SOP — prism's independent scan QA: re-run every check against the code, adversarial probes, verdict + score to checklist.md. Loaded when prism validates a brain; never trusts the producer's claims."
4
+ ---
5
+
1
6
  # Validate — prism's SOP (scan QA)
2
7
 
3
8
  Standard operating procedure for **prism** to validate a atlas scan. Independent,
@@ -20,19 +25,19 @@ agent. So:
20
25
 
21
26
  ## Inputs / preconditions
22
27
  - The brain: `.rafa/brain/` (`rules/`, `playbooks/`, `coverage.md`).
23
- - The repo (ground truth) + the checker `.rafa/bin/verify-citations.mjs`.
28
+ - The repo (ground truth) + the checker ``rafa verify-citations` (in @rafinery/cli)`.
24
29
  - The bar: `scan.md` § Acceptance criteria (A–D) + the quality rubric below.
25
30
  - You run in **fresh context** — you have NOT seen how the scan was produced. Keep it that way.
26
31
 
27
32
  ## Procedure (run in order; ground everything in code)
28
33
 
29
- 1. **Re-run the checker yourself** — `node .rafa/bin/verify-citations.mjs`. Record exit
34
+ 1. **Re-run the checker yourself** — `npx @rafinery/cli verify-citations`. Record exit
30
35
  code + counts (resolution / completeness / policy). Exit ≠ 0 → **blocker(s)**. Never
31
36
  trust a pasted table.
32
37
  2. **Trust-but-verify the checker** —
33
38
  (a) independently re-verify a sample (~10) of cites against the raw files by a *different*
34
39
  method (hand grep / read), to confirm the checker isn't lying.
35
- (b) **Mutation self-test** — run `node .rafa/bin/verify-citations.mjs --selftest`. It
40
+ (b) **Mutation self-test** — run `npx @rafinery/cli verify-citations --selftest`. It
36
41
  proves the checker's logic *fails on a bad cite and passes a good one* (on a throwaway
37
42
  temp file — no brain/repo pollution, no debris). It must print PASS and **exit 0**.
38
43
  Re-running the checker (step 1) is not proof it still works; this is.
@@ -66,7 +71,10 @@ agent. So:
66
71
  - **Blocker** — hard-gate failure, broken checker, or a wrong/misleading note. Must be **0**.
67
72
  - **Major** — a real gap: missing essence, under-covered domain, unanswerable question.
68
73
  - **Minor** — nits (orphan, cosmetic). Logged, not looped on.
69
- - **PASS** = hard gates pass · quality score ≥ 85 · 0 blockers · majors ≤ 2. Else **ITERATE**.
74
+ - **PASS** = hard gates pass · quality score ≥ 85 · 0 blockers · majors ≤ 2 · **and no
75
+ unflagged salient-but-wrong exemplar** (an open 5b finding → **auto-ITERATE regardless
76
+ of score** — salience gets copied, so one unflagged exception poisons every cold agent;
77
+ the numeric bar alone must never pass it). Else **ITERATE**.
70
78
 
71
79
  ## Quality score (0–100) — only meaningful once hard gates (fidelity + coverage) pass
72
80
  | Weight | Dimension | Measure |
@@ -106,7 +114,8 @@ hard-gate fail write `score: 0` (not `null` — the contract requires an int).
106
114
  - **Findings ledger** — blocker / major / minor, each cited (`file:line`) + a suggested fix.
107
115
 
108
116
  You write **`checklist.md` only**. The **conductor** owns `log.md` (the round-by-round trail).
109
- PASS = hard gates pass · score ≥ 85 · 0 blockers · majors ≤ 2.
117
+ PASS = hard gates pass · score ≥ 85 · 0 blockers · majors ≤ 2 · no unflagged salient-but-wrong
118
+ exemplar (5b override — auto-ITERATE).
110
119
 
111
120
  ## The ratchet
112
121
  Any judgment finding you raise repeatedly that *could* be mechanized → recommend it become a
package/lib/blueprint.mjs CHANGED
@@ -16,33 +16,43 @@ import { dirname, join } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
17
17
 
18
18
  // Two classes of blueprint file:
19
- // - owned → the client tunes these (agents, SOPs). We NEVER clobber a modified copy;
20
- // a changed file is written alongside as `<file>.new` so the dev merges by hand.
21
- // - lockstep → tools + contract. These move with the CLI version, so they overwrite.
19
+ // - owned → the client tunes these (agents, commands, skills). We NEVER clobber a
20
+ // modified copy; a conflict asks (shadcn-style).
21
+ // - lockstep → the contract. It moves with the CLI version (the CLI implements it),
22
+ // so it overwrites.
23
+ // Blueprint split (0.4.0): the gate TOOLS are not here — they live inside this
24
+ // package (lib/gate). Capabilities ship as committed SKILLS (.claude/skills/rafa-*)
25
+ // so behavior versions with the code repo, MR-reviewed and clone-complete. `.rafa/`
26
+ // itself is never vendored — it is lazy, gitignored knowledge+state.
22
27
  export const BLUEPRINT = {
23
28
  owned: [
24
29
  ".claude/agents/atlas.md",
25
30
  ".claude/agents/prism.md",
26
31
  ".claude/agents/bloom.md",
32
+ ".claude/agents/compass.md",
27
33
  ".claude/commands/rafa.md",
28
34
  ],
29
- ownedDirs: [".rafa/capabilities"],
30
- lockstep: [
31
- ".rafa/contract.md",
32
- ".rafa/bin/verify-citations.mjs",
33
- ".rafa/bin/rafa-compile.mjs",
34
- ".rafa/bin/rafa-push.mjs",
35
+ ownedDirs: [
36
+ ".claude/skills/rafa-scan",
37
+ ".claude/skills/rafa-validate",
38
+ ".claude/skills/rafa-plan",
39
+ ".claude/skills/rafa-build",
40
+ ".claude/skills/rafa-improve",
41
+ ".claude/skills/rafa-distill",
42
+ ".claude/skills/rafa-insights",
43
+ ".claude/skills/rafa-leverage",
35
44
  ],
45
+ lockstep: [".claude/rafa/contract.md"],
36
46
  };
37
47
 
38
48
  const HERE = dirname(fileURLToPath(import.meta.url)); // …/packages/cli/lib
39
49
 
40
- // Walk up to the monorepo root (the dir that holds the source `.rafa/contract.md`).
50
+ // Walk up to the monorepo root (the dir that holds the canonical contract).
41
51
  function findRepoRoot() {
42
52
  let d = HERE;
43
53
  for (let i = 0; i < 8; i++) {
44
54
  d = join(d, "..");
45
- if (existsSync(join(d, ".rafa", "contract.md"))) return d;
55
+ if (existsSync(join(d, ".claude", "rafa", "contract.md"))) return d;
46
56
  }
47
57
  return null;
48
58
  }
@@ -52,7 +62,7 @@ function findRepoRoot() {
52
62
  // - dev in the monorepo → the repo root itself
53
63
  export function blueprintSource() {
54
64
  const bundled = join(HERE, "..", "blueprint");
55
- if (existsSync(join(bundled, ".rafa", "contract.md"))) return bundled;
65
+ if (existsSync(join(bundled, ".claude", "rafa", "contract.md"))) return bundled;
56
66
  const root = findRepoRoot();
57
67
  if (root) return root;
58
68
  throw new Error(
@@ -0,0 +1,100 @@
1
+ // The lazy `.rafa/` bootstrap — the committed rafa.json is the anchor.
2
+ //
3
+ // Blueprint split (0.4.0): `.rafa/` is created lazily, gitignored, per working
4
+ // copy — it is NEVER vendored and never travels through the code repo. Any
5
+ // command that needs the brain repo (push/pull/distill) calls ensureBrainRepo(),
6
+ // which materializes the skeleton + git wiring from the committed rafa.json so
7
+ // `clone → npx rafa <cmd>` just works with no init step on the machine.
8
+
9
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
10
+ import { execSync } from "node:child_process";
11
+ import { join } from "node:path";
12
+ import { readStamp } from "./stamp.mjs";
13
+
14
+ export const die = (m) => {
15
+ console.error(`✗ ${m}`);
16
+ process.exit(1);
17
+ };
18
+
19
+ export const inDir = (dir) => (cmd) =>
20
+ execSync(cmd, { cwd: dir, stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }).trim();
21
+
22
+ // The brain remote recorded at provision time (committed, secret-free).
23
+ export function brainRemote(cwd) {
24
+ const stamp = readStamp(cwd);
25
+ return typeof stamp.brainRepo === "string" && stamp.brainRepo !== ""
26
+ ? stamp.brainRepo
27
+ : null;
28
+ }
29
+
30
+ // Ensure `.rafa/` exists as its own git repo wired to the brain remote.
31
+ // Order of truth: an existing `.rafa/.git` origin wins (a machine wired by an
32
+ // older init keeps working); otherwise bootstrap from the committed rafa.json.
33
+ // Returns { rafaDir, remote, bootstrapped }.
34
+ export function ensureBrainRepo(cwd, { requireRemote = true } = {}) {
35
+ const rafaDir = join(cwd, ".rafa");
36
+ const inRafa = inDir(rafaDir);
37
+ let bootstrapped = false;
38
+
39
+ if (!existsSync(rafaDir)) {
40
+ mkdirSync(rafaDir, { recursive: true });
41
+ bootstrapped = true;
42
+ }
43
+ if (!existsSync(join(rafaDir, ".git"))) {
44
+ inDir(cwd)(`git -C ${JSON.stringify(rafaDir)} init -b main`);
45
+ bootstrapped = true;
46
+ }
47
+
48
+ let remote = null;
49
+ try {
50
+ remote = inRafa("git remote get-url origin");
51
+ } catch {
52
+ const fromStamp = brainRemote(cwd);
53
+ if (fromStamp) {
54
+ inRafa(`git remote add origin ${fromStamp}`);
55
+ remote = fromStamp;
56
+ bootstrapped = true;
57
+ }
58
+ }
59
+ if (requireRemote && !remote)
60
+ die(
61
+ "no brain remote — this repo's rafa.json has no `brainRepo` and `.rafa/` has no origin.\n" +
62
+ " Provision via `rafa init <setup-url>` (platform), or add `\"brainRepo\": \"<git url>\"` to rafa.json.",
63
+ );
64
+
65
+ // CI auth (rafa ci-setup): a workflow has no interactive git credentials —
66
+ // RAFA_BRAIN_TOKEN (repo secret) is injected into the https remote for THIS
67
+ // working copy only. Never stored on the rafinery platform, never printed.
68
+ const token = process.env.RAFA_BRAIN_TOKEN;
69
+ if (token && remote?.startsWith("https://") && !remote.includes("@")) {
70
+ inRafa(
71
+ `git remote set-url origin ${remote.replace("https://", `https://x-access-token:${token}@`)}`,
72
+ );
73
+ }
74
+
75
+ // Session/state debris never rides a brain push: the sidecar, conflict
76
+ // copies, and distill staging are transport-excluded here (not knowledge).
77
+ const ignore = join(rafaDir, ".gitignore");
78
+ const IGNORES = "hydration.json\n*.theirs.md\ndistill-incoming/\ndistill-verdicts.json\n";
79
+ if (!existsSync(ignore)) writeFileSync(ignore, IGNORES);
80
+
81
+ return { rafaDir, remote, bootstrapped };
82
+ }
83
+
84
+ // The brain repo's default branch (HEAD symref) — never assume "main".
85
+ export function remoteDefaultBranch(rafaDir, fallback = "main") {
86
+ try {
87
+ const symref = inDir(rafaDir)("git ls-remote --symref origin HEAD");
88
+ const m = symref.match(/ref:\s+refs\/heads\/(\S+)\s+HEAD/);
89
+ if (m) return m[1];
90
+ } catch {
91
+ /* unreachable remote — caller decides how loud to be */
92
+ }
93
+ return fallback;
94
+ }
95
+
96
+ // Seed the generated active pointer if absent (state, not knowledge).
97
+ export function ensureActivePointer(rafaDir) {
98
+ const p = join(rafaDir, "active.md");
99
+ if (!existsSync(p)) writeFileSync(p, "# No active plan\n");
100
+ }