@rafinery/cli 0.6.0 → 0.7.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.
@@ -24,7 +24,7 @@ agents write .md (frontmatter per this contract)
24
24
  ```
25
25
 
26
26
  Two enforcement points: **compile-time** (structure — this contract) and **prism**
27
- (semantics — truth of the content). Structure is validated *before* semantics.
27
+ (semantics — truth of the content). Structure is validated _before_ semantics.
28
28
 
29
29
  ---
30
30
 
@@ -35,23 +35,23 @@ a schema (parse it), a `verbatim` type is prose (display it, never parse for dat
35
35
  `generated` type is machine-written by a named tool. If a file matching a `structured` path
36
36
  doesn't validate, compile fails.
37
37
 
38
- | Type | Path glob | Class | Schema | Author |
39
- |---|---|---|---|---|
40
- | rule | `brain/rules/*.md` | **structured** | §2 | atlas |
41
- | playbook | `brain/playbooks/*.md` | **structured** | §2 | atlas |
42
- | health | `brain/checklist.md` | **structured** | §4 | prism |
43
- | coverage | `brain/coverage.md` | **structured** | §6 | atlas |
44
- | improvement | `improve/improvements/*.md` | **structured** | §3 | bloom |
45
- | ledger | `improve/ledger.md` | **structured** | §5 | bloom |
46
- | plan | `plans/**/*.md` | **structured** | §7 | plan/build |
47
- | log | `brain/log.md` | **verbatim** | — (prose trail) | conductor |
48
- | citation-check | `**/citation-check.md` | **generated** | — (by `rafa verify-citations`) | tool |
49
- | citation-check record | `**/citation-check.json` | **generated** | `{ checkerVersion, at, pass, gates, warns }` (by `rafa verify-citations`; compile folds it into `manifest.citations`) | tool |
50
- | active pointer | `active.md` | **generated** | — (`# <plan-id>` or "No active plan"; compile emits it as `activePlanId`, §7) | conductor |
51
- | contract copy | `contract.md` | **generated** | — (stamped copy of `.claude/rafa/contract.md`, written by `rafa push`) | tool |
52
- | dirty queue | `dirty.jsonl` | **generated** (local state — transport-excluded, NEVER pushed/ingested) | one `{f, t}` JSON line per code edit (by the PostToolUse sensor hook; read by `rafa dirty` + the SessionStart digest) | tool |
53
- | reflex queue | `reflex.jsonl` | **generated** (local state — transport-excluded, NEVER pushed/ingested; transcript pointers are LOCAL, raw transcripts never ship) | `{id, p, t, tp}` per detected correction + append-only `{id, done, verdict, at}` markers (by the UserPromptSubmit sensor; read by `rafa reflex` + the digest) | tool |
54
- | agent | `.claude/agents/*.md` (code repo) | **structured** (local gate — NOT in manifest) | §10 | rafa |
38
+ | Type | Path glob | Class | Schema | Author |
39
+ | --------------------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
40
+ | rule | `brain/rules/*.md` | **structured** | §2 | atlas |
41
+ | playbook | `brain/playbooks/*.md` | **structured** | §2 | atlas |
42
+ | health | `brain/checklist.md` | **structured** | §4 | prism |
43
+ | coverage | `brain/coverage.md` | **structured** | §6 | atlas |
44
+ | improvement | `improve/improvements/*.md` | **structured** | §3 | bloom |
45
+ | ledger | `improve/ledger.md` | **structured** | §5 | bloom |
46
+ | plan | `plans/**/*.md` | **structured** | §7 | plan/build |
47
+ | log | `brain/log.md` | **verbatim** | — (prose trail) | conductor |
48
+ | citation-check | `**/citation-check.md` | **generated** | — (by `rafa verify-citations`) | tool |
49
+ | citation-check record | `**/citation-check.json` | **generated** | `{ checkerVersion, at, pass, gates, warns }` (by `rafa verify-citations`; compile folds it into `manifest.citations`) | tool |
50
+ | active pointer | `active.md` | **generated** | — (`# <plan-id>` or "No active plan"; compile emits it as `activePlanId`, §7) | conductor |
51
+ | contract copy | `contract.md` | **generated** | — (stamped copy of `.claude/rafa/contract.md`, written by `rafa push`) | tool |
52
+ | dirty queue | `dirty.jsonl` | **generated** (local state — transport-excluded, NEVER pushed/ingested) | one `{f, t}` JSON line per code edit (by the PostToolUse sensor hook; read by `rafa dirty` + the SessionStart digest) | tool |
53
+ | reflex queue | `reflex.jsonl` | **generated** (local state — transport-excluded, NEVER pushed/ingested; transcript pointers are LOCAL, raw transcripts never ship) | `{id, p, t, tp}` per detected correction + append-only `{id, done, verdict, at}` markers (by the UserPromptSubmit sensor; read by `rafa reflex` + the digest) | tool |
54
+ | agent | `.claude/agents/*.md` (code repo) | **structured** (local gate — NOT in manifest) | §10 | rafa |
55
55
 
