@rafinery/cli 0.8.16 → 0.11.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 +130 -0
- package/bin/rafa.mjs +11 -0
- package/blueprint/.claude/agents/atlas.md +21 -3
- package/blueprint/.claude/agents/bloom.md +2 -2
- package/blueprint/.claude/agents/prism.md +42 -3
- package/blueprint/.claude/agents/sage.md +1 -1
- package/blueprint/.claude/commands/rafa.md +24 -13
- package/blueprint/.claude/rafa/contract.md +23 -0
- package/blueprint/.claude/rafa/hooks/brain-commit.mjs +4 -0
- package/blueprint/.claude/rafa/hooks/session-start.mjs +66 -0
- package/blueprint/.claude/skills/rafa-build/SKILL.md +111 -20
- package/blueprint/.claude/skills/rafa-commit/SKILL.md +22 -0
- package/blueprint/.claude/skills/rafa-distill/SKILL.md +37 -4
- package/blueprint/.claude/skills/rafa-improve/SKILL.md +22 -0
- package/blueprint/.claude/skills/rafa-leverage/SKILL.md +6 -1
- package/blueprint/.claude/skills/rafa-plan/SKILL.md +36 -1
- package/blueprint/.claude/skills/rafa-review/SKILL.md +16 -2
- package/blueprint/.claude/skills/rafa-sage/SKILL.md +8 -2
- package/blueprint/.claude/skills/rafa-scan/SKILL.md +4 -1
- package/lib/brain-repo.mjs +1 -1
- package/lib/checkpoint.mjs +26 -15
- package/lib/distill.mjs +56 -3
- package/lib/distiller/doctrine.mjs +5 -16
- package/lib/distiller/schema-ladder.mjs +186 -0
- package/lib/doctor.mjs +19 -1
- package/lib/facts.mjs +130 -0
- package/lib/gate/compile.mjs +12 -0
- package/lib/gate/verify-citations.mjs +78 -1
- package/lib/hydrate.mjs +45 -0
- package/lib/init.mjs +9 -1
- package/lib/leverage/engine.mjs +32 -3
- package/lib/leverage.mjs +11 -2
- package/lib/loop-cache.mjs +67 -0
- package/lib/mcp-client.mjs +19 -0
- package/lib/push.mjs +18 -4
- package/lib/reflex.mjs +5 -1
- package/lib/releases.mjs +78 -0
- package/lib/review.mjs +32 -6
- package/lib/session-facts.mjs +108 -0
- package/lib/skill-deps.mjs +377 -0
- package/lib/stamp.mjs +24 -0
- package/lib/update.mjs +9 -0
- package/package.json +3 -2
- package/skills-bundle/frontend-design/LICENSE.txt +177 -0
- package/skills-bundle/frontend-design/SKILL.md +55 -0
- package/{LICENSE → skills-bundle/grill-me/LICENSE} +6 -6
- package/skills-bundle/grill-me/SKILL.md +7 -0
- package/skills-bundle/grill-me/agents/openai.yaml +5 -0
- package/skills-bundle/grilling/SKILL.md +53 -0
- package/skills-bundle/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/skills-bundle/improve-codebase-architecture/LICENSE +21 -0
- package/skills-bundle/improve-codebase-architecture/SKILL.md +71 -0
- package/skills-bundle/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/skills-bundle/requesting-code-review/LICENSE +21 -0
- package/skills-bundle/requesting-code-review/SKILL.md +95 -0
- package/skills-bundle/requesting-code-review/code-reviewer.md +172 -0
- package/skills-bundle/skills-manifest.json +72 -0
- package/skills-bundle/tdd/LICENSE +21 -0
- package/skills-bundle/tdd/SKILL.md +36 -0
- package/skills-bundle/tdd/agents/openai.yaml +3 -0
- package/skills-bundle/tdd/mocking.md +59 -0
- package/skills-bundle/tdd/tests.md +77 -0
- package/skills-bundle/vercel-composition-patterns/AGENTS.md +946 -0
- package/skills-bundle/vercel-composition-patterns/README.md +60 -0
- package/skills-bundle/vercel-composition-patterns/SKILL.md +89 -0
- package/skills-bundle/vercel-composition-patterns/metadata.json +11 -0
- package/skills-bundle/vercel-composition-patterns/rules/_sections.md +29 -0
- package/skills-bundle/vercel-composition-patterns/rules/_template.md +24 -0
- package/skills-bundle/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills-bundle/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills-bundle/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills-bundle/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills-bundle/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills-bundle/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/skills-bundle/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills-bundle/vercel-composition-patterns/rules/state-lift-state.md +125 -0
|
@@ -17,7 +17,7 @@ platform MCP (one read path — the same surface any third-party agent uses).
|
|
|
17
17
|
| Role | Agent | Job per task |
|
|
18
18
|
|---|---|---|
|
|
19
19
|
| **Executor** | atlas | RECALL the task's brain slice via MCP (`search_knowledge` + `get_rule`/`get_playbook`; honor non-exemplars) → implement, convention-adherent |
|
|
20
|
-
| **Validator** | prism | validate the execution against the child's `## Done-check` — strict, unbiased, against code + brain, never against atlas's claims. **`status: done` only on prism PASS**; FAIL → atlas corrects (validate-and-correct at work time). Plan-done adds one line to the verdict: **working set reviewed — captured, or clean-with-reason** (a build that learned nothing SAYS so; a build that learned something SHOWS the files) |
|
|
20
|
+
| **Validator** | prism | validate the execution against the child's `## Done-check` — strict, unbiased, against code + brain, never against atlas's claims. **`status: done` only on prism PASS**; FAIL → atlas corrects (validate-and-correct at work time). **TDD tasks (wave 6): green re-run LIVE; red per tier (standard = attested `method:"static"` · full = re-run at the red commit in a throwaway worktree). Findings carry `Critical (Must Fix) · Important (Should Fix) · Minor (Nice to Have)` — ITERATE iff any Critical/Important; Minor NEVER flips the verdict.** Plan-done adds one line to the verdict: **working set reviewed — captured, or clean-with-reason** (a build that learned nothing SAYS so; a build that learned something SHOWS the files) |
|
|
21
21
|
| **Improver** | bloom | **push**: new improvement opportunities spotted during execution → new ledger files. **close**: improvements fixed in passing → `status: fixed` in the ledger file + `report_improvement_status(id, fixed)` so the platform shows it LIVE as pending-reconciliation (the ledger row itself changes only at the next brain push — K1). **nudge**: top-leverage open item in the task's blast radius — opt-in, never blocking |
|
|
22
22
|
| **Coach** | compass | **sitback** (harness-arc): after each task's verdict + sweep, one beat of reflection — did THIS task reveal something about how this DEV works (a preference, a recurring friction, a steering pattern)? Repo knowledge goes to the working set, never here. A genuine dev-level observation becomes its OWN opt-in offer (consent doctrine: insights are NEVER under session consent) → `put_dev_insight` on yes. No observation = no offer — silence is the honest default |
|
|
23
23
|
|
|
@@ -47,22 +47,33 @@ platform MCP (one read path — the same surface any third-party agent uses).
|
|
|
47
47
|
**and append a dated entry to the child's `## Log`** (body links: markdown,
|
|
48
48
|
per [rafa-okf](../rafa-okf/SKILL.md)) — what was done, what was
|
|
49
49
|
decided, what surprised (body prose: displayed verbatim on the platform, never
|
|
50
|
-
parsed; the plan files at `.rafa/plans/<plan>/` ARE the local cache) →
|
|
51
|
-
task-done CHECKPOINT
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
parsed; the plan files at `.rafa/plans/<plan>/` ARE the local cache) → the
|
|
51
|
+
task-done CHECKPOINT is **ONE beat (wave 5), two calls total**, under the
|
|
52
|
+
session consent:
|
|
53
|
+
**(a) `checkpoint_task`** — the atomic boundary write (all-or-nothing,
|
|
54
|
+
idempotent on retry) carrying together: `patch: {status, body}` (the item's
|
|
55
|
+
new status + its full body with the fresh `## Log` entry — never a
|
|
56
|
+
whole-tree `push_plan` for one task); `loopEvent: {category:
|
|
57
|
+
"prism-verdict", outcome: PASS|ITERATE, subject: <task id>, tier: <the
|
|
58
|
+
task's validation_tier>, verification: <prism's evidence entry — method
|
|
59
|
+
static|live + tool + exit code, straight from its structured return>,
|
|
60
|
+
actorMeta: {model: <ruling model>, agent: "prism@<card ver>", runner:
|
|
61
|
+
"session"}, dedupeKey: "<task-id>·v<attempt>"}` (the emit rides the ruling's
|
|
62
|
+
OWN moment, never a session-end sweep; the dedupeKey makes a retried beat
|
|
63
|
+
count ONCE — the non-idempotent-emit learning, closed); and `decisions:
|
|
64
|
+
[...]` — each deliberation since the last beat with a CLIENT-STABLE id
|
|
65
|
+
(`<task-id>·d1`, `·d2` …): what came up, what was considered, what the DEV
|
|
66
|
+
chose, why (actor = the dev for steering, the agent for its own proposals;
|
|
67
|
+
PARAPHRASE + short verbatim quotes only where the wording carries the
|
|
68
|
+
decision — transcripts never land in shared stores; mirror each into the
|
|
69
|
+
item's `## Decisions` section).
|
|
70
|
+
**(b) `rafa checkpoint`** — the branch working set + sensor heartbeat +
|
|
71
|
+
brain mirror (the CLI beat; it also refreshes the loop-event cache the
|
|
72
|
+
sage-due digest reads).
|
|
73
|
+
`push_plan` remains for CREATION and structure changes only;
|
|
74
|
+
`update_plan_status`/`log_decision` stay valid but the one-beat call
|
|
75
|
+
replaces them at task boundaries. Checkpoint moments: task done · plan
|
|
63
76
|
approved · explicit ask · cadence · git push/pull — never session-end. The
|
|
64
|
-
loop-event emits ride the SAME checkpoint beats — one per outcome as it
|
|
65
|
-
occurs, monotonic, NEVER a session-end sweep. The
|
|
66
77
|
git-push boundary is MECHANICAL (M5): the pre-push hook runs `rafa checkpoint`
|
|
67
78
|
itself, non-blocking — the session still owns the task-done/plan-approved
|
|
68
79
|
moments. A checkpoint CONFLICT (a teammate's newer copy of the same file) is
|
|
@@ -71,10 +82,11 @@ platform MCP (one read path — the same surface any third-party agent uses).
|
|
|
71
82
|
3. **Brain changes mid-build — WHERE you are decides WHERE it goes.**
|
|
72
83
|
- **On the default branch (main):** run a full `/rafa scan` (regenerate →
|
|
73
84
|
prism → compile → push); the brain re-stamps at the new sha, so
|
|
74
|
-
`brain = f(code@sha)` stays exact.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
`brain = f(code@sha)` stays exact. gate-result loop events are
|
|
86
|
+
**MECHANICAL** (wave 5): the push gates emit them CLI-stamped — actor
|
|
87
|
+
envelope, `method: "live"` verification with the real exit code, and a
|
|
88
|
+
`(gate·sha·outcome)` dedupeKey. The session never hand-emits gate-result;
|
|
89
|
+
the double-emit class died with the non-idempotent-emit learning.
|
|
78
90
|
- **On any other branch:** the org brain is NEVER written from a branch —
|
|
79
91
|
it describes main, and a branch-state scan would poison it for everyone.
|
|
80
92
|
Invalidated/learned knowledge → the branch **working set**: hydrate the
|
|
@@ -106,6 +118,85 @@ platform MCP (one read path — the same surface any third-party agent uses).
|
|
|
106
118
|
scoped-refresh offer NOW (on main: refresh → gates → push; on a branch:
|
|
107
119
|
working-set edit → checkpoint), and `rafa dirty --consume` only after it ships.
|
|
108
120
|
|
|
121
|
+
## TDD-default (wave 6 — code tasks build red → green)
|
|
122
|
+
|
|
123
|
+
**Eligibility gate (deterministic, decided once per session):** the `tdd` skill
|
|
124
|
+
is installed (`.agents/skills/tdd/` — the toolbox slice says so) AND the repo
|
|
125
|
+
has a test harness — checklist, first hit wins: `package.json` `test` script ·
|
|
126
|
+
`vitest.config.*`/`jest.config.*` · `pytest.ini`/`pyproject [tool.pytest]` ·
|
|
127
|
+
`go.mod` with `*_test.go` · `Cargo.toml`. Bank the answer as a session fact
|
|
128
|
+
(`rafa facts add --claim="test harness: <runner>" --command="<detect>" --exit=0
|
|
129
|
+
--depends=package.json`) so no later spawn re-derives it. Not eligible → the
|
|
130
|
+
pre-wave path below, unchanged, plus a `factsDiscovered: ["tdd unavailable:
|
|
131
|
+
<reason>"]` note in atlas's return — bloom may later surface a tooling-fit
|
|
132
|
+
improvement; NEVER a mid-build nag.
|
|
133
|
+
|
|
134
|
+
**The loop (per the installed tdd skill — atlas INVOKES it; its anti-patterns
|
|
135
|
+
are prism's criteria):**
|
|
136
|
+
1. **Red** — atlas authors the failing test FIRST, at the seam the plan's
|
|
137
|
+
Done-check names (the seam was dev-confirmed at approval — never invented
|
|
138
|
+
here). Run it; it must FAIL for the right reason. Commit
|
|
139
|
+
`[<task-id>] test: red — <seam>` (full tier prefers the split commit; at
|
|
140
|
+
standard tier the evidence object below is the durable proof and red+green
|
|
141
|
+
may land as one commit).
|
|
142
|
+
2. **Green** — implement the minimal slice to exit 0. One seam, one test, one
|
|
143
|
+
vertical slice per cycle — never all-tests-then-all-code.
|
|
144
|
+
3. **Refactor is NOT part of the loop** — it belongs to the review stage
|
|
145
|
+
(tdd SKILL.md's own rule + the [rafa-review](../rafa-review/SKILL.md) gate;
|
|
146
|
+
bloom's no-big-bang directive is the same law).
|
|
147
|
+
Evidence rides atlas's structured return —
|
|
148
|
+
`evidence: [{claim: "red", tool: "<runner>", exitCode: 1}, {claim: "green",
|
|
149
|
+
tool: "<runner>", exitCode: 0}]` + `skillsUsed: ["tdd"]` — and
|
|
150
|
+
`how: via the tdd skill` lands in the item's `approach`.
|
|
151
|
+
|
|
152
|
+
**prism's verification, tier-scaled:** green is ALWAYS re-run live (tool
|
|
153
|
+
authenticity: binary + real exit code). Red at **standard** = attested from
|
|
154
|
+
atlas's evidence, labeled `method: "static"`; at **full** = re-run at the red
|
|
155
|
+
commit in a throwaway worktree (`git worktree add`), labeled `method: "live"`.
|
|
156
|
+
Mock discipline is a finding class: mocks only at system boundaries, never the
|
|
157
|
+
repo's own collaborators (the tdd skill's mocking reference is the criteria).
|
|
158
|
+
|
|
159
|
+
## UI tasks — the design method (wave 6)
|
|
160
|
+
|
|
161
|
+
**Trigger:** the task's epic `domains:` include a UI domain, or its target
|
|
162
|
+
files land in the app's component/route surfaces — the BRAIN's domain map
|
|
163
|
+
decides, never a hardcoded path list.
|
|
164
|
+
|
|
165
|
+
**Method (when the skills are installed — the toolbox slice says):** atlas
|
|
166
|
+
**INVOKES** `frontend-design` (Skill tool) for the two-pass plan — token
|
|
167
|
+
system (Color 4–6 named values · Type roles · Layout · the ONE signature
|
|
168
|
+
element), then self-critique against the brief before any code — and CONSULTS
|
|
169
|
+
`vercel-composition-patterns` for component structure (no boolean-prop
|
|
170
|
+
sprawl · compound components · state/actions/meta context · children over
|
|
171
|
+
render props), gating the `react19-*` rules on the repo's actual React version
|
|
172
|
+
(a React ≤18 repo skips them, stated in the return).
|
|
173
|
+
|
|
174
|
+
**Adherence vs distinctive — the brain decides:** when the brain documents a
|
|
175
|
+
design system (theme tokens, component conventions), those notes ARE the
|
|
176
|
+
brief and the skill's own "the brief's own words always win" rule applies —
|
|
177
|
+
adherence mode, distinctiveness spent within the system. No design-system
|
|
178
|
+
notes (greenfield/marketing surface) → distinctive mode, full method. Record
|
|
179
|
+
`how: via the frontend-design skill` (+ vercel-composition-patterns when
|
|
180
|
+
consulted). Skills declined → the pre-wave path + a `factsDiscovered` note.
|
|
181
|
+
|
|
182
|
+
## Spawn-prompt discipline (wave 5 — pay once, cite after)
|
|
183
|
+
|
|
184
|
+
- **Session facts first.** Every atlas/prism spawn prompt begins by pointing at
|
|
185
|
+
`.rafa/session-facts.json` (read first; cite `SF-n unchanged` where it holds;
|
|
186
|
+
bank new expensive verifications via `rafa facts add`). Six agents
|
|
187
|
+
re-deriving one environment fact was the QA session's headline waste.
|
|
188
|
+
- **Never restate card doctrine.** The card already binds the agent's creed
|
|
189
|
+
(prism's skepticism, atlas's toolbox-first) — a spawn prompt carries ONLY the
|
|
190
|
+
task-specific grounding: the task file, the exact target files/patterns, the
|
|
191
|
+
`validation_tier`, the facts pointer. Re-explaining the card in every prompt
|
|
192
|
+
is conductor inefficiency, not rigor.
|
|
193
|
+
- **Compact agent-to-agent register.** A subagent's primary reader is the
|
|
194
|
+
CONDUCTOR: terse-with-citations by default, ending in the structured return
|
|
195
|
+
(prism: `{verdict, evidence[], residualRisk}` · atlas: `{filesChanged,
|
|
196
|
+
commitSha, notes, factsDiscovered}`) — Logs and loop events assemble from
|
|
197
|
+
FIELDS, never re-paraphrased prose. Expand to full prose only on ITERATE or
|
|
198
|
+
when the human will read it directly.
|
|
199
|
+
|
|
109
200
|
**Lite plans** (single-child, from plan-lite) run the same per-task loop — the
|
|
110
201
|
Done-check gate never relaxes; only the ceremony around it shrinks (no bloom nudge,
|
|
111
202
|
single checkpoint + plan push at the end).
|
|
@@ -36,6 +36,28 @@ description: "rafa SOP — generate the commit message with the id join-key: the
|
|
|
36
36
|
outcome one-line ("Done-check: prism PASS") — the receipt travels with the
|
|
37
37
|
code.
|
|
38
38
|
|
|
39
|
+
## The body is REQUIRED and written for an AGENT reader (owner 2026-07-26)
|
|
40
|
+
|
|
41
|
+
A subject-only commit is a broken contract here. The next reader of this
|
|
42
|
+
message is most often an AGENT — ours (atlas recalling why a line exists, the
|
|
43
|
+
distiller judging a claim against this change, sage reading shapes) or a
|
|
44
|
+
foreign one — so the body must be SELF-CONTAINED and joinable:
|
|
45
|
+
|
|
46
|
+
- **What, concretely** — the surfaces/behaviors that changed, named (not
|
|
47
|
+
"various fixes"); a reader must know the blast radius without the diff.
|
|
48
|
+
- **Why — the intent** — the one thing the diff cannot show. State the
|
|
49
|
+
problem/decision that made this change necessary; paraphrase the pivotal
|
|
50
|
+
choice if one was made (it should also be a `log_decision`).
|
|
51
|
+
- **Join keys** — mention the brain notes this change follows or invalidates
|
|
52
|
+
(`per rule <id>` / `staleness: touches <id>`), so the cite-graph and the
|
|
53
|
+
dirty queue read straight out of the log.
|
|
54
|
+
- **No session deixis** — never "as discussed", "per the above", "the same
|
|
55
|
+
fix as before": the message must stand alone in `git log` a year later,
|
|
56
|
+
with zero conversation context.
|
|
57
|
+
|
|
58
|
+
TDD-default red commits keep their fixed form (`[<task-id>] test: red —
|
|
59
|
+
<seam>`) — the seam IS the body's content there.
|
|
60
|
+
|
|
39
61
|
## Why this exists (don't skip the bracket)
|
|
40
62
|
|
|
41
63
|
The brain-commit hook captures every subject into `intent/<sha>.md`;
|
|
@@ -39,6 +39,28 @@ code it describes.
|
|
|
39
39
|
1. **Collect** — `get_working_set(repo, branch, status: active)`. Zero files →
|
|
40
40
|
nothing to do, say so, stop. Rows already `needs-adjudication` are a HUMAN's
|
|
41
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.
|
|
42
64
|
2. **Validate (prism, context-isolated)** — the target is **merged MAIN as of
|
|
43
65
|
NOW, never the fork point and never a stale checkout**: `git fetch origin`
|
|
44
66
|
first; judge every claim against the fetched trunk. Confirm every citation
|
|
@@ -51,7 +73,13 @@ code it describes.
|
|
|
51
73
|
files lost it — re-declare); fold into an existing note when one covers the
|
|
52
74
|
topic (supersede, never duplicate).
|
|
53
75
|
4. **Gate + ship** — `rafa verify-citations` AND `rafa compile` to exit 0 →
|
|
54
|
-
`rafa push
|
|
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
|
|
55
83
|
aborts EVERYTHING: nothing resolved, nothing pushed, working set intact.
|
|
56
84
|
5. **Resolve** — `resolve_working_file(path, distilled)` for survivors — authored
|
|
57
85
|
per the OKF surface ([rafa-okf](../rafa-okf/SKILL.md); push emits the rest) —
|
|
@@ -61,9 +89,14 @@ code it describes.
|
|
|
61
89
|
which files died and why (cited); refutation is feedback, not silence. At
|
|
62
90
|
that same resolve beat emit **`report_loop_event(category:
|
|
63
91
|
"distill-refutation", outcome: refuted|distilled, subject: <path or note
|
|
64
|
-
id
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
67
100
|
`resolve_working_file(path, needs-adjudication, note)` for the undecidable —
|
|
68
101
|
the next session's digest offers the decision.
|
|
69
102
|
|
|
@@ -91,6 +91,24 @@ convention — see [RSC client boundary](/brain/rules/rsc-client-boundary.md)).
|
|
|
91
91
|
performance · architecture · product · ops**. Weight the **silent** issues (directive 2):
|
|
92
92
|
convention violations the brain documents, framework boundary breaches (e.g. RSC), missing
|
|
93
93
|
authz, drifting contracts. The brain's conventions are your oracle for "what's a violation here."
|
|
94
|
+
|
|
95
|
+
**The architecture lens has a METHOD (wave 6)** — criteria adapted from the installed
|
|
96
|
+
`improve-codebase-architecture` skill (READ as criteria, never invoked — it is
|
|
97
|
+
`disable-model-invocation`; the method below stands whether or not the skill is installed):
|
|
98
|
+
- **Scope by heat, not by sweep** (YAGNI): unless the dev named a direction, walk
|
|
99
|
+
`git log --oneline` for the hot spots — churn is where depth pays; cold code waits.
|
|
100
|
+
- **Deep vs shallow judgment**: a module earns its interface when the interface is smaller
|
|
101
|
+
than what it hides. Bouncing between many small modules to follow one behavior, pure
|
|
102
|
+
functions extracted only for testability, and leaky seams are the friction signals.
|
|
103
|
+
- **The deletion test**: would deleting this module CONCENTRATE complexity somewhere honest,
|
|
104
|
+
or just smear it around? "Concentrates" marks a candidate.
|
|
105
|
+
- **Candidates are normal §3 improvement files** (zero schema change): before/after as body
|
|
106
|
+
prose + a `Confidence: Strong | Worth exploring | Speculative` body line; inside these
|
|
107
|
+
notes use the depth vocabulary — module · interface · implementation · seam · adapter ·
|
|
108
|
+
depth · leverage · locality — and keep it INSIDE the architecture lens (the rest of the
|
|
109
|
+
ledger speaks the repo's own language).
|
|
110
|
+
- **The big-bang guard is sovereign** (directive 1): a `Strong` candidate lands as its first
|
|
111
|
+
10-minute slice or a P2 backlog item — never a grand refactor plan.
|
|
94
112
|
3. **Delegate security.** Run `pnpm audit` (dep CVEs). Recommend/run the **`security-review`**
|
|
95
113
|
skill and semgrep/CodeQL if available. Record *tool* results as improvements; the LLM pass is
|
|
96
114
|
observational only — never present it as an authoritative audit.
|
|
@@ -166,3 +184,7 @@ the ledger the nudge will read.
|
|
|
166
184
|
beyond the deterministic cite-check.
|
|
167
185
|
- Debt-score weighting calibrated from real runs.
|
|
168
186
|
- Per-lens diverse validators (a security lens checked by a security-minded validator).
|
|
187
|
+
- A visual before/after artifact for architecture candidates (the
|
|
188
|
+
improve-codebase-architecture skill's HTML report) — deliberately SKIPPED for now: its
|
|
189
|
+
CDN-loaded Tailwind/Mermaid + temp-file-and-open mechanics violate the deterministic,
|
|
190
|
+
no-network posture; the ledger IS the report.
|
|
@@ -44,7 +44,12 @@ at the fix. This is platform DNA: **leverage what's present; never reinvent it.*
|
|
|
44
44
|
|
|
45
45
|
The engine ([`lib/leverage/engine.mjs`](../../../packages/cli/lib/leverage/engine.mjs)) gathers
|
|
46
46
|
light, tool-agnostic repo signals (`repoContext`: stack deps, `.env` files + whether they're
|
|
47
|
-
ignored, declared MCP servers
|
|
47
|
+
ignored, declared MCP servers, **and the harness-neutral `.agents/skills/` inventory +
|
|
48
|
+
skill-dependency status (wave 6)** — `.agents/` is ONE convention Claude Code · Codex ·
|
|
49
|
+
Cursor all read, so it lives in the engine, never inside a per-toolchain adapter), then runs
|
|
50
|
+
every **detected** adapter and ranks the tips. The engine emits its own rafa-level tip —
|
|
51
|
+
`skill-deps-missing` (`P2`, fix = `rafa update --skills`) — because rafa's declared skill
|
|
52
|
+
dependencies are harness-neutral by definition.
|
|
48
53
|
|
|
49
54
|
Each adapter implements one contract ([`lib/leverage/adapters/index.mjs`](../../../packages/cli/lib/leverage/adapters/index.mjs)):
|
|
50
55
|
|
|
@@ -41,6 +41,34 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
|
|
|
41
41
|
A plan whose children lack Done-checks is REJECTED here — compile never parses
|
|
42
42
|
bodies (invariant #3); this gate is prism's.
|
|
43
43
|
|
|
44
|
+
**The grill pass (wave 6 — tier-bounded adversarial interview).** On top of
|
|
45
|
+
the two invariants, prism GRILLS the draft — the same procedure the installed
|
|
46
|
+
`grilling` skill carries (`.agents/skills/grilling/SKILL.md`; prism READS it
|
|
47
|
+
as criteria when installed, and this SOP carries the method regardless —
|
|
48
|
+
never invoked mid-validation, per its card). Per task, up the probe ladder:
|
|
49
|
+
**grounding** (which claims rest on assumption? name it, demand its
|
|
50
|
+
evidence — a session-fact id counts, vibes don't) · **unstated dependencies**
|
|
51
|
+
(what must exist for this task that no `blocked_by` declares?) · **vacuous
|
|
52
|
+
Done-check** (can it pass while the feature is broken? a check that
|
|
53
|
+
recomputes its expectation the way the code will, or that a no-op satisfies,
|
|
54
|
+
is TAUTOLOGICAL — criteria per the tdd skill's anti-patterns when installed)
|
|
55
|
+
· **YAGNI** (does the INTENT need this task, or did thoroughness invent it?).
|
|
56
|
+
Budget scales with the task's `validation_tier`: **light** = the two
|
|
57
|
+
invariants only (exactly today) · **standard** = ≤2 probes per task, ≤10 per
|
|
58
|
+
plan · **full** = the complete grilling procedure (one probe at a time,
|
|
59
|
+
survives-three-consecutive ends it). Every probe finding cites the plan line
|
|
60
|
+
it hits — an uncited probe is dropped, and findings feed the existing
|
|
61
|
+
REJECT-with-cited-reasons loop. Plan-lite drafts grill at light.
|
|
62
|
+
|
|
63
|
+
**The seam ritual (wave 6 — TDD's consent moment, no new prompt).** For code
|
|
64
|
+
tasks in TDD-eligible repos (the tdd skill installed + a test harness
|
|
65
|
+
detected — the build SOP's gate), every LEAF's `## Done-check` NAMES the seam
|
|
66
|
+
under test: *"`<runner>` on `<test-file>` at seam `<the public interface>`
|
|
67
|
+
exits 0"*. Seams come from recall (the brain's contracts/flows) — never
|
|
68
|
+
invented at build time. The plan-approval summary lists the seams as their
|
|
69
|
+
own line, so **the dev approving the plan IS the tdd skill's "confirm seams
|
|
70
|
+
with the user" step** — consent rides the existing approval gate.
|
|
71
|
+
|
|
44
72
|
## Procedure
|
|
45
73
|
|
|
46
74
|
1. **Staleness check** — compare the platform envelope's `brainForSha` against the
|
|
@@ -58,7 +86,14 @@ Planning is a choreography, not one agent (spec: knowledge-mcp-build-agent):
|
|
|
58
86
|
carries the glimpse fields — `title` (what) · `description` (why) ·
|
|
59
87
|
`approach` (how, one line) · `assignee` when known · `blocked_by` for
|
|
60
88
|
intra-plan dependencies (a dependency IS a blocker; blocked is DERIVED,
|
|
61
|
-
never a status) · optional `priority` 0–4 / `estimate
|
|
89
|
+
never a status) · optional `priority` 0–4 / `estimate` ·
|
|
90
|
+
**`validation_tier` (wave 5 — rigor scales with blast radius, not with
|
|
91
|
+
process): assign per LEAF from what the task touches — contract/state-shape/
|
|
92
|
+
graph surface → `full` · pure types/docs/leaf-UI → `light` · everything else
|
|
93
|
+
→ `standard` (the default when omitted). The tier bounds prism's
|
|
94
|
+
RE-DERIVATION DEPTH only; the Done-check gate itself never relaxes, and the
|
|
95
|
+
tier rides the task's loop events so sage can audit the tiering against
|
|
96
|
+
escaped-bug reality instead of anyone guessing.** Every LEAF carries a
|
|
62
97
|
`## Done-check`. The blast radius goes on the EPIC's `domains:` — it rides
|
|
63
98
|
`push_plan` and the platform renders the plan's brain slice beside it.
|
|
64
99
|
ADR material (alternatives, risks, non-goals) lives in the epic body AND
|
|
@@ -104,8 +104,16 @@ its OWN question:
|
|
|
104
104
|
7. **The active task's Done-check** (from `get_active_plan`): does the diff
|
|
105
105
|
satisfy what the plan said done means?
|
|
106
106
|
8. Verdict: **PASS** or **ITERATE** with per-finding citations (note id +
|
|
107
|
-
file:line).
|
|
108
|
-
|
|
107
|
+
file:line). **Severity rides every finding (wave 6 — the
|
|
108
|
+
requesting-code-review taxonomy): `Critical (Must Fix)` · `Important
|
|
109
|
+
(Should Fix)` · `Minor (Nice to Have)`. ITERATE iff ≥1 Critical or
|
|
110
|
+
Important; Minor NEVER flips the verdict** (advisory noise must not
|
|
111
|
+
masquerade as a gate — and "not everything is Critical" is the taxonomy's
|
|
112
|
+
own first rule). Findings feed atlas (validate-and-correct — atlas fixes,
|
|
113
|
+
the judge re-checks; the reviewer never edits). The fresh-context reviewer
|
|
114
|
+
discipline the installed requesting-code-review skill prescribes
|
|
115
|
+
(precisely-crafted context, never the session's history) IS rafa's spawn
|
|
116
|
+
model already — confirmation, not new machinery.
|
|
109
117
|
9. Emit **`report_loop_event(category: "review-verdict", outcome:
|
|
110
118
|
PASS|ITERATE, subject: <task id or branch>)`** at the ruling — sage's
|
|
111
119
|
evidence, monotonic, never a session-end sweep.
|
|
@@ -124,3 +132,9 @@ its OWN question:
|
|
|
124
132
|
- Trusting the diff author's summary — judge code against rules.
|
|
125
133
|
- Blocking on the gate — it is advisory; the merge reconciler is the hard gate.
|
|
126
134
|
- Findings without citations (rule id + file:line) — uncited = unactionable.
|
|
135
|
+
- **Mutating anything while judging** (wave 6, from the code-reviewer's
|
|
136
|
+
read-only rule): the review never touches the working tree, the index, HEAD,
|
|
137
|
+
or branch state — inspect another revision via `git show`/a throwaway
|
|
138
|
+
worktree, never a checkout on this one.
|
|
139
|
+
- **Severity inflation** — a verdict where everything is Critical has stopped
|
|
140
|
+
discriminating; that is itself a review failure.
|
|
@@ -58,8 +58,14 @@ boundary; its product is a ledger reviewed *later*, never a live nudge. No mid-f
|
|
|
58
58
|
### 2. evidence-cited — SHAPES only, via `get_loop_events`
|
|
59
59
|
Every learning cites its evidence, and evidence is **loop-event shapes and categories only**:
|
|
60
60
|
verdict TYPES, gap CLASSES, miss TAXONOMIES, and the aggregate counts/rates over them — read via
|
|
61
|
-
`get_loop_events` (category ∈ `prism-verdict | gate-result | reflex-outcome | distill-refutation
|
|
62
|
-
outcome enum per category; `subject` is a shape reference, never content).
|
|
61
|
+
`get_loop_events` (category ∈ `prism-verdict | gate-result | reflex-outcome | distill-refutation |
|
|
62
|
+
review-verdict`; outcome enum per category; `subject` is a shape reference, never content). Wave 5
|
|
63
|
+
enriched every event with THREE new analysis dimensions — split every rate by them: **verification
|
|
64
|
+
method** (`static` vs `live` — a PASS-rate that holds only on static rulings is a coverage gap, not
|
|
65
|
+
health), **actor envelope** (model · agent@version · runner — attribute a miss class to the agent
|
|
66
|
+
structure that produced it), and **tier** (`light|standard|full` — audit the tiering itself: a
|
|
67
|
+
light-tier task class that later produced escaped bugs is evidence the planner's tier rule needs a
|
|
68
|
+
diff; a full-tier class whose findings-density is ~zero is evidence of ceremony). A learning with no
|
|
63
69
|
event-shape evidence is a hunch — drop it. sage never cites a customer artifact (no `file:line`
|
|
64
70
|
into customer code; that field does not exist in the schema below).
|
|
65
71
|
|
|
@@ -155,7 +155,10 @@ retrieval index. Bodies read like a senior engineer explaining that one concept
|
|
|
155
155
|
3b. **Toolbox inventory** [deterministic] — ratified 2026-07-12: the REPO toolbox is a
|
|
156
156
|
first-class brain domain (`toolbox`). Run `npx @rafinery/cli leverage --json` (the
|
|
157
157
|
deterministic extractor) and author cited notes for the committed toolbox — skills
|
|
158
|
-
(`.claude/skills/*/SKILL.md` name+description),
|
|
158
|
+
(`.claude/skills/*/SKILL.md` name+description), **the harness-neutral
|
|
159
|
+
`.agents/skills/*/SKILL.md` (wave 6 — the `agentSkills` block in the JSON: one
|
|
160
|
+
convention Claude Code · Codex · Cursor all read; consent-installed by
|
|
161
|
+
`rafa update`)**, commands, `.mcp.json` servers,
|
|
159
162
|
granted permissions — cites into the config files themselves (contract §2; they are
|
|
160
163
|
citable file:line). This makes the toolbox recallable through the SAME MCP surface
|
|
161
164
|
as all knowledge and refreshable like any note. Personal `~/.claude/` is NEVER
|
package/lib/brain-repo.mjs
CHANGED
|
@@ -77,7 +77,7 @@ export function ensureBrainRepo(cwd, { requireRemote = true } = {}) {
|
|
|
77
77
|
// (not knowledge). MERGED line-by-line so clones ignored under an older CLI
|
|
78
78
|
// still gain new exclusions.
|
|
79
79
|
const ignore = join(rafaDir, ".gitignore");
|
|
80
|
-
const IGNORES = ["hydration.json", "*.theirs.md", "distill-incoming/", "distill-verdicts.json", "dirty.jsonl", "reflex.jsonl", "sensor-errors.jsonl", "benchmark.demo.json"];
|
|
80
|
+
const IGNORES = ["hydration.json", "*.theirs.md", "distill-incoming/", "distill-verdicts.json", "dirty.jsonl", "reflex.jsonl", "sensor-errors.jsonl", "benchmark.demo.json", "session-facts.json", "loop-events-tail.json"];
|
|
81
81
|
const cur = existsSync(ignore) ? readFileSync(ignore, "utf8") : "";
|
|
82
82
|
const have = new Set(cur.split("\n").map((l) => l.trim()).filter(Boolean));
|
|
83
83
|
const missing = IGNORES.filter((l) => !have.has(l));
|
package/lib/checkpoint.mjs
CHANGED
|
@@ -108,11 +108,20 @@ export default async function checkpoint() {
|
|
|
108
108
|
|
|
109
109
|
// The working set is BRANCH state. On the trunk, knowledge changes go
|
|
110
110
|
// through scan → compile → rafa push (the org brain's one writer surface).
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
// The trunk is RESOLVED, never hardcoded (owner 2026-07-26): the stamped
|
|
112
|
+
// prodBranch → origin/HEAD → "main"; the main|master pair stays as a belt
|
|
113
|
+
// ONLY for unconfigured repos (with prodBranch set, "main" can be a plain
|
|
114
|
+
// feature-carrying branch and its working set checkpoints normally).
|
|
115
|
+
{
|
|
116
|
+
const { trunkBranchOf, readStamp } = await import("./stamp.mjs");
|
|
117
|
+
const trunk = trunkBranchOf(ROOT);
|
|
118
|
+
const configured = typeof readStamp(ROOT).prodBranch === "string";
|
|
119
|
+
if (branch === trunk || (!configured && (branch === "main" || branch === "master"))) {
|
|
120
|
+
console.log(
|
|
121
|
+
`• on ${branch} (the trunk) — the working set is branch-scoped; org-brain changes on the trunk go through /rafa scan + rafa push. Nothing to checkpoint.`,
|
|
122
|
+
);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
// The commit this capture is grounded against (contract: capturedAtSha —
|
|
@@ -248,22 +257,24 @@ export default async function checkpoint() {
|
|
|
248
257
|
// durable mirror of the dev's current copies; .theirs.md files are ignored.)
|
|
249
258
|
mirrorTail(ROOT, branch);
|
|
250
259
|
|
|
251
|
-
// prism-verdict backstop (harness-arc wave 0)
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
260
|
+
// prism-verdict backstop (harness-arc wave 0) + the sage-due cache (wave
|
|
261
|
+
// 5.5): the SAME single best-effort call, enriched. It now fetches recent
|
|
262
|
+
// events (no category filter) and caches their SHAPES locally
|
|
263
|
+
// (.rafa/loop-events-tail.json — category/at only, subjects excluded) so the
|
|
264
|
+
// SessionStart digest computes "sage due" with ZERO added platform calls.
|
|
265
|
+
// The old prism-verdict reminder derives from the same response.
|
|
255
266
|
try {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
});
|
|
260
|
-
if ((
|
|
267
|
+
const { writeLoopCache } = await import("./loop-cache.mjs");
|
|
268
|
+
const evts = await callTool(ROOT, "get_loop_events", { limit: 200 });
|
|
269
|
+
const events = evts.events ?? [];
|
|
270
|
+
writeLoopCache(ROOT, { fetchedAt: new Date().toISOString(), events });
|
|
271
|
+
if (events.filter((e) => e.category === "prism-verdict").length === 0)
|
|
261
272
|
console.log(
|
|
262
273
|
" ℹ no prism-verdict loop events recorded yet — the build SOP emits " +
|
|
263
274
|
"report_loop_event(prism-verdict, PASS|ITERATE, <task id>) at each Done-check ruling.",
|
|
264
275
|
);
|
|
265
276
|
} catch {
|
|
266
|
-
/* unprovisioned/unreachable — the reminder
|
|
277
|
+
/* unprovisioned/unreachable — the reminder + cache are best-effort */
|
|
267
278
|
}
|
|
268
279
|
if (conflicts) process.exit(2);
|
|
269
280
|
}
|
package/lib/distill.mjs
CHANGED
|
@@ -40,8 +40,17 @@ import { createRequire } from "node:module";
|
|
|
40
40
|
import { dirname, join } from "node:path";
|
|
41
41
|
import { pathToFileURL } from "node:url";
|
|
42
42
|
import { callTool } from "./mcp-client.mjs";
|
|
43
|
-
import {
|
|
43
|
+
import { trunkBranchOf } from "./stamp.mjs";
|
|
44
|
+
import { runCompile, SCHEMA_VERSION } from "./gate/compile.mjs";
|
|
44
45
|
import { runVerifyCitations } from "./gate/verify-citations.mjs";
|
|
46
|
+
import {
|
|
47
|
+
applyLadder,
|
|
48
|
+
brainSchemaVersion,
|
|
49
|
+
classOf,
|
|
50
|
+
resolveSchemaPlan,
|
|
51
|
+
sourceSchemaVersion,
|
|
52
|
+
walkBrainNotes,
|
|
53
|
+
} from "./distiller/schema-ladder.mjs";
|
|
45
54
|
import pull from "./pull.mjs";
|
|
46
55
|
import push from "./push.mjs";
|
|
47
56
|
|
|
@@ -192,7 +201,7 @@ export default async function distill(args = []) {
|
|
|
192
201
|
try {
|
|
193
202
|
await callTool(ROOT, "report_distill_run", {
|
|
194
203
|
branch,
|
|
195
|
-
toBranch: process.env.RAFA_TARGET_BRANCH ||
|
|
204
|
+
toBranch: process.env.RAFA_TARGET_BRANCH || trunkBranchOf(ROOT),
|
|
196
205
|
mergeCommitSha: mergeSha,
|
|
197
206
|
status,
|
|
198
207
|
note: summary,
|
|
@@ -371,6 +380,35 @@ export default async function distill(args = []) {
|
|
|
371
380
|
await pull(["--full"]);
|
|
372
381
|
console.log(` ⏱ brain mirror: ${secs(tPull)}s`);
|
|
373
382
|
|
|
383
|
+
// 2b · SCHEMA LADDER (0.11.0 — the owner's 4-case doctrine): resolve the
|
|
384
|
+
// version lattice BEFORE any claim-level judging, so knowledge from two
|
|
385
|
+
// lineages always meets on ONE schema — the newest this runner understands.
|
|
386
|
+
// Lifted target notes ride THIS run's gates + push (one migration+reconcile
|
|
387
|
+
// commit); an unknown future schema aborts loudly, never downgrades.
|
|
388
|
+
{
|
|
389
|
+
const target = brainSchemaVersion(join(ROOT, ".rafa"));
|
|
390
|
+
const source = sourceSchemaVersion(active.map((f) => f.content));
|
|
391
|
+
const plan = resolveSchemaPlan({ target, source, latest: SCHEMA_VERSION });
|
|
392
|
+
console.log(
|
|
393
|
+
`• schema ladder: target v${target} · source v${source} · runner v${SCHEMA_VERSION} → ${plan.mode}`,
|
|
394
|
+
);
|
|
395
|
+
if (plan.mode === "abort-newer-than-runner") die(plan.reason);
|
|
396
|
+
if (plan.mode !== "diff") console.log(` ${plan.reason}`);
|
|
397
|
+
// Per-OKF-type lifts (class derives from the PATH — typed-candidates
|
|
398
|
+
// doctrine); non-laddered paths (intent records …) pass through untouched.
|
|
399
|
+
if (plan.upgradeSource)
|
|
400
|
+
for (const f of active) {
|
|
401
|
+
const cls = classOf(f.path);
|
|
402
|
+
if (cls) f.content = applyLadder(f.content, plan.toVersion, cls);
|
|
403
|
+
}
|
|
404
|
+
if (plan.rewriteTarget)
|
|
405
|
+
for (const noteFile of walkBrainNotes(join(ROOT, ".rafa"))) {
|
|
406
|
+
const cls = classOf(noteFile);
|
|
407
|
+
if (cls)
|
|
408
|
+
writeFileSync(noteFile, applyLadder(readFileSync(noteFile, "utf8"), plan.toVersion, cls));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
374
412
|
// 3 · stage the incoming working set for the agent (gitignored inside .rafa).
|
|
375
413
|
const stagingRoot = join(ROOT, ".rafa", "distill-incoming");
|
|
376
414
|
rmSync(stagingRoot, { recursive: true, force: true });
|
|
@@ -554,7 +592,22 @@ ${fileList}`;
|
|
|
554
592
|
(f) => byPath.get(f.path).verdict === "distilled",
|
|
555
593
|
).length;
|
|
556
594
|
if (distilledCount > 0) {
|
|
557
|
-
|
|
595
|
+
// Descriptive reconciliation commit (owner 2026-07-26): the subject says
|
|
596
|
+
// WHAT this run did — branch, per-verdict counts, the banked ids — so any
|
|
597
|
+
// agent (ours or a foreign OKF reader) walking the brain log understands
|
|
598
|
+
// the commit without opening it. brain-for trailer untouched (push owns it).
|
|
599
|
+
{
|
|
600
|
+
const counts = { distilled: 0, refuted: 0, "needs-adjudication": 0 };
|
|
601
|
+
for (const v of verdicts) counts[v.verdict] = (counts[v.verdict] ?? 0) + 1;
|
|
602
|
+
const banked = verdicts
|
|
603
|
+
.filter((v) => v.verdict === "distilled")
|
|
604
|
+
.map((v) => String(v.path).split("/").pop().replace(/\.md$/, ""));
|
|
605
|
+
const idList = banked.slice(0, 4).join(", ") + (banked.length > 4 ? " …" : "");
|
|
606
|
+
await push([
|
|
607
|
+
"--verb=distill",
|
|
608
|
+
`--message=reconcile(${branch}): ${counts.distilled} banked · ${counts.refuted} refuted · ${counts["needs-adjudication"]} adjudication${banked.length ? ` — ${idList}` : ""}`,
|
|
609
|
+
]);
|
|
610
|
+
}
|
|
558
611
|
} else {
|
|
559
612
|
console.log("• no survivors — nothing to push to the brain repo");
|
|
560
613
|
}
|
|
@@ -164,22 +164,11 @@ export function citesGround(content, cwd = process.cwd()) {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
// Rewrite a survivor's frontmatter `cites:` DSL so a re-grounded citation points
|
|
167
|
-
// at where the token NOW lives on main
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
export
|
|
172
|
-
let out = content;
|
|
173
|
-
for (const r of regroundings ?? []) {
|
|
174
|
-
const fromLoc = r.cite.loc; // file:line[-end]
|
|
175
|
-
const toLoc = r.to.loc; // file:line
|
|
176
|
-
// Match the DSL line ` - <fromLoc> :: <token>` and swap the locator only.
|
|
177
|
-
const esc = fromLoc.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
178
|
-
const re = new RegExp(`(-\\s+)${esc}(\\s*::\\s*)`, "g");
|
|
179
|
-
out = out.replace(re, `$1${toLoc}$2`);
|
|
180
|
-
}
|
|
181
|
-
return out;
|
|
182
|
-
}
|
|
167
|
+
// at where the token NOW lives on main. MOVED to the gate (wave 5.3) so
|
|
168
|
+
// `verify-citations --fix` can use it without an import cycle (this module
|
|
169
|
+
// imports the gate's primitives, never the reverse); re-exported here so the
|
|
170
|
+
// doctrine's callers keep their import path.
|
|
171
|
+
export { rewriteCites } from "../gate/verify-citations.mjs";
|
|
183
172
|
|
|
184
173
|
// ── folding N branch working sets into ONE run (case 1 · case 5) ────────────────
|
|
185
174
|
// Input: [{ branch, capturedAtSha, mergedAt, files: [{ path, content }] }].
|