ruvnet-brain 4.0.1 → 4.0.2

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 (185) hide show
  1. package/.claude-plugin/marketplace.json +1 -0
  2. package/README.md +4 -4
  3. package/bin/install.mjs +100 -5
  4. package/console/CONTRACT.md +172 -0
  5. package/console/activity.js +753 -0
  6. package/console/app.js +4189 -0
  7. package/console/architecture.html +1221 -0
  8. package/console/assets/depth-1.webp +0 -0
  9. package/console/assets/depth-2.webp +0 -0
  10. package/console/assets/depth-3.webp +0 -0
  11. package/console/assets/harness-vs-plain.svg +259 -0
  12. package/console/assets/hero.webp +0 -0
  13. package/console/assets/memory.webp +0 -0
  14. package/console/assets/metaharness.svg +247 -0
  15. package/console/index.html +777 -0
  16. package/console/install-architecture.html +162 -0
  17. package/console/install-mockup.html +543 -0
  18. package/console/style.css +2144 -0
  19. package/console/tips.css +926 -0
  20. package/console/tips.html +858 -0
  21. package/console/tips.js +128 -0
  22. package/docs/RELEASE-NOTES-4.0.md +88 -0
  23. package/kb/model-requirements.mjs +37 -6
  24. package/keys/ruvnet-brain-signing.pub.pem +3 -0
  25. package/package.json +8 -22
  26. package/plugin/.claude-plugin/marketplace.json +1 -0
  27. package/plugin/.claude-plugin/plugin.json +2 -3
  28. package/plugin/.codex-plugin/plugin.json +1 -1
  29. package/plugin/commands/brain-console.md +2 -2
  30. package/plugin/commands/configure.md +3 -2
  31. package/plugin/commands/rvbc.md +4 -3
  32. package/plugin/commands/rvcb.md +2 -2
  33. package/plugin/hooks/hooks.json +1 -2
  34. package/plugin/mcp/managed-cli-interface.mjs +47 -4
  35. package/plugin/mcp/server.mjs +21 -0
  36. package/plugin/scripts/detach.mjs +14 -0
  37. package/plugin/scripts/first-session-worker.mjs +38 -0
  38. package/plugin/scripts/ground-ruvnet.sh +16 -6
  39. package/plugin/scripts/hook-shim.mjs +7 -7
  40. package/plugin/scripts/learn-capture.sh +22 -3
  41. package/plugin/scripts/learn-flush.mjs +21 -4
  42. package/plugin/scripts/runtime-preferences.mjs +269 -0
  43. package/plugin/scripts/session-start-core.mjs +477 -0
  44. package/plugin/scripts/session-start.sh +3 -858
  45. package/plugin/skills/brain-console/SKILL.md +4 -2
  46. package/plugin/skills/release-proof/SKILL.md +81 -0
  47. package/plugin/skills/release-proof/agents/openai.yaml +4 -0
  48. package/plugin/skills/release-proof/references/receipt-contract.md +38 -0
  49. package/plugin/skills/release-proof/scripts/release-proof.mjs +210 -0
  50. package/plugin/skills/ruvnet-brain/PLAYBOOK.md +5 -1
  51. package/plugin/skills/rvbc/SKILL.md +9 -6
  52. package/scripts/adr-backfill.mjs +107 -0
  53. package/scripts/advocacy-outcomes.mjs +808 -0
  54. package/scripts/agentdb-context.mjs +216 -0
  55. package/scripts/agentdb-fleet-doctor.mjs +101 -0
  56. package/scripts/ascii-drift.mjs +236 -0
  57. package/scripts/behavioral-l1-l4.mjs +210 -0
  58. package/scripts/brain-capability-check.mjs +72 -0
  59. package/scripts/brain-grade-groundtruth.mjs +100 -0
  60. package/scripts/brain-latency-50.mjs +227 -0
  61. package/scripts/brain-novice-50.mjs +189 -0
  62. package/scripts/brain-stamp.mjs +94 -0
  63. package/scripts/brain-state.mjs +212 -0
  64. package/scripts/build-bundle.mjs +522 -0
  65. package/scripts/build-concepts.mjs +132 -0
  66. package/scripts/build-l2.mjs +71 -0
  67. package/scripts/build-primer.mjs +73 -0
  68. package/scripts/build-symbols.mjs +68 -0
  69. package/scripts/calibrate-router.mjs +97 -0
  70. package/scripts/capability-audit.mjs +321 -0
  71. package/scripts/capability-registry.mjs +876 -0
  72. package/scripts/check-indexation.mjs +108 -0
  73. package/scripts/check-legibility.mjs +189 -0
  74. package/scripts/ci/build-fixture-kb.mjs +67 -0
  75. package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
  76. package/scripts/ci/learning-replay-recorder.mjs +59 -0
  77. package/scripts/ci/mutate-hook-timeout.mjs +70 -0
  78. package/scripts/ci/stranger-fixture-stage.mjs +17 -0
  79. package/scripts/ci/stranger-scenario.mjs +228 -0
  80. package/scripts/ci/stranger-timeout.mjs +25 -0
  81. package/scripts/ci-verdict.mjs +29 -0
  82. package/scripts/claims-verify.mjs +710 -0
  83. package/scripts/clear-claude-tmp.sh +31 -0
  84. package/scripts/console-engine.mjs +434 -0
  85. package/scripts/console-engine.test.mjs +125 -0
  86. package/scripts/corpus-qa.mjs +250 -0
  87. package/scripts/correction-detect-embed.mjs +346 -0
  88. package/scripts/correction-detect-measure.mjs +270 -0
  89. package/scripts/correction-detect.mjs +686 -0
  90. package/scripts/count-chunks.mjs +54 -0
  91. package/scripts/described-questions.json +30 -0
  92. package/scripts/design-grade.mjs +58 -0
  93. package/scripts/dev-plugin-link.sh +105 -0
  94. package/scripts/distill-project.mjs +200 -0
  95. package/scripts/doc-currency.mjs +801 -0
  96. package/scripts/eval-brain.mjs +244 -0
  97. package/scripts/fix-metaharness-memretrieve.mjs +121 -0
  98. package/scripts/full-hints.mjs +87 -0
  99. package/scripts/gate.sh +39 -0
  100. package/scripts/gates.mjs +146 -0
  101. package/scripts/gen-console-images.mjs +54 -0
  102. package/scripts/gen-images.mjs +47 -0
  103. package/scripts/git-clone-refresh.mjs +52 -0
  104. package/scripts/git-hooks/pre-push +126 -0
  105. package/scripts/goal-match.mjs +398 -0
  106. package/scripts/goldie-research.mjs +223 -0
  107. package/scripts/goldie-weekly.sh +67 -0
  108. package/scripts/health-repair.mjs +250 -0
  109. package/scripts/helix-scenario-questions.json +10 -0
  110. package/scripts/ingest-gists.mjs +230 -0
  111. package/scripts/ingest-meeting.mjs +115 -0
  112. package/scripts/ingest-repo.mjs +79 -0
  113. package/scripts/install-npx-witness.sh +49 -0
  114. package/scripts/issue-fix.mjs +639 -0
  115. package/scripts/issue-watch.mjs +276 -0
  116. package/scripts/issue4-close-note.md +31 -0
  117. package/scripts/key-canary.mjs +91 -0
  118. package/scripts/latency-to-surface.mjs +233 -0
  119. package/scripts/learning-enable.mjs +380 -0
  120. package/scripts/learning-replay.mjs +1570 -0
  121. package/scripts/learnings.mjs +62 -0
  122. package/scripts/lesson-gate.mjs +680 -0
  123. package/scripts/lesson-lifecycle.mjs +449 -0
  124. package/scripts/lesson-promote.mjs +262 -0
  125. package/scripts/lesson-ratify.mjs +98 -0
  126. package/scripts/lesson-seed.mjs +252 -0
  127. package/scripts/lesson-store.mjs +447 -0
  128. package/scripts/loop-checkpoint.mjs +86 -0
  129. package/scripts/memdb-health.sh +14 -0
  130. package/scripts/memory-doctor.mjs +271 -0
  131. package/scripts/model-catalog.mjs +79 -0
  132. package/scripts/nightly-controller.mjs +66 -0
  133. package/scripts/nightly-gists.sh +72 -0
  134. package/scripts/nightly-wrapper.sh +180 -0
  135. package/scripts/notify.sh +12 -0
  136. package/scripts/npx-witness.sh +56 -0
  137. package/scripts/onboarding-console.mjs +2749 -0
  138. package/scripts/private-fence.mjs +69 -0
  139. package/scripts/proactivity-metrics.mjs +118 -0
  140. package/scripts/proof-questions.json +56 -0
  141. package/scripts/prove.mjs +95 -0
  142. package/scripts/proxy/claude-proxied.sh +57 -0
  143. package/scripts/proxy/proxy-revert.sh +59 -0
  144. package/scripts/proxy/proxy-up.sh +60 -0
  145. package/scripts/proxy/proxy-verify.mjs +142 -0
  146. package/scripts/published-surface-probe.mjs +241 -0
  147. package/scripts/qe/card-lane-gate.mjs +162 -0
  148. package/scripts/qe/session-start-gate.mjs +229 -0
  149. package/scripts/qe/ux-suite.mjs +323 -0
  150. package/scripts/reconcile-project.mjs +0 -0
  151. package/scripts/record-lesson.mjs +113 -0
  152. package/scripts/refresh-model-catalog.mjs +99 -0
  153. package/scripts/release-proof.mjs +9 -0
  154. package/scripts/release-vector.mjs +281 -0
  155. package/scripts/release.mjs +395 -0
  156. package/scripts/remedy-registry.mjs +247 -0
  157. package/scripts/rerank-cap-eval.mjs +265 -0
  158. package/scripts/rerank-cap-warm-ab.mjs +129 -0
  159. package/scripts/route-cheap.mjs +20 -15
  160. package/scripts/router-utilization.mjs +182 -0
  161. package/scripts/routing-flywheel.mjs +596 -0
  162. package/scripts/rvf-generation.mjs +104 -0
  163. package/scripts/rvf-index-audit.mjs +138 -0
  164. package/scripts/self-update.mjs +508 -0
  165. package/scripts/selfcheck.mjs +7 -1
  166. package/scripts/sign-bundle.mjs +69 -0
  167. package/scripts/signal-watch.mjs +171 -0
  168. package/scripts/stack-sync.mjs +469 -0
  169. package/scripts/stamp-existing-rvf-generations.mjs +53 -0
  170. package/scripts/stamp-sweep.mjs +144 -0
  171. package/scripts/status-honesty.mjs +102 -0
  172. package/scripts/sync-version.mjs +217 -0
  173. package/scripts/token-report.mjs +102 -0
  174. package/scripts/top100-benchmark.mjs +479 -0
  175. package/scripts/top100-corpus.mjs +112 -0
  176. package/scripts/top100-semantic-assertions.mjs +449 -0
  177. package/scripts/update-apply.mjs +9 -0
  178. package/scripts/upgrade-notice.mjs +14 -0
  179. package/scripts/verify-bundle.mjs +51 -0
  180. package/scripts/verify-channels.mjs +184 -0
  181. package/scripts/verify-model-catalog.mjs +104 -0
  182. package/scripts/verify-nightly-close-issue4.sh +31 -0
  183. package/scripts/version.mjs +40 -0
  184. package/scripts/wired-check.mjs +864 -0
  185. package/plugin/scripts/finalize-token-meter.mjs +0 -25
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
+ "description": "Source-grounded RuvNet Brain integrations for Claude Code and Codex.",
3
4
  "owner": {
4
5
  "name": "Stuart Kerr"
5
6
  },
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # 🧠 RuvNet Brain
6
6
 
