@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.
Files changed (76) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/bin/rafa.mjs +11 -0
  3. package/blueprint/.claude/agents/atlas.md +21 -3
  4. package/blueprint/.claude/agents/bloom.md +2 -2
  5. package/blueprint/.claude/agents/prism.md +42 -3
  6. package/blueprint/.claude/agents/sage.md +1 -1
  7. package/blueprint/.claude/commands/rafa.md +24 -13
  8. package/blueprint/.claude/rafa/contract.md +23 -0
  9. package/blueprint/.claude/rafa/hooks/brain-commit.mjs +4 -0
  10. package/blueprint/.claude/rafa/hooks/session-start.mjs +66 -0
  11. package/blueprint/.claude/skills/rafa-build/SKILL.md +111 -20
  12. package/blueprint/.claude/skills/rafa-commit/SKILL.md +22 -0
  13. package/blueprint/.claude/skills/rafa-distill/SKILL.md +37 -4
  14. package/blueprint/.claude/skills/rafa-improve/SKILL.md +22 -0
  15. package/blueprint/.claude/skills/rafa-leverage/SKILL.md +6 -1
  16. package/blueprint/.claude/skills/rafa-plan/SKILL.md +36 -1
  17. package/blueprint/.claude/skills/rafa-review/SKILL.md +16 -2
  18. package/blueprint/.claude/skills/rafa-sage/SKILL.md +8 -2
  19. package/blueprint/.claude/skills/rafa-scan/SKILL.md +4 -1
  20. package/lib/brain-repo.mjs +1 -1
  21. package/lib/checkpoint.mjs +26 -15
  22. package/lib/distill.mjs +56 -3
  23. package/lib/distiller/doctrine.mjs +5 -16
  24. package/lib/distiller/schema-ladder.mjs +186 -0
  25. package/lib/doctor.mjs +19 -1
  26. package/lib/facts.mjs +130 -0
  27. package/lib/gate/compile.mjs +12 -0
  28. package/lib/gate/verify-citations.mjs +78 -1
  29. package/lib/hydrate.mjs +45 -0
  30. package/lib/init.mjs +9 -1
  31. package/lib/leverage/engine.mjs +32 -3
  32. package/lib/leverage.mjs +11 -2
  33. package/lib/loop-cache.mjs +67 -0
  34. package/lib/mcp-client.mjs +19 -0
  35. package/lib/push.mjs +18 -4
  36. package/lib/reflex.mjs +5 -1
  37. package/lib/releases.mjs +78 -0
  38. package/lib/review.mjs +32 -6
  39. package/lib/session-facts.mjs +108 -0
  40. package/lib/skill-deps.mjs +377 -0
  41. package/lib/stamp.mjs +24 -0
  42. package/lib/update.mjs +9 -0
  43. package/package.json +3 -2
  44. package/skills-bundle/frontend-design/LICENSE.txt +177 -0
  45. package/skills-bundle/frontend-design/SKILL.md +55 -0
  46. package/{LICENSE → skills-bundle/grill-me/LICENSE} +6 -6
  47. package/skills-bundle/grill-me/SKILL.md +7 -0
  48. package/skills-bundle/grill-me/agents/openai.yaml +5 -0
  49. package/skills-bundle/grilling/SKILL.md +53 -0
  50. package/skills-bundle/improve-codebase-architecture/HTML-REPORT.md +123 -0
  51. package/skills-bundle/improve-codebase-architecture/LICENSE +21 -0
  52. package/skills-bundle/improve-codebase-architecture/SKILL.md +71 -0
  53. package/skills-bundle/improve-codebase-architecture/agents/openai.yaml +5 -0
  54. package/skills-bundle/requesting-code-review/LICENSE +21 -0
  55. package/skills-bundle/requesting-code-review/SKILL.md +95 -0
  56. package/skills-bundle/requesting-code-review/code-reviewer.md +172 -0
  57. package/skills-bundle/skills-manifest.json +72 -0
  58. package/skills-bundle/tdd/LICENSE +21 -0
  59. package/skills-bundle/tdd/SKILL.md +36 -0
  60. package/skills-bundle/tdd/agents/openai.yaml +3 -0
  61. package/skills-bundle/tdd/mocking.md +59 -0
  62. package/skills-bundle/tdd/tests.md +77 -0
  63. package/skills-bundle/vercel-composition-patterns/AGENTS.md +946 -0
  64. package/skills-bundle/vercel-composition-patterns/README.md +60 -0
  65. package/skills-bundle/vercel-composition-patterns/SKILL.md +89 -0
  66. package/skills-bundle/vercel-composition-patterns/metadata.json +11 -0
  67. package/skills-bundle/vercel-composition-patterns/rules/_sections.md +29 -0
  68. package/skills-bundle/vercel-composition-patterns/rules/_template.md +24 -0
  69. package/skills-bundle/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  70. package/skills-bundle/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  71. package/skills-bundle/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  72. package/skills-bundle/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  73. package/skills-bundle/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
  74. package/skills-bundle/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  75. package/skills-bundle/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  76. package/skills-bundle/vercel-composition-patterns/rules/state-lift-state.md +125 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,136 @@ 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.11.0 — schema-aware reconciliation (the 4-case ladder)
