@rafinery/cli 0.5.0 → 0.6.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 CHANGED
@@ -4,6 +4,103 @@ The machine-readable version of this lives in [`lib/releases.mjs`](lib/releases.
4
4
  CLI reads it to tell a client, on `rafa update`, exactly what an upgrade requires (nothing,
5
5
  a re-scan, or a `rafa migrate`).
6
6
 
7
+ ## 0.6.0 — core solidity: checker v2 + the M5 capture engine
8
+
9
+ **Adopt with:** `npx @rafinery/cli@latest update` — re-vendors the new lockstep hook
10
+ scripts (`.claude/rafa/hooks/`), merges the sensor hooks into `.claude/settings.json`
11
+ (never clobbers yours), and installs the per-clone git pre-push boundary. Brain data
12
+ unchanged; no re-scan. Teammates get the git hook at their next `rafa pull`.
13
+
14
+ **The ratchet (checker v2)** — the two catches prism recommended in the 2026-06-08 live
15
+ run are now deterministic gates in `rafa verify-citations`:
16
+
17
+ - **Absence (B3)**: a note that claims something does NOT exist declares
18
+ `absent: <token>` (repeatable, anchor-style). Every declared absence is re-grepped
19
+ every run (docs/`.md` excluded) — the moment code grows the thing, the gate fails with
20
+ the exact site. Mechanizes the 2026-06-08 blocker class (a "stubbed" note the code had
21
+ outgrown).
22
+ - **Inventory**: `coverage.md` declares `inventory:` entries
23
+ (`- <name> :: <glob> :: <count>`); each is recomputed via `git ls-files ':(glob)…'` —
24
+ coverage claiming a surface inventory the repo has outgrown fails with the path diff.
25
+ - **WARN heuristic (non-failing)**: an absence-shaped `title`/`summary` with no `absent:`
26
+ declared is listed for prism's 3b audit — a heuristic never fails the gate (that would
27
+ be an assumed value).
28
+ - **Recorded gate level**: the checker writes `citation-check.json`
29
+ (`{checkerVersion, at, pass, gates, warns}`) and `rafa compile` folds it into
30
+ `manifest.citations` — the platform knows which checker version a brain actually
31
+ passed. No recorded run → `citations: null`, honestly.
32
+ - Selftest extended: absence + inventory logic proven on throwaway git fixtures
33
+ (prism's mutation probe covers the new gates).
34
+
35
+ **The M5 capture engine** — checkpoint/staleness moments become deterministic code, not
36
+ SOP adherence (the E2E-proven weak link: a full build once produced zero captures,
37
+ silently):
38
+
39
+ - **SessionStart digest** (`.claude/rafa/hooks/session-start.mjs`): the session starts
40
+ knowing — dirty code files → the brain notes citing them (local manifest, or
41
+ `get_code_context` fail-soft ≤2s), pending `*.theirs.md` checkpoint conflicts, the
42
+ active plan. Prints nothing when there is nothing (silence is the honest digest).
43
+ - **PostToolUse dirty-marker** (`.claude/rafa/hooks/post-tool.mjs`): every Edit/Write is
44
+ appended to `.rafa/dirty.jsonl` the moment it happens — monotonic, no session-end
45
+ dependency (there is no session end), never blocks, transport-excluded from brain
46
+ pushes via `.rafa/.gitignore`.
47
+ - **git pre-push boundary**: `git push` of the code branch runs `rafa checkpoint`
48
+ (non-blocking — a checkpoint problem never stops a code push; conflicts land as
49
+ `.theirs.md` for YOUR decision). Installed at init/pull/update; a foreign pre-push
50
+ hook is respected, never clobbered (the chain line is printed instead).
51
+ - **`rafa dirty [--json|--consume]`**: the cite-graph invalidator's query surface —
52
+ dirty files → citing notes; at boundaries the conductor OFFERS a scoped refresh
53
+ (atlas re-derives only the citing notes → gates → checkpoint), `--consume` clears the
54
+ queue after the refresh ships. Past the drift threshold (15 files / 10 notes) the
55
+ digest recommends `/rafa scan --brain-only` — the fire-alarm rung.
56
+ - **Recursion guard (U7)**: headless runs (CI fold/distill, the Agent SDK worker) set
57
+ `RAFA_HOOKS_DISABLED=1`; every hook exits 0 immediately. Hooks also no-op outside a
58
+ rafa-provisioned repo (no `rafa.json` → silent).
59
+ - **The correction reflex** (`.claude/rafa/hooks/user-prompt-submit.mjs` +
60
+ `rafa reflex`): a correction-shaped prompt ("no —", "never use X", "use Y instead")
61
+ is queued to `.rafa/reflex.jsonl` AND answered with a steering injection at that
62
+ exact moment — the session validates and banks it through the normal gates
63
+ same-session (branch: cited note + checkpoint · main: full gates), confirms in one
64
+ line, and consumes the queue entry with a verdict. Ungroundable claims are never
65
+ banked; unprocessed corrections from abandoned sessions surface at the next
66
+ session start (monotonic). Corrections are the highest-value knowledge signal —
67
+ they no longer evaporate.
68
+ - **`rafa push` re-runs the checker itself** (before compile; improvements ledger too
69
+ when present) — a stale or hand-stamped `citation-check.json` can never ride a push;
70
+ the record that ships is the record of THAT push. Mechanized same-day from the
71
+ 2026-07-13 prism live-run recommendation (the disposition protocol working).
72
+ - Checker hardening: grep hits under `.rafa/**` are excluded categorically — the brain
73
+ never counts as "code", so an accidentally-tracked `.rafa/` can't self-collide the
74
+ completeness/absence gates.
75
+
76
+ **The frictionless loop** — devs won't run commands in order, so the loop guides
77
+ itself (owner directive; anti-nag etiquette unwaived — guidance is ambient,
78
+ front-loaded, or earned, never interleaved):
79
+
80
+ - **Loop-state statusline**: `.claude/rafa/hooks/statusline.mjs` renders
81
+ `rafa ▸ plan <id> 2/5 → <task> · 3 stale · 1 correction` at the bottom of every
82
+ session (silent outside rafa repos; a dev's existing statusline is NEVER replaced).
83
+ `rafa status [--line|--json]` is the harness-neutral core it mirrors.
84
+ - **Suggested next**: the SessionStart digest gains one deterministic
85
+ `[rafa · next]` line — ranked conflicts → corrections → resume plan → staleness —
86
+ so every session opens knowing the single best next step.
87
+ - **Direct-do path** (conductor 1.8.0): small work (≤ 1 domain, no contract surface,
88
+ one sitting) gets NO plan files and no approval — act first; the sensors carry the
89
+ loop; escalates to plan-lite with one announce line only when the work grows.
90
+ - **Brainstorm mode**: during exploration the conductor is a grounded, cited
91
+ participant with zero workflow talk; at crystallization, exactly one offer (a plan
92
+ if it's work, a working-set note if it's a conclusion).
93
+
94
+ **Portability (owner directive, 2026-07-13):** built for Claude Code now, ships for
95
+ Cursor/Codex later — loop logic lives in the harness-neutral core (CLI verbs + the
96
+ `.rafa/*.jsonl` queues + gates + MCP); everything under `.claude/rafa/hooks/` is a
97
+ thin per-harness adapter. The event protocol (detect → queue → steer →
98
+ bank-through-gates) is the portable contract.
99
+
100
+ The honest boundary, by design: hooks make **sync, staleness, and boundaries**
101
+ deterministic; **authoring stays model work** — steered by the injected digest, visible
102
+ on the platform ("working set clean" is a real answer, not a gap).
103
+
7
104
  ## 0.5.0 — plans v2: the work-item tree
8
105
 
9
106
  **Adopt with:** `npx @rafinery/cli@latest update` → it runs the mechanical `rafa migrate`
package/bin/rafa.mjs CHANGED
@@ -1,18 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  // rafa — the rafinery CLI. Thin dispatcher; each command lives in ../lib.
3
3
  //
4
- // rafa init [<setup-url>] provision this repo (vendor blueprint + wire the platform)
4
+ // rafa init [<setup-url>] provision this repo (vendor blueprint + wire the platform + sensors)
5
5
  // rafa update re-sync the blueprint into this repo (shadcn-style)
6
6
  // rafa compile run the contract gate → .rafa/manifest.json
7
- // rafa verify-citations deterministic citation checker (B1/B2/policy gates)
7
+ // rafa verify-citations deterministic citation checker (B1/B2/policy/absence/inventory)
8
8
  // rafa push compile + push .rafa/ to the brain remote (main-branch act)
9
9
  // rafa pull [--full] make this clone brain-ready (lazy skeleton; --full mirrors)
10
+ // rafa checkpoint sync this branch's working set (CAS; conflicts → .theirs.md)
11
+ // rafa hydrate fault knowledge into the lazy .rafa/
12
+ // rafa dirty [--json] staleness queue → citing notes; --consume after a refresh
13
+ // rafa fold branch→parent mechanical fold of the working set (no LLM)
14
+ // rafa distill --headless CI merge-to-main distillation (org's own LLM key)
15
+ // rafa ci-setup write the reconcile workflow (fold + distill on PR merge)
10
16
  // rafa leverage inspect your agent toolbox → proactive tips
11
17
  // rafa migrate bring structured files (plans/config) up to the current schema
12
18
  //
13
19
  // The gate tools live INSIDE this package (blueprint split, 0.4.0) — nothing is
14
20
  // vendored into .rafa/bin anymore. The LLM work (scan / improve / plan) runs
15
- // inside Claude Code via /rafa.
21
+ // inside Claude Code via /rafa. The M5 sensor hooks (.claude/rafa/hooks/) are
22
+ // vendored LOCKSTEP; commands are agent-internal machinery (zero-command rule).
16
23
 
17
24
  import { readFileSync } from "node:fs";
18
25
  import { dirname, join } from "node:path";
@@ -30,6 +37,9 @@ const COMMANDS = [
30
37
  "pull",
31
38
  "checkpoint",
32
39
  "hydrate",
40
+ "dirty",
41
+ "reflex",
42
+ "status",
33
43
  "fold",
34
44
  "distill",
35
45
  "ci-setup",
@@ -53,8 +63,10 @@ Commands:
53
63
  --keep keep all changed owned files without asking
54
64
  (default: ask per file; in a non-TTY it keeps them)
55
65
  compile Validate the brain against the contract → .rafa/manifest.json.
56
- verify-citations Deterministic citation checker: every cite resolves (B1), every
57
- contract anchor's code occurrences are cited (B2), policy holds.
66
+ verify-citations Deterministic citation checker (v2): every cite resolves (B1),
67
+ every contract anchor's code occurrences are cited (B2), policy
68
+ holds, declared absences re-grepped (B3), coverage inventories
69
+ re-counted. Records the run (citation-check.json → manifest).
58
70
  push Compile, then push .rafa/ to your brain remote (from main — the
59
71
  org brain describes the default branch). Stamps the contract copy.
60
72
  pull Make this clone brain-ready — bootstraps the lazy .rafa/ skeleton
@@ -67,6 +79,19 @@ Commands:
67
79
  hydrate Fault knowledge into the lazy .rafa/: hydrate rule <id> ·
68
80
  hydrate playbook <id> (org brain, stamped disposable-cache) ·
69
81
  hydrate --working-set (this branch's synced files).
82
+ dirty The staleness queue (M5): code files edited since the last brain
83
+ reconcile → the brain notes citing them. --json for agents;
84
+ --consume clears it AFTER a scoped refresh shipped. The queue is
85
+ written by the PostToolUse sensor hook the moment an edit happens.
86
+ reflex The correction queue (M5): corrections the UserPromptSubmit sensor
87
+ detected. List/--json; --consume <id> [verdict] AFTER banking it
88
+ through the gates (or judging it session-only). Corrections are
89
+ the highest-value knowledge signal — they must never evaporate.
90
+ status Where the loop stands, from local state only: active plan +
91
+ task progress · stale count · corrections · conflicts.
92
+ --line = the one-line ambient form (statuslines/prompts) ·
93
+ --json = machine shape. Harness-neutral core: the Claude Code
94
+ statusline adapter mirrors exactly this.
70
95
  fold Branch→parent MECHANICAL fold of the working set (no LLM):
71
96
  rafa fold --from <branch> --to <parent>. Divergence → flagged
72
97
  needs-adjudication for the next dev session, never guessed.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: atlas
3
- version: 3.6.0
3
+ version: 3.7.0
4
4
  model: opus # authoring is correctness-critical — a hallucinated note poisons the brain; best model, never cheap
5
5
  groundTruth: code-at-sha
6
6
  description: >-
@@ -17,6 +17,7 @@ duties:
17
17
  - "repair :: .claude/skills/rafa-scan/SKILL.md :: every blocker + major in checklist.md fixed against the code · checker re-run to exit 0 · never weaken a check to pass it"
18
18
  - "plan-drafting :: .claude/skills/rafa-plan/SKILL.md :: recall-grounded decomposition (coverage → search → notes) · blast radius named · contract §7 files · every child carries a Done-check"
19
19
  - "build-execution :: .claude/skills/rafa-build/SKILL.md :: implement per recalled knowledge · never hand-edit brain files around the gate · return what changed, cited"
20
+ - "scoped-refresh :: .claude/skills/rafa-scan/SKILL.md :: re-derive ONLY the dirty-cited notes against current code (input: rafa dirty --json) · same gates as scan (verify-citations exit 0) · on main: compile+push; on a branch: working-set edit + checkpoint · queue consumed only after the refresh ships"
20
21
  ---
21
22
 
22
23
  # atlas — senior design engineer
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 1.5.0
2
+ version: 1.8.0
3
3
  description: rafa — the repo's engineering SOP. Use whenever the dev wants to plan a feature or change, build/execute against the active plan, improve code health, or ground work in the repo's brain — intent counts, the dev does NOT need to type /rafa. Also explicit: /rafa <init|scan|improve|plan|build|push|leverage|migrate|update|help> [--brain-only]. Admin verbs (init/scan/push/migrate/update) run ONLY when explicitly invoked.
4
4
  ---
5
5
 
@@ -13,14 +13,40 @@ Admin verbs are expensive or side-effectful and run **only when explicitly typed
13
13
 
14
14
  | Prompt shape | Route |
15
15
  |---|---|
16
- | "let's add / build / implement / refactor X" (no active plan) | `## plan` weight by blast radius: lite vs full (see plan.md § plan-lite) |
16
+ | small work radius 1 domain, no contract surface, fits one sitting | **DIRECT-DO**: recall → implement → verify → done. NO plan files, NO approval, NOTHING to remember the sensors carry the loop (dirty-mark, reflex, checkpoint-at-push). Escalate only if it GROWS (below). |
17
+ | "let's add / build / implement / refactor X" — real blast radius (≥ 2 domains, a contract surface, or multi-session) (no active plan) | `## plan` — weight by blast radius: lite vs full (see plan.md § plan-lite) |
17
18
  | same, with an active plan covering it | `## build` |
18
19
  | "why / how / where does X …" (a question) | recall-and-answer: MCP `search_knowledge` → cited answer. No choreography. |
20
+ | exploration — "what if / should we / compare / thinking about X" | **BRAINSTORM mode** (§ below): grounded participant, zero workflow talk, one offer at crystallization. |
19
21
  | "list plans / what plans are there" | `list_plans` — NAMES ONLY (id · title · status · progress); never dump bodies |
20
22
  | "load / open / work on plan X" | `get_plan(X)` → materialize parent + children (bodies) into `.rafa/plans/<X>/` + set `active.md` → resume per `## build` |
21
23
  | "improve / clean up / what's rotting" | `## improve` |
22
24
  | explicit `/rafa <admin verb>` — init · scan · push · migrate · update | that verb, and ONLY then |
23
25
 
26
+ **Direct-do — the light path must be lighter than bypassing rafa.** For small work
27
+ the default INVERTS: act first, record only if it earns recording. No announcement
28
+ ceremony either — just do it (recall still automatic; verify still real: types/lint/
29
+ tests as the repo warrants). **The escalation rule:** the moment the work grows — a
30
+ second domain, a contract surface, or it clearly won't finish this sitting — say ONE
31
+ line (*"this grew — recording it as a plan so it's resumable"*), create the plan-lite
32
+ files THEN, and continue; never ask permission to keep the loop honest, and never
33
+ escalate a task that stayed small. What keeps direct-do safe with zero ceremony: the
34
+ sensors (every edit dirty-marked, corrections reflexed, `git push` checkpoints) plus
35
+ §capture for anything learned — the loop closes without the dev or you filing
36
+ paperwork.
37
+
38
+ **Brainstorm mode — grounded participant, not a process.** During exploration:
39
+ recall automatically and cite (the brain is WHY the brainstorm is good), think WITH
40
+ the dev, and say NOTHING about plans, captures, checkpoints, or process — zero
41
+ workflow talk mid-exploration. At CRYSTALLIZATION (the dev stops exploring and
42
+ starts concluding — "ok let's do X", "so the decision is Y", or asks to proceed):
43
+ exactly ONE offer, shaped by what the conversation produced — work → *"want me to
44
+ turn this into a plan?"* · a durable conclusion with no work attached → *"bank the
45
+ conclusion as a note?"* (a plan-less decision lands as a working-set note under
46
+ §capture, so brainstorm output survives even when no work item ever exists). A "no"
47
+ stands for the session. If nothing crystallized, offer nothing — an open exploration
48
+ is a fine place to end.
49
+
24
50
  **Announce-and-proceed, never ask-permission:** state the route in one line — *"this
25
51
  looks plan-shaped — following the plan SOP (say 'skip' to bypass)"* — then go. "skip"
26
52
  always wins, immediately and without argument. Never narrate other devs' activity
@@ -63,6 +89,11 @@ Three interaction modes by the weight of the action:
63
89
  - session start, local ahead of platform → *"brain is N commits behind — push?"*
64
90
  - reconciliation pending → *"branch <x> merged with N working-set files — distill now?"*
65
91
  - adjudication pending → *"1 file diverged at the <x>→<y> fold — pick a copy?"*
92
+ - staleness (the M5 dirty queue, injected by the SessionStart digest or read via
93
+ `rafa dirty --json` at boundaries) → *"N notes cite files you've touched — refresh
94
+ just those?"* — accepted = atlas scoped-refresh (only the citing notes, gates as
95
+ usual, checkpoint/push per branch rules), then `rafa dirty --consume`. Past the
96
+ drift threshold the offer escalates to */rafa scan --brain-only* (the fire-alarm).
66
97
  - user brain EMPTY (first session / founding scan just completed) → *"want me to
67
98
  bootstrap your insights from your usage report? (compass distills it; every
68
99
  candidate is offered — nothing banked without your yes)"* — accepted = run
@@ -74,13 +105,47 @@ blocks; keep working, the offer stands · admin verbs stay explicit — an ACCEP
74
105
  offer is the explicit invocation.
75
106
 
76
107
  **Bootstrap digest — one question, however much is pending.** Session start may
77
- accumulate many offers (staleness push, N distills, needs-adjudication flags, a
78
- nudge). NEVER ask them serially — batch into ONE itemized digest: *"3 things
79
- pending: brain push · 2 merged branches to distill · 1 file needs adjudication —
80
- want any now, or later?"*
108
+ accumulate many offers (staleness push, N distills, needs-adjudication flags, dirty
109
+ notes to refresh, a nudge). NEVER ask them serially — batch into ONE itemized digest:
110
+ *"4 things pending: brain push · 2 merged branches to distill · 1 file needs
111
+ adjudication · 3 notes cite code you changed — want any now, or later?"*
81
112
  Dismissible as a unit; offer fatigue trains reflexive yes, which corrodes every
82
113
  consent rung above it.
83
114
 
115
+ **The M5 sensors feed you — you never poll.** Four deterministic instruments run
116
+ outside the model (blueprint hooks + the git boundary): the SessionStart digest
117
+ injects staleness/conflicts/corrections/active-plan as context at session start ·
118
+ every Edit/Write is dirty-marked into `.rafa/dirty.jsonl` the moment it happens ·
119
+ `git push` runs `rafa checkpoint` mechanically (non-blocking) · a correction-shaped
120
+ prompt is queued to `.rafa/reflex.jsonl` AND arrives with a `[rafa · reflex <id>]`
121
+ steering injection. Your part is the JUDGMENT half: consume `rafa dirty --json` at
122
+ natural boundaries, offer the scoped refresh (above), and run `rafa dirty --consume`
123
+ ONLY after a refresh actually shipped through the gates. Sensors make the moments
124
+ deterministic; authoring stays yours.
125
+
126
+ **Guidance is ambient · front-loaded · earned — never interleaved.** The dev sees
127
+ the loop three ways and ONLY three: the **statusline** (always-on one-liner —
128
+ plan progress · stale · corrections · conflicts; `rafa status --line` is the same
129
+ truth for any surface) · the digest's **`[rafa · next] suggested next:`** line
130
+ (deterministic, ranked: conflicts → corrections → resume plan → staleness) — honor
131
+ it by putting that item FIRST in the bootstrap digest · the **boundary offers**.
132
+ Never emit mid-flow "next step" reminders; the statusline already says it silently.
133
+
134
+ **The correction reflex (bank it THIS session, not next scan).** When the
135
+ `[rafa · reflex <id>]` injection arrives: first address the dev's correction; then
136
+ judge it — is this DURABLE repo knowledge (a convention/contract/fact a future
137
+ session needs), or a one-off steer? Durable → bank NOW through the gates: on a
138
+ branch, author/edit the note under `.rafa/brain/**` (≥1 resolving cite) +
139
+ `rafa checkpoint`; on main, full gates (verify-citations · compile · push). Confirm
140
+ to the dev in ONE line — note id + where it landed — then
141
+ `rafa reflex --consume <id>`. Not durable (preference, out-of-code claim,
142
+ one-off) → say it stays session-only and consume with verdict `session-only`. An
143
+ ungroundable claim is NEVER banked (no cite = no note — tell the dev honestly).
144
+ Unprocessed corrections from abandoned sessions surface in the bootstrap digest —
145
+ work them like any pending item. Dev-level preferences route to compass's offer
146
+ path (user brain), never the org stores. Raw transcripts never enter any shared
147
+ store; only the distilled, cited note ships.
148
+
84
149
  **The frame is CONSENT (owner, 2026-07-10).** Four rungs, each action needs exactly
85
150
  one: (1) **implied** — the message itself consents to what it asks, and only that ·
86
151
  (2) **session** — asked once, remembered, revocable mid-session ("stop pushing") ·
@@ -46,8 +46,11 @@ doesn't validate, compile fails.
46
46
  | plan | `plans/**/*.md` | **structured** | §7 | plan/build |
47
47
  | log | `brain/log.md` | **verbatim** | — (prose trail) | conductor |
48
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 |
49
50
  | active pointer | `active.md` | **generated** | — (`# <plan-id>` or "No active plan"; compile emits it as `activePlanId`, §7) | conductor |
50
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 |
51
54
  | agent | `.claude/agents/*.md` (code repo) | **structured** (local gate — NOT in manifest) | §10 | rafa |
52
55
 
53
56
  The `agent` type is the one structured type outside `.rafa/`: the shipped agent cards
@@ -146,6 +149,12 @@ JSON. This is the exact shape the platform binds to.
146
149
  ]
147
150
  },
