@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.
- package/CHANGELOG.md +79 -0
- package/README.md +17 -7
- package/bin/rafa.mjs +68 -20
- package/blueprint/.claude/agents/atlas.md +28 -20
- package/blueprint/.claude/agents/bloom.md +15 -8
- package/blueprint/.claude/agents/compass.md +46 -0
- package/blueprint/.claude/agents/prism.md +42 -25
- package/blueprint/.claude/commands/rafa.md +151 -26
- package/blueprint/{.rafa → .claude/rafa}/contract.md +108 -22
- package/blueprint/.claude/skills/rafa-build/SKILL.md +76 -0
- package/blueprint/.claude/skills/rafa-distill/SKILL.md +87 -0
- package/blueprint/{.rafa/capabilities/improve.md → .claude/skills/rafa-improve/SKILL.md} +10 -5
- package/blueprint/.claude/skills/rafa-insights/SKILL.md +71 -0
- package/blueprint/{.rafa/capabilities/leverage.md → .claude/skills/rafa-leverage/SKILL.md} +9 -4
- package/blueprint/{.rafa/capabilities/plan.md → .claude/skills/rafa-plan/SKILL.md} +30 -3
- package/blueprint/{.rafa/capabilities/scan.md → .claude/skills/rafa-scan/SKILL.md} +14 -9
- package/blueprint/{.rafa/capabilities/validate.md → .claude/skills/rafa-validate/SKILL.md} +14 -5
- package/lib/blueprint.mjs +22 -12
- package/lib/brain-repo.mjs +100 -0
- package/lib/checkpoint.mjs +138 -0
- package/lib/ci-setup.mjs +109 -0
- package/lib/claude-config.mjs +5 -5
- package/lib/compile.mjs +6 -21
- package/lib/distill.mjs +237 -0
- package/lib/fold.mjs +53 -0
- package/lib/gate/compile.mjs +549 -0
- package/lib/gate/verify-citations.mjs +156 -0
- package/lib/hydrate.mjs +96 -0
- package/lib/init.mjs +15 -30
- package/lib/leverage/adapters/claude-code.mjs +5 -4
- package/lib/mcp-client.mjs +97 -0
- package/lib/mcp-config.mjs +1 -1
- package/lib/migrations/index.mjs +39 -3
- package/lib/pull.mjs +129 -0
- package/lib/push.mjs +93 -14
- package/lib/releases.mjs +23 -0
- package/lib/verify-citations.mjs +9 -0
- package/lib/working-set.mjs +113 -0
- package/package.json +2 -2
- package/blueprint/.rafa/bin/rafa-compile.mjs +0 -478
- package/blueprint/.rafa/bin/rafa-push.mjs +0 -75
- package/blueprint/.rafa/bin/verify-citations.mjs +0 -153
- 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`](
|
|
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`](
|
|
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
|
-
`
|
|
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](
|
|
98
|
-
7. **Contract gate.** Run `
|
|
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`](
|
|
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`](
|
|
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** —
|
|
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`
|
|
42
|
-
|
|
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 `
|
|
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`](
|
|
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`](
|
|
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** — `
|
|
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** — `
|
|
186
|
-
every file against [`.rafa/contract.md`](
|
|
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** `
|
|
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`](
|
|
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** `
|
|
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
|
|
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** — `
|
|
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 `
|
|
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
|
|
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,
|
|
20
|
-
//
|
|
21
|
-
// - lockstep →
|
|
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: [
|
|
30
|
-
|
|
31
|
-
".rafa
|
|
32
|
-
".
|
|
33
|
-
".
|
|
34
|
-
".
|
|
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
|
|
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
|
+
}
|