@rafinery/cli 0.13.0 → 0.15.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 +41 -0
- package/bin/rafa.mjs +4 -15
- package/blueprint/.claude/agents/atlas.md +27 -5
- package/blueprint/.claude/agents/bloom.md +24 -4
- package/blueprint/.claude/agents/prism.md +12 -3
- package/blueprint/.claude/agents/sage.md +7 -5
- package/blueprint/.claude/commands/rafa.md +19 -8
- package/blueprint/.claude/rafa/contract.md +116 -2
- package/blueprint/.claude/skills/rafa-build/SKILL.md +5 -6
- package/blueprint/.claude/skills/rafa-improve/SKILL.md +18 -3
- package/blueprint/.claude/skills/rafa-plan/SKILL.md +17 -17
- package/blueprint/.claude/skills/rafa-sage/SKILL.md +11 -7
- package/blueprint/.claude/skills/rafa-scan/SKILL.md +44 -10
- package/blueprint/.claude/skills/rafa-security/SKILL.md +15 -8
- package/lib/blueprint.mjs +0 -1
- package/lib/doctor.mjs +1 -1
- package/lib/gate/compile.mjs +10 -3
- package/lib/gate/verify-citations.mjs +8 -1
- package/lib/migrations/index.mjs +49 -2
- package/lib/releases.mjs +45 -0
- package/package.json +1 -1
- package/skills-bundle/skills-manifest.json +1 -1
- package/blueprint/.claude/skills/rafa-distill/SKILL.md +0 -126
- package/lib/ci-setup.mjs +0 -172
- package/lib/distill.mjs +0 -665
- package/lib/distiller/schema-ladder.mjs +0 -186
|
@@ -23,13 +23,13 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
|
|
|
23
23
|
the tools; the dev never asks for it. Repo not platform-connected (no `rafinery`
|
|
24
24
|
MCP in the session)? Fall back to reading `.rafa/brain/` files directly — same
|
|
25
25
|
knowledge, just unserved.*
|
|
26
|
-
- **bloom pulls** — `list_improvements` in the blast radius
|
|
27
|
-
`category: security` P0 regardless of region — the one class that travels;
|
|
28
|
-
one line, dismissible, never blocking); surface the
|
|
26
|
+
- **bloom pulls** — `list_improvements` in the blast radius, surfacing the
|
|
29
27
|
top-leverage open items as optional *"while-you're-here"* child tasks
|
|
30
|
-
(leverage-ranked, dismissible, never blocking).
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
(leverage-ranked, dismissible, never blocking). Open `category: security` P0s
|
|
29
|
+
come along too — the one class that travels regardless of region (contract
|
|
30
|
+
§12.5), one line, still dismissible, never blocking. **Any improvement the
|
|
31
|
+
plan ADOPTS** — as a task or as the plan's very subject — **is hydrated NOW**
|
|
32
|
+
(`rafa hydrate improvement <id>`), so the canonical ledger file is in the
|
|
33
33
|
branch working set from the start and the eventual `status: fixed` edit lands
|
|
34
34
|
on that same file, never a path-drifted twin authored at build's end.
|
|
35
35
|
**Also pull `get_knowledge_gaps`** (the open backlog — what devs asked that
|
|
@@ -102,17 +102,17 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
|
|
|
102
102
|
the pivotal choices are logged as DECISIONS at approval (`log_decision`:
|
|
103
103
|
context · options · decision · rationale; actor = the dev for their calls).
|
|
104
104
|
3. **Ledger pull** (bloom) → optional leverage tasks in the blast radius.
|
|
105
|
-
3b. **Security transparency
|
|
106
|
-
Run `npx @rafinery/cli audit --json` (seconds; reuse a
|
|
107
|
-
`security-audit.json` if fresh) and
|
|
108
|
-
and unburied
|
|
109
|
-
which tiers ran, then any critical/high finding whose
|
|
110
|
-
in THIS plan's blast radius (per
|
|
111
|
-
reachability method). A
|
|
112
|
-
child task
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
never silence.
|
|
105
|
+
3b. **Security transparency — integral, never an aftermath** (contract §12.5;
|
|
106
|
+
owner 2026-07-26). Run `npx @rafinery/cli audit --json` (seconds; reuse a
|
|
107
|
+
same-session `security-audit.json` if fresh) and put the picture in front of
|
|
108
|
+
the dev — verbatim and unburied — before the plan is approved: one line of
|
|
109
|
+
severity totals plus which tiers ran, then any critical/high finding whose
|
|
110
|
+
package or file sits in THIS plan's blast radius (per
|
|
111
|
+
[rafa-security](../rafa-security/SKILL.md)'s reachability method). A
|
|
112
|
+
blast-radius critical/high becomes a plan child task, so the fix rides the
|
|
113
|
+
work. Transparency is the whole point — the dev sees the counts even when
|
|
114
|
+
nothing is wrong: *"dependency tier clean at <sha>"* is one honest line,
|
|
115
|
+
said out loud, never silence.
|
|
116
116
|
4. **Leverage-match** — recommend existing skills/tools/MCP that fit the tasks;
|
|
117
117
|
never plan to hand-roll what a capability already does.
|
|
118
118
|
5. **prism plan-validation** → REJECT/fix loop until clean.
|
|
@@ -102,16 +102,20 @@ human.
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
-
## Output — the learnings ledger (to `.
|
|
105
|
+
## Output — the learnings ledger (to `.rafa/learnings/`)
|
|
106
106
|
|
|
107
|
-
The ledger is a **
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
The ledger is a **sibling of the improvement ledger** (owner 2026-07-27): it lives in gitignored
|
|
108
|
+
`.rafa/learnings/` — a `.rafa/` artifact like `.rafa/improve/`, NOT committed under `.claude/`,
|
|
109
|
+
NOT inside any customer `.rafa/brain/` (asset-free: learnings are about OUR agents; they never mix
|
|
110
|
+
with customer knowledge). Storage mirrors improvements exactly: gitignored locally, durable via
|
|
111
|
+
the brain-repo mirror + the platform DB. The DB (`agentLearnings`) is the store; sage writes each
|
|
112
|
+
entry via `report_learning`, and the platform's "agent learnings" card renders it. The local
|
|
113
|
+
`.rafa/learnings/` files are sage's working copy (regenerated per pass, like the brain):
|
|
113
114
|
|
|
114
115
|
- `learnings/<id>.md` — one file per learning (one proposed card/SOP diff, cited to event shapes).
|
|
116
|
+
Each is an **OKF concept** (`type: Learning`, self-describing like every `.rafa/` file —
|
|
117
|
+
contract §11): stable `id` == filename stem, non-empty `type`/`title`/`description`. `rafa
|
|
118
|
+
compile` gates the quartet from `.rafa/learnings/`.
|
|
115
119
|
- `ledger.md` — generated index: counts by category / target / status, and the top-leverage few.
|
|
116
120
|
|
|
117
121
|
**Learnings-ledger ENTRY SCHEMA** (defined here; deliberately has no code-content-capable field):
|
|
@@ -201,12 +201,13 @@ retrieval index. Bodies read like a senior engineer explaining that one concept
|
|
|
201
201
|
6. **Flow tracing** [LLM, cited] — from each entry point, hop-by-hop to termination
|
|
202
202
|
(render / DB write / response) → `flow` playbooks. Add `how-to` playbooks for the
|
|
203
203
|
recurring "add X" procedures these flows imply.
|
|
204
|
-
**One `security-posture` playbook is required** (2026-07-26): the repo's
|
|
205
|
-
boundaries (what's server-exposed vs build-time), auth chokepoints
|
|
206
|
-
authz is enforced — cited), and the secret-handling convention (where
|
|
207
|
-
names are read; names only, per the step-4 guardrail). This is durable
|
|
208
|
-
KNOWLEDGE, not findings — it
|
|
209
|
-
|
|
204
|
+
**One `security-posture` playbook is required** (owner 2026-07-26): the repo's
|
|
205
|
+
trust boundaries (what's server-exposed vs build-time), auth chokepoints
|
|
206
|
+
(where authz is enforced — cited), and the secret-handling convention (where
|
|
207
|
+
env names are read; names only, per the step-4 guardrail). This is durable
|
|
208
|
+
KNOWLEDGE, not findings — it is the flow-trace of the repo's trust surface,
|
|
209
|
+
and it grounds bloom's security profile
|
|
210
|
+
([rafa-security](../rafa-security/SKILL.md)): the reachability annotations on
|
|
210
211
|
CVE rows come FROM this note's map.
|
|
211
212
|
|
|
212
213
|
7. **Verification & synthesis** — apply the 100× filter (a note earns a file only if it
|
|
@@ -246,10 +247,38 @@ subagent it calls in step 1/5. **`init`** = ensure structure idempotently (`.raf
|
|
|
246
247
|
pipeline; **`--brain-only`** stops after the brain is validated (step 5 PASS) — skips improve
|
|
247
248
|
+ push, a cheap knowledge refresh.
|
|
248
249
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
`npx @rafinery/cli
|
|
252
|
-
|
|
250
|
+
0. **RESOLVE THE REAL BRAIN FIRST — mandatory and mechanical (the canonical
|
|
251
|
+
continuity law, [contract §12.4](../../rafa/contract.md)).** Before ANY scan
|
|
252
|
+
step, run `npx @rafinery/cli pull --full` — materialize the org brain into
|
|
253
|
+
`.rafa/` and confirm it sits at the brain remote's HEAD
|
|
254
|
+
(`git -C .rafa fetch origin && git -C .rafa status`). A stale local `.rafa`
|
|
255
|
+
(e.g. parked on an old genesis commit) is exactly how a session scans blind
|
|
256
|
+
and mints a parallel brain (the incident this step exists to prevent — see
|
|
257
|
+
the anti-patterns). **Founding is PLATFORM truth, never local emptiness**:
|
|
258
|
+
founding ⇔ the platform serves zero knowledge
|
|
259
|
+
(`search_knowledge`/`list_improvements` both empty). If ANY notes exist, this
|
|
260
|
+
is a **REFRESH**, not a founding scan:
|
|
261
|
+
- **Ids are stable forever.** One concept = one id. atlas UPDATES the
|
|
262
|
+
existing file in place (content, cites, links); it NEVER mints a new id
|
|
263
|
+
for a concept an existing note covers.
|
|
264
|
+
- **Retirement is a TOMBSTONE, never a deletion** (the §2 lifecycle law): set
|
|
265
|
+
`status: retired` in the existing file and append a dated `## Retired`
|
|
266
|
+
body section — why it no longer holds, what supersedes it (linked).
|
|
267
|
+
The file stays as history (excluded from recall by the platform);
|
|
268
|
+
a state change is never recorded by removing a file.
|
|
269
|
+
- New ids are for genuinely new concepts only — and each new note names, in
|
|
270
|
+
its body, why no existing note covers it.
|
|
271
|
+
- The founding-scan coach offer (step 9) NEVER fires on a refresh.
|
|
272
|
+
1. **Scan — spawn `atlas`** context-isolated. On a REFRESH, the spawn prompt
|
|
273
|
+
MUST carry the existing inventory: *"REFRESH mode — the org brain is
|
|
274
|
+
materialized under `.rafa/brain/` (<N> rules · <M> playbooks · <K>
|
|
275
|
+
improvements). Update in place: ids are stable; never write a new note for
|
|
276
|
+
a concept an existing note covers; deletions are explicit. Run the scan per
|
|
277
|
+
this SOP: comprehensive, breadth-before-depth, cited notes →
|
|
278
|
+
`.rafa/brain/{rules,playbooks}/` + `coverage.md`. Run
|
|
279
|
+
`npx @rafinery/cli verify-citations` until it **exits 0**. Return a
|
|
280
|
+
coverage summary only — not the raw reads."* (Founding keeps the same
|
|
281
|
+
prompt minus the refresh preamble.)
|
|
253
282
|
2. **Gate 1 — checker (trust-but-verify):** re-run `npx @rafinery/cli verify-citations`
|
|
254
283
|
yourself. Must **exit 0** (else re-spawn atlas to fix). It writes `citation-check.md`.
|
|
255
284
|
3. **Gate 2 — prism:** spawn `prism` **context-isolated**, passing ONLY: *"Validate the scan
|
|
@@ -344,6 +373,11 @@ The report must state PASS/FAIL **per criterion** (not a summary verdict), so it
|
|
|
344
373
|
targets the exact failing box.
|
|
345
374
|
|
|
346
375
|
## Anti-patterns (do NOT do)
|
|
376
|
+
- **Scan without pulling the real brain (the parallel-brain incident,
|
|
377
|
+
2026-07-27).** A blind re-scan mints new ids for concepts that already have
|
|
378
|
+
notes — 28 duplicates against 14 existing notes, zero continuity. Step 0 is
|
|
379
|
+
mandatory; the reconciler's duplicate guard will CONTEST parallel notes, but
|
|
380
|
+
contested duplicates are cleanup, not a workflow.
|
|
347
381
|
- Cherry-pick the flashiest seam — the exact failure this capability prevents.
|
|
348
382
|
- Single-file conventions only — the old scan's shallowness.
|
|
349
383
|
- **Inferred / sampled / from-memory citations** — every `file:line` is grep-or-read
|
|
@@ -6,9 +6,10 @@ description: "rafa SOP — the security profile: run the self-contained `rafa au
|
|
|
6
6
|
# security — the profile (woven, never a command)
|
|
7
7
|
|
|
8
8
|
rafa's security posture is **woven into the loop, not a chore**: the dev never
|
|
9
|
-
types a security command. Owner doctrine (2026-07-26
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
types a security command. Owner doctrine (owner 2026-07-26, consolidated in
|
|
10
|
+
[contract §12.5](../../rafa/contract.md)): *"security is not an aftermath — it
|
|
11
|
+
is an integral part of the development cycle."* The engine fires at every
|
|
12
|
+
moment of that cycle:
|
|
12
13
|
|
|
13
14
|
- **plan** — the audit runs before approval and its picture is presented to
|
|
14
15
|
the dev verbatim (rafa-plan §3b): totals + blast-radius criticals; clean is
|
|
@@ -28,9 +29,13 @@ the platform's Security tab reads the merge-level rows.
|
|
|
28
29
|
Run **`npx @rafinery/cli audit --json`** and parse the `rafa.audit/v1`
|
|
29
30
|
envelope. The engine is SELF-CONTAINED — it needs no installs, ever:
|
|
30
31
|
|
|
31
|
-
- **dependency** — built-in
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
- **dependency** — a built-in **multi-manager** lockfile parser (pnpm · npm ·
|
|
33
|
+
yarn · bun, resolved dynamically richest-first — never a pnpm hardcode) +
|
|
34
|
+
the keyless OSV.dev advisory API, merged with `pnpm audit` on pnpm repos;
|
|
35
|
+
per finding: package · GHSA/CVE + aliases · severity + CVSS ·
|
|
36
|
+
direct-vs-transitive chain (pnpm/npm; yarn/bun are honestly packages-only) ·
|
|
37
|
+
fixed-in · dev-only flag. No lockfile → the tier is `ran:false` and
|
|
38
|
+
`rafa doctor` prints the per-manager command to generate one.
|
|
34
39
|
- **secrets** — built-in curated ruleset over tracked files (`.env*` never
|
|
35
40
|
opened; fingerprints, never secret bytes). gitleaks enhances if present.
|
|
36
41
|
- **sast** — semgrep if installed (pinned `p/security-audit`), otherwise the
|
|
@@ -59,8 +64,10 @@ Required frontmatter as ever (`title` · `summary` · `fix` · `leverage` ·
|
|
|
59
64
|
|
|
60
65
|
**Reconcile like any improvement**: dedupe against existing rows by the stable
|
|
61
66
|
finding id (in the body); auto-close rows whose finding vanished from a fresh
|
|
62
|
-
audit (`status: fixed` — the lockfile bump landed)
|
|
63
|
-
(
|
|
67
|
+
audit (`status: fixed` — the lockfile bump landed) **with a dated closure line
|
|
68
|
+
in the body naming the evidence (the new lockfile version / the vanished
|
|
69
|
+
advisory) — a tombstone, never a removal** (the §2/§12.4 lifecycle law);
|
|
70
|
+
preserve the dev's triage (backlog/wontfix stay).
|
|
64
71
|
|
|
65
72
|
## Brain-ground the reachability (the judgment part)
|
|
66
73
|
|
package/lib/blueprint.mjs
CHANGED
package/lib/doctor.mjs
CHANGED
|
@@ -27,7 +27,7 @@ export async function runDoctor(ROOT = process.cwd()) {
|
|
|
27
27
|
console.log("rafa doctor — proving the capture machinery\n");
|
|
28
28
|
|
|
29
29
|
// 1 · provisioning — the same resolution the real tools use, so a doctor
|
|
30
|
-
// pass here means checkpoint/
|
|
30
|
+
// pass here means checkpoint/reconcile auth will resolve identically.
|
|
31
31
|
console.log("• provisioning");
|
|
32
32
|
let provisioned = false;
|
|
33
33
|
try {
|
package/lib/gate/compile.mjs
CHANGED
|
@@ -221,9 +221,15 @@ export function runCompile(argv = []) {
|
|
|
221
221
|
const t = citeTargetTokens(c.file);
|
|
222
222
|
if (t !== null) c.targetTokens = t;
|
|
223
223
|
}
|
|
224
|
+
// Lifecycle (r7c, tombstone doctrine): notes are NEVER deleted — a
|
|
225
|
+
// retired note keeps its file with `status: retired` + a dated body
|
|
226
|
+
// section explaining why. Optional; absent = active by contract.
|
|
227
|
+
if ("status" in data && !isEnum(data.status, ["active", "retired"]))
|
|
228
|
+
fail(path, "status", "optional · active|retired (retirement is a tombstone, never a deletion)");
|
|
224
229
|
notes.push({
|
|
225
230
|
id,
|
|
226
231
|
kind,
|
|
232
|
+
...(data.status === "retired" ? { status: "retired" } : {}),
|
|
227
233
|
type: reqEnum(
|
|
228
234
|
data,
|
|
229
235
|
"type",
|
|
@@ -844,11 +850,12 @@ export function runCompile(argv = []) {
|
|
|
844
850
|
return count;
|
|
845
851
|
}
|
|
846
852
|
|
|
847
|
-
// sage's learnings (.
|
|
848
|
-
//
|
|
853
|
+
// sage's learnings (.rafa/learnings/*.md) — a gitignored `.rafa/` sibling of
|
|
854
|
+
// the improvement ledger (2026-07-27); durable via the brain-repo mirror + the
|
|
855
|
+
// platform DB. Self-describing (§11), mechanically gated: OKF quartet + stable
|
|
849
856
|
// id. ledger.md there is sage's generated index (skipped like a listing).
|
|
850
857
|
function compileLearnings() {
|
|
851
|
-
const dir = join(ROOT, "
|
|
858
|
+
const dir = join(ROOT, "learnings");
|
|
852
859
|
let count = 0;
|
|
853
860
|
for (const path of walk(dir)) {
|
|
854
861
|
const name = path.split("/").pop();
|
|
@@ -263,7 +263,14 @@ export function runVerifyCitations(argv = []) {
|
|
|
263
263
|
|
|
264
264
|
for (const note of notes) {
|
|
265
265
|
const rel = note.replace(ROOT + "/", "");
|
|
266
|
-
const
|
|
266
|
+
const raw = readFileSync(note, "utf8");
|
|
267
|
+
// TOMBSTONES are history, not claims (r7c): a retired note / closed
|
|
268
|
+
// improvement keeps its (possibly dead) citations as the record of what it
|
|
269
|
+
// once claimed — the checker skips them entirely; only LIVE knowledge is
|
|
270
|
+
// verified. (status: is frontmatter-only, so a body mention can't trip this.)
|
|
271
|
+
const fm = raw.startsWith("---") ? (raw.split("\n---")[0] ?? "") : "";
|
|
272
|
+
if (/^status:\s*(retired|fixed|wontfix)\s*$/m.test(fm)) continue;
|
|
273
|
+
const lines = raw.split("\n");
|
|
267
274
|
const cites = [];
|
|
268
275
|
const anchors = [];
|
|
269
276
|
const absents = [];
|
package/lib/migrations/index.mjs
CHANGED
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
// run: (cwd) => backfillPlanField(cwd, "owner", ownerOf(cwd)),
|
|
29
29
|
// };
|
|
30
30
|
|
|
31
|
-
import { existsSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
31
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
32
|
+
import { execSync } from "node:child_process";
|
|
32
33
|
import { join } from "node:path";
|
|
33
34
|
import { cmpSemver } from "../releases.mjs";
|
|
34
35
|
|
|
@@ -121,7 +122,53 @@ const plans_v1_to_v2 = {
|
|
|
121
122
|
},
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
// 0.15.0: learnings become a `.rafa/` sibling of the improvement ledger (owner
|
|
126
|
+
// 2026-07-27). They used to be COMMITTED under `.claude/rafa/learnings/` (MR-
|
|
127
|
+
// gated governance); the new model stores them like improvements — gitignored
|
|
128
|
+
// `.rafa/learnings/`, durable via the brain-repo mirror + the platform DB. This
|
|
129
|
+
// relocates a consumer's existing committed learnings and untracks the old copy
|
|
130
|
+
// (git rm --cached; the files themselves MOVE, nothing is dropped).
|
|
131
|
+
const learnings_to_rafa_0_15_0 = {
|
|
132
|
+
id: "learnings-to-rafa-0.15.0",
|
|
133
|
+
kind: "config",
|
|
134
|
+
mode: "mechanical",
|
|
135
|
+
summary:
|
|
136
|
+
"learnings move `.claude/rafa/learnings/` → `.rafa/learnings/` (a gitignored " +
|
|
137
|
+
"sibling of the improvement ledger; durable via the brain repo + the platform DB)",
|
|
138
|
+
appliesTo: (from, to) =>
|
|
139
|
+
cmpSemver(from.cli, "0.15.0") < 0 && cmpSemver(to.version, "0.15.0") >= 0,
|
|
140
|
+
run: (cwd) => {
|
|
141
|
+
const src = join(cwd, ".claude", "rafa", "learnings");
|
|
142
|
+
const dst = join(cwd, ".rafa", "learnings");
|
|
143
|
+
if (!existsSync(src)) return { changed: [], notes: ["no committed learnings — nothing to migrate"] };
|
|
144
|
+
const changed = [];
|
|
145
|
+
mkdirSync(dst, { recursive: true });
|
|
146
|
+
for (const name of readdirSync(src)) {
|
|
147
|
+
const s = join(src, name);
|
|
148
|
+
if (!statSync(s).isFile()) continue;
|
|
149
|
+
const d = join(dst, name);
|
|
150
|
+
if (!existsSync(d)) writeFileSync(d, readFileSync(s, "utf8")); // MOVE the content, never drop it
|
|
151
|
+
}
|
|
152
|
+
// Untrack the old committed copies (best-effort — a non-git checkout still
|
|
153
|
+
// gets the physical move above) and remove the emptied directory.
|
|
154
|
+
try {
|
|
155
|
+
execSync(`git rm -r -q --cached --ignore-unmatch ${JSON.stringify(join(".claude", "rafa", "learnings"))}`, { cwd, stdio: "ignore" });
|
|
156
|
+
} catch {
|
|
157
|
+
/* not a git repo / already untracked — the filesystem move is what matters */
|
|
158
|
+
}
|
|
159
|
+
rmSync(src, { recursive: true, force: true });
|
|
160
|
+
changed.push(".claude/rafa/learnings/ → .rafa/learnings/");
|
|
161
|
+
return {
|
|
162
|
+
changed,
|
|
163
|
+
notes: [
|
|
164
|
+
"learnings are now a gitignored `.rafa/` sibling of the improvement ledger — " +
|
|
165
|
+
"durable via the brain-repo mirror + the platform DB; commit the removal of the old `.claude/rafa/learnings/`.",
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const MIGRATIONS = [blueprint_split_0_4_0, plans_v1_to_v2, learnings_to_rafa_0_15_0];
|
|
125
172
|
|
|
126
173
|
// The migrations that apply to a given move (stamped `from` → shipped `to`), in order.
|
|
127
174
|
export function pendingMigrations(from, to) {
|
package/lib/releases.mjs
CHANGED
|
@@ -456,6 +456,51 @@ export const RELEASES = [
|
|
|
456
456
|
"lockfile and prints the per-manager generate command. review.json " +
|
|
457
457
|
"joins the brain-repo bootstrap ignore.",
|
|
458
458
|
},
|
|
459
|
+
{
|
|
460
|
+
version: "0.14.0",
|
|
461
|
+
contract: 1,
|
|
462
|
+
plans: 2,
|
|
463
|
+
requires: "update",
|
|
464
|
+
summary:
|
|
465
|
+
"CONTINUITY + CLEAN SOP + DISTILL REMOVED (owner 2026-07-27). CONTINUITY " +
|
|
466
|
+
"IS THE PRODUCT: scans/improves are REFRESH-FIRST — Step 0 `pull --full` " +
|
|
467
|
+
"+ brain-remote-HEAD check before any pass; founding = PLATFORM truth " +
|
|
468
|
+
"(zero knowledge served), never local emptiness; ids are STABLE FOREVER, " +
|
|
469
|
+
"refresh updates in place, triage + debt-trend never reset. The " +
|
|
470
|
+
"reconciler's PARALLEL-BRAIN GUARD contests a NEW note/improvement that " +
|
|
471
|
+
"overlaps an existing one (same domain/category + equal title OR ≥50% " +
|
|
472
|
+
"shared cited files vs the trunk manifest) — fold into the existing id " +
|
|
473
|
+
"or defend a genuinely new one; duplicates never bank silently. CLOSURES " +
|
|
474
|
+
"ARE TOMBSTONES, never deletions: notes gain `status: retired` + a dated " +
|
|
475
|
+
"`## Retired` section, improvements close `status: fixed|wontfix` + a " +
|
|
476
|
+
"dated evidence line — recall excludes tombstones, verify-citations " +
|
|
477
|
+
"skips them, the reconciler's prune WRITES the tombstone. The whole " +
|
|
478
|
+
"capture/continuity/security doctrine is CONSOLIDATED in contract §12 " +
|
|
479
|
+
"(the guarded loop: moments · signals · gates · lanes) and every " +
|
|
480
|
+
"agent/skill/reconciler SOP rewritten clean against it. The DISTILL " +
|
|
481
|
+
"surface is REMOVED (the rafa-distill skill + the `distill`/`ci-setup` " +
|
|
482
|
+
"commands): reconciliation is SERVER-SIDE only, the reconciler is the " +
|
|
483
|
+
"org brain's single writer. All guards resolve the trunk DYNAMICALLY " +
|
|
484
|
+
"(stamped prodBranch → origin/HEAD → main) — the configurable default " +
|
|
485
|
+
"branch is honored across checkpoint, guard, branch-merge audit, and " +
|
|
486
|
+
"reconciliation; no path hardcodes main.",
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
version: "0.15.0",
|
|
490
|
+
contract: 1,
|
|
491
|
+
plans: 2,
|
|
492
|
+
requires: "update",
|
|
493
|
+
summary:
|
|
494
|
+
"LEARNINGS ARE A `.rafa/` SIBLING (owner 2026-07-27): sage's learnings " +
|
|
495
|
+
"move `.claude/rafa/learnings/` → `.rafa/learnings/` — stored like the " +
|
|
496
|
+
"improvement ledger (gitignored locally, durable via the brain-repo " +
|
|
497
|
+
"mirror + the platform DB `agentLearnings`), each an OKF `type: Learning` " +
|
|
498
|
+
"concept gated by the quartet. `rafa update` runs a MECHANICAL migration " +
|
|
499
|
+
"that relocates a repo's existing committed learnings and untracks the " +
|
|
500
|
+
"old copy (files MOVE, nothing dropped). The committed-`.claude/`-" +
|
|
501
|
+
"governance framing is retired; the DB is the store, `.rafa/learnings/` " +
|
|
502
|
+
"the local working copy — no learning files clutter `.claude/`.",
|
|
503
|
+
},
|
|
459
504
|
];
|
|
460
505
|
|
|
461
506
|
// The release this CLI build ships (last entry).
|
package/package.json
CHANGED
|
@@ -1,126 +0,0 @@
|
|
|
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
|
-
1b. **Schema ladder (0.11.0 — the owner's 4-case doctrine).** Before ANY
|
|
43
|
-
claim-level judging, resolve the version lattice: **target** = the org
|
|
44
|
-
brain's `manifest.json → schemaVersion` (else max over its notes; a
|
|
45
|
-
pre-manifest brain is the founding v1) · **source** = max over the
|
|
46
|
-
collected candidates' own `schemaVersion` · **latest** = the running CLI's
|
|
47
|
-
`SCHEMA_VERSION`. Then: **target < source → REWRITE-TARGET** (every org-
|
|
48
|
-
brain note lifts to the newer schema and rides THIS run's gates + push —
|
|
49
|
-
one migration+reconcile commit) · **target > source → UPGRADE-SOURCE**
|
|
50
|
-
(incoming candidates lift into the target's schema before judging) ·
|
|
51
|
-
**equal but < latest → REWRITE-MERGED** (both lift; the merged target is a
|
|
52
|
-
full rewrite onto latest) · **equal and == latest → DIFF** (the happy flow,
|
|
53
|
-
steps 2–6 exactly as written). **Either side > latest → ABORT LOUDLY** — a
|
|
54
|
-
newer CLI authored it; update the runner, knowledge is never downgraded.
|
|
55
|
-
Transforms are REGISTERED per step AND per OKF type
|
|
56
|
-
(`lib/distiller/schema-ladder.mjs` — each step declares rule · playbook ·
|
|
57
|
-
improvement explicitly: a function, or the EXPLICIT "unchanged", which
|
|
58
|
-
still re-stamps the note's schemaVersion line); a missing step OR class is
|
|
59
|
-
a loud error, never a guess. Only durable knowledge ladders — derived/
|
|
60
|
-
generated files (coverage · checklist · ledger · reports · manifest)
|
|
61
|
-
REGENERATE at the new schema; intent records pass through untouched. The
|
|
62
|
-
headless CLI runs this mechanically; the session path applies the same
|
|
63
|
-
lattice by hand.
|
|
64
|
-
2. **Validate (prism, context-isolated)** — the target is **merged MAIN as of
|
|
65
|
-
NOW, never the fork point and never a stale checkout**: `git fetch origin`
|
|
66
|
-
first; judge every claim against the fetched trunk. Confirm every citation
|
|
67
|
-
resolves (the cited line contains its token — grep it yourself). A claim
|
|
68
|
-
that can't be confirmed with a `file:line` is REFUTED (cited reason).
|
|
69
|
-
Contested/low-confidence → `needs-adjudication`, never a guess. Files about
|
|
70
|
-
code the merge did NOT touch are judged on their own merits, same bar.
|
|
71
|
-
3. **Author (atlas)** — for each survivor: write/update the org-brain file
|
|
72
|
-
(contract §2) with real cites into main; `anchor:` on contracts (hydrated
|
|
73
|
-
files lost it — re-declare); fold into an existing note when one covers the
|
|
74
|
-
topic (supersede, never duplicate).
|
|
75
|
-
4. **Gate + ship** — `rafa verify-citations` AND `rafa compile` to exit 0 →
|
|
76
|
-
`rafa push --verb=distill --message="reconcile(<branch>): <N> banked ·
|
|
77
|
-
<M> refuted · <K> adjudication — <banked ids…>"`. The reconciliation
|
|
78
|
-
commit is DESCRIPTIVE (owner 2026-07-26): its subject states what THIS run
|
|
79
|
-
did — branch, per-verdict counts, the banked note ids — so any agent
|
|
80
|
-
walking the brain log understands the commit without opening it (the
|
|
81
|
-
headless CLI composes this automatically; the session path passes it
|
|
82
|
-
explicitly). Only now has anything entered the org brain. A failed gate
|
|
83
|
-
aborts EVERYTHING: nothing resolved, nothing pushed, working set intact.
|
|
84
|
-
5. **Resolve** — `resolve_working_file(path, distilled)` for survivors — authored
|
|
85
|
-
per the OKF surface ([rafa-okf](../rafa-okf/SKILL.md); push emits the rest) —
|
|
86
|
-
CAS: only a live row resolves; a failure means ANOTHER runner already
|
|
87
|
-
distilled this branch — STOP and reconcile against what it shipped.
|
|
88
|
-
`resolve_working_file(path, refuted, note)` for failures — TELL the author
|
|
89
|
-
which files died and why (cited); refutation is feedback, not silence. At
|
|
90
|
-
that same resolve beat emit **`report_loop_event(category:
|
|
91
|
-
"distill-refutation", outcome: refuted|distilled, subject: <path or note
|
|
92
|
-
id>, actorMeta: {model: <the judging model>, agent: "distiller@<ver>" (or
|
|
93
|
-
"prism@<ver>" on the session path), runner: session|ci|sandbox — the tier
|
|
94
|
-
this distill runs in}, dedupeKey: "<branch>·<path>·<outcome>")`** — sage's
|
|
95
|
-
evidence that a claim met the gates or was refuted (shapes only: the
|
|
96
|
-
outcome + a file/note reference, never the refuted content, never the
|
|
97
|
-
cited code). Wave 5 is STRICT: an emit without the actor envelope is
|
|
98
|
-
rejected loudly; the dedupeKey makes a re-run of the same distillation
|
|
99
|
-
count ONCE. Checkpoint-adjacent, monotonic, never a session-end sweep.
|
|
100
|
-
`resolve_working_file(path, needs-adjudication, note)` for the undecidable —
|
|
101
|
-
the next session's digest offers the decision.
|
|
102
|
-
|
|
103
|
-
## Branch→branch merges (sub-feature → feature): FOLD, don't distill
|
|
104
|
-
|
|
105
|
-
Distillation targets ONLY the default branch. When a sub-feature merges into
|
|
106
|
-
its parent branch, the working set is **folded forward mechanically** —
|
|
107
|
-
`rafa fold --from <branch> --to <parent>` (or the CI fold job): absent on the
|
|
108
|
-
parent → re-keyed · identical content → merged silently · true same-file
|
|
109
|
-
divergence → `needs-adjudication` on the parent row (incoming preserved,
|
|
110
|
-
attributed). No LLM, no prism — the knowledge keeps riding with the code until
|
|
111
|
-
the code reaches main.
|
|
112
|
-
|
|
113
|
-
Sub-feature CONTEXT: a branch's working knowledge = org brain (via MCP) + its
|
|
114
|
-
own working set + unmerged ANCESTOR branches' working sets (the conductor
|
|
115
|
-
derives the ancestor chain from git and calls `get_working_set` per ancestor —
|
|
116
|
-
the platform stores no lineage).
|
|
117
|
-
|
|
118
|
-
## Rules
|
|
119
|
-
|
|
120
|
-
- Validation target is main at distillation time, never the fork point.
|
|
121
|
-
- An abandoned branch's working set is never distilled — it dies with the
|
|
122
|
-
branch (propagation mirrors code).
|
|
123
|
-
- No file skips steps 2–4; there is no fast path into the org brain.
|
|
124
|
-
- CI can detect and flag; only a dev session resolves a human's divergence.
|
|
125
|
-
- Dev-level observations found among the files route to `put_dev_insight`
|
|
126
|
-
(the author's user brain), never to the org brain.
|