8
+
9
+ Distillation is where knowledge from two lineages meets — so it is now also
10
+ where brain SCHEMA VERSIONS meet. Before any claim-level judging, the
11
+ reconciler resolves target (the org brain) vs source (the branch's candidates)
12
+ vs the runner's own latest schema:
13
+
14
+ 1. **target < source** → full target-brain rewrite onto the newer schema,
15
+ riding the same run's gates + push (one migration+reconcile commit).
16
+ 2. **target > source** → incoming old-schema candidates lift into the
17
+ target's schema before judging.
18
+ 3. **target == source, both < latest** → the merged target rewrites onto the
19
+ runner's latest.
20
+ 4. **target == source == latest** → plain diff reconcile — the happy flow,
21
+ exactly as before.
22
+
23
+ Either side NEWER than the runner aborts loudly — update the runner;
24
+ knowledge is never downgraded. Transforms are registered per step AND per
25
+ OKF type (`lib/distiller/schema-ladder.mjs` — each step declares rule ·
26
+ playbook · improvement explicitly: a function, or the EXPLICIT "unchanged",
27
+ which still re-stamps the version line; derived/generated files regenerate
28
+ at the new schema, they never migrate); a missing step or class fails
29
+ loudly — the ladder never guesses a schema. v1 is the only schema in existence, so today
30
+ every reconcile is case 4; this ships the SEAM (fully tested) that turns a
31
+ future v2 into a one-entry registry change instead of a migration crisis.
32
+
33
+ **Descriptive commits (owner 2026-07-26).** Reconciliation commits now SAY
34
+ what the run did: the headless distiller composes
35
+ `reconcile(<branch>): N banked · M refuted · K adjudication — <ids…>` and
36
+ passes it via the new `rafa push --message=` override (the brain-for trailer
37
+ is untouched — provenance consumers keep their key); the session SOP carries
38
+ the same rule. And the commit contract (rafa-commit) now REQUIRES an
39
+ agent-readable body on dev commits: what concretely changed, the why the diff
40
+ can't show, brain-note join keys, no session deixis — the next reader of a
41
+ commit is usually an agent.
42
+
43
+ **The configurable prod branch reaches the CLI.** `trunkBranchOf` resolves
44
+ the stamped `rafa.json → prodBranch` → git's origin/HEAD → "main"; the
45
+ checkpoint trunk guard and the CI distill target use it (with prodBranch set,
46
+ "main" can be a plain feature-carrying branch). The platform side shipped the
47
+ `repos.prodBranch` setting + repo-page card in the same arc.
48
+
49
+ Also in this release (landed after 0.10.0's publish): distill-refutation loop
50
+ events carry the required actor envelope + a dedupeKey (a retried distill
51
+ counts each resolution once); `cliActorMeta` honors `RAFA_ACTOR_RUNNER` for
52
+ sandbox/CI attribution; and dependsOn invalidation is documented as NOT
53
+ transitive, with breadth-of-declaration taught as the conservative pattern at
54
+ `rafa facts add`, the CLI help, and prism's citation rule (pinned by test).
55
+
56
+ ## 0.10.0 — wave 6: the capability arc (skill dependencies + agent upgrades)
57
+
58
+ rafa's agents get materially better at engineering — and the mechanism is the
59
+ owner's design: rafa **declares** third-party skill dependencies and installs
60
+ them **with your consent**, package-manager style, so the agents adopt YOUR
61
+ toolbox instead of hardcoding one.
62
+
63
+ - **The skill installer.** `rafa update`/`init` offers 7 declared skills (tdd,
64
+ frontend-design, vercel-composition-patterns, requesting-code-review,
65
+ improve-codebase-architecture, grill-me, and rafa's own grilling procedure)
66
+ with one-line reasons; on yes it installs them one at a time into the
67
+ **harness-neutral `.agents/skills/`** (one convention Claude Code, Codex,
68
+ and Cursor all read — Codex manifests ride along), verifies each
69
+ (frontmatter parses · sha256s match the tarball's own manifest · license
70
+ files present), and stamps `rafa.json`. Declining is remembered — re-offer
71
+ with `rafa update --skills`; `--yes` for CI; non-TTY without `--yes` skips
72
+ and stamps nothing. A declined set degrades gracefully, never breaks:
73
+ vendored cards never reference `.agents/` (pinned by test).
74
+ - **TDD by default** (atlas 4.0.0): eligible code tasks (tdd installed + a
75
+ harness detected) build red→green — the Done-check IS a failing test
76
+ authored first at a seam the dev approved at plan time; evidence (red
77
+ exit≠0 before green exit 0) rides the structured return; prism re-runs
78
+ green live and verifies red per validation tier. Refactor defers to review.
79
+ - **The design method**: UI tasks invoke frontend-design (token system,
80
+ signature element, critique-before-code) and consult Vercel's composition
81
+ rules (React-19-gated) — with the brain's design-system notes as the brief.
82
+ - **The grill gate** (prism 0.9.0): plans get a tier-bounded adversarial
83
+ interview before approval — grounding, unstated dependencies, VACUOUS
84
+ Done-checks (tautological tests caught), YAGNI. Devs also get `/grill-me`.
85
+ - **Severity-labeled findings** everywhere prism/review rule: Critical ·
86
+ Important · Minor — Minor never flips a verdict.
87
+ - **bloom's architecture method** (0.9.0): git-log heat scoping, the deletion
88
+ test, Confidence-lined candidates, first-slice-or-backlog — never big-bang.
89
+ - **Toolbox surfaces**: conductor 2.2.0 recalls `.agents/skills` in every
90
+ spawn slice; `rafa leverage --json` carries top-level `agentSkills` +
91
+ `skillDeps`; `rafa doctor` verifies installed skills against the bundle.
92
+
93
+ Adopt with `rafa update` — the skills prompt runs first, then the blueprint
94
+ sync. All licenses confirmed and vendored (MIT ×5, Apache-2.0 frontend-design).
95
+
96
+ ## 0.9.0 — wave 5: trust plane + transport economics
97
+
98
+ The research-canvas QA dogfood session (2026-07-25) produced a P0–P3 bottleneck
99
+ backlog; its root causes converged with sage's first two learnings on the same
100
+ seam. This release lands the whole wave:
101
+
102
+ - **Strict loop events, no legacy path.** `report_loop_event` requires the
103
+ actor envelope (`model` · `agent` · `runner ∈ sandbox|ci|session`; mechanical
104
+ CLI emits stamp `model:"mechanical"`) and `verification {method: static|live,
105
+ tool?, evidence?}` on verdict/gate categories — the static-vs-executed
106
+ distinction is structural, so a fabricated "tests passed" can't survive the
107
+ schema. `dedupeKey` makes every emit idempotent; `tier` records the
108
+ validation tier the ruling ran at. Pre-0.9.0 emits are rejected server-side
109
+ (pre-launch: old CLIs discarded). Both sage learnings flipped accepted.
110
+ - **One beat per task boundary.** `patch_plan_item` patches ONE item's fields
111
+ (structure stays `push_plan`-only); `checkpoint_task` lands the item patch +
112
+ the prism-verdict loop event + decision records in ONE transaction,
113
+ idempotent on retry. A task boundary went from ~6 platform calls + a
114
+ full-tree resend to 2 beats.
115
+ - **Self-healing citations.** `verify-citations --fix` rewrites a drifted cite
116
+ when its token still lives at exactly ONE grep site (gone/ambiguous stay
117
+ failing — the checker never guesses), then re-runs clean. `rafa hydrate`
118
+ re-bases served cites against the local checkout on arrival.
119
+ - **`rafa facts` — the session-facts plane.** Bank a verified fact once (claim
120
+ + the command/exit that proved it + dependsOn paths, sha-anchored); agents
121
+ read first and cite `SF-n unchanged`. Staleness is mechanical (dependsOn ∩
122
+ git diff since the verifying sha, working tree included).
123
+ - **The sage-due bridge.** `rafa checkpoint` caches loop-event shapes locally;
124
+ the SessionStart digest prints "sage due: N" with zero added platform calls,
125
+ and the conductor card makes honoring it a MUST — sage's trigger is
126
+ mechanical instead of discretionary.
127
+ - **`validation_tier` on plan tasks** (light|standard|full): bounds prism's
128
+ re-derivation depth only — the Done-check gate never relaxes.
129
+ - Cards/SOPs re-vendored: prism 0.8.0 (tool-authenticity rule · structured
130
+ return · bounded invariant citation) · atlas 3.9.0 (structured return ·
131
+ facts duty) · sage 0.4.0 (verification/actor/tier analysis dimensions) ·
132
+ conductor 2.1.0 (mechanical sage trigger). Review summary counts dedupe by
133
+ note id.
134
+
135
+ Adopt with `rafa update` (re-vendors hooks, cards, SOPs, contract).
136
+
7
137
  ## 0.8.16 — the session tail never strands (both planes, one beat)
8
138
 
9
139
  Live-catch (2026-07-24, research-canvas): brain notes authored AFTER the last code
package/bin/rafa.mjs CHANGED
@@ -46,6 +46,7 @@ const COMMANDS = [
46
46
  "hydrate",
47
47
  "dirty",
48
48
  "reflex",
49
+ "facts",
49
50
  "status",
50
51
  "doctor",
51
52
  "fold",
@@ -73,6 +74,8 @@ Commands:
73
74
  Flags (init/update): --overwrite replace changed owned files without asking
74
75
  --keep keep all changed owned files without asking
75
76
  (default: ask per file; in a non-TTY it keeps them)
77
+ --skills re-offer declared skill dependencies (incl. declined)
78
+ --yes install all skill dependencies unprompted (CI)
76
79
  compile Validate the brain against the contract → .rafa/manifest.json.
77
80
  manifest The BRANCH manifest (lenient snapshot — WIP files land under
78
81
  drafts, never fail). You never need to run this: the post-commit
@@ -113,6 +116,14 @@ Commands:
113
116
  detected. List/--json; --consume <id> [verdict] AFTER banking it
114
117
  through the gates (or judging it session-only). Corrections are
115
118
  the highest-value knowledge signal — they must never evaporate.
119
+ facts The session-facts plane (wave 5.4 — memoized verification): bank a
120
+ VERIFIED fact once (claim + the command/exit that proved it +
121
+ dependsOn paths, sha-anchored); agents read first and cite
122
+ "SF-n unchanged" instead of re-deriving. Staleness is mechanical
123
+ (dependsOn ∩ git diff since the verifying sha) and NOT transitive —
124
+ declare the enclosing directory for subsystem-spanning facts
125
+ (breadth is the conservative move). add · invalidate ·
126
+ prune --stale · --json.
116
127
  status Where the loop stands, from local state only: active plan +
117
128
  task progress · stale count · corrections · conflicts.
118
129
  --line = the one-line ambient form (statuslines/prompts) ·
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: atlas
3
- version: 3.8.1
3
+ version: 4.0.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: >-
@@ -16,7 +16,7 @@ duties:
16
16
  - "scan :: .claude/skills/rafa-scan/SKILL.md :: comprehensive breadth-first cited brain · verify-citations exits 0 · coverage honest (thin/gap stated, never hidden)"
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
- - "build-execution :: .claude/skills/rafa-build/SKILL.md :: implement per recalled knowledge · never hand-edit brain files around the gate · return what changed, cited"
19
+ - "build-execution :: .claude/skills/rafa-build/SKILL.md :: implement per recalled knowledge · TDD-DEFAULT when the tdd skill + a harness are present (the Done-check is a failing test FIRST at the plan-named seam; red→green evidence in the return) · UI tasks run the installed design skills per SOP (brain conventions win) · read session-facts FIRST and bank new expensive verifications (rafa facts add) · never hand-edit brain files around the gate · return what changed, cited"
20
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"
21
21
  - "okf-surface :: .claude/skills/rafa-okf/SKILL.md :: authored files pass rafa okf check as written — body links are markdown (never wikilinks); emit-owned sections untouched"
22
22
  ---
@@ -62,6 +62,22 @@ whole-codebase read happens in *your* window, not the conductor's, so the main s
62
62
  stays lean. You **never spawn** other agents — subagents can't nest, so the conductor owns
63
63
  the loop. Return summaries + on-disk artifacts, not the raw reads.
64
64
 
65
+ **Structured return (wave 5).** A build-execution report ends with the machine
66
+ block the conductor assembles Logs from — never re-paraphrased prose:
67
+
68
+ ```json
69
+ { "filesChanged": ["path", "…"], "commitSha": "…",
70
+ "notes": "one-paragraph what/why",
71
+ "factsDiscovered": ["SF-n banked: <claim>", "…"] }
72
+ ```
73
+
74
+ **Session facts (wave 5 — read first, bank once).** Before re-deriving any
75
+ environment/tooling fact, read `.rafa/session-facts.json` (`rafa facts`) and
76
+ cite `SF-<n> unchanged` when it holds. When YOU verify something expensive
77
+ (a harness exists, an install works, an env constraint), bank it once:
78
+ `rafa facts add --claim="…" --command="…" --exit=N [--depends=a,b]` — the next
79
+ spawn inherits it instead of re-learning it.
80
+
65
81
  ## The brain is your index, once it exists
66
82
  Generic at provision-time, repo-aware after the scan: **if a brain exists at
67
83
  `.rafa/brain/`, consult it as your index before re-reading code** — route via its cited
@@ -91,7 +107,9 @@ Never act cold; never over-load.
91
107
  *name* is a contract; its *value* is a secret. If a note genuinely needs a value, stop and ask
92
108
  the dev — don't harvest it. (Enforced in [the scan skill](../skills/rafa-scan/SKILL.md) step 4.)
93
109
  - **Toolbox-first execution (automatic — never an offer).** Before implementing any
94
- task step, CHECK the repo's installed toolbox — committed `.claude/skills/`,
110
+ task step, CHECK the repo's installed toolbox — committed `.claude/skills/`, the
111
+ **harness-neutral `.agents/skills/`** (consent-installed skill dependencies —
112
+ tdd, frontend-design, vercel-composition-patterns are yours to INVOKE),
95
113
  `.mcp.json` servers, commands — for a capability that already does it, and INVOKE
96
114
  it (Skill tool / MCP) instead of hand-rolling. The conductor passes the matching
97
115
  inventory slice in your spawn prompt; consult it first. Only what is actually
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bloom
3
- version: 0.8.1
3
+ version: 0.9.0
4
4
  model: opus # a false flag mutes the whole ledger — best model, never cheap
5
5
  groundTruth: code-trend
6
6
  description: >-
@@ -13,7 +13,7 @@ description: >-
13
13
  tools: Read, Grep, Glob, Bash, Skill, Write, mcp__rafinery
14
14
  color: green
15
15
  duties:
16
- - "improve-pass :: .claude/skills/rafa-improve/SKILL.md :: cited · prioritized P0–P3 · leverage-ranked · cite-checked (unresolved dropped) · ledger + debt trend regenerated"
16
+ - "improve-pass :: .claude/skills/rafa-improve/SKILL.md :: cited · prioritized P0–P3 · the architecture lens applies the deletion-test/depth method with its vocabulary (Confidence-lined candidates, first-slice-or-backlog, never big-bang) · leverage-ranked · cite-checked (unresolved dropped) · ledger + debt trend regenerated"
17
17
  - "plan-pull :: .claude/skills/rafa-plan/SKILL.md :: top-leverage OPEN improvements in the blast radius surfaced as optional child tasks — dismissible, never blocking"
18
18
  - "build-sweep :: .claude/skills/rafa-build/SKILL.md :: newly spotted → ledger files · fixed-in-passing → status fixed · at most ONE opt-in nudge per task"
19
19
  - "staleness-watch :: .claude/skills/rafa-improve/SKILL.md :: median open-item age stays minimal — every sweep re-validates or closes aged items; no improvement ages silently"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: prism
3
- version: 0.7.1
3
+ version: 0.9.0
4
4
  model: opus # the trust anchor — a hallucinated verdict/finding is the worst failure; best model, never cheap
5
5
  groundTruth: code-vs-claim
6
6
  description: >-
@@ -14,8 +14,8 @@ tools: Read, Grep, Glob, Bash, Write, mcp__rafinery
14
14
  color: orange
15
15
  duties:
16
16
  - "scan-validation :: .claude/skills/rafa-validate/SKILL.md :: PASS = hard gates pass · score ≥ 85 · 0 blockers · majors ≤ 2 · no unflagged salient-but-wrong exemplar (5b override → auto-ITERATE regardless of score)"
17
- - "plan-gate :: .claude/skills/rafa-plan/SKILL.md :: PASS = every task grounded in brain + code (none hallucinated) · every child carries a Done-check · blast radius named from coverage; else REJECT with cited reasons"
18
- - "execution-validation :: .claude/skills/rafa-build/SKILL.md :: done only when the child's Done-check demonstrably holds — run it yourself against the working tree; FAIL returns cited reasons to the producer"
17
+ - "plan-gate :: .claude/skills/rafa-plan/SKILL.md :: PASS = every task grounded in brain + code (none hallucinated) · every child's Done-check present AND non-vacuous (grilled per validation_tier — light = invariants only · standard = bounded probes · full = the whole interview) · TDD-eligible code tasks name their seam in the Done-check · blast radius named from coverage; else REJECT with cited reasons"
18
+ - "execution-validation :: .claude/skills/rafa-build/SKILL.md :: done only when the child's Done-check demonstrably holds — run it yourself against the working tree · TDD tasks: green re-run live, red per tier (static-attested at standard, worktree re-run at full) · findings labeled Critical/Important/Minor — ITERATE iff any Critical or Important, Minor never flips the verdict; FAIL returns cited reasons to the producer"
19
19
  - "distillation-validation :: .claude/skills/rafa-distill/SKILL.md :: every working-set file judged against MERGED MAIN (never the fork point) with a confirming/refuting file:line — survivors enter the org brain only through compile; refutations go back to their author, cited"
20
20
  - "okf-surface :: .claude/skills/rafa-okf/SKILL.md :: every checker LINKS warn judged (unwritten knowledge vs typo) — never ignored, never auto-failed"
21
21
  ---
@@ -37,6 +37,45 @@ brain as a trusted index, whatever you are validating is the thing on trial —
37
37
  against the code (ground truth) and the stated acceptance criteria. Trust nothing in
38
38
  it until you've confirmed it.
39
39
 
40
+ **Tool authenticity (wave 5 — the npx-placeholder trap).** A tool result is
41
+ evidence ONLY if the tool actually ran: name the binary (path or resolved
42
+ command) and the REAL exit code. `npx <name>` can silently resolve a joke/
43
+ placeholder package and "pass" — a class that already produced one fabricated
44
+ verdict in the field. If you cannot execute the check (no keys, no deps, no
45
+ env), the claim is `method: "static"` — say so in the same breath, never
46
+ smooth it into an unqualified PASS. Fabricating a `live` result is the one
47
+ unforgivable failure: the whole trust model prices from it.
48
+
49
+ **Structured return (wave 5).** Your report ends with the machine block the
50
+ conductor assembles Logs and loop events from — never re-paraphrased prose:
51
+
52
+ ```json
53
+ { "verdict": "PASS|ITERATE",
54
+ "evidence": [{ "claim": "…", "method": "static|live", "tool": "…", "exitCode": 0 }],
55
+ "residualRisk": "what was NOT proven, named — or the explicit empty string" }
56
+ ```
57
+
58
+ The `evidence` entries feed `report_loop_event.verification` verbatim; the
59
+ static-vs-live split is structural, not prose discipline.
60
+
61
+ **Bounded invariant citation (wave 5 — no-redundant-reverification).** An
62
+ invariant YOU (or a prior prism pass this plan) verified once is citable as
63
+ `SF-<n> unchanged` **iff** `.rafa/session-facts.json` shows its `dependsOn`
64
+ untouched since its verifying sha (the session-start digest surfaces staleness;
65
+ `rafa facts` lists it). Re-deriving an unchanged, evidence-backed fact is waste,
66
+ not rigor. The bound is hard: anything NOT in the facts file — and everything
67
+ about the artifact under test itself — stays trust-nothing. **Invalidation is
68
+ NOT transitive** (dependsOn is intersected with the diff, imports are never
69
+ walked — pinned by test): trust a fact only as far as its declared breadth, and
70
+ when a fact you rely on spans a subsystem whose dependsOn names single files,
71
+ treat it as under-declared — re-verify instead of citing it.
72
+
73
+ **Validation tier (wave 5).** A task's `validation_tier` (plan §7) bounds your
74
+ RE-DERIVATION DEPTH — how much surrounding context you re-read and re-prove:
75
+ light = the Done-check itself + direct evidence · standard = + the touched
76
+ files' immediate seams · full = unbounded re-derivation. The Done-check gate
77
+ NEVER relaxes at any tier, and your emitted loop event carries the tier it ran at.
78
+
40
79
  ## Duties (each duty's bar is in the frontmatter; the SOP carries the procedure)
41
80
  1. **Scan validation** — judge the brain in `.rafa/brain/` against the repo per
42
81
  [the validate skill](../skills/rafa-validate/SKILL.md). Verdict + score + severity-
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sage
3
- version: 0.3.0
3
+ version: 0.4.0
4
4
  model: opus # a wrong learning re-shapes an agent for every repo — best model, never cheap
5
5
  groundTruth: sessions-over-time
6
6
  description: >-
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 2.0.2
2
+ version: 2.2.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
 
@@ -59,10 +59,15 @@ MCP tools, checkpoints, pushes, decisions logging — is AGENT-INTERNAL machiner
59
59
  dev speaks intent; YOU run the machinery. Never instruct the dev to run a command you
60
60
  can run yourself. The only human acts: init (provision) · `npx rafa pull` (once per
61
61
  clone) · ci-setup approval · npm publish. **Toolbox is a recall surface:** at bootstrap,
62
- load the committed toolbox (`.claude/skills/`, `.mcp.json`, commands) alongside brain
63
- status; pass the matching slice into every atlas/bloom spawn prompt agents USE the fit
64
- capability instead of hand-rolling (only what's installed, never guessed; personal
65
- `~/.claude/` stays live-recommend-only). **Actor envelope:** stamp `actorMeta {model,
62
+ load the committed toolbox `.claude/skills/` (this harness) **and
63
+ `.agents/skills/` (the HARNESS-NEUTRAL third-party skillsone convention
64
+ Claude Code · Codex · Cursor all read; installed by `rafa update` with
65
+ consent, wave 6)** plus `.mcp.json` and commands, alongside brain status
66
+ (`rafa leverage --json` is the deterministic read); pass the matching slice
67
+ into every atlas/bloom spawn prompt — agents USE the fit capability instead of
68
+ hand-rolling (only what's installed, never guessed; a DECLINED skill dep means
69
+ the SOP's fallback path, never a nag; personal `~/.claude/` stays
70
+ live-recommend-only). **Actor envelope:** stamp `actorMeta {model,
66
71
  agent, runner: "session"}` on every state-plane write (checkpoints, plan pushes,
67
72
  decisions, reports) and export `RAFA_ACTOR_MODEL` before CLI calls — the platform records
68
73
  WHO/WHAT executed; "unreported" when unknown, never guessed.
@@ -240,14 +245,20 @@ explicitly typed; an ACCEPTED boundary offer counts as the explicit invocation.
240
245
 
241
246
  ## sage — the silent L5 observer (runs IMPLICITLY, zero-command; owner 2026-07-13)
242
247
 
243
- Devs never type `/rafa sage`. **Trigger:** at completion boundaries build final-verify ·
244
- distill close · bootstrap when new loop events exist since the learnings ledger's newest
245
- entry, spawn sage per [rafa-sage](../skills/rafa-sage/SKILL.md); announce ONE line, proceed;
246
- its proposals ride the NEXT bootstrap digest. Writing proposals is the just-do rung; the
247
- consent moment is ACCEPTING one (accepted = a versioned, MR-reviewed card/SOP edit proposals
248
- never self-apply). `/rafa sage` is the explicit override. Full mechanics (≥10-event threshold,
249
- `get_loop_events` shape read, scrub step, ledger schema, the person-free/asset-free creed) live
250
- in the skill.
248
+ Devs never type `/rafa sage`. **Trigger MECHANICAL as of wave 5 (owner
249
+ 2026-07-25):** the SessionStart digest computes it a `[rafa · sage] sage due:
250
+ N loop events…` line (the checkpoint-written loop-event cache vs the learnings
251
+ ledger, compared locally). **When that line is present, spawning sage at the
252
+ next completion boundary build final-verify · distill close · bootstrapis
253
+ a MUST, not a judgment call**: the line IS the trigger condition already
254
+ evaluated; a discretionary trigger is how sage never fired organically. Spawn
255
+ per [rafa-sage](../skills/rafa-sage/SKILL.md); announce ONE line, proceed; its
256
+ proposals ride the NEXT bootstrap digest. No digest line = not due (skip
257
+ silently). Writing proposals is the just-do rung; the consent moment is
258
+ ACCEPTING one (accepted = a versioned, MR-reviewed card/SOP edit — proposals
259
+ never self-apply). `/rafa sage` is the explicit override. Full mechanics
260
+ (≥10-event threshold, `get_loop_events` shape read, scrub step, ledger schema,
261
+ the person-free/asset-free creed) live in the skill.
251
262
 
252
263
  ## migrate · update — brain-side schema migration (no skill; the intelligence-only half)
253
264
 
@@ -382,6 +382,7 @@ blocked_by: [payments-c2] # optional · item ids this waits on — a dependency
382
382
  blocked_reason: "vendor sandbox access" # optional · external blocks (no dependency id to point at)
383
383
  priority: 2 # optional · 0 none · 1 urgent · 2 high · 3 medium · 4 low (Linear scale)
384
384
  estimate: 3 # optional · points (Linear estimate / Jira story points)
385
+ validation_tier: standard # optional · light|standard|full — bounds prism's re-derivation DEPTH only (wave 5); the Done-check gate itself never relaxes. Planner assigns from blast radius: contract/state/graph surface → full · types/docs/leaf-UI → light · default standard
385
386
  branch: feat/webhooks # optional · ↔ Linear branchName
386
387
  baseSha: abc123 # optional · commit it was cut from
387
388
  domains: [payments] # optional · EPIC only · the blast radius — the brain link
@@ -454,6 +455,23 @@ imported comments are never scraped into decisions.
454
455
  fields do NOT bump it** — e.g. the note size stamps (`bodyTokens`,
455
456
  `cites[].targetTokens`, §1): ingest tolerates their absence, so a legacy manifest
456
457
  without them stays valid at the same `schemaVersion`.
458
+ - **The reconciliation schema ladder (0.11.0 — owner's 4-case doctrine).** When a
459
+ version bump DOES ship, distillation is the upgrade moment — the reconciler
460
+ resolves target-vs-source-vs-runner before judging: target below source → full
461
+ target rewrite onto the newer schema; source below target → incoming candidates
462
+ lift before judging; both equal-but-old → the merged target rewrites onto the
463
+ runner's latest; both current → plain diff (today). Either side newer than the
464
+ runner → **abort loudly** (update the runner; knowledge is never downgraded).
465
+ Transforms are registered per step AND per OKF type in the CLI
466
+ (`lib/distiller/schema-ladder.mjs`): each step declares rule · playbook · improvement
467
+ explicitly — a function, or the EXPLICIT `"unchanged"` (still re-stamps the note's
468
+ `schemaVersion:`); an unregistered step or class fails loudly — the ladder never
469
+ guesses a schema. Class resolves by CHAIN: the governed directory first (inside the
470
+ transport the path IS identity — the CAS row key), then the portable filename suffix
471
+ (`.rule.md` · `.playbook.md` · `.improvement.md`) for files travelling outside the
472
+ canonical tree; a contradiction between the two is a loud error. Only durable
473
+ knowledge migrates; derived/generated files
474
+ (coverage · checklist · ledger · reports · the manifest) regenerate at the new schema.
457
475
  - **Validate-and-correct:** `rafa compile` validates every file. On failure it emits
458
476
  a structured error — `path · field · rule` — to the **authoring agent** (atlas for
459
477
  notes, bloom for improvements/ledger, prism for checklist). The agent edits the
@@ -573,12 +591,17 @@ state**, marked by `envelope.plane: "state"`:
573
591
  | `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 |
574
592
  | `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 |
575
593
  | `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 |
594
+ | `patch_plan_item` | plans (one item) | wave 5.2 — field-level patch on ONE item (no full-tree resend): provided fields replace, absent stay; CLEARING a field and any STRUCTURE change (kind/parent/add/remove/domains/external/merged) remain `push_plan`-only, so "a vanished optional field must not survive" keeps holding at tree granularity; snapshot-before-change rides noteRevisions like a re-push |
595
+ | `checkpoint_task` | plans + loopEvents + planDecisions (**ONE transaction**) | wave 5.2 — the ONE-beat boundary write: optional item patch + the loop-event ruling + decision records land atomically (all-or-nothing; `mcp.call` is an action, so composing the separate tools can never be atomic — this is one internalMutation). Idempotent end-to-end (patch no-ops on same values · loop event dedupes on `dedupeKey` · decisions dedupe on client-stable ids). The embedded loopEvent obeys `report_loop_event`'s exact rules — one rulebook, no side door. `checkpoint_sync` stays separate: per-file CAS partial-accept is a different failure model than an atomic beat |
576
596
  | `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 |
577
597
  | `reconcile_claim` | **reconciliations queue** — (repo) run rows | executor claim (`plane:"state"`): ACID no-running-sibling assert → returns the attempt token; delegates to `reconciliations.claim`; stamps the actor envelope `{model, agent, runner}` (runner ∈ `sandbox`\|`ci`\|`session`, enum-checked at the boundary) on the row |
578
598
  | `reconcile_heartbeat` | reconciliations queue | extends the wall-clock lease + sets the phase; **attempt-fenced** (a non-current attempt is rejected, nothing written); actor envelope re-stamped |
579
599
  | `reconcile_log_append` | **reconciliationLogs** — the run log tail | batched chunks, **secret-screened PER LINE** (the same `looksLikeSecret` screen — a credential chunk is rejected by chunk/line index, nothing stored or echoed) + **attempt-fenced**; `seq`-ordered; UNLOGGED (emits its own `reconcile.log` event, counts only) |
580
600
  | `reconcile_report` | reconciliations queue (+ knowledge node on success) | terminal outcome + node delta + meter; **attempt-fenced**; success COMPOSES the pointer advance atomically (insert node + move pointer, one mutation); refutation/deletion ride the success outcomes; actor envelope stamped |
581
601
  | `list_working_sets` | branch working set (**READ**) | enumerates branches with LIVE candidate rows + counts only (no bodies) — closes the 07-14 sensor gap (`get_working_set` needs a branch arg; nothing listed them); feeds the SessionStart digest pending line |
602
+ | `report_loop_event` | **loopEvents** — the loop-outcome ledger (sage's evidence) | ONE structured outcome per ruling, shapes only (enum category + per-category enum outcome + `subject` shape ref, secret-screened). Wave 5 trust plane, STRICT (no legacy path): **required actor envelope** `{model, agent, runner ∈ sandbox\|ci\|session}` (mechanical CLI emits use `model:"mechanical"`); **required `verification {method: static\|live, tool?, evidence?}`** on `prism-verdict`/`gate-result`/`review-verdict` — the static-vs-executed distinction is structural, never prose; optional `dedupeKey` (≤120) makes the emit **idempotent** (same `(repo, key)` never lands twice — response says `deduped`) and `tier ∈ light\|standard\|full` records the §7 `validation_tier` the ruling ran at |
603
+ | `get_loop_events` | loopEvents (**READ**) | newest-first shape read (optional category filter, limit ≤500): category/outcome/subject + actorMeta/verification/dedupeKey/tier + at — no bodies, no code; sage splits miss patterns by verification method, actor, and tier |
604
+ | `report_learning` | **agentLearnings** — sage's summary-row mirror | sage ONLY (wave 4.1): mirrors the SUMMARY ROW of one committed ledger entry (`.claude/rafa/learnings/<id>.md` stays the single truth, MR-gated, never self-applied). Asset-free ENFORCED server-side: enum status/leverage/categories, `.claude/`-only diff target, length caps, code-fence + secret screens; upsert by id |
582
605
 
583
606
  State tools work with or without an ingested brain (a working set can exist —
584
607
  and a plan can be pushed — before the first scan) and are exempt from the
@@ -181,6 +181,10 @@ try {
181
181
  // rafa review's scored-ranges artifact (wave 2.3) — regenerated per
182
182
  // review, machine state like distill-verdicts; never brain knowledge.
183
183
  "review.json",
184
+ // wave 5: session-scoped machine state — memoized verification + the
185
+ // checkpoint-written loop-event cache the sage-due digest reads.
186
+ "session-facts.json",
187
+ "loop-events-tail.json",
184
188
  ];
185
189
  const gi = join(rafa, ".gitignore");
186
190
  const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
@@ -300,6 +300,72 @@ try {
300
300
  }
301
301
  }
302
302
 
303
+ // Session facts (wave 5.4 — memoized verification): banked verified facts +
304
+ // mechanical staleness (dependsOn ∩ git diff since the verifying sha, working
305
+ // tree included). Spawned agents read the file FIRST and cite "SF-n unchanged"
306
+ // instead of re-deriving. Local reads only — no platform call.
307
+ {
308
+ const doc = readJson(join(rafaDir, "session-facts.json"));
309
+ if (doc?.schemaVersion === 1 && Array.isArray(doc.facts) && doc.facts.length) {
310
+ let stale = 0;
311
+ for (const f of doc.facts) {
312
+ try {
313
+ const changed = execSync(`git diff --name-only ${f.verifiedAtSha}`, {
314
+ cwd: root,
315
+ encoding: "utf8",
316
+ stdio: ["ignore", "pipe", "ignore"],
317
+ })
318
+ .split("\n")
319
+ .filter(Boolean);
320
+ if (
321
+ (f.dependsOn ?? []).some((p) =>
322
+ changed.some((c) => c === p || c.startsWith(p.endsWith("/") ? p : p + "/")),
323
+ )
324
+ )
325
+ stale++;
326
+ } catch {
327
+ stale++; // unknown sha — fail-honest, count it stale
328
+ }
329
+ }
330
+ lines.push(
331
+ `[rafa · facts] ${doc.facts.length} session fact(s) banked (.rafa/session-facts.json — spawned agents read it FIRST and cite "SF-n unchanged" instead of re-deriving)` +
332
+ (stale ? ` · ${stale} STALE — re-verify or \`rafa facts prune --stale\`` : ""),
333
+ );
334
+ }
335
+ }
336
+
337
+ // Sage-due bridge (wave 5.5 — mechanical detection): the checkpoint-written
338
+ // loop-event cache vs the learnings ledger, compared LOCALLY (zero platform
339
+ // calls). Sage's trigger was a discretionary end-of-session SOP step and
340
+ // never fired organically — detection now lands front-of-session where
341
+ // reminders get obeyed; only the SPAWN stays with the conductor.
342
+ {
343
+ const cache = readJson(join(rafaDir, "loop-events-tail.json"));
344
+ if (cache?.schemaVersion === 1 && Array.isArray(cache.events)) {
345
+ let ledgerAt = null;
346
+ const ledgerPath = join(root, ".claude", "rafa", "learnings", "ledger.md");
347
+ if (existsSync(ledgerPath)) {
348
+ const m = readFileSync(ledgerPath, "utf8").match(
349
+ /^-\s*Generated:\s*([0-9]{4}-[0-9]{2}-[0-9]{2}(?:[T ][0-9:.Z+-]+)?)/m,
350
+ );
351
+ if (m) {
352
+ const t = Date.parse(m[1]);
353
+ if (Number.isFinite(t)) ledgerAt = t;
354
+ }
355
+ }
356
+ const fresh =
357
+ ledgerAt == null
358
+ ? cache.events
359
+ : cache.events.filter((e) => Number.isFinite(e.at) && e.at > ledgerAt);
360
+ if (fresh.length >= 10)
361
+ lines.push(
362
+ `[rafa · sage] sage due: ${fresh.length} loop events since the last learnings pass` +
363
+ (ledgerAt == null ? " (no ledger yet — sage has never run here)" : "") +
364
+ ` — spawn sage at the next completion boundary (zero-command; this line is the trigger, not a suggestion).`,
365
+ );
366
+ }
367
+ }
368
+
303
369
  // Benchmark liveness (harness-arc: the proof engine is workflow-woven, never a
304
370
  // dev-typed command) — a scanned brain with no MEASURED benchmark is a gap the
305
371
  // scan SOP's Prove-it step closes. Absence sensor only; never a nag mid-flow.