56
56
  The `agent` type is the one structured type outside `.rafa/`: the shipped agent cards
57
57
  in the code repo. Compile validates them (§10) so a malformed card fails the gate like
@@ -72,7 +72,7 @@ error (no rogue unschema'd data files).
72
72
 
73
73
  - **Frontmatter is the only machine-read surface.** The markdown **body is prose**
74
74
  and is NEVER parsed for data. Every value the platform shows comes from frontmatter
75
- (via the compiled manifest), or from the body rendered *verbatim* (never scraped).
75
+ (via the compiled manifest), or from the body rendered _verbatim_ (never scraped).
76
76
  - **`schemaVersion` is required** on every file and in the manifest. Unknown version
77
77
  → ingest reports an error, does not guess.
78
78
  - **`id` is required and stable.** It MUST equal the filename stem (`foo.md` → `foo`).
@@ -108,7 +108,7 @@ Deterministic to parse: indented non-empty lines after `>-` join with a single s
108
108
 
109
109
  Each cite is one string: `` `<path>:<line> :: <token>` ``
110
110
 
111
- - split on the **first** ` :: ` → (`left`, `token`)
111
+ - split on the **first** `::` → (`left`, `token`)
112
112
  - in `left`, split on the **last** `:` → (`path`, `line`)
113
113
  - `line` is digits (`"42"`) or a range (`"14-18"`)
114
114
 
@@ -124,67 +124,82 @@ JSON. This is the exact shape the platform binds to.
124
124
  ```jsonc
125
125
  {
126
126
  "schemaVersion": 1,
127
- "generatedAt": "2026-07-03T10:00:00Z", // ISO-8601, stamped by compile
128
- "repo": "owner/repo", // CODE repo full_name (identity)
129
- "branch": "main", // code branch this brain reflects
130
- "codeSha": "abc123…", // code commit the brain was built for
131
-
132
- "health": { // from brain/checklist.md — or null
133
- "verdict": "PASS", // "PASS" | "ITERATE"
134
- "score": 95, // 0–100
127
+ "generatedAt": "2026-07-03T10:00:00Z", // ISO-8601, stamped by compile
128
+ "repo": "owner/repo", // CODE repo full_name (identity)
129
+ "branch": "main", // code branch this brain reflects
130
+ "codeSha": "abc123…", // code commit the brain was built for
131
+
132
+ "health": {
133
+ // from brain/checklist.md — or null
134
+ "verdict": "PASS", // "PASS" | "ITERATE"
135
+ "score": 95, // 0–100
135
136
  "gates": { "fidelity": "pass", "coverage": "pass" },
136
- "counts": { "blockers": 0, "majors": 0, "minors": 2 }
137
+ "counts": { "blockers": 0, "majors": 0, "minors": 2 },
137
138
  },
138
139
 
139
- "ledger": { // from improve/ledger.md — or null
140
+ "ledger": {
141
+ // from improve/ledger.md — or null
140
142
  "open": 7,
141
143
  "debtScore": 13,
142
- "byPriority": { "P0": 0, "P1": 1, "P2": 3, "P3": 3 }
144
+ "byPriority": { "P0": 0, "P1": 1, "P2": 3, "P3": 3 },
143
145
  },
144
146
 
145
- "coverage": { // from brain/coverage.md — or null
146
- "domains": [ // one row per domain, its scan status
147
+ "coverage": {
148
+ // from brain/coverage.md or null
149
+ "domains": [
150
+ // one row per domain, its scan status
147
151
  { "domain": "design-system", "status": "mapped" },
148
- { "domain": "external-integrations", "status": "thin" }
149
- ]
152
+ { "domain": "external-integrations", "status": "thin" },
153
+ ],
150
154
  },
151
155
 
152
- "citations": { // from brain/citation-check.json — or null
153
- "checkerVersion": 2, // the gate level this brain passed
154
- "pass": true, // that run's verdict (recorded, never assumed)
155
- "at": "2026-07-12T10:00:00Z" // when the checker last ran
156
- }, // null = no recorded checker run rode this push
156
+ "citations": {
157
+ // from brain/citation-check.json or null
158
+ "checkerVersion": 2, // the gate level this brain passed
159
+ "pass": true, // that run's verdict (recorded, never assumed)
160
+ "at": "2026-07-12T10:00:00Z", // when the checker last ran
161
+ }, // null = no recorded checker run rode this push
157
162
 
158
163
  "notes": [
159
164
  {
160
165
  "id": "agent-name-contract",
161
- "kind": "rule", // "rule" | "playbook" (from directory)
162
- "type": "contract", // contract | convention | flow | how-to
166
+ "kind": "rule", // "rule" | "playbook" (from directory)
167
+ "type": "contract", // contract | convention | flow | how-to
163
168
  "domain": "web-agent-bridge",
164
169
  "title": "…",
165
170
  "summary": "…",
166
- "links": ["other-note-id"], // [] if none
167
- "failure": "silent", // "silent" | "loud" | omitted
168
- "cites": [{ "file": "src/x.tsx", "line": "42", "token": "research_agent" }],
169
- "path": "brain/rules/agent-name-contract.md" // prose body (lazy fetch)
170
- }
171
+ "links": ["other-note-id"], // [] if none
172
+ "failure": "silent", // "silent" | "loud" | omitted
173
+ "bodyTokens": 180, // OPTIONAL · ceil(bodyChars/4) size estimate of the
174
+ // prose body — deterministic (no LLM), for recall-savings
175
+ "cites": [
176
+ {
177
+ "file": "src/x.tsx",
178
+ "line": "42",
179
+ "token": "research_agent",
180
+ "targetTokens": 940,
181
+ },
182
+ ], // targetTokens OPTIONAL · ceil(fileChars/4) of the CITED
183
+ // file — OMITTED when unreadable at compile (never 0)
184
+ "path": "brain/rules/agent-name-contract.md", // prose body (lazy fetch)
185
+ },
171
186
  ],
172
187
 
173
188
  "improvements": [
174
189
  {
175
190
  "id": "dead-model-options-google-crewai",
176
- "priority": "P1", // P0 | P1 | P2 | P3
177
- "lens": "product", // security|correctness|performance|architecture|product|ops
178
- "status": "open", // open | backlog | fixed | wontfix
191
+ "priority": "P1", // P0 | P1 | P2 | P3
192
+ "lens": "product", // security|correctness|performance|architecture|product|ops
193
+ "status": "open", // open | backlog | fixed | wontfix
179
194
  "title": "…",
180
195
  "summary": "…",
181
196
  "fix": "…",
182
- "leverage": { "impact": "high", "effort": "low" }, // impact/effort ∈ low|medium|high
183
- "blastRadius": ["web-agent-bridge"], // [] if none
197
+ "leverage": { "impact": "high", "effort": "low" }, // impact/effort ∈ low|medium|high
198
+ "blastRadius": ["web-agent-bridge"], // [] if none
184
199
  "cites": [{ "file": "…", "line": "25", "token": "…" }],
185
- "path": "improve/improvements/dead-model-options-google-crewai.md"
186
- }
187
- ]
200
+ "path": "improve/improvements/dead-model-options-google-crewai.md",
201
+ },
202
+ ],
188
203
  }
189
204
  ```
190
205
 
@@ -197,6 +212,16 @@ Ingest is `JSON.parse` + a shape-check against this schema. Any mismatch (missin
197
212
  key, wrong enum, wrong type, unknown `schemaVersion`) → a **surfaced ingest error**
198
213
  on the platform, never a guessed value.
199
214
 
215
+ **Size stamps (`bodyTokens`, `cites[].targetTokens`) are ADDITIVE OPTIONAL.** compile
216
+ stamps each note's body-token estimate and, per cite, the cited file's token estimate —
217
+ both `ceil(chars/4)`, computed deterministically at compile time (no LLM, no network) to
218
+ feed the platform's recall-savings estimator. `targetTokens` is **omitted** for any cite
219
+ whose target is unreadable at compile (honest absence — never a guessed `0`). Because
220
+ these fields are purely additive and optional, `schemaVersion` **stays 1**: ingest never
221
+ refuses a manifest for lacking them, so a legacy manifest produced without them (or with
222
+ some cites carrying no `targetTokens`) remains valid. The shape-check treats them as
223
+ optional and tolerates their absence.
224
+
200
225
  ---
201
226
 
202
227
  ## 2. Note files — `brain/rules/*.md`, `brain/playbooks/*.md`
@@ -206,22 +231,24 @@ on the platform, never a guessed value.
206
231
  ```yaml
207
232
  ---
208
233
  schemaVersion: 1
209
- id: agent-name-contract # required · == filename stem
210
- type: contract # required · contract | convention | flow | how-to
211
- domain: web-agent-bridge # required · non-empty
212
- title: The agent name is a cross-process contract # required
213
- summary: Only "research_agent" is fully wired end to end # required
214
- links: [copilotkit-runtime-route-convention] # optional · default []
215
- failure: silent # optional · silent | loud
216
- anchor: research_agent # optional · checker gate B2: EVERY code hit of this
217
- # token must be a cited site (`anchor: none` = explicit
218
- # exemption for composition/ordering contracts)
219
- absent: legacy_agent_name # optional · checker gate B3: this token must appear
220
- # NOWHERE in code (docs/.md excluded) — declare one for
221
- # every claim that depends on something NOT existing;
222
- # the checker re-greps it every run, so the claim can
223
- # never silently go stale. Repeatable (one per line).
224
- cites: # required · 1
234
+ id: agent-name-contract # required · == filename stem
235
+ type: contract # required · contract | convention | flow | how-to
236
+ domain: web-agent-bridge # required · non-empty
237
+ title: The agent name is a cross-process contract # required
238
+ summary: Only "research_agent" is fully wired end to end # required
239
+ links: [copilotkit-runtime-route-convention] # optional · default []
240
+ failure: silent # optional · silent | loud
241
+ anchor:
242
+ research_agent # optional · checker gate B2: EVERY code hit of this
243
+ # token must be a cited site (`anchor: none` = explicit
244
+ # exemption for composition/ordering contracts)
245
+ absent:
246
+ legacy_agent_name # optional · checker gate B3: this token must appear
247
+ # NOWHERE in code (docs/.md excluded) declare one for
248
+ # every claim that depends on something NOT existing;
249
+ # the checker re-greps it every run, so the claim can
250
+ # never silently go stale. Repeatable (one per line).
251
+ cites: # required · ≥ 1
225
252
  - src/lib/model-selector-provider.tsx:42 :: research_agent
226
253
  - agents/python/main.py:20 :: research_agent
227
254
  ---
@@ -241,18 +268,18 @@ value).
241
268
  ```yaml
242
269
  ---
243
270
  schemaVersion: 1
244
- id: dead-model-options-google-crewai # required · == filename stem
245
- priority: P1 # required · P0 | P1 | P2 | P3
246
- lens: product # required · security|correctness|performance|architecture|product|ops
247
- status: open # required · open | backlog | fixed | wontfix
248
- title: Two of the four model options are dead and fail silently # required
249
- summary: google_genai and crewai route to unwired agents # required
250
- fix: Wire both agents in main.py, or remove them from the picker # required
251
- leverage: { impact: high, effort: low } # required · impact/effort ∈ low|medium|high
252
- blast_radius: [web-agent-bridge, external-integrations] # optional · default []
253
- cites: # required · ≥ 1
271
+ id: dead-model-options-google-crewai # required · == filename stem
272
+ priority: P1 # required · P0 | P1 | P2 | P3
273
+ lens: product # required · security|correctness|performance|architecture|product|ops
274
+ status: open # required · open | backlog | fixed | wontfix
275
+ title: Two of the four model options are dead and fail silently # required
276
+ summary: google_genai and crewai route to unwired agents # required
277
+ fix: Wire both agents in main.py, or remove them from the picker # required
278
+ leverage: { impact: high, effort: low } # required · impact/effort ∈ low|medium|high
279
+ blast_radius: [web-agent-bridge, external-integrations] # optional · default []
280
+ cites: # required · ≥ 1
254
281
  - src/components/ModelSelector.tsx:25 :: google_genai
255
- found: 2026-07-02 # optional · ISO date
282
+ found: 2026-07-02 # optional · ISO date
256
283
  ---
257
284
  (prose body)
258
285
  ```
@@ -267,10 +294,10 @@ from prose. That scraping is gone; these are authored values.
267
294
  ```yaml
268
295
  ---
269
296
  schemaVersion: 1
270
- verdict: PASS # required · PASS | ITERATE
271
- score: 95 # required · 0–100
272
- gates: { fidelity: pass, coverage: pass } # required · each pass | fail
273
- counts: { blockers: 0, majors: 0, minors: 2 } # required · non-negative ints
297
+ verdict: PASS # required · PASS | ITERATE
298
+ score: 95 # required · 0–100
299
+ gates: { fidelity: pass, coverage: pass } # required · each pass | fail
300
+ counts: { blockers: 0, majors: 0, minors: 2 } # required · non-negative ints
274
301
  ---
275
302
  (prism report body)
276
303
  ```
@@ -332,24 +359,25 @@ Each item owns exactly one file (merges never conflict; progress is derived).
332
359
  ```yaml
333
360
  ---
334
361
  schemaVersion: 1
335
- id: payments-c3-webhooks # required · == filename stem · GLOBALLY unique across plans/**
336
- plan: payments # required · the ROOT (epic) id
337
- parent: payments-c3 # required · the parent ITEM id (any rank above), or null (epic)
338
- kind: epic | task | subtask # required · epic = root only · task's parent = the epic · subtask's parent = a task
339
- title: Handle provider webhooks # required · WHAT (Linear title · Jira summary · Asana name)
340
- description: >- # optional · WHY + context — syncs 1:1 with vendor description/notes
362
+ id: payments-c3-webhooks # required · == filename stem · GLOBALLY unique across plans/**
363
+ plan: payments # required · the ROOT (epic) id
364
+ parent: payments-c3 # required · the parent ITEM id (any rank above), or null (epic)
365
+ kind: epic | task | subtask # required · epic = root only · task's parent = the epic · subtask's parent = a task
366
+ title: Handle provider webhooks # required · WHAT (Linear title · Jira summary · Asana name)
367
+ description:
368
+ >- # optional · WHY + context — syncs 1:1 with vendor description/notes
341
369
  reconciliation must be automatic; manual matching doesn't scale
342
- approach: verify HMAC → enqueue → idempotent apply # optional · HOW, one line — rafa value-add
343
- status: todo | in-progress | done | superseded | abandoned # required (task/subtask); epic may omit
344
- assignee: rohik # optional · WHO (free string pre-orgs; unanimous vendor word)
345
- blocked_by: [payments-c2] # optional · item ids this waits on — a dependency IS a blocker
346
- blocked_reason: "vendor sandbox access" # optional · external blocks (no dependency id to point at)
347
- priority: 2 # optional · 0 none · 1 urgent · 2 high · 3 medium · 4 low (Linear scale)
348
- estimate: 3 # optional · points (Linear estimate / Jira story points)
349
- branch: feat/webhooks # optional · ↔ Linear branchName
350
- baseSha: abc123 # optional · commit it was cut from
351
- domains: [payments] # optional · EPIC only · the blast radius — the brain link
352
- external: { provider: jira, key: PAY-142 } # optional · tracker identity — READ-ONLY to sessions
370
+ approach: verify HMAC → enqueue → idempotent apply # optional · HOW, one line — rafa value-add
371
+ status: todo | in-progress | done | superseded | abandoned # required (task/subtask); epic may omit
372
+ assignee: rohik # optional · WHO (free string pre-orgs; unanimous vendor word)
373
+ blocked_by: [payments-c2] # optional · item ids this waits on — a dependency IS a blocker
374
+ blocked_reason: "vendor sandbox access" # optional · external blocks (no dependency id to point at)
375
+ priority: 2 # optional · 0 none · 1 urgent · 2 high · 3 medium · 4 low (Linear scale)
376
+ estimate: 3 # optional · points (Linear estimate / Jira story points)
377
+ branch: feat/webhooks # optional · ↔ Linear branchName
378
+ baseSha: abc123 # optional · commit it was cut from
379
+ domains: [payments] # optional · EPIC only · the blast radius — the brain link
380
+ external: { provider: jira, key: PAY-142 } # optional · tracker identity — READ-ONLY to sessions
353
381
  ---
354
382
  (prose body — a LEAF item (a task with no subtasks, or a subtask) MUST carry a
355
383
  `## Done-check`; `## Log` is the execution journal; `## Decisions` mirrors the
@@ -384,7 +412,7 @@ Rules (all compile-enforced unless noted):
384
412
  **Decisions — first-class deliberation records (channel-borne, never frontmatter):**
385
413
  the frontmatter grammar is flat and bodies are never parsed, so structured decisions
386
414
  live on the PLATFORM, pushed via `log_decision` at checkpoint moments:
387
- `{ item, at, actor, context, options[], decision, rationale }` — *actor* is who
415
+ `{ item, at, actor, context, options[], decision, rationale }` — _actor_ is who
388
416
  decided (the dev for steering; the agent for proposals it made under standing
389
417
  consent). Text fidelity: **paraphrase + short verbatim quotes only where the exact
390
418
  wording carries the decision** (transcripts never land in shared stores). Sessions
@@ -414,7 +442,10 @@ imported comments are never scraped into decisions.
414
442
  ## 8. Versioning & the validate-and-correct loop
415
443
 
416
444
  - **`schemaVersion`** bumps when this contract changes incompatibly. Ingest refuses
417
- an unknown version (surfaced error) rather than mis-reading it.
445
+ an unknown version (surfaced error) rather than mis-reading it. **Additive OPTIONAL
446
+ fields do NOT bump it** — e.g. the note size stamps (`bodyTokens`,
447
+ `cites[].targetTokens`, §1): ingest tolerates their absence, so a legacy manifest
448
+ without them stays valid at the same `schemaVersion`.
418
449
  - **Validate-and-correct:** `rafa compile` validates every file. On failure it emits
419
450
  a structured error — `path · field · rule` — to the **authoring agent** (atlas for
420
451
  notes, bloom for improvements/ledger, prism for checklist). The agent edits the
@@ -428,18 +459,18 @@ imported comments are never scraped into decisions.
428
459
 
429
460
  The platform serves the **ingested** brain to any MCP client (the build agent, a
430
461
  teammate's session, Slack/incident.io agents) — the read-side twin of this contract.
431
- One backend: the platform. Local files are the *authoring* surface; the platform is
432
- the *serving* surface, and it serves only what passed compile + ingest.
462
+ One backend: the platform. Local files are the _authoring_ surface; the platform is
463
+ the _serving_ surface, and it serves only what passed compile + ingest.
433
464
 
434
465
  **Envelope — mandatory on every tool response:**
435
466
 
436
467
  ```jsonc
437
468
  {
438
469
  "source": "platform",
439
- "brainForSha": "<snapshot codeSha>", // the ingested manifest's codeSha — one source
470
+ "brainForSha": "<snapshot codeSha>", // the ingested manifest's codeSha — one source
440
471
  "ingestedAt": "<ISO time of ingest>",
441
472
  "schemaVersion": 1,
442
- "synthesized": false // raw tools: always false
473
+ "synthesized": false, // raw tools: always false
443
474
  }
444
475
  ```
445
476
 
@@ -451,15 +482,16 @@ match.
451
482
 
452
483
  **Tools (read-only — ALL writes flow files → compile → push → ingest):**
453
484
 
454
- | Tool | Args | Returns |
455
- |---|---|---|
456
- | `get_brain_status` | `repo` | envelope + per-type counts, health, coverage summary, `activePlanId`, `ingestError?` |
457
- | `get_coverage` | `repo` | coverage rows (the domain map — how an agent navigates) |
458
- | `search_knowledge` | `q`, `types?`, `domain?`, `limit?` | ranked candidates (fields per type below) |
459
- | `get_rule` / `get_playbook` / `get_improvement` | `repo`, `id` | full frontmatter + lazy-fetched body + cites as objects |
460
- | `list_improvements` | `repo`, `status?`, `priority?`, `domain?` | ledger rows |
461
- | `get_plan` | `repo`, `id` (parent or child) | parent + all children **with stored bodies** + **derived** progress; a child id resolves the whole plan with `requestedChild` set — this is what "load plan X" materializes into `.rafa/plans/` |
462
- | `get_active_plan` | `repo` | resolves the channel pointer (`set_active_plan`) `get_plan`, or "no active plan" |
485
+ | Tool | Args | Returns |
486
+ | ----------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
487
+ | `get_brain_status` | `repo` | envelope + per-type counts, health, coverage summary, `activePlanId`, `ingestError?` |
488
+ | `get_coverage` | `repo` | coverage rows (the domain map — how an agent navigates) |
489
+ | `search_knowledge` | `q`, `types?`, `domain?`, `limit?` | ranked candidates (fields per type below) |
490
+ | `ask_knowledge` | `repo`, `q` | the guarded synthesis read (below) `{answer, synthesized, citations[], retrieval, usage?}` |
491
+ | `get_rule` / `get_playbook` / `get_improvement` | `repo`, `id` | full frontmatter + lazy-fetched body + cites as objects |
492
+ | `list_improvements` | `repo`, `status?`, `priority?`, `domain?` | ledger rows |
493
+ | `get_plan` | `repo`, `id` (parent or child) | parent + all children **with stored bodies** + **derived** progress; a child id resolves the whole plan with `requestedChild` set this is what "load plan X" materializes into `.rafa/plans/` |
494
+ | `get_active_plan` | `repo` | resolves the channel pointer (`set_active_plan`) → `get_plan`, or "no active plan" |
463
495
 
464
496
  `repo` must match the key's scope exactly; `branch` is reserved (branch-keyed
465
497
  instances land in Slice 2 — third-party clients default to the production brain,
@@ -471,8 +503,32 @@ improvement: `title, summary, lens, blast_radius` · plan: `title, plan, parent`
471
503
  Score = Σ over matched case-folded query tokens of field weight (`title` 3 ·
472
504
  `domain`/`lens`/`blast_radius`/`type`/`plan`/`parent` 2 · `summary` 1); tie-break
473
505
  score desc then id asc; fields a type lacks are omitted, never defaulted. Candidates
474
- carry `matchKind: "lexical"` (`"semantic"` reserved). `ask_knowledge` is a reserved
475
- tool id for the synthesis layer (responses will carry `synthesized: true`).
506
+ carry `matchKind: "lexical"` (`"semantic"` reserved).
507
+
508
+ **`ask_knowledge` — the guarded synthesis read (the reserved id, filled 2026-07-13
509
+ per the served-brain spec).** For THIN clients (Slack bots, dashboards); full agents
510
+ should prefer `search_knowledge` + `get_*` and compose with their own model. The
511
+ guarantees, in order: 0. **Enumerable intents route deterministically BEFORE retrieval** — command-shaped
512
+ asks ("list improvements [open|P0]", "list plans", "status/health", "coverage")
513
+ return the actual rows (`retrieval.matchKind: "intent"`, `items[]`), no LLM
514
+ involved. Interrogatives (how/why/where/when) always retrieve instead — a missed
515
+ intent degrades to search; a wrong intent would answer the wrong thing.
516
+
517
+ 1. **Retrieval first, deterministic** — the same lexical scorer, top-K candidates.
518
+ 2. **Empty = honest empty**: `{answer: null, synthesized: false, found: 0}` with a
519
+ "no knowledge found" message — never composed from general knowledge; the empty
520
+ ask is logged into the same knowledge-gaps feed as an empty search
521
+ (`payload.via: "ask"`).
522
+ 3. **Composition uses retrieved cited notes ONLY**; answers are marked
523
+ `synthesized: true` and carry `citations[]` ({id, title, kind, domain?, path}).
524
+ 4. **Raw mode**: with no org synthesis key configured, returns `rawMode: true` +
525
+ the top cited candidates (no LLM call) — the surface degrades to search, never
526
+ to silence or invention.
527
+ 5. **Custody**: synthesis runs on the ORG'S OWN LLM key (a platform tokens row,
528
+ provider `anthropic`, encrypted at rest, decrypted per call server-side, never
529
+ logged). Usage is metered to `usageEvents` (purpose `ask`, runner `platform`).
530
+ 6. **Caps are loud**: a per-repo daily cap returns an explicit error, never a
531
+ silent throttle.
476
532
 
477
533
  **Auth:** per-repo machine keys, minted on the platform, sent as
478
534
  `Authorization: Bearer`. Keys are stored hashed platform-side and live client-side in
@@ -491,15 +547,15 @@ The knowledge plane stays strictly read-only (org-brain writes go ONLY through
491
547
  files → compile → push → ingest). Seven additional tools write **collaboration
492
548
  state**, marked by `envelope.plane: "state"`:
493
549
 
494
- | Tool | Store | Notes |
495
- |---|---|---|
496
- | `list_dev_insights` / `put_dev_insight` / `remove_dev_insight` | **user brain** — account-scoped, cross-repo, PRIVATE | consent-gated by the conductor; never logged to repo activity; nothing person-scoped is ever served to another user |
497
- | `checkpoint_sync` | **branch working set** — (repo, branch, file path) rows | pushes edited/new brain files from the lazy `.rafa/` instance at CHECKPOINTS (task done · plan approved · explicit ask · cadence · git push/pull — never session-end) under **base-version CAS**: each file carries the row `version` last seen (null = create); a stale base returns the newer copy as a per-file conflict for the HUMAN in that session to resolve |
498
- | `get_working_set` | branch working set | hydration (a session starts with its branch's working set), the branch view, distillation collect; `needs-adjudication` rows carry the incoming copy in `pending` |
499
- | `resolve_working_file` | branch working set | `distilled` / `refuted` (+ cited note) at merge-to-main distillation; `keep-current` for adjudication decisions; CAS on status — a racing distill fails loudly |
500
- | `fold_working_set` | branch working set | branch→parent-branch MECHANICAL fold (no LLM, no prism — rigor only at main): absent → re-keyed · identical → merged · divergent → `needs-adjudication` on the parent row |
501
- | `push_plan` / `list_plans` / `update_plan_status` / `set_active_plan` / `log_decision` | **plans** — (repo) work objects | the push-on-approval channel (§7 v2): approval pushes the whole work-item TREE (items[], bodies stored); `list_plans` = names only; statuses push back (5-value enum — blocked is derived); `log_decision` records the deliberation trail (paraphrase + pivotal quotes); the active pointer is explicit, never inferred |
502
- | `report_improvement_status` | **events only** — no store | a LIVE session signal (bloom's fixed-in-passing during build). The improvement ledger row is untouched — it has ONE writer, the ingest (K1); the platform overlays the report as pending-reconciliation until the next brain push confirms it |
550
+ | Tool | Store | Notes |
551
+ | -------------------------------------------------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
552
+ | `list_dev_insights` / `put_dev_insight` / `remove_dev_insight` | **user brain** — account-scoped, cross-repo, PRIVATE | consent-gated by the conductor; never logged to repo activity; nothing person-scoped is ever served to another user |
553
+ | `checkpoint_sync` | **branch working set** — (repo, branch, file path) rows | pushes edited/new brain files from the lazy `.rafa/` instance at CHECKPOINTS (task done · plan approved · explicit ask · cadence · git push/pull — never session-end) under **base-version CAS**: each file carries the row `version` last seen (null = create); a stale base returns the newer copy as a per-file conflict for the HUMAN in that session to resolve |
554
+ | `get_working_set` | branch working set | hydration (a session starts with its branch's working set), the branch view, distillation collect; `needs-adjudication` rows carry the incoming copy in `pending` |
555
+ | `resolve_working_file` | branch working set | `distilled` / `refuted` (+ cited note) at merge-to-main distillation; `keep-current` for adjudication decisions; CAS on status — a racing distill fails loudly |
556
+ | `fold_working_set` | branch working set | branch→parent-branch MECHANICAL fold (no LLM, no prism — rigor only at main): absent → re-keyed · identical → merged · divergent → `needs-adjudication` on the parent row |
557
+ | `push_plan` / `list_plans` / `update_plan_status` / `set_active_plan` / `log_decision` | **plans** — (repo) work objects | the push-on-approval channel (§7 v2): approval pushes the whole work-item TREE (items[], bodies stored); `list_plans` = names only; statuses push back (5-value enum — blocked is derived); `log_decision` records the deliberation trail (paraphrase + pivotal quotes); the active pointer is explicit, never inferred |
558
+ | `report_improvement_status` | **events only** — no store | a LIVE session signal (bloom's fixed-in-passing during build). The improvement ledger row is untouched — it has ONE writer, the ingest (K1); the platform overlays the report as pending-reconciliation until the next brain push confirms it |
503
559
 
504
560
  State tools work with or without an ingested brain (a working set can exist —
505
561
  and a plan can be pushed — before the first scan) and are exempt from the
@@ -529,7 +585,8 @@ duties: # required · block list, ≥ 1 · each a duty DSL s
529
585
  ---
530
586
  ```
531
587
 
532
- **Duty DSL** (one string per duty, split on ` :: `, exactly three parts):
588
+ **Duty DSL** (one string per duty, split on `::`, exactly three parts):
589
+
533
590
  - `duty` — kebab-case name of the responsibility.
534
591
  - `sop-path` — path relative to the CODE repo root (e.g.
535
592
  `.claude/skills/rafa-validate/SKILL.md`); the file MUST exist at compile time (a
@@ -538,7 +595,7 @@ duties: # required · block list, ≥ 1 · each a duty DSL s
538
595
  - `bar` — the explicit pass/fail sentence for this duty. Never empty: a duty whose
539
596
  bar can't be stated can't be validated, so it doesn't ship.
540
597
 
541
- Versioning: bump `version:` with any card change; record the *why* outside the card
598
+ Versioning: bump `version:` with any card change; record the _why_ outside the card
542
599
  (cards ship to end users and stay clean of build-log clutter).
543
600
 
544
601
  ---
@@ -45,9 +45,14 @@ platform MCP (one read path — the same surface any third-party agent uses).
45
45
  steering, the agent for its own proposals; PARAPHRASE + short verbatim
46
46
  quotes only where the wording carries the decision — transcripts never land
47
47
  in shared stores; mirror each into the item's `## Decisions` section) +
48
+ **`report_loop_event(category: "prism-verdict", outcome: PASS|ITERATE,
49
+ subject: <task id>)`** at the moment prism rules on the Done-check (sage's
50
+ evidence — shapes only: the verdict + the task id, never code) +
48
51
  `rafa checkpoint` (the branch working set), so the platform and every MCP
49
52
  consumer reflect live progress. Checkpoint moments: task done · plan
50
53
  approved · explicit ask · cadence · git push/pull — never session-end. The
54
+ loop-event emits ride the SAME checkpoint beats — one per outcome as it
55
+ occurs, monotonic, NEVER a session-end sweep. The
51
56
  git-push boundary is MECHANICAL (M5): the pre-push hook runs `rafa checkpoint`
52
57
  itself, non-blocking — the session still owns the task-done/plan-approved
53
58
  moments. A checkpoint CONFLICT (a teammate's newer copy of the same file) is
@@ -56,7 +61,10 @@ platform MCP (one read path — the same surface any third-party agent uses).
56
61
  3. **Brain changes mid-build — WHERE you are decides WHERE it goes.**
57
62
  - **On the default branch (main):** run a full `/rafa scan` (regenerate →
58
63
  prism → compile → push); the brain re-stamps at the new sha, so
59
- `brain = f(code@sha)` stays exact.
64
+ `brain = f(code@sha)` stays exact. When the gate runs, emit
65
+ **`report_loop_event(category: "gate-result", outcome: exit0|failed,
66
+ subject: <compile|verify-citations>)`** at that checkpoint beat — the gate's
67
+ own moment, never deferred to session-end.
60
68
  - **On any other branch:** the org brain is NEVER written from a branch —
61
69
  it describes main, and a branch-state scan would poison it for everyone.
62
70
  Invalidated/learned knowledge → the branch **working set**: hydrate the
@@ -57,7 +57,12 @@ code it describes.
57
57
  CAS: only a live row resolves; a failure means ANOTHER runner already
58
58
  distilled this branch — STOP and reconcile against what it shipped.
59
59
  `resolve_working_file(path, refuted, note)` for failures — TELL the author
60
- which files died and why (cited); refutation is feedback, not silence.
60
+ which files died and why (cited); refutation is feedback, not silence. At
61
+ that same resolve beat emit **`report_loop_event(category:
62
+ "distill-refutation", outcome: refuted|distilled, subject: <path or note
63
+ id>)`** — sage's evidence that a claim met the gates or was refuted (shapes
64
+ only: the outcome + a file/note reference, never the refuted content, never
65
+ the cited code). Checkpoint-adjacent, monotonic, never a session-end sweep.
61
66
  `resolve_working_file(path, needs-adjudication, note)` for the undecidable —
62
67
  the next session's digest offers the decision.
63
68