148
151
 
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
157
+
149
158
  "notes": [
150
159
  {
151
160
  "id": "agent-name-contract",
@@ -204,6 +213,14 @@ title: The agent name is a cross-process contract # required
204
213
  summary: Only "research_agent" is fully wired end to end # required
205
214
  links: [copilotkit-runtime-route-convention] # optional · default []
206
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).
207
224
  cites: # required · ≥ 1
208
225
  - src/lib/model-selector-provider.tsx:42 :: research_agent
209
226
  - agents/python/main.py:20 :: research_agent
@@ -211,6 +228,12 @@ cites: # required · ≥ 1
211
228
  (prose body — rendered verbatim, never parsed)
212
229
  ```
213
230
 
231
+ `anchor:`/`absent:` are **checker declarations** (consumed by `rafa verify-citations`
232
+ gates B2/B3, not emitted into the manifest). A note whose title/summary reads as an
233
+ absence claim without declaring `absent:` is listed as a checker WARN — prism's
234
+ worklist, never a gate failure (a heuristic that fails the gate would be an assumed
235
+ value).
236
+
214
237
  ---
215
238
 
216
239
  ## 3. Improvement files — `improve/improvements/*.md`
@@ -282,10 +305,19 @@ domain found (compile emits it as `[{ domain, status }]` in the manifest). Statu
282
305
  ---
283
306
  schemaVersion: 1
284
307
  domains: { design-system: mapped, components: mapped, api: thin, external-integrations: empty }
308
+ inventory: # optional · declared surface inventories, re-counted by
309
+ - route-pages :: apps/web/app/**/page.tsx :: 23 # the checker every run
310
+ - api-routes :: apps/web/app/api/**/route.ts :: 2
285
311
  ---
286
312
  (per-criterion PASS/FAIL narrative in the body)
287
313
  ```
288
314
 
315
+ `inventory:` entries are `<name> :: <glob> :: <count>` — the checker recomputes each
316
+ count via `git ls-files ':(glob)<glob>'` and FAILS on drift (coverage claiming a
317
+ surface inventory the repo has outgrown). atlas declares one per framework surface it
318
+ maps (route pages, API routes, workflows — whatever the repo's shape makes load-bearing);
319
+ compile validates the grammar, the checker owns the truth.
320
+
289
321
  ---
290
322
 
291
323
  ## 7. Plan files — `plans/**/*.md` (+ the plans channel) — **v2: the work-item tree**
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ // rafa hook · PostToolUse (Edit|Write|MultiEdit|NotebookEdit) — the dirty-marker.
3
+ //
4
+ // M5 capture engine, sensor #1: every code edit is recorded the moment it happens
5
+ // (monotonic, event-driven — never at session end, because there is no session end).
6
+ // Appends {f, t} to .rafa/dirty.jsonl; the SessionStart digest and `rafa dirty`
7
+ // resolve entries to the brain notes that cite those files (the codeRefs plane).
8
+ //
9
+ // Hard rules: NEVER block the session (exit 0 on every path, all errors swallowed),
10
+ // no network, no LLM, O(append). Honors RAFA_HOOKS_DISABLED=1 (headless workers /
11
+ // CI distillation set it — the recursion guard).
12
+
13
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
14
+ import { join, relative, isAbsolute, sep } from "node:path";
15
+
16
+ try {
17
+ if (process.env.RAFA_HOOKS_DISABLED === "1") process.exit(0);
18
+
19
+ let input = "";
20
+ try {
21
+ input = readFileSync(0, "utf8"); // stdin: the hook event JSON
22
+ } catch {
23
+ process.exit(0);
24
+ }
25
+ let evt = null;
26
+ try {
27
+ evt = JSON.parse(input);
28
+ } catch {
29
+ process.exit(0);
30
+ }
31
+
32
+ const root = process.env.CLAUDE_PROJECT_DIR || evt?.cwd || process.cwd();
33
+ if (!existsSync(join(root, "rafa.json"))) process.exit(0); // not a rafa-provisioned repo
34
+
35
+ const fp = evt?.tool_input?.file_path || evt?.tool_input?.notebook_path;
36
+ if (!fp || typeof fp !== "string") process.exit(0);
37
+
38
+ const rel = isAbsolute(fp) ? relative(root, fp) : fp;
39
+ // Outside the repo, or not code-plane: knowledge/state/tooling edits don't dirty the brain.
40
+ if (rel.startsWith("..") || isAbsolute(rel)) process.exit(0);
41
+ const top = rel.split(sep)[0];
42
+ if ([".rafa", ".claude", ".git", "node_modules"].includes(top) || rel === "rafa.json") process.exit(0);
43
+
44
+ const rafaDir = join(root, ".rafa");
45
+ mkdirSync(rafaDir, { recursive: true });
46
+
47
+ // Transport exclusion, belt-and-braces: the queue is session-local state and must
48
+ // never ride a brain push (`rafa push` commits .rafa/ wholesale). ensureBrainRepo
49
+ // writes this too — this covers the window before any pull/push ran here.
50
+ const gi = join(rafaDir, ".gitignore");
51
+ const body = existsSync(gi) ? readFileSync(gi, "utf8") : "";
52
+ if (!/^dirty\.jsonl$/m.test(body))
53
+ writeFileSync(gi, body + (body === "" || body.endsWith("\n") ? "" : "\n") + "dirty.jsonl\n");
54
+
55
+ appendFileSync(
56
+ join(rafaDir, "dirty.jsonl"),
57
+ JSON.stringify({ f: rel.split(sep).join("/"), t: new Date().toISOString() }) + "\n",
58
+ );
59
+ } catch {
60
+ /* a sensor must never take down the session */
61
+ }
62
+ process.exit(0);
@@ -0,0 +1,24 @@
1
+ #!/bin/sh
2
+ # rafa pre-push hook v1 (installed by @rafinery/cli init/pull/update — safe to
3
+ # delete; it will be offered again, never forced over a foreign hook).
4
+ #
5
+ # M5 capture engine, sensor #3: `git push` of the CODE branch is a ratified
6
+ # natural checkpoint boundary — so the branch WORKING SET (edited/new brain
7
+ # files) syncs to the platform deterministically here, not only when a session
8
+ # remembers its SOP. Non-blocking by design: a checkpoint problem must never
9
+ # stop a code push (exit 0 on every path; conflicts land as .theirs.md copies
10
+ # for the next session to decide — printed, never auto-resolved).
11
+
12
+ [ "$RAFA_HOOKS_DISABLED" = "1" ] && exit 0
13
+ [ -f "rafa.json" ] || exit 0
14
+
15
+ echo "rafa · checkpoint (pre-push boundary) …"
16
+ npx -y @rafinery/cli checkpoint || {
17
+ code=$?
18
+ if [ "$code" = "2" ]; then
19
+ echo "rafa · checkpoint CONFLICT — a teammate's newer copy landed as *.theirs.md; decide in your next session (merge/adopt/keep, re-run rafa checkpoint). Push continues."
20
+ else
21
+ echo "rafa · checkpoint skipped (exit $code) — push continues; run 'rafa checkpoint' manually when convenient."
22
+ fi
23
+ }
24
+ exit 0