@rafinery/cli 0.2.2 → 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 (45) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +17 -7
  3. package/bin/rafa.mjs +78 -8
  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 +190 -13
  9. package/blueprint/.claude/rafa/contract.md +482 -0
  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/.claude/skills/rafa-plan/SKILL.md +74 -0
  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 +69 -35
  30. package/lib/leverage/adapters/claude-code.mjs +5 -4
  31. package/lib/mcp-client.mjs +97 -0
  32. package/lib/mcp-config.mjs +93 -0
  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 +36 -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 -390
  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 -38
  44. package/blueprint/.rafa/capabilities/plan.md +0 -38
  45. package/blueprint/.rafa/contract.md +0 -303
@@ -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`
@@ -0,0 +1,74 @@
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
+
6
+ # plan — brain-grounded, prism-validated decomposition (capability #3)
7
+
8
+ > Status: **active.** The first *consumer* of the stores — where the brain starts to
9
+ > pay off. Depends on: brain (#1), ledger (#2). Invoke via `/rafa plan <intent>`.
10
+
11
+ Turn an intent into an approval-gated plan (contract §7 files), *grounded* in the
12
+ brain (don't re-derive), *aware* of the ledger (fold improvement into the work), and
13
+ **validated by prism before the owner ever sees it**.
14
+
15
+ ## The trio at plan time
16
+
17
+ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
18
+
19
+ - **atlas drafts** — RECALL the brain slice for the intent's domains through the
20
+ knowledge MCP (`get_coverage` to navigate → `search_knowledge` → `get_rule` /
21
+ `get_playbook`, including non-exemplars), name the BLAST RADIUS from coverage,
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.*
26
+ - **bloom pulls** — `list_improvements` in the blast radius; surface the
27
+ top-leverage open items as optional *"while-you're-here"* child tasks
28
+ (leverage-ranked, dismissible, never blocking).
29
+ - **prism validates the plan itself** — before the approval gate: is every task
30
+ grounded in real brain/code (not hallucinated ground)? does every child carry a
31
+ `## Done-check` (the expected outcome prism will validate execution against)?
32
+ A plan whose children lack Done-checks is REJECTED here — compile never parses
33
+ bodies (invariant #3); this gate is prism's.
34
+
35
+ ## Procedure
36
+
37
+ 1. **Staleness check** — compare the platform envelope's `brainForSha` against the
38
+ local brain stamp; if the platform is behind, surface "run `rafa push`" (never
39
+ proceed silently on knowledge you know is stale — never block either).
40
+ 2. **Recall** (atlas, via MCP) → **decompose** ADR-style: decision + rationale +
41
+ alternatives + risk surface + non-goals; tasks bound to domains with a
42
+ `## Done-check` each.
43
+ 3. **Ledger pull** (bloom) → optional leverage tasks in the blast radius.
44
+ 4. **Leverage-match** — recommend existing skills/tools/MCP that fit the tasks;
45
+ never plan to hand-roll what a capability already does.
46
+ 5. **prism plan-validation** → REJECT/fix loop until clean.
47
+ 6. **Approval gate** (owner). Then materialize `plans/<plan>/*.md` per contract §7
48
+ (parent + child-owned files, globally-unique prefixed ids) + `active.md` pointer
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).
70
+
71
+ ## Deferred / open
72
+ - Capture-back of plan-time decisions → brain (needs the capture engine).
73
+ - Pivot detection (mark superseded; the path is data).
74
+ - Plan lifecycle + tiering (cap, archive→remote, restore).
@@ -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
+ }