7
- ### 🧠 RuvNet Brain — [![RuvNet Brain version 4.0.1 — updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.0.1-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
7
+ ### 🧠 RuvNet Brain — [![RuvNet Brain version 4.0.2 — updated 2026-07-30 03:24 EDT](https://img.shields.io/badge/version_4.0.2-updated_2026--07--30_03:24_EDT-1E90FF?style=for-the-badge&labelColor=0757BA)](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
8
8
 
9
9
  **A portable, source-grounded brain over Reuven Cohen's (rUv's) RuvNet stack — delivered as a Claude Code plugin that makes Claude _use_ the stack instead of fighting it.**
10
10
 
@@ -30,7 +30,7 @@
30
30
  [![explainer](https://img.shields.io/badge/▶%20see%20it%20live-isovision.ai%2Fruvnet--brain-e8a13a?style=flat-square)](https://isovision.ai/ruvnet-brain/)
31
31
  [![license](https://img.shields.io/badge/license-MIT-8ecae6?style=flat-square)](LICENSE)
32
32
  [![grounded](https://img.shields.io/badge/answers-cited%20rUv%20source-333?style=flat-square)](#testing--proof)
33
- [![coverage](https://img.shields.io/badge/coverage-32%25%20of%20ALL%20source%20·%20honest-b58900?style=flat-square)](#testing--proof)
33
+ [![coverage](https://img.shields.io/badge/coverage-34%25%20of%20ALL%20source%20·%20honest-b58900?style=flat-square)](#testing--proof)
34
34
 
35
35
  > **One Brain generation everywhere.** npm, the GitHub tag/release, bundle manifests, source metadata, and checksum-bound RVF generations must share the same product version. Headline claims are regenerated and checked by the claims ledger (`scripts/claims-verify.mjs`); other numbers below are hand-stamped and dated:
36
36
  > - **`plugin`** (badge above) — the Claude Code plugin itself: SKILL.md, the grounding hooks, the MCP server. Read live from [`plugin/.claude-plugin/plugin.json`](plugin/.claude-plugin/plugin.json). Updates often — this is where behavior fixes land.
@@ -485,7 +485,7 @@ node plugin/test/run-tests.mjs # full plugin QA over real JSO
485
485
  | **L4 "orchestrate"** | **downgraded — measures speech, not obedience** | L4 asserts the hook's own injected prose contains required words (`must: ['take the wheel','SPARC','swarm',…]`). That proves **the brain spoke**. It cannot fail when the advice is read and ignored — which is the failure this product exists to prevent. Counterfactual replay against a brain-off control (ADR-058 §D4) is what will earn this row back |
486
486
  | **Plugin QA** | **60 / 60** | manifests, hook firing, MCP `initialize`/`tools/list`, capability battery |
487
487
  | **Clean-room install** | **3 / 3** | download the published bundle fresh → unzip → query → grounded, cited answers |
488
- | **Unit tests** | **2,327 passing, 169 todo** · 32% of ALL source covered | `npm run test:cov` regenerates both — the coverage floor fails CI if it slips (`claims:verify` re-derives the %, it is not a hand-typed badge). 32% is the honest number over every shipped file; the previous "75%" measured a hand-picked 8-file subset |
488
+ | **Unit tests** | **2,327 passing, 169 todo** · 34% of ALL source covered | `npm run test:cov` regenerates both — the coverage floor fails CI if it slips (`claims:verify` re-derives the %, it is not a hand-typed badge). 34% is the honest number over every shipped file; the previous "75%" measured a hand-picked 8-file subset |
489
489
  | **Grounding proof** | `npx ruvnet-brain --doctor` | asks a real question, then checks the cited path really exists in the on-disk store; a citation that doesn't resolve is reported as **NOT grounded** |
490
490
  | **Held-out eval** | **grounded 100/100** · routed 63/80 | `npm run eval` — 120 frozen, hash-pinned questions across 5 strata, never used for tuning, graded on ground truth, never by a model |
491
491
 
@@ -522,7 +522,7 @@ node forge-ask-all.mjs --dir . --q "How does RuVector implement HNSW vector sear
522
522
 
523
523
  This project versions in the open (see the live badge up top for the exact plugin version; the downloadable knowledge bundle is a separate track) — we don't claim “done,” “complete,” or “zero hallucinations.” Where it stands:
524
524
 
525
- - ✅ **The grounding brain is real and proven** — 62 public stores · 151,033 public source chunks (69 built stores incl. private), dual embeddings, cross-encoder rerank, plugin (MCP tool + enforcement hook + skill), all re-runnable.
525
+ - ✅ **The grounding brain is real and proven** — 63 public stores · 153,369 public source chunks (70 built stores incl. private), dual embeddings, cross-encoder rerank, plugin (MCP tool + enforcement hook + skill), all re-runnable.
526
526
  - ✅ **Code-level depth** — the code-rich repos are indexed to full function bodies; “how is it implemented?” returns the implementation. Verified in the shipped bundle (clean-room 3/3).
527
527
  - ✅ **Routing holds** — named 47/48, described 26/28, scenario 7/8; behavioral L1–L3 all pass (**L4 downgraded — it measures that the brain spoke, not that anything listened**); private stores fenced out of the public bundle (zero-leak verified).
528
528
  - ⚠️ **Two routing residuals** (above) — surfaced, not hidden.
package/bin/install.mjs CHANGED
@@ -95,6 +95,7 @@ const FLAG_ENABLE_SPEND_GUARD = argv.includes('--enable-spend-guard');
95
95
  const FLAG_DISABLE_SPEND_GUARD = argv.includes('--disable-spend-guard'); // the missing undo
96
96
  const FLAG_UNINSTALL = argv.includes('--uninstall'); // reverse everything, in one command
97
97
  const FLAG_WHAT_CHANGED = argv.includes('--what-changed'); // show our footprint on this machine
98
+ const FLAG_WHATS_NEW = argv.includes('--whats-new'); // show curated major-release highlights
98
99
  // ── onboarding-experience flags (all optional; every offer is safe to decline) ──
99
100
  const FLAG_YES = argv.includes('--yes') || argv.includes('-y'); // accept every optional offer non-interactively
100
101
  const FLAG_PLAN = argv.includes('--plan') || argv.includes('--dry-run'); // show the interactive checklist, then exit — install NOTHING
@@ -598,6 +599,50 @@ function installReader(cacheDir) {
598
599
  ok('reader installed');
599
600
  }
600
601
 
602
+ // The Console must survive the temporary npm/npx extraction directory. Before 4.0.2 its skills
603
+ // searched a developer checkout, so `/rvbc` worked for maintainers and failed for clean users even
604
+ // though installation was green. Persist the exact runtime shipped by the installer underneath the
605
+ // installed KB: Node then resolves optional reader dependencies from `<cacheDir>/node_modules`, and
606
+ // both Claude Code and Codex get one stable path with no source clone or second package install.
607
+ export function installConsoleRuntime(cacheDir, sourceRoot = REPO_ROOT) {
608
+ const runtime = path.join(cacheDir, '.console-runtime');
609
+ const staged = `${runtime}.tmp-${process.pid}`;
610
+ const prior = `${runtime}.prior-${process.pid}`;
611
+ fs.rmSync(staged, { recursive: true, force: true });
612
+ fs.rmSync(prior, { recursive: true, force: true });
613
+ fs.mkdirSync(staged, { recursive: true, mode: 0o700 });
614
+
615
+ const required = [
616
+ ['console', 'console'],
617
+ ['scripts', 'scripts'],
618
+ ['plugin/scripts', 'plugin/scripts'],
619
+ ['kb/brain-profile.mjs', 'kb/brain-profile.mjs'],
620
+ ['bin/install.mjs', 'bin/install.mjs'],
621
+ ['package.json', 'package.json'],
622
+ ];
623
+ for (const [from, to] of required) {
624
+ const source = path.join(sourceRoot, from);
625
+ if (!fs.existsSync(source)) {
626
+ fs.rmSync(staged, { recursive: true, force: true });
627
+ throw new Error(`console runtime is incomplete: missing ${from}`);
628
+ }
629
+ const target = path.join(staged, to);
630
+ fs.mkdirSync(path.dirname(target), { recursive: true });
631
+ fs.cpSync(source, target, { recursive: true, force: true, preserveTimestamps: true });
632
+ }
633
+
634
+ if (fs.existsSync(runtime)) fs.renameSync(runtime, prior);
635
+ try {
636
+ fs.renameSync(staged, runtime);
637
+ fs.rmSync(prior, { recursive: true, force: true });
638
+ } catch (error) {
639
+ if (fs.existsSync(prior) && !fs.existsSync(runtime)) fs.renameSync(prior, runtime);
640
+ fs.rmSync(staged, { recursive: true, force: true });
641
+ throw error;
642
+ }
643
+ return path.join(runtime, 'scripts', 'onboarding-console.mjs');
644
+ }
645
+
601
646
  // ── plugin presence: the ONLY reliable proof the slash commands will exist ───────────────────────
602
647
  // Reported by a user on 3.4.21-dev whose install was otherwise healthy: `/rvbc` returned
603
648
  // "Unknown command: /rvbc. Did you mean /rvf?". search_ruvnet worked, the KB was current — the
@@ -856,6 +901,7 @@ export function wireCodexHost({
856
901
  serverDir = codexServerDir(),
857
902
  source = path.join(__dirname, '..', 'plugin', 'mcp', 'server.mjs'),
858
903
  hookWrapperSource = path.join(__dirname, '..', 'plugin', 'scripts', 'codex-hook-wrapper.mjs'),
904
+ runtimePreferencesSource = path.join(__dirname, '..', 'plugin', 'scripts', 'runtime-preferences.mjs'),
859
905
  hookWrapperPath = codexHookWrapperPath(codexDir),
860
906
  announce = true,
861
907
  } = {}) {
@@ -876,14 +922,21 @@ export function wireCodexHost({
876
922
  if (announce) warn('MCP structured-interface module missing from this bundle — Codex left untouched (non-fatal)');
877
923
  return { host: true, action: 'no-source' };
878
924
  }
925
+ if (!fs.existsSync(runtimePreferencesSource)) {
926
+ if (announce) warn('MCP runtime-preferences module missing from this bundle — Codex left untouched (non-fatal)');
927
+ return { host: true, action: 'no-source' };
928
+ }
879
929
  const serverPath = path.join(serverDir, 'server.mjs');
880
930
  const managedCliPath = path.join(serverDir, 'managed-cli-interface.mjs');
931
+ const runtimePreferencesPath = path.join(path.dirname(serverDir), 'scripts', 'runtime-preferences.mjs');
881
932
  fs.mkdirSync(serverDir, { recursive: true });
882
933
  // Write-beside-then-rename, both here and for the config below (issue #43): an interrupted plain
883
934
  // copy leaves a TORN server.mjs at the exact path a prior install's config already points at, so
884
935
  // Codex spawns half a file. rename() over the target is atomic; a failure leaves the old bytes.
885
936
  // Copy the dependency first. If the later server swap fails, the previously registered server
886
937
  // remains byte-intact and continues to import a backward-compatible module at the same path.
938
+ fs.mkdirSync(path.dirname(runtimePreferencesPath), { recursive: true });
939
+ atomicReplace(runtimePreferencesPath, (tmp) => fs.copyFileSync(runtimePreferencesSource, tmp));
887
940
  atomicReplace(managedCliPath, (tmp) => fs.copyFileSync(managedCliSource, tmp));
888
941
  atomicReplace(serverPath, (tmp) => fs.copyFileSync(source, tmp));
889
942
  if (fs.existsSync(hookWrapperSource)) {
@@ -899,7 +952,7 @@ export function wireCodexHost({
899
952
  ok('Codex already declares ruvnet-brain in your own config — left exactly as you wrote it');
900
953
  info(` to hand it to us instead, delete that ${c.bold('[mcp_servers.ruvnet-brain]')} block and re-run this installer`);
901
954
  }
902
- return { host: true, action, serverPath, managedCliPath, hookWrapperPath };
955
+ return { host: true, action, serverPath, managedCliPath, runtimePreferencesPath, hookWrapperPath };
903
956
  }
904
957
  if (text !== before) {
905
958
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
@@ -910,7 +963,7 @@ export function wireCodexHost({
910
963
  info(` server: ${serverPath} ${c.dim('(persistent copy — the npx dir vanishes)')}`);
911
964
  info(` ${c.dim('only our marked block is written; every other section is byte-preserved')}`);
912
965
  }
913
- return { host: true, action, serverPath, managedCliPath, hookWrapperPath, changed: text !== before };
966
+ return { host: true, action, serverPath, managedCliPath, runtimePreferencesPath, hookWrapperPath, changed: text !== before };
914
967
  }
915
968
 
916
969
  const CODEX_PLUGIN_ID = 'ruvnet-brain@ruvnet-brain';
@@ -1553,7 +1606,9 @@ async function doctor() {
1553
1606
  if (env.ruflo) {
1554
1607
  rufloOperational = probeRufloOperationalHealth();
1555
1608
  if (rufloOperational.healthy) {
1556
- ok('Ruflo operational — runtime, memory, and learning signals agree');
1609
+ ok(rufloOperational.directMode
1610
+ ? 'Ruflo direct mode ready — daemon/swarm is stopped by design; AgentDB remains CLI-backed'
1611
+ : 'Ruflo operational — runtime, memory, and learning signals agree');
1557
1612
  } else {
1558
1613
  warn('Ruflo CLI is present, but operational learning is DEGRADED — availability is not enforcement.');
1559
1614
  if (rufloOperational.stopped) warn(' live status reports STOPPED / swarm not running');
@@ -2421,6 +2476,18 @@ function printFootprint({ heading = 'What this put on your machine' } = {}) {
2421
2476
  return items;
2422
2477
  }
2423
2478
 
2479
+ function showWhatsNew() {
2480
+ const notes = path.join(REPO_ROOT, 'docs', 'RELEASE-NOTES-4.0.md');
2481
+ if (!fs.existsSync(notes)) {
2482
+ console.error('RuvNet Brain release notes are missing from this artifact.');
2483
+ process.exitCode = 1;
2484
+ return;
2485
+ }
2486
+ const text = fs.readFileSync(notes, 'utf8');
2487
+ process.stdout.write(text);
2488
+ if (!text.endsWith('\n')) process.stdout.write('\n');
2489
+ }
2490
+
2424
2491
  /**
2425
2492
  * Surgically remove ONLY our block from CLAUDE.md, leaving every other line exactly as it was.
2426
2493
  * Backed up and written atomically, same as when it was added — taking something away is at least
@@ -3070,8 +3137,15 @@ export function classifyRufloOperationalHealth({ status = '', memory = '', metri
3070
3137
  && /Total Routes\s*[|:]?\s*0\b/i.test(metrics)
3071
3138
  && /Total Executed\s*[|:]?\s*0\b/i.test(metrics);
3072
3139
  const memoryContradiction = statusSaysNoMemory && memoryEntries > 0;
3140
+ // Brain 4 is explicitly zero-daemon: `ruflo memory store/search` open the per-project
3141
+ // AgentDB directly. A stopped orchestration daemon therefore makes daemon-owned status
3142
+ // and metrics non-authoritative; treating that intentional state as degraded made Doctor
3143
+ // exit 1 immediately after proving source-grounded retrieval worked. Keep the strict
3144
+ // contradiction/learning checks when a daemon is actually running.
3145
+ const directMode = stopped;
3073
3146
  return {
3074
- healthy: !stopped && !memoryContradiction && !zeroLearning,
3147
+ healthy: directMode || (!memoryContradiction && !zeroLearning),
3148
+ directMode,
3075
3149
  stopped,
3076
3150
  zeroLearning,
3077
3151
  memoryContradiction,
@@ -3653,6 +3727,7 @@ Usage:
3653
3727
  npx ruvnet-brain --disable-nightly Remove the nightly schedule (safe to run any time)
3654
3728
  npx ruvnet-brain --what-changed Show exactly what RuvNet Brain has put on this machine,
3655
3729
  with the undo command for each piece
3730
+ npx ruvnet-brain --whats-new Show the curated v3 → v4 capabilities and honest limits
3656
3731
  npx ruvnet-brain --uninstall Remove all of it (bundle, LaunchAgents, and our CLAUDE.md
3657
3732
  block only — your own CLAUDE.md content is preserved and backed up)
3658
3733
  npx ruvnet-brain --enable-spend-guard Install the hourly runaway-agent spend alarm (alert-only)
@@ -3692,7 +3767,17 @@ It is safe to re-run at any time. After installing, restart Claude Code so the g
3692
3767
 
3693
3768
  // ── main ─────────────────────────────────────────────────────────────────────────────────────────
3694
3769
  (async () => {
3695
- if (IMPORT_ONLY) return; // imported for its exports (tests) — never run the installer as a side effect
3770
+ // Importing an executable module for its exported policy helpers must never install software.
3771
+ // The old contract required every caller to remember RUVNET_BRAIN_IMPORT_ONLY=1; one missed flag
3772
+ // in a unit test ran a real install against the maintainer's HOME. Direct argv identity is the
3773
+ // authority boundary. The old IMPORT_ONLY flag may still be present in a long-lived test worker;
3774
+ // it must never suppress a genuinely direct CLI execution.
3775
+ const canonical = (value) => {
3776
+ try { return fs.realpathSync(value); } catch { return path.resolve(value); }
3777
+ };
3778
+ const invokedDirectly = Boolean(process.argv[1])
3779
+ && canonical(process.argv[1]) === canonical(fileURLToPath(import.meta.url));
3780
+ if (!invokedDirectly) return;
3696
3781
  if (FLAG_HELP) return showHelp();
3697
3782
  // `process.exitCode`, not `return` — doctor()'s verdict is the whole point of running it in a
3698
3783
  // script. A bare `return await doctor()` discarded the number, which is how "! Needs attention"
@@ -3711,6 +3796,7 @@ It is safe to re-run at any time. After installing, restart Claude Code so the g
3711
3796
  if (FLAG_DISABLE_SPEND_GUARD) { disableSpendGuard(); return; }
3712
3797
  if (FLAG_UNINSTALL) { uninstallAll(); return; }
3713
3798
  if (FLAG_WHAT_CHANGED) { printBanner('what RuvNet Brain put on this machine'); printFootprint(); return; }
3799
+ if (FLAG_WHATS_NEW) { showWhatsNew(); return; }
3714
3800
 
3715
3801
  printBanner('installer');
3716
3802
  console.log(c.dim("I'll set up the brain for Claude Code and Codex, explaining each step as I go.\n"));
@@ -3856,6 +3942,15 @@ It is safe to re-run at any time. After installing, restart Claude Code so the g
3856
3942
  }
3857
3943
 
3858
3944
  installReader(cacheDir);
3945
+ try {
3946
+ const consoleEntry = installConsoleRuntime(cacheDir);
3947
+ ok(`Brain Console installed at ${consoleEntry}`);
3948
+ } catch (error) {
3949
+ die(
3950
+ `the Brain Console runtime could not be installed (${error.message})`,
3951
+ `The knowledge base is present, but /rvbc would be broken. Re-run the installer from a complete package.`,
3952
+ );
3953
+ }
3859
3954
  const plugin = wirePlugin();
3860
3955
  // Codex hosts got nothing before this (issue #42): shipped, never registered. Non-fatal like every
3861
3956
  // other wiring step — a second host we cannot reach must never break the one we can.
@@ -0,0 +1,172 @@
1
+ # Onboarding Console — API + data contract (v1)
2
+
3
+ Updated: 2026-07-17
4
+ Created: 2026-07-15
5
+
6
+ The single source of truth both the backend (`scripts/onboarding-console.mjs`) and the
7
+ frontend (`console/index.html` + `app.js` + `style.css`) build against. Implements ADR-0013
8
+ and DDD-0002. **The ordering is the design: Mirror → Explain → Recommend → (consent) Apply → Undo.**
9
+
10
+ The server binds `127.0.0.1` only and mints a random `token` per launch. Every mutating request
11
+ must echo the token. The page receives the token inlined at render time (`window.__CONSOLE_TOKEN__`).
12
+ A GET with a wrong/absent token still serves read-only state; a POST with a wrong token is `403`.
13
+
14
+ ---
15
+
16
+ ## GET `/api/state` — fast sections (no network)
17
+
18
+ Returns in well under a second. `memory.fleet` is **`null` here by design** — scanning every memory
19
+ store on the machine costs ~90ms each and a real machine has 100+, which is far too slow to sit in
20
+ front of the page's first paint. The fleet arrives separately from `GET /api/memory`.
21
+
22
+ `GET /api/state?fast=1` replays the last good gather from disk (~3ms) so a repeat load paints
23
+ instantly, then the live call replaces it. The cached copy never contains `token` — that is minted
24
+ per server run and spliced in on read.
25
+
26
+ ```jsonc
27
+ {
28
+ "token": "…",
29
+ "generatedAt": "2026-07-14T…Z",
30
+ "host": { "user": "stuartkerr", "platform": "darwin", "node": "v22…", "npmPrefix": "~/.npm-global" },
31
+ "sections": {
32
+ "wiring": {
33
+ "summary": { "npx": 190, "global": 12, "mcp": 6, "plugin": 5, "projectsWithNpx": 16 },
34
+ "sites": [
35
+ { "scope": "project", "project": "ruvnet-brain", "file": ".claude/settings.json",
36
+ "event": "PreToolUse", "matcher": "Bash", "spec": "npx @claude-flow/cli@latest hooks …",
37
+ "mechanism": "NPX" }
38
+ ]
39
+ },
40
+ "memory": {
41
+ "fleet": [
42
+ { "name": "ruvnet-brain", "total": 1023, "embedded": 1021, "coverPct": 99.8,
43
+ "patterns": 456, "learns": true, "findings": [] }
44
+ ],
45
+ "health": {
46
+ "project": "ruvnet-brain", "score": 92, "summary": "learns; recall-quality not probed",
47
+ "dimensions": [
48
+ { "key": "liveness", "label": "Liveness", "status": "ok",
49
+ "detail": "store→search round-trip works on the live path", "deduction": 0 },
50
+ { "key": "coverage", "label": "Coverage", "status": "ok", "detail": "checkpoint present, <1d old", "deduction": 0 },
51
+ { "key": "recallQuality", "label": "Recall quality", "status": "notTested",
52
+ "detail": "no embedding round-trip run this session", "deduction": 0 },
53
+ { "key": "compactionSurvival", "label": "Compaction survival", "status": "ok",
54
+ "detail": "PreCompact snapshot present", "deduction": 0 },
55
+ { "key": "sessionSurfacing", "label": "Session surfacing", "status": "ok",
56
+ "detail": "SessionStart hook surfaces state", "deduction": 0 }
57
+ ],
58
+ "notTested": ["recallQuality"]
59
+ }
60
+ },
61
+ "savings": {
62
+ "totals": { "count": 3, "usdSaved": 0.42, "msSaved": 18400 }, // null ⇒ render "nothing measured yet"
63
+ "note": "receipts only — no modelled or projected savings",
64
+ "receipts": [
65
+ { "at": "2026-07-13T…Z", "capability": "model-routing", "task": "…",
66
+ "chosenTier": "haiku", "baselineTier": "opus", "measuredMs": 4200, "measuredUsd": 0.14 }
67
+ ]
68
+ },
69
+ "config": {
70
+ "path": "~/.claude/ruvnet-brain/config.json",
71
+ "exists": true,
72
+ "values": { "openrouterKey": true, "nightly": true, "routing": "auto", "qeFleet": false },
73
+ "schema": [
74
+ { "key": "openrouterKey", "label": "OpenRouter API key", "type": "secret",
75
+ "help": "Unlocks cheap-model routing + the self-improvement loop", "secret": true },
76
+ { "key": "nightly", "label": "Nightly brain refresh", "type": "bool",
77
+ "help": "Rebuild the KB from pinned SHAs overnight" },
78
+ { "key": "routing", "label": "Token-smart routing", "type": "enum",
79
+ "options": ["auto", "off"], "help": "Route cheap tasks to smaller models" },
80
+ { "key": "qeFleet", "label": "On-demand QE fleet", "type": "bool",
81
+ "help": "Agentic-QE test fleet, spun up on request" }
82
+ ]
83
+ },
84
+ "recommendations": [ /* Recommendation[] — non-stack, e.g. de-npx a project */ ]
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## GET `/api/memory` — the across-your-projects fleet scan (slow, ~7–10s)
90
+
91
+ ```jsonc
92
+ { "fleet": [ { "name": "ruvnet-brain", "total": 1023, "embedded": 1021, "coverPct": 99.8, … } ] }
93
+ ```
94
+
95
+ Split out of `/api/state` so it cannot block first paint. The page renders memory health immediately
96
+ and merges this list into the same card once it lands.
97
+
98
+ ## GET `/api/stack` — the network audit (slow, ~5–20s; page loads a skeleton first)
99
+
100
+ ```jsonc
101
+ {
102
+ "packages": [
103
+ { "name": "ruflo", "installed": "3.30.2", "target": "3.30.2", "tag": "alpha", "state": "CURRENT" }
104
+ // state ∈ CURRENT | BEHIND | AHEAD | BROKEN | UNRESOLVED
105
+ ],
106
+ "shadows": [
107
+ { "name": "@ruvector/rvf", "version": "0.1.9", "global": "0.2.3", "dir": "~/.npm/_npx/…", "stale": true }
108
+ ],
109
+ "summary": { "total": 40, "behind": 0, "broken": 1, "ahead": 0, "current": 38, "shadows": 15, "stale": 15 },
110
+ "recommendations": [ /* Recommendation[] — sync BEHIND, purge stale shadows */ ]
111
+ }
112
+ ```
113
+
114
+ ## Recommendation — the aggregate the whole page is built around
115
+
116
+ **A Recommendation CANNOT exist without non-empty `evidence`, a `cost`, and an `undo`.** The
117
+ backend factory throws otherwise (DDD invariant, schema-enforced). The UI must render all four.
118
+
119
+ ```jsonc
120
+ {
121
+ "id": "sync-stack",
122
+ "title": "Sync 1 stale shadow of @ruvector/rvf",
123
+ "rationale": "A second copy in the npx cache preempts your global binary and quietly serves 0.1.9.",
124
+ "severity": "IMPORTANT", // INFO | SUGGESTED | IMPORTANT
125
+ "touchesMachine": true, // ⇐ if true, the UI MUST show plainImpact + require an explicit confirm
126
+ "plainImpact": "This removes an extra, out-of-date copy of a tool sitting in a temporary folder on your computer. Your main copy is newer and stays untouched. Nothing you use will stop working — the temporary copy rebuilds itself automatically the next time it's needed. Fully reversible.",
127
+ "evidence": [ { "observed": "@ruvector/rvf@0.1.9 in ~/.npm/_npx while global is 0.2.3", "source": "stack-sync findShadows" } ],
128
+ "cost": { "time": "~0s", "latency": "none", "usd": 0, "risk": "low" },
129
+ "change": { "kind": "run-script", "human": "purge the stale npx shadow", "cmd": "node scripts/stack-sync.mjs --sync" },
130
+ "undo": { "kind": "restore-dir", "human": "npx re-resolves on next use; backup kept at <dir>.bak-<ts>" }
131
+ }
132
+ ```
133
+
134
+ **`touchesMachine` and `plainImpact` are load-bearing.** `touchesMachine: false` means the action
135
+ only writes RuvNet-Brain's own settings file in your user folder and changes nothing about how your
136
+ computer runs other software (e.g. Save Settings). `touchesMachine: true` means it installs, removes,
137
+ or rewires something the rest of your system uses — those **must** render `plainImpact` (jargon-free,
138
+ says what happens + why it's safe + that it's reversible) and a distinct "this changes your computer"
139
+ confirm step before Apply is allowed. Write `plainImpact` for a smart person who has never heard of npx.
140
+
141
+ ## POST `/api/apply` — the ONLY writer (consent-gated)
142
+
143
+ Request: `{ "token", "ids": ["sync-stack", …], "preStateHash": "…" }`
144
+ Behavior (DDD Change Plan invariants): **re-read the world**, abort with `worldMoved` if `preStateHash`
145
+ no longer matches, **record the inverse first**, back up every mutated file to `<file>.bak-<ts>`, then run.
146
+ Response: `{ "results": [ { "id", "ok", "undoToken", "log" } ] }`
147
+
148
+ ## POST `/api/save-config` — save Settings at user level
149
+
150
+ Request: `{ "token", "values": { … } }` → writes `~/.claude/ruvnet-brain/config.json`
151
+ (backup first, undo recorded). Response: `{ "ok", "backup", "undoToken" }`
152
+
153
+ ## POST `/api/undo` — reverse a prior apply/save
154
+
155
+ Request: `{ "token", "undoToken" }` → Response: `{ "ok" }`
156
+
157
+ ---
158
+
159
+ ## Section order on the page (progressive disclosure — each collapsible, each independently useful)
160
+
161
+ 1. **Your stack** — installed / current / duplicated / broken (from `/api/stack`)
162
+ 2. **How it's wired** — npx vs global, where (from wiring)
163
+ 3. **What we'd suggest** — the Recommendation cards (evidence · cost · undo · Apply/Skip)
164
+ 4. **Is your memory actually working?** — the 0–100 health score with named deductions
165
+ 5. **MetaHarness / Agentic-QE savings** — receipts only, or an honest "nothing measured yet"
166
+ 6. **Settings** — the editable form, one **Save** button, writes at user level
167
+
168
+ ## Design law (from ADR-0013)
169
+ - Read-only by default. Opening the page changes nothing.
170
+ - Existing choices are data, not errors. `AHEAD` is legal. `NPX` is shown with its tradeoff, never labelled "wrong".
171
+ - Every number traces to a receipt or an observation. No "up to 90%". No estimates.
172
+ - Nothing above `SUGGESTED` severity for anything not measured on THIS machine.