ruvnet-brain 4.0.1 → 4.0.4

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 (195) hide show
  1. package/.claude-plugin/marketplace.json +1 -0
  2. package/README.md +4 -4
  3. package/bin/install.mjs +303 -24
  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/commands/whats-new.md +6 -6
  34. package/plugin/docs/RELEASE-NOTES-4.0.md +88 -0
  35. package/plugin/hooks/hooks.json +1 -2
  36. package/plugin/mcp/managed-cli-interface.mjs +47 -4
  37. package/plugin/mcp/server.mjs +90 -32
  38. package/plugin/scripts/detach.mjs +14 -0
  39. package/plugin/scripts/first-session-worker.mjs +38 -0
  40. package/plugin/scripts/ground-ruvnet.sh +16 -6
  41. package/plugin/scripts/hook-shim.mjs +34 -29
  42. package/plugin/scripts/learn-capture.sh +22 -3
  43. package/plugin/scripts/learn-flush.mjs +21 -4
  44. package/plugin/scripts/runtime-preferences.mjs +269 -0
  45. package/plugin/scripts/session-start-core.mjs +503 -0
  46. package/plugin/scripts/session-start.sh +3 -858
  47. package/plugin/scripts/whats-new.mjs +42 -0
  48. package/plugin/skills/brain-console/SKILL.md +4 -2
  49. package/plugin/skills/release-proof/SKILL.md +98 -0
  50. package/plugin/skills/release-proof/agents/openai.yaml +4 -0
  51. package/plugin/skills/release-proof/references/receipt-contract.md +44 -0
  52. package/plugin/skills/release-proof/scripts/release-proof.mjs +286 -0
  53. package/plugin/skills/ruvnet-brain/PLAYBOOK.md +5 -1
  54. package/plugin/skills/ruvnet-brain/SKILL.md +22 -7
  55. package/plugin/skills/rvbc/SKILL.md +9 -6
  56. package/plugin/skills/whats-new/SKILL.md +4 -4
  57. package/scripts/adr-backfill.mjs +107 -0
  58. package/scripts/advocacy-outcomes.mjs +808 -0
  59. package/scripts/agentdb-context.mjs +216 -0
  60. package/scripts/agentdb-fleet-doctor.mjs +101 -0
  61. package/scripts/ascii-drift.mjs +236 -0
  62. package/scripts/behavioral-l1-l4.mjs +210 -0
  63. package/scripts/brain-capability-check.mjs +72 -0
  64. package/scripts/brain-grade-groundtruth.mjs +100 -0
  65. package/scripts/brain-latency-50.mjs +227 -0
  66. package/scripts/brain-novice-50.mjs +189 -0
  67. package/scripts/brain-stamp.mjs +94 -0
  68. package/scripts/brain-state.mjs +212 -0
  69. package/scripts/build-bundle.mjs +531 -0
  70. package/scripts/build-concepts.mjs +132 -0
  71. package/scripts/build-l2.mjs +71 -0
  72. package/scripts/build-primer.mjs +73 -0
  73. package/scripts/build-symbols.mjs +68 -0
  74. package/scripts/calibrate-router.mjs +97 -0
  75. package/scripts/capability-audit.mjs +321 -0
  76. package/scripts/capability-registry.mjs +876 -0
  77. package/scripts/check-indexation.mjs +108 -0
  78. package/scripts/check-legibility.mjs +189 -0
  79. package/scripts/ci/build-fixture-kb.mjs +67 -0
  80. package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
  81. package/scripts/ci/learning-replay-recorder.mjs +59 -0
  82. package/scripts/ci/mutate-hook-timeout.mjs +70 -0
  83. package/scripts/ci/stranger-fixture-stage.mjs +17 -0
  84. package/scripts/ci/stranger-scenario.mjs +228 -0
  85. package/scripts/ci/stranger-timeout.mjs +25 -0
  86. package/scripts/ci-verdict.mjs +29 -0
  87. package/scripts/claims-verify.mjs +710 -0
  88. package/scripts/clear-claude-tmp.sh +31 -0
  89. package/scripts/console-engine.mjs +434 -0
  90. package/scripts/console-engine.test.mjs +125 -0
  91. package/scripts/corpus-qa.mjs +250 -0
  92. package/scripts/correction-detect-embed.mjs +346 -0
  93. package/scripts/correction-detect-measure.mjs +270 -0
  94. package/scripts/correction-detect.mjs +686 -0
  95. package/scripts/count-chunks.mjs +54 -0
  96. package/scripts/described-questions.json +30 -0
  97. package/scripts/design-grade.mjs +58 -0
  98. package/scripts/dev-plugin-link.sh +105 -0
  99. package/scripts/distill-project.mjs +200 -0
  100. package/scripts/doc-currency.mjs +801 -0
  101. package/scripts/eval-brain.mjs +244 -0
  102. package/scripts/fix-metaharness-memretrieve.mjs +121 -0
  103. package/scripts/fix-workstream.mjs +291 -0
  104. package/scripts/full-hints.mjs +87 -0
  105. package/scripts/gate.sh +39 -0
  106. package/scripts/gates.mjs +146 -0
  107. package/scripts/gen-console-images.mjs +54 -0
  108. package/scripts/gen-images.mjs +47 -0
  109. package/scripts/git-clone-refresh.mjs +52 -0
  110. package/scripts/git-hooks/pre-push +126 -0
  111. package/scripts/goal-match.mjs +398 -0
  112. package/scripts/goldie-research.mjs +223 -0
  113. package/scripts/goldie-weekly.sh +67 -0
  114. package/scripts/health-repair.mjs +237 -0
  115. package/scripts/helix-scenario-questions.json +10 -0
  116. package/scripts/ingest-gists.mjs +230 -0
  117. package/scripts/ingest-meeting.mjs +115 -0
  118. package/scripts/ingest-repo.mjs +79 -0
  119. package/scripts/install-npx-witness.sh +49 -0
  120. package/scripts/issue-fix.mjs +558 -0
  121. package/scripts/issue-watch.mjs +276 -0
  122. package/scripts/issue4-close-note.md +31 -0
  123. package/scripts/key-canary.mjs +91 -0
  124. package/scripts/latency-to-surface.mjs +233 -0
  125. package/scripts/learning-enable.mjs +380 -0
  126. package/scripts/learning-replay.mjs +1570 -0
  127. package/scripts/learnings.mjs +62 -0
  128. package/scripts/lesson-gate.mjs +680 -0
  129. package/scripts/lesson-lifecycle.mjs +449 -0
  130. package/scripts/lesson-promote.mjs +262 -0
  131. package/scripts/lesson-ratify.mjs +98 -0
  132. package/scripts/lesson-seed.mjs +252 -0
  133. package/scripts/lesson-store.mjs +447 -0
  134. package/scripts/loop-checkpoint.mjs +86 -0
  135. package/scripts/memdb-health.sh +14 -0
  136. package/scripts/memory-doctor.mjs +326 -0
  137. package/scripts/model-catalog.mjs +79 -0
  138. package/scripts/nightly-controller.mjs +66 -0
  139. package/scripts/nightly-gists.sh +72 -0
  140. package/scripts/nightly-wrapper.sh +172 -0
  141. package/scripts/notify.sh +12 -0
  142. package/scripts/npx-witness.sh +56 -0
  143. package/scripts/onboarding-console.mjs +2922 -0
  144. package/scripts/private-fence.mjs +69 -0
  145. package/scripts/proactivity-metrics.mjs +118 -0
  146. package/scripts/proof-questions.json +56 -0
  147. package/scripts/protected-release-invocation.mjs +76 -0
  148. package/scripts/prove.mjs +95 -0
  149. package/scripts/proxy/claude-proxied.sh +57 -0
  150. package/scripts/proxy/proxy-revert.sh +59 -0
  151. package/scripts/proxy/proxy-up.sh +60 -0
  152. package/scripts/proxy/proxy-verify.mjs +142 -0
  153. package/scripts/publication-receipt.mjs +307 -0
  154. package/scripts/published-surface-probe.mjs +241 -0
  155. package/scripts/qe/card-lane-gate.mjs +162 -0
  156. package/scripts/qe/session-start-gate.mjs +229 -0
  157. package/scripts/qe/ux-suite.mjs +323 -0
  158. package/scripts/reconcile-project.mjs +0 -0
  159. package/scripts/record-lesson.mjs +113 -0
  160. package/scripts/refresh-model-catalog.mjs +99 -0
  161. package/scripts/release-authority.mjs +93 -0
  162. package/scripts/release-proof.mjs +9 -0
  163. package/scripts/release-vector.mjs +281 -0
  164. package/scripts/release.mjs +439 -0
  165. package/scripts/remedy-registry.mjs +247 -0
  166. package/scripts/rerank-cap-eval.mjs +265 -0
  167. package/scripts/rerank-cap-warm-ab.mjs +129 -0
  168. package/scripts/route-cheap.mjs +20 -15
  169. package/scripts/router-utilization.mjs +182 -0
  170. package/scripts/routing-flywheel.mjs +596 -0
  171. package/scripts/rvf-generation.mjs +104 -0
  172. package/scripts/rvf-index-audit.mjs +138 -0
  173. package/scripts/self-update.mjs +296 -0
  174. package/scripts/selfcheck.mjs +7 -1
  175. package/scripts/sign-bundle.mjs +69 -0
  176. package/scripts/signal-watch.mjs +171 -0
  177. package/scripts/stabilization-receipt.mjs +108 -0
  178. package/scripts/stack-sync.mjs +469 -0
  179. package/scripts/stamp-existing-rvf-generations.mjs +53 -0
  180. package/scripts/stamp-sweep.mjs +144 -0
  181. package/scripts/status-honesty.mjs +102 -0
  182. package/scripts/sync-version.mjs +217 -0
  183. package/scripts/token-report.mjs +102 -0
  184. package/scripts/top100-benchmark.mjs +479 -0
  185. package/scripts/top100-corpus.mjs +112 -0
  186. package/scripts/top100-semantic-assertions.mjs +449 -0
  187. package/scripts/update-apply.mjs +9 -0
  188. package/scripts/upgrade-notice.mjs +14 -0
  189. package/scripts/verify-bundle.mjs +51 -0
  190. package/scripts/verify-channels.mjs +184 -0
  191. package/scripts/verify-model-catalog.mjs +104 -0
  192. package/scripts/verify-nightly-close-issue4.sh +31 -0
  193. package/scripts/version.mjs +40 -0
  194. package/scripts/wired-check.mjs +867 -0
  195. package/plugin/scripts/finalize-token-meter.mjs +0 -25
@@ -0,0 +1,1221 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="color-scheme" content="dark light" />
7
+ <title>RuvNet Brain — the pieces</title>
8
+ <!-- Same version-free brain mark as the console and tips. -->
9
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 64 64%22><rect x=%222%22 y=%222%22 width=%2260%22 height=%2260%22 rx=%2216%22 fill=%22%230b0d12%22 stroke=%22%232c3850%22 stroke-width=%221.5%22/><path d=%22M25 48 C17 48 11.5 41.5 13.5 34.5 C10 29 13 21 19 19 C21 12.5 29 9.5 35 12 C43 9 50.5 14.5 50.5 22 C55 26 55 34.5 50 38 C49 43.5 43 47.5 37 46.5 L31 47.5 Z%22 fill=%22none%22 stroke=%22%23f0a830%22 stroke-width=%223.4%22 stroke-linejoin=%22round%22/><path d=%22M31 47 L31 33 M31 33 L24 27 M31 33 L40 24.5 M31 33 L41 36.5%22 fill=%22none%22 stroke=%22%23f0a830%22 stroke-width=%222.4%22 stroke-linecap=%22round%22 opacity=%22.9%22/><circle cx=%2224%22 cy=%2227%22 r=%223.4%22 fill=%22%235ad6ff%22/><circle cx=%2240%22 cy=%2224.5%22 r=%223.4%22 fill=%22%23f4b13a%22/><circle cx=%2241%22 cy=%2236.5%22 r=%223.4%22 fill=%22%235fd38a%22/></svg>" />
10
+
11
+ <script>
12
+ /* Resolve theme BEFORE first paint — same mechanism and same storage key as index.html and
13
+ tips.html, so a person switching between the three pages never sees a flash or a reset. */
14
+ (function () {
15
+ var t = null;
16
+ try { t = localStorage.getItem('rbc-theme'); } catch (e) {}
17
+ if (t !== 'light' && t !== 'dark') {
18
+ try { t = matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; }
19
+ catch (e) { t = 'dark'; }
20
+ }
21
+ document.documentElement.dataset.theme = t;
22
+ })();
23
+ </script>
24
+
25
+ <style>
26
+ /* ============================================================================
27
+ RUVNET BRAIN — THE PIECES
28
+ ----------------------------------------------------------------------------
29
+ Tokens, header, cards, tables and footer are lifted VERBATIM from
30
+ console/style.css so this page is the same object as the console, not a
31
+ lookalike. It carries its own copy rather than linking style.css for one
32
+ reason: this page must render correctly when opened straight off disk or
33
+ copied out of the repo on its own — the compatibility matrix is the single
34
+ most useful thing to hand to somebody who has NOT installed the plugin, and
35
+ a page that needs a sibling stylesheet cannot be handed to anybody.
36
+
37
+ Kept identical on purpose (do not "improve" independently — they must drift
38
+ together or not at all): the :root token block, .wrap, .head, .card,
39
+ table.tb, .foot, and the three-color semantics below.
40
+
41
+ cyan = observed (what we measured)
42
+ amber = the mind acting on your turn
43
+ green = grounded / reversible
44
+ red = broken only, never "you chose wrong"
45
+ ============================================================================ */
46
+
47
+ /* ============================== 1 · TOKENS ============================== */
48
+ :root {
49
+ color-scheme: dark;
50
+
51
+ --bg: #0a0c10;
52
+ --bg-2: #0e1116;
53
+ --surface: #13171e;
54
+ --surface-2: #181d26;
55
+ --ridge: #2a3140;
56
+
57
+ --ink: #ece8dc;
58
+ --ink-2: #c3c2b6;
59
+ --muted: #8f94a1;
60
+ --faint: #5a5f6e;
61
+ --on-accent: #0a0c10;
62
+
63
+ --amber: #f0a830; --amber-text: #f0b04a;
64
+ --cyan: #5ad6ff; --cyan-text: #5ad6ff;
65
+ --green: #5fd38a; --green-text: #5fd38a;
66
+ --red: #ff6b5e; --red-text: #ff8a7e;
67
+ --warn: #f0b65a; --warn-text: #f0b65a;
68
+
69
+ --spectrum: linear-gradient(96deg, #f0a830 0%, #ffce6a 24%, #5ad6ff 58%, #5fd38a 100%);
70
+
71
+ --shadow: 0 18px 42px -24px rgba(0, 0, 0, 0.65);
72
+ --shadow-deep: 0 30px 70px -34px rgba(0, 0, 0, 0.8);
73
+
74
+ --display: "Iowan Old Style", "Palatino Nova", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
75
+ --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
76
+ --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;
77
+
78
+ --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
79
+ --radius: 16px;
80
+ --radius-s: 10px;
81
+ --maxw: 1080px;
82
+ --gutter: clamp(1.1rem, 4vw, 2.6rem);
83
+
84
+ --fs--2: clamp(0.685rem, 0.66rem + 0.12vw, 0.76rem);
85
+ --fs--1: clamp(0.815rem, 0.79rem + 0.14vw, 0.885rem);
86
+ --fs-0: clamp(0.94rem, 0.91rem + 0.15vw, 1rem);
87
+ --fs-1: clamp(1.06rem, 1.0rem + 0.3vw, 1.19rem);
88
+ --fs-2: clamp(1.24rem, 1.12rem + 0.6vw, 1.5rem);
89
+ --fs-3: clamp(1.85rem, 1.4rem + 2.3vw, 2.95rem);
90
+ }
91
+
92
+ :root[data-theme="light"] {
93
+ color-scheme: light;
94
+
95
+ --bg: #f1efe8;
96
+ --bg-2: #e9e6dd;
97
+ --surface: #fbfaf6;
98
+ --surface-2: #f2efe8;
99
+ --ridge: #d9d5c9;
100
+
101
+ --ink: #20242e;
102
+ --ink-2: #3d4353;
103
+ --muted: #575d6e;
104
+ --faint: #878c99;
105
+ --on-accent: #241a06;
106
+
107
+ --amber: #e09614; --amber-text: #8f5c05;
108
+ --cyan: #2ba6cf; --cyan-text: #0a6d8f;
109
+ --green: #3aa768; --green-text: #1e7a45;
110
+ --red: #e05b4b; --red-text: #b03225;
111
+ --warn: #d99a2b; --warn-text: #8a5c06;
112
+
113
+ --spectrum: linear-gradient(96deg, #c47d08 0%, #d9a12e 24%, #0e87ad 58%, #20824d 100%);
114
+
115
+ --shadow: 0 16px 36px -24px rgba(60, 50, 20, 0.35);
116
+ --shadow-deep: 0 26px 60px -30px rgba(60, 50, 20, 0.45);
117
+ }
118
+
119
+ :root {
120
+ --hair: color-mix(in srgb, var(--ridge) 78%, transparent);
121
+ --hair-strong: color-mix(in srgb, var(--ridge) 100%, transparent);
122
+ --line: color-mix(in srgb, var(--amber) 22%, transparent);
123
+ --fill: color-mix(in srgb, var(--amber) 9%, transparent);
124
+ --fill-cyan: color-mix(in srgb, var(--cyan) 9%, transparent);
125
+ --fill-green: color-mix(in srgb, var(--green) 9%, transparent);
126
+ }
127
+
128
+ /* ============================== 2 · BASE ============================== */
129
+ *, *::before, *::after { box-sizing: border-box; }
130
+ html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
131
+
132
+ body {
133
+ margin: 0;
134
+ background: var(--bg);
135
+ color: var(--ink);
136
+ font-family: var(--sans);
137
+ font-size: var(--fs-0);
138
+ line-height: 1.62;
139
+ -webkit-font-smoothing: antialiased;
140
+ text-rendering: optimizeLegibility;
141
+ background-image:
142
+ radial-gradient(color-mix(in srgb, var(--ink) 4.5%, transparent) 1px, transparent 1.5px),
143
+ radial-gradient(1000px 560px at 82% -6%, color-mix(in srgb, var(--amber) 9%, transparent), transparent 62%),
144
+ radial-gradient(820px 640px at -8% 4%, color-mix(in srgb, var(--cyan) 7%, transparent), transparent 56%),
145
+ linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
146
+ background-size: 26px 26px, auto, auto, auto;
147
+ background-attachment: fixed;
148
+ }
149
+
150
+ img, svg { max-width: 100%; height: auto; }
151
+ a { color: var(--cyan-text); text-decoration: none; }
152
+ a:hover { color: var(--amber-text); }
153
+
154
+ code {
155
+ font-family: var(--mono); font-size: 0.86em;
156
+ color: var(--cyan-text); background: var(--fill-cyan);
157
+ border: 1px solid color-mix(in srgb, var(--cyan) 26%, transparent);
158
+ border-radius: 6px; padding: 0.08em 0.42em;
159
+ }
160
+ pre {
161
+ font-family: var(--mono); font-size: 0.8rem; line-height: 1.65;
162
+ background: color-mix(in srgb, var(--ink) 4%, transparent);
163
+ border: 1px solid var(--hair); border-radius: 12px;
164
+ padding: 13px 16px; margin: 12px 0 0;
165
+ overflow-x: auto; color: var(--ink-2);
166
+ }
167
+ pre code { background: none; border: 0; padding: 0; color: inherit; font-size: inherit; }
168
+
169
+ .mono { font-family: var(--mono); }
170
+ .muted { color: var(--muted); }
171
+
172
+ :focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; border-radius: 6px; }
173
+ ::selection { background: color-mix(in srgb, var(--amber) 30%, transparent); color: var(--ink); }
174
+
175
+ .skip-link {
176
+ position: absolute; left: 0; top: 0; z-index: 100;
177
+ transform: translateY(-130%);
178
+ background: var(--amber); color: var(--on-accent);
179
+ font-family: var(--mono); font-size: var(--fs--1); font-weight: 600;
180
+ padding: 10px 16px; border-radius: 0 0 var(--radius-s) 0;
181
+ transition: transform 0.18s var(--ease);
182
+ }
183
+ .skip-link:focus { transform: translateY(0); }
184
+
185
+ .wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
186
+
187
+ h1, h2, h3 {
188
+ font-family: var(--display); font-weight: 600;
189
+ letter-spacing: -0.012em; line-height: 1.08;
190
+ color: var(--ink); margin: 0 0 0.4em; text-wrap: balance;
191
+ }
192
+ h1 { font-size: var(--fs-3); }
193
+ h1 em {
194
+ font-style: italic;
195
+ background: var(--spectrum); -webkit-background-clip: text; background-clip: text;
196
+ color: transparent;
197
+ }
198
+ h3 { font-size: var(--fs-1); margin: 1.7em 0 0.5em; }
199
+ h3:first-child { margin-top: 0.2em; }
200
+
201
+ p { margin: 0 0 0.9rem; color: var(--ink-2); max-width: 70ch; text-wrap: pretty; }
202
+ p:last-child { margin-bottom: 0; }
203
+
204
+ .eyebrow {
205
+ font-family: var(--mono); font-size: var(--fs--2);
206
+ letter-spacing: 0.26em; text-transform: uppercase;
207
+ color: var(--cyan-text); margin: 0 0 14px;
208
+ }
209
+ .lede { font-size: var(--fs-1); line-height: 1.55; color: var(--ink-2); max-width: 56ch; }
210
+
211
+ /* ============================== 3 · HEADER ============================== */
212
+ .head {
213
+ position: sticky; top: 0; z-index: 50;
214
+ background: color-mix(in srgb, var(--bg) 82%, transparent);
215
+ backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
216
+ }
217
+ .head-inner { display: flex; align-items: center; justify-content: space-between; height: 58px; gap: 16px; }
218
+ .spectrum-edge { height: 2px; background: var(--spectrum); opacity: 0.9; }
219
+
220
+ .brand { display: inline-flex; align-items: center; gap: 11px; min-width: 0; }
221
+ .brand .mark { width: 28px; height: 28px; flex: none; }
222
+ /* The mark keeps its dark plate in BOTH themes — the amber brain is drawn for that substrate,
223
+ so these are colour literals on purpose, not tokens. Identical to index.html. */
224
+ .brand .mark .mk-plate { fill: #0b0d12; stroke: #2c3850; stroke-width: 1.5; }
225
+ .brand .mark .mk-brain { fill: none; stroke: #f0a830; stroke-width: 3.4; stroke-linejoin: round; }
226
+ .brand .mark .mk-branch { fill: none; stroke: #f0a830; stroke-width: 2.4; stroke-linecap: round; opacity: 0.9; }
227
+ .brand .mark .n1 { fill: #f4b13a; } .brand .mark .n2 { fill: #5ad6ff; } .brand .mark .n3 { fill: #5fd38a; }
228
+ .brand-text {
229
+ font-family: var(--display); font-weight: 600; font-size: 1.02rem;
230
+ letter-spacing: -0.01em; color: var(--ink); white-space: nowrap;
231
+ overflow: hidden; text-overflow: ellipsis;
232
+ }
233
+ .brand-sub {
234
+ font-family: var(--mono); font-size: 11px; font-weight: 500;
235
+ letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); margin-left: 8px;
236
+ }
237
+ .head-right { display: inline-flex; align-items: center; gap: 10px; }
238
+ .back-chip {
239
+ font-size: 12.5px; color: var(--ink-2);
240
+ border: 1px solid var(--hair); border-radius: 100px; padding: 4px 12px; white-space: nowrap;
241
+ }
242
+ .back-chip:hover { border-color: var(--amber); color: var(--amber-text); background: var(--fill); }
243
+
244
+ .theme-toggle {
245
+ width: 36px; height: 36px; border-radius: 10px;
246
+ border: 1px solid var(--hair-strong); background: transparent;
247
+ display: grid; place-items: center; cursor: pointer; color: var(--ink-2);
248
+ transition: border-color 0.18s var(--ease), background 0.18s var(--ease), transform 0.15s var(--ease);
249
+ }
250
+ .theme-toggle:hover { border-color: var(--amber); color: var(--amber-text); background: var(--fill); }
251
+ .theme-toggle:active { transform: scale(0.94); }
252
+ .theme-toggle svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
253
+ :root[data-theme="dark"] .ic-moon { display: none; }
254
+ :root[data-theme="light"] .ic-sun { display: none; }
255
+
256
+ /* ============================== 4 · HERO ============================== */
257
+ main { padding-bottom: clamp(2rem, 5vw, 4rem); }
258
+ .intro { padding: clamp(2.2rem, 5vw, 4rem) 0 clamp(1rem, 2.5vw, 1.6rem); }
259
+ .intro-grid { display: grid; grid-template-columns: 1fr; gap: clamp(1.6rem, 4vw, 3rem); align-items: center; }
260
+ @media (min-width: 62em) { .intro-grid { grid-template-columns: 1.02fr 0.98fr; } }
261
+
262
+ .intro, .card { animation: rise 0.55s var(--ease) both; }
263
+ .card:nth-of-type(1) { animation-delay: 0.06s; }
264
+ .card:nth-of-type(2) { animation-delay: 0.10s; }
265
+ .card:nth-of-type(3) { animation-delay: 0.14s; }
266
+ .card:nth-of-type(4) { animation-delay: 0.18s; }
267
+ .card:nth-of-type(5) { animation-delay: 0.22s; }
268
+ @keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
269
+
270
+ /* The complaint that produced this page, in the user's own words. Same treatment tips.html
271
+ gives rUv's quote: it is evidence, so it is set apart and attributed, never paraphrased. */
272
+ .user-quote {
273
+ margin: 1.3rem 0 0; padding: 14px 18px;
274
+ border-left: 3px solid var(--amber);
275
+ background: var(--fill); border-radius: 0 var(--radius-s) var(--radius-s) 0;
276
+ }
277
+ .user-quote p { font-family: var(--display); font-size: var(--fs-1); font-style: italic; color: var(--ink); margin: 0; }
278
+ .user-quote footer { margin-top: 8px; font-size: 11.5px; letter-spacing: 0.06em; color: var(--muted); }
279
+
280
+ /* ============================== 5 · THE STACK DIAGRAM ============================== */
281
+ /* Four layers, drawn bottom-up in the order you actually install them. Each layer's colour
282
+ is its stage semantics, and the right-hand column states what that layer ADDS — so the
283
+ diagram answers "what do I lose by stopping here?" by being read from the top down. */
284
+ .stack-fig { margin: 0; }
285
+ .stack-scene { width: 100%; height: auto; display: block; }
286
+
287
+ /* PHONE: the SVG is replaced, not shrunk. Measured at 390px — the 470-unit viewBox renders into
288
+ a ~342px column (scale 0.73), which drops the 11.5px labels to ~8.4px effective. That is the
289
+ exact defect tips.html's hero art hit and documented; the fix there was to stop rendering text
290
+ the reader cannot read. Same content, same order, same colours — as native HTML at full size,
291
+ which also means it is real selectable text rather than an aria-label standing in for it. */
292
+ .stack-key { display: none; }
293
+ @media (max-width: 40em) {
294
+ .stack-scene { display: none; }
295
+ .stack-key { display: grid; gap: 8px; margin: 0; }
296
+ .sk-row { border: 1px solid var(--hair); border-left-width: 3px; border-radius: 0 10px 10px 0; padding: 10px 13px; }
297
+ .sk-row.k1 { border-left-color: var(--green); }
298
+ .sk-row.k2 { border-left-color: var(--cyan); }
299
+ .sk-row.k3 { border-left-color: var(--amber); }
300
+ .sk-row.k4 { border-left-color: color-mix(in srgb, var(--amber) 45%, transparent); }
301
+ .sk-name { font-family: var(--mono); font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; }
302
+ .k1 .sk-name { color: var(--green-text); } .k2 .sk-name { color: var(--cyan-text); }
303
+ .k3 .sk-name, .k4 .sk-name { color: var(--amber-text); }
304
+ .sk-what { font-size: var(--fs--1); color: var(--ink); margin-top: 2px; }
305
+ .sk-add { font-size: var(--fs--1); color: var(--muted); margin-top: 3px; }
306
+ }
307
+ .sf-plate { fill: color-mix(in srgb, var(--ink) 4%, transparent); stroke: var(--hair); stroke-width: 1; }
308
+ .sf-l1 { fill: color-mix(in srgb, var(--green) 13%, transparent); stroke: color-mix(in srgb, var(--green) 55%, transparent); stroke-width: 1.4; }
309
+ .sf-l2 { fill: color-mix(in srgb, var(--cyan) 13%, transparent); stroke: color-mix(in srgb, var(--cyan) 55%, transparent); stroke-width: 1.4; }
310
+ .sf-l3 { fill: color-mix(in srgb, var(--amber) 13%, transparent); stroke: color-mix(in srgb, var(--amber) 58%, transparent); stroke-width: 1.4; }
311
+ .sf-l4 { fill: color-mix(in srgb, var(--amber) 7%, transparent); stroke: color-mix(in srgb, var(--amber) 34%, transparent); stroke-width: 1.4; stroke-dasharray: 4 3; }
312
+ .sf-name { font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; }
313
+ .sf-n1 { fill: var(--green-text); } .sf-n2 { fill: var(--cyan-text); } .sf-n3, .sf-n4 { fill: var(--amber-text); }
314
+ .sf-what { font-family: var(--sans); font-size: 12px; fill: var(--ink-2); }
315
+ .sf-add { font-family: var(--sans); font-size: 11.5px; fill: var(--muted); }
316
+ .sf-cap { font-family: var(--mono); font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; fill: var(--faint); }
317
+ .sf-tick { stroke: var(--hair-strong); stroke-width: 1; }
318
+
319
+ /* ============================== 6 · CARDS ============================== */
320
+ .card {
321
+ position: relative;
322
+ background: linear-gradient(180deg, var(--surface) 0%, color-mix(in srgb, var(--surface) 55%, var(--bg-2)) 100%);
323
+ border: 1px solid var(--ridge); border-radius: var(--radius);
324
+ box-shadow: var(--shadow); margin: 0 0 clamp(14px, 2vw, 20px); overflow: hidden;
325
+ }
326
+ .card[open] { border-color: var(--line); box-shadow: var(--shadow-deep); }
327
+ .card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; z-index: 1; pointer-events: none; }
328
+ .rail-mirror::before { background: color-mix(in srgb, var(--cyan) 64%, transparent); }
329
+ .rail-prove::before { background: color-mix(in srgb, var(--green) 64%, transparent); }
330
+ .rail-act::before { background: color-mix(in srgb, var(--amber) 66%, transparent); }
331
+
332
+ .card > summary {
333
+ list-style: none; cursor: pointer; user-select: none;
334
+ display: flex; align-items: center; gap: 16px;
335
+ padding: clamp(15px, 2.4vw, 20px) clamp(16px, 3vw, 26px);
336
+ transition: background 0.2s var(--ease);
337
+ }
338
+ .card > summary::-webkit-details-marker { display: none; }
339
+ .card > summary:hover { background: var(--fill); }
340
+
341
+ .card-ic { flex: none; width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; border: 1px solid; }
342
+ .card-ic svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
343
+ .ic-mirror { color: var(--cyan-text); border-color: color-mix(in srgb, var(--cyan) 22%, transparent); background: color-mix(in srgb, var(--cyan) 8%, transparent); }
344
+ .ic-prove { color: var(--green-text); border-color: color-mix(in srgb, var(--green) 22%, transparent); background: color-mix(in srgb, var(--green) 8%, transparent); }
345
+ .ic-act { color: var(--amber-text); border-color: color-mix(in srgb, var(--amber) 24%, transparent); background: color-mix(in srgb, var(--amber) 8%, transparent); }
346
+
347
+ .card-head { flex: 1 1 auto; min-width: 0; }
348
+ .card-head h2 { font-size: var(--fs-2); margin: 2px 0 0; }
349
+ .card-head .q { display: block; margin-top: 4px; font-size: var(--fs--1); color: var(--ink-2); line-height: 1.45; }
350
+
351
+ .chips { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; max-width: 40%; }
352
+ .chip {
353
+ display: inline-flex; align-items: center; gap: 6px;
354
+ font-family: var(--mono); font-size: 10.5px; font-weight: 600;
355
+ letter-spacing: 0.14em; text-transform: uppercase;
356
+ padding: 2.5px 10px; border-radius: 100px; border: 1px solid;
357
+ white-space: nowrap;
358
+ }
359
+ .tone-req { color: var(--green-text); border-color: color-mix(in srgb, var(--green) 34%, transparent); background: color-mix(in srgb, var(--green) 8%, transparent); }
360
+ .tone-opt { color: var(--muted); border-color: var(--hair-strong); background: transparent; }
361
+ .tone-cost { color: var(--amber-text); border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: color-mix(in srgb, var(--amber) 8%, transparent); }
362
+
363
+ .chev {
364
+ flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
365
+ display: grid; place-items: center; border: 1px solid var(--hair-strong);
366
+ color: var(--amber-text); font-size: 14px; line-height: 1;
367
+ transition: transform 0.28s var(--ease), background 0.2s var(--ease);
368
+ }
369
+ .card[open] .chev { transform: rotate(90deg); background: var(--fill); }
370
+
371
+ .card::details-content {
372
+ opacity: 0; block-size: 0; overflow-y: clip;
373
+ transition: content-visibility 0.4s allow-discrete, opacity 0.32s var(--ease), block-size 0.4s var(--ease);
374
+ }
375
+ .card[open]::details-content { opacity: 1; block-size: auto; }
376
+ .card .body { padding: 6px clamp(16px, 3vw, 26px) clamp(20px, 3vw, 28px); border-top: 1px solid var(--hair); }
377
+
378
+ /* The four-beat structure every piece card repeats: what · gives · costs · lose.
379
+ Same four questions in the same order on all nine, so the page can be skimmed vertically. */
380
+ .beat { display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 4px 16px; margin-top: 14px; }
381
+ .beat > dt {
382
+ font-family: var(--mono); font-size: 10px; font-weight: 600;
383
+ letter-spacing: 0.18em; text-transform: uppercase; color: var(--faint);
384
+ padding-top: 4px;
385
+ }
386
+ /* 74ch, matching the measure `p` already enforces. Without it these ran the full card width —
387
+ ~100ch at 1280 — which is past the point where the eye loses the line on the return sweep.
388
+ Caught by looking at the render, not by reading the CSS. */
389
+ .beat > dd { margin: 0; color: var(--ink-2); font-size: var(--fs--1); line-height: 1.6; max-width: 74ch; }
390
+ .beat > dd b, .beat > dd strong { color: var(--ink); font-weight: 650; }
391
+ .beat .lose { color: var(--ink); }
392
+ @media (max-width: 40em) { .beat { grid-template-columns: minmax(0, 1fr); gap: 2px; } .beat > dt { padding-top: 10px; } }
393
+
394
+ /* A measured-fact callout: the number, then the command that produced it. Never one without
395
+ the other — a figure with no derivation is the exact thing this project refuses to ship. */
396
+ .facts { display: grid; gap: 8px; margin-top: 14px; }
397
+ .fact {
398
+ display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: 4px 14px;
399
+ align-items: baseline;
400
+ padding: 9px 13px; border: 1px solid var(--hair); border-radius: 10px;
401
+ background: color-mix(in srgb, var(--ink) 2.5%, transparent);
402
+ }
403
+ .fact .fv { font-family: var(--mono); font-weight: 650; color: var(--green-text); font-variant-numeric: tabular-nums; white-space: nowrap; }
404
+ .fact .fl { font-size: var(--fs--1); color: var(--ink-2); }
405
+ .fact .fsrc { grid-column: 1 / -1; font-family: var(--mono); font-size: 10.5px; color: var(--faint); word-break: break-all; }
406
+ @media (max-width: 40em) { .fact { grid-template-columns: minmax(0, 1fr); } }
407
+
408
+ /* The dated-failure note. Every one of these is a real incident with a real date; the amber
409
+ left rule marks "this is why the thing above is shaped the way it is". */
410
+ .why {
411
+ margin-top: 14px; padding: 11px 15px;
412
+ border-left: 3px solid color-mix(in srgb, var(--amber) 60%, transparent);
413
+ background: var(--fill); border-radius: 0 10px 10px 0;
414
+ font-size: var(--fs--1); color: var(--ink-2);
415
+ }
416
+ .why b { color: var(--amber-text); }
417
+
418
+ /* ============================== 7 · TABLES ============================== */
419
+ .scroll-x { overflow-x: auto; border: 1px solid var(--hair); border-radius: 12px; background: color-mix(in srgb, var(--ink) 2%, transparent); margin-top: 16px; }
420
+ table.tb { width: 100%; border-collapse: collapse; font-size: var(--fs--1); min-width: 640px; }
421
+ .tb th, .tb td { text-align: left; padding: 9px 14px; border-bottom: 1px solid var(--hair); vertical-align: top; }
422
+ .tb thead th {
423
+ font-family: var(--mono); font-size: 11.5px; font-weight: 600;
424
+ letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
425
+ background: color-mix(in srgb, var(--ink) 4%, transparent);
426
+ position: sticky; top: 0; z-index: 1;
427
+ }
428
+ .tb tbody tr:last-child td { border-bottom: 0; }
429
+ .tb tbody tr { transition: background 0.15s var(--ease); }
430
+ .tb tbody tr:hover { background: var(--fill); }
431
+ .tb td.cell-name { font-family: var(--mono); font-size: 0.82rem; color: var(--ink); }
432
+ .tb td.cell-mono { font-family: var(--mono); font-size: 0.8rem; color: var(--ink-2); font-variant-numeric: tabular-nums; }
433
+ .tb .num { font-variant-numeric: tabular-nums; }
434
+ .tb tr.row-cost td { background: color-mix(in srgb, var(--amber) 6%, transparent); }
435
+ .tb tr.row-cost td:first-child { color: var(--ink); }
436
+
437
+ /* Matrix marks. The glyph carries the meaning for colour-blind readers; colour is redundant
438
+ reinforcement, never the only channel — and every ~ has words next to it, never a bare tilde. */
439
+ .yes { color: var(--green-text); font-weight: 700; }
440
+ .no { color: var(--red-text); font-weight: 700; }
441
+ .part{ color: var(--warn-text); font-weight: 700; }
442
+ .tb td small { display: block; color: var(--muted); font-size: 0.9em; line-height: 1.4; margin-top: 1px; }
443
+
444
+ /* ============================== 8 · LEGEND + FOOTER ============================== */
445
+ .stage-legend {
446
+ display: flex; flex-wrap: wrap; align-items: center; gap: 5px 16px;
447
+ margin: clamp(1.2rem, 2.5vw, 1.8rem) 0 14px;
448
+ font-size: 11px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
449
+ }
450
+ .stage-legend .lg-cap { color: var(--faint); letter-spacing: 0.2em; }
451
+ .stage-legend .lg { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
452
+ .stage-legend .lg-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
453
+ .stage-legend .d-cyan { background: var(--cyan); } .stage-legend .d-green { background: var(--green); } .stage-legend .d-amber { background: var(--amber); }
454
+
455
+ .sec-head { margin: clamp(2.2rem, 5vw, 3.4rem) 0 0.2em; }
456
+ .sec-head h2 { font-size: var(--fs-2); }
457
+ .sec-head p { margin-top: 6px; }
458
+
459
+ .foot { border-top: 1px solid var(--hair); padding: clamp(1.6rem, 3vw, 2.4rem) 0 clamp(2.2rem, 4vw, 3rem); margin-top: clamp(2rem, 4vw, 3rem); }
460
+ .foot-inner { display: flex; flex-wrap: wrap; gap: 8px 26px; align-items: baseline; justify-content: space-between; }
461
+ .foot-line { margin: 0; font-size: var(--fs--1); color: var(--ink-2); max-width: none; }
462
+ .foot-meta { margin: 0; font-size: 12px; color: var(--muted); letter-spacing: 0.03em; }
463
+
464
+ @media (max-width: 40em) {
465
+ .card > summary { flex-wrap: wrap; }
466
+ .chips { max-width: none; width: 100%; order: 4; justify-content: flex-start; margin-top: 2px; }
467
+ .chev { order: 3; }
468
+ .brand-sub { display: none; }
469
+ }
470
+ @media (prefers-reduced-motion: reduce) {
471
+ *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
472
+ html { scroll-behavior: auto; }
473
+ }
474
+ </style>
475
+ </head>
476
+ <body>
477
+
478
+ <!-- ============================================================================
479
+ WHY THIS PAGE EXISTS — the same reason docs/ARCHITECTURE-MAP.md exists, and
480
+ this page is that document rendered, not a summary of it. Keep them in sync.
481
+
482
+ A power user, to the owner: "I can't use your stuff because it has hooks and
483
+ this and that. I just loaded the brain into my RuVector Brain and I don't get
484
+ the rest of it." He is not confused — he took one piece, and nobody could tell
485
+ him what the others were worth.
486
+
487
+ HONESTY CONSTRAINTS, both load-bearing:
488
+
489
+ 1. This page is STATIC and reads nothing off the machine. So every number
490
+ below is a MEASUREMENT WITH A DATE AND A COMMAND attached, never a live
491
+ status. It never says "your brain is healthy" — that is the console's job,
492
+ and this page links there rather than guessing. A static page that implies
493
+ live state is the fabrication this project exists to refuse.
494
+ 2. No version literal appears anywhere. A CI gate greps for one, and beyond
495
+ the gate: a hardcoded version in a page nobody remembers to edit is how a
496
+ surface starts lying quietly.
497
+ ============================================================================ -->
498
+
499
+ <a class="skip-link" href="#main">Skip to content</a>
500
+
501
+ <header class="head">
502
+ <div class="wrap head-inner">
503
+ <div class="brand">
504
+ <svg class="mark" viewBox="0 0 64 64" aria-hidden="true">
505
+ <rect class="mk-plate" x="2" y="2" width="60" height="60" rx="16" />
506
+ <path class="mk-brain" d="M25 48 C17 48 11.5 41.5 13.5 34.5 C10 29 13 21 19 19 C21 12.5 29 9.5 35 12 C43 9 50.5 14.5 50.5 22 C55 26 55 34.5 50 38 C49 43.5 43 47.5 37 46.5 L31 47.5 Z" />
507
+ <path class="mk-branch" d="M31 47 L31 33 M31 33 L24 27 M31 33 L40 24.5 M31 33 L41 36.5" />
508
+ <circle class="n2" cx="24" cy="27" r="4" /><circle class="n1" cx="40" cy="24.5" r="4" /><circle class="n3" cx="41" cy="36.5" r="4" />
509
+ </svg>
510
+ <span class="brand-text">RuvNet Brain <span class="brand-sub">the pieces</span></span>
511
+ </div>
512
+ <div class="head-right">
513
+ <a class="back-chip mono" href="./">‹ console</a>
514
+ <button class="theme-toggle" id="theme-toggle" type="button" aria-label="Switch color theme">
515
+ <svg class="ic-sun" viewBox="0 0 24 24" aria-hidden="true">
516
+ <circle cx="12" cy="12" r="4.5" />
517
+ <g class="rays"><line x1="12" y1="2" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="2" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="22" y2="12"/><line x1="4.9" y1="4.9" x2="7" y2="7"/><line x1="17" y1="17" x2="19.1" y2="19.1"/><line x1="4.9" y1="19.1" x2="7" y2="17"/><line x1="17" y1="7" x2="19.1" y2="4.9"/></g>
518
+ </svg>
519
+ <svg class="ic-moon" viewBox="0 0 24 24" aria-hidden="true">
520
+ <path d="M20.4 14.3A8.5 8.5 0 0 1 9.7 3.6a8.5 8.5 0 1 0 10.7 10.7Z" />
521
+ </svg>
522
+ </button>
523
+ </div>
524
+ </div>
525
+ <div class="spectrum-edge" aria-hidden="true"></div>
526
+ </header>
527
+
528
+ <main id="main" class="wrap">
529
+
530
+ <!-- ======================================================= HERO -->
531
+ <section class="intro">
532
+ <div class="intro-grid">
533
+ <div class="intro-copy">
534
+ <p class="eyebrow">RuvNet Brain · the architecture</p>
535
+ <h1>Nine pieces. <em>Take the ones you want.</em></h1>
536
+ <p class="lede">Most of this is optional, and taking only part of it is a legitimate choice —
537
+ not a broken install. What has been missing is an honest account of <b>what each piece costs
538
+ and what its absence costs</b>, in numbers, so you can decide rather than guess. That is this
539
+ page. Nothing here is trying to talk you into installing more.</p>
540
+
541
+ <blockquote class="user-quote">
542
+ <p>“I can't use your stuff because it has hooks and this and that. I just loaded the brain
543
+ into my RuVector Brain and I don't get the rest of it.”</p>
544
+ <footer class="mono">— a power user, to the owner. He is right, and nobody could tell him what he was missing.</footer>
545
+ </blockquote>
546
+ </div>
547
+
548
+ <!-- THE STACK. Four layers in install order, bottom-up. The right column says what each
549
+ layer ADDS, so reading downward from any layer tells you what stopping there costs.
550
+ viewBox is ~1:1 with its rendered column (tips.html learned this the hard way: a
551
+ 640-unit viewBox in a ~453px column downscaled every label to ~8px). -->
552
+ <figure class="stack-fig">
553
+ <svg class="stack-scene" viewBox="0 0 470 306" role="img"
554
+ aria-label="Four stacked layers, bottom to top. Layer one, the corpus: 69 repos as vector stores, 2.7 gigabytes, required by everything above. Layer two, the MCP server: adds search_ruvnet inside Claude Code, and the model can consult the corpus mid-answer. Layer three, the hooks: adds unprompted grounding, drift interception, health alarms and learning capture. Layer four, the surfaces: adds the console, slash commands and skills, which is how you see any of it.">
555
+
556
+ <rect class="sf-plate" x="0.5" y="0.5" width="469" height="305" rx="14" />
557
+
558
+ <text class="sf-cap" x="20" y="26">what you install</text>
559
+ <text class="sf-cap" x="286" y="26">what it adds</text>
560
+ <line class="sf-tick" x1="20" y1="34" x2="450" y2="34" />
561
+
562
+ <!-- L4 · surfaces (dashed: the layer that only exists to be looked at) -->
563
+ <rect class="sf-l4" x="20" y="46" width="252" height="50" rx="10" />
564
+ <text class="sf-name sf-n4" x="34" y="68">04 · SURFACES</text>
565
+ <text class="sf-what" x="34" y="85">console · 4 commands · 5 skills</text>
566
+ <text class="sf-add" x="286" y="68">you can SEE the other three</text>
567
+ <text class="sf-add" x="286" y="85">— and switch pieces off</text>
568
+
569
+ <!-- L3 · hooks -->
570
+ <rect class="sf-l3" x="20" y="106" width="252" height="62" rx="10" />
571
+ <text class="sf-name sf-n3" x="34" y="128">03 · HOOKS</text>
572
+ <text class="sf-what" x="34" y="145">10 scripts · 6 events</text>
573
+ <text class="sf-what" x="34" y="161">lessons · spine</text>
574
+ <text class="sf-add" x="286" y="128">grounding without asking</text>
575
+ <text class="sf-add" x="286" y="145">drift caught as you write</text>
576
+ <text class="sf-add" x="286" y="161">a dead brain says so</text>
577
+
578
+ <!-- L2 · MCP -->
579
+ <rect class="sf-l2" x="20" y="178" width="252" height="50" rx="10" />
580
+ <text class="sf-name sf-n2" x="34" y="200">02 · MCP SERVER</text>
581
+ <text class="sf-what" x="34" y="217">one tool: search_ruvnet</text>
582
+ <text class="sf-add" x="286" y="200">the model consults 69 repos</text>
583
+ <text class="sf-add" x="286" y="217">mid-answer, without you</text>
584
+
585
+ <!-- L1 · corpus -->
586
+ <rect class="sf-l1" x="20" y="238" width="252" height="50" rx="10" />
587
+ <text class="sf-name sf-n1" x="34" y="260">01 · THE CORPUS</text>
588
+ <text class="sf-what" x="34" y="277">69 repos · 2.7 GB · inert files</text>
589
+ <text class="sf-add" x="286" y="260">rUv's real source, searchable</text>
590
+ <text class="sf-add" x="286" y="277">required by everything above</text>
591
+ </svg>
592
+
593
+ <!-- The phone rendering of the same four layers. Not a summary — the identical strings,
594
+ in the identical order, at a size a person can actually read. -->
595
+ <figcaption class="stack-key">
596
+ <div class="sk-row k4">
597
+ <div class="sk-name">04 · Surfaces</div>
598
+ <div class="sk-what">console · 4 commands · 5 skills</div>
599
+ <div class="sk-add">Adds: you can SEE the other three — and switch pieces off</div>
600
+ </div>
601
+ <div class="sk-row k3">
602
+ <div class="sk-name">03 · Hooks</div>
603
+ <div class="sk-what">10 scripts · 6 events · lessons · spine</div>
604
+ <div class="sk-add">Adds: grounding without asking · drift caught as you write · a dead brain says so</div>
605
+ </div>
606
+ <div class="sk-row k2">
607
+ <div class="sk-name">02 · MCP server</div>
608
+ <div class="sk-what">one tool: search_ruvnet</div>
609
+ <div class="sk-add">Adds: the model consults 69 repos mid-answer, without you</div>
610
+ </div>
611
+ <div class="sk-row k1">
612
+ <div class="sk-name">01 · The corpus</div>
613
+ <div class="sk-what">69 repos · 2.7 GB · inert files</div>
614
+ <div class="sk-add">Adds: rUv's real source, searchable — required by everything above</div>
615
+ </div>
616
+ </figcaption>
617
+ </figure>
618
+ </div>
619
+ </section>
620
+
621
+ <div class="stage-legend">
622
+ <span class="lg-cap">colour</span>
623
+ <span class="lg"><i class="lg-dot d-green"></i> grounds you</span>
624
+ <span class="lg"><i class="lg-dot d-amber"></i> acts on your turn</span>
625
+ <span class="lg"><i class="lg-dot d-cyan"></i> shows you what is true</span>
626
+ </div>
627
+
628
+ <!-- ======================================================= THE PIECES -->
629
+
630
+ <!-- 1 · CORPUS -->
631
+ <details class="card rail-prove" open>
632
+ <summary>
633
+ <span class="card-ic ic-prove" aria-hidden="true">
634
+ <svg viewBox="0 0 24 24"><ellipse cx="12" cy="6" rx="7.5" ry="3"/><path d="M4.5 6v12c0 1.7 3.4 3 7.5 3s7.5-1.3 7.5-3V6"/><path d="M4.5 12c0 1.7 3.4 3 7.5 3s7.5-1.3 7.5-3"/></svg>
635
+ </span>
636
+ <span class="card-head">
637
+ <h2>1 · The KB corpus</h2>
638
+ <span class="q">69 of rUv's repos, chunked, embedded, and stored next to their original text.</span>
639
+ </span>
640
+ <span class="chips"><span class="chip tone-req">required</span></span>
641
+ <span class="chev" aria-hidden="true">›</span>
642
+ </summary>
643
+ <div class="body">
644
+ <dl class="beat">
645
+ <dt>What</dt><dd>Repositories walked file by file, split into passages, embedded, and written to RVF vector stores that sit beside the passage text they came from.</dd>
646
+ <dt>Gives</dt><dd>Answers about the rUv ecosystem sourced from rUv's <b>actual code</b> instead of a model's training prior. Every hit is a whole document labelled <code>repo/path</code>, so you can go check it.</dd>
647
+ <dt>Costs</dt><dd>2.7 GB of disk, an 843 MB download. <b>Zero runtime cost</b> when nothing is querying it — no server, no daemon, no port. It is inert files.</dd>
648
+ <dt class="lose">Lose</dt><dd class="lose">Everything. This is the substrate — the other eight pieces are ways of reaching it or keeping it honest.</dd>
649
+ </dl>
650
+
651
+ <div class="facts">
652
+ <div class="fact"><span class="fv">69</span><span class="fl">repos built (123 more catalogued and pending, of ~248 in the org)</span><span class="fsrc">node -e "console.log(JSON.parse(require('fs').readFileSync('data/manifest.json')).coverage)"</span></div>
653
+ <div class="fact"><span class="fv">160,238</span><span class="fl">passages across the sharp variants</span><span class="fsrc">cat ~/.cache/ruvnet-brain/kb/*.big.passages.jsonl | wc -l</span></div>
654
+ <div class="fact"><span class="fv">136</span><span class="fl">vector stores on disk — 69 sharp + 67 small</span><span class="fsrc">ls ~/.cache/ruvnet-brain/kb/*.rvf | wc -l</span></div>
655
+ <div class="fact"><span class="fv">2.7 GB</span><span class="fl">on disk after install; 843 MB downloaded</span><span class="fsrc">du -sh ~/.cache/ruvnet-brain</span></div>
656
+ </div>
657
+
658
+ <h3>Two embedders, from the same passages</h3>
659
+ <p>Read from the sidecar files, not recalled. <code>*.big.rvf</code> uses <b>bge-base-en-v1.5</b>,
660
+ 768-dim, CLS pooling, <b>asymmetric</b> — queries carry a prefix, passages do not. Retrieval
661
+ auto-selects it when present. <code>*.rvf</code> uses <b>all-MiniLM-L6-v2</b>, 384-dim, mean
662
+ pooling, symmetric: the edge variant, for machines and runtimes that cannot carry the big model.</p>
663
+
664
+ <div class="why"><b>The corpus is a snapshot, not a mirror — and it says so on every response.</b>
665
+ Each <code>search_ruvnet</code> answer carries a staleness line computed from the real file
666
+ mtimes of the stores it queried. That exists because of issue&nbsp;#31 (Jan Lafko): a model
667
+ quoted a version out of the corpus as though the corpus were live. For anything about "latest"
668
+ or a dist-tag, check the live registry — the tool now tells you that every single time rather
669
+ than leaving you to remember it.</div>
670
+ </div>
671
+ </details>
672
+
673
+ <!-- 2 · MCP -->
674
+ <details class="card rail-prove">
675
+ <summary>
676
+ <span class="card-ic ic-prove" aria-hidden="true">
677
+ <svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="6.5"/><line x1="15.8" y1="15.8" x2="21" y2="21"/></svg>
678
+ </span>
679
+ <span class="card-head">
680
+ <h2>2 · The MCP server</h2>
681
+ <span class="q">Puts exactly one tool inside Claude Code: <code>search_ruvnet</code>.</span>
682
+ </span>
683
+ <span class="chips"><span class="chip tone-opt">optional</span><span class="chip tone-cost">90–150 s / call</span></span>
684
+ <span class="chev" aria-hidden="true">›</span>
685
+ </summary>
686
+ <div class="body">
687
+ <dl class="beat">
688
+ <dt>What</dt><dd>Two processes. A thin protocol shell that Claude Code spawns and talks to, supervising a warm child that holds the models and does the retrieval.</dd>
689
+ <dt>Gives</dt><dd>The model can consult all 69 repos <b>mid-answer</b>, without you doing anything. A query fans out to every store, pools the candidates, then re-scores the whole pool in one cross-encoder pass so hits from different repos are comparable.</dd>
690
+ <dt>Costs</dt><dd>One resident Node process holding the embedding models (~0.5 GB). One JSON line per call to the local token ledger. No network. No project files touched. And the latency below, which is <b>real and large</b>.</dd>
691
+ <dt class="lose">Lose</dt><dd class="lose">The corpus stops being reachable from inside a conversation. You keep every byte and can still query it by hand at <b>~110 s per question</b>, in a separate terminal, pasting results back yourself. <b>This is exactly the configuration the power user described.</b></dd>
692
+ </dl>
693
+
694
+ <h3>Measured latency — three real round-trips through the MCP protocol</h3>
695
+ <div class="scroll-x">
696
+ <table class="tb">
697
+ <thead><tr><th>Query (k=6)</th><th class="num">Latency</th><th class="num">Response</th></tr></thead>
698
+ <tbody>
699
+ <tr><td class="cell-name">how does agenticow branch memory</td><td class="cell-mono">122.3 s</td><td class="cell-mono">20,020 chars · ~5.0K tok</td></tr>
700
+ <tr><td class="cell-name">what is the RVF witness chain</td><td class="cell-mono">91.4 s</td><td class="cell-mono">43,066 chars · ~10.8K tok</td></tr>
701
+ <tr><td class="cell-name">ruflo swarm init topology</td><td class="cell-mono">151.4 s</td><td class="cell-mono">7,557 chars · ~1.9K tok</td></tr>
702
+ </tbody>
703
+ </table>
704
+ </div>
705
+
706
+ <p style="margin-top:14px"><b>Read that table honestly.</b> A <code>search_ruvnet</code> call is
707
+ a 1.5–2.5 minute operation on an M3 Max, and it can return 10K tokens. That is the true price of
708
+ fanning out across 69 stores and re-ranking with a cross-encoder, and it should not be sold as a
709
+ fast lookup. It earns its cost when the alternative is a confident wrong answer about a tool that
710
+ already exists — the failure this whole project was built after. It does not earn its cost for a
711
+ question you could answer by opening one file.</p>
712
+
713
+ <p>The same retrieval through the CLI took <b>110 seconds</b> for a single question, because each
714
+ invocation reloads both models from scratch. The warm child is what amortises that away.</p>
715
+
716
+ <div class="why"><b>A fixed bug worth knowing, because it is the class of thing this piece could
717
+ get wrong again.</b> The token meter originally wrote to <code>process.cwd()/.ruvnet-brain/</code>
718
+ — and an MCP server inherits its working directory from the Claude Code session, so it scattered
719
+ hidden folders through users' project trees. Issue&nbsp;#36 (mamd69) reported this against two
720
+ shell hooks; the MCP server had the identical bug and was <b>not</b> in the report. Fixing only
721
+ what was reported would have left the symptom alive and made the fix look wrong. All three now
722
+ write user-level only, keeping the working directory as a data <i>field</i> so per-project
723
+ analysis survives without writing into a project.</div>
724
+ </div>
725
+ </details>
726
+
727
+ <!-- 3 · HOOKS -->
728
+ <details class="card rail-act">
729
+ <summary>
730
+ <span class="card-ic ic-act" aria-hidden="true">
731
+ <svg viewBox="0 0 24 24"><path d="M13 2 4.5 13H11l-1 9 8.5-11H12l1-9Z"/></svg>
732
+ </span>
733
+ <span class="card-head">
734
+ <h2>3 · The hooks</h2>
735
+ <span class="q">The part people mean by “hooks and this and that”. Ten scripts, six events, one readable file.</span>
736
+ </span>
737
+ <span class="chips"><span class="chip tone-opt">optional, individually</span><span class="chip tone-cost">~630 tok / prompt</span></span>
738
+ <span class="chev" aria-hidden="true">›</span>
739
+ </summary>
740
+ <div class="body">
741
+ <p>Everything here is one file you can open and edit line by line:
742
+ <code>plugin/hooks/hooks.json</code>. Six Claude Code events, 14 invocations, 10 distinct
743
+ scripts. Nothing is all-or-nothing — each row below is an entry you can delete.</p>
744
+
745
+ <div class="scroll-x">
746
+ <table class="tb">
747
+ <thead><tr><th>Event</th><th>Script</th><th>Mode</th><th>What it actually does</th></tr></thead>
748
+ <tbody>
749
+ <tr><td class="cell-mono">SessionStart</td><td class="cell-name">session-start.sh</td><td><span class="part">nudge</span></td><td>Tells you the brain is on. Runs 4 pure-filesystem health checks in under 5 ms — no cache dir? no <code>.rvf</code> at all? reader deps gone? last real search failed? Injects the capability playbook once per session.</td></tr>
750
+ <tr><td class="cell-mono">UserPromptSubmit</td><td class="cell-name">ground-ruvnet.sh</td><td><span class="part">nudge</span></td><td>Three gates on your prompt: <b>RuvNet</b> (you named the stack → ground before asserting), <b>drift</b> (you reached for a classical default → name the rUv replacement), <b>build</b> (a build request → apply the playbook).</td></tr>
751
+ <tr><td class="cell-mono">PreToolUse<small>Write · Edit · Bash</small></td><td class="cell-name">hijack-ruvnet.sh</td><td><span class="part">nudge</span></td><td>Scans the payload for four categories of classical default — generic vector stores, paid embedding APIs, generic RAG frameworks, hand-rolled memory glue — and names the rUv replacement. <b>Never blocks.</b> One word on line 12 would make it block; it ships as advisory because a false-positive refusal bricks real work.</td></tr>
752
+ <tr><td class="cell-mono">PreToolUse<small>Task</small></td><td class="cell-name">route-dispatch.sh</td><td><span class="no">blocks</span></td><td>Refuses a subagent dispatch that would silently inherit your expensive main-loop model. <b>Opt-in only</b> — with no router profile on disk it does nothing, not even warn.</td></tr>
753
+ <tr><td class="cell-mono">PreToolUse<small>Bash</small></td><td class="cell-name">verify-interface.sh</td><td><span class="no">blocks</span></td><td>Refuses an ecosystem CLI subcommand whose <code>--help</code> you have not read in 24 h. Born from reporting AgentDB broken three times when the real defect was passing a query positionally instead of with <code>-q</code>.</td></tr>
754
+ <tr><td class="cell-mono">PreToolUse<small>Bash</small></td><td class="cell-name">design-wall.sh</td><td><span class="no">blocks</span></td><td>Refuses shipping or opening a visual surface with no fresh design grade. <b>Scoped to this repo</b> since issue&nbsp;#17, after a plain <code>git commit</code> in an unrelated project got blocked demanding someone else's ritual.</td></tr>
755
+ <tr><td class="cell-mono">PreToolUse · Stop</td><td class="cell-name">lesson-hooks.sh</td><td><span class="part">nudge</span></td><td>Surfaces your own recorded corrections at the moment they apply — writing code, changing the machine, asserting a fact, claiming done.</td></tr>
756
+ <tr><td class="cell-mono">PostToolUse</td><td class="cell-name">learn-capture.sh</td><td><span class="part">nudge</span></td><td>Appends one compact step to the session's learning queue: a command <i>verb</i> or a file's <i>basename</i>. Never content, never full paths, never secrets.</td></tr>
757
+ <tr><td class="cell-mono">SessionEnd</td><td class="cell-name">learn-flush.mjs</td><td><span class="part">nudge</span></td><td>Drains that queue into the learner.</td></tr>
758
+ <tr><td class="cell-mono">Stop</td><td class="cell-name">continuation-gate.mjs</td><td><span class="part">nudge</span></td><td>Fires on <i>stopping</i> — the one thing no other gate can catch, because stopping is the absence of an action and every other gate needs a tool call to intercept.</td></tr>
759
+ </tbody>
760
+ </table>
761
+ </div>
762
+
763
+ <h3>What the hooks have actually cost, in your context window</h3>
764
+ <p>Real byte counts of what was injected, not estimates. Window: <b>10 h 39 m, 2,272 recorded
765
+ injections.</b></p>
766
+ <div class="scroll-x">
767
+ <table class="tb">
768
+ <thead><tr><th></th><th class="num">Fires</th><th class="num">Median</th><th class="num">Mean</th><th class="num">p90</th><th class="num">Silent</th></tr></thead>
769
+ <tbody>
770
+ <tr><td class="cell-name">Per prompt</td><td class="cell-mono">1,986</td><td class="cell-mono">2,520 ch · ~630 tok</td><td class="cell-mono">2,519 ch</td><td class="cell-mono">3,771 ch</td><td class="cell-mono">165 (8.3%)</td></tr>
771
+ <tr><td class="cell-name">Per session</td><td class="cell-mono">286</td><td class="cell-mono">8,488 ch · ~2,120 tok</td><td class="cell-mono">8,637 ch</td><td class="cell-mono">—</td><td class="cell-mono">0</td></tr>
772
+ <tr class="row-cost"><td class="cell-name"><b>Total</b></td><td class="cell-mono" colspan="5"><b>7,472,556 characters ≈ 1.87 M tokens</b> across that window</td></tr>
773
+ </tbody>
774
+ </table>
775
+ </div>
776
+ <pre><code>node scripts/token-report.mjs # re-derive this on your own machine
777
+ RUVNET_BRAIN_METER=0 # stop the measuring (editing hooks.json stops the injecting)</code></pre>
778
+
779
+ <p style="margin-top:14px">That is the honest bill and it is not small: roughly 630 tokens on a
780
+ typical prompt, ~2,120 once per session. In exchange, grounding and drift correction happen
781
+ without you remembering to ask for them. <b>If that trade is wrong for you, delete the entries.</b>
782
+ The point is that the number is measurable at all — nothing else in the Claude Code stack prints
783
+ it, and you cannot make an informed trade against a cost nobody shows you.</p>
784
+
785
+ <h3>What the blocking gates have actually refused</h3>
786
+ <p>Real refusals over 2.5 days, across 13 different project directories.</p>
787
+ <div class="scroll-x">
788
+ <table class="tb">
789
+ <thead><tr><th>Gate</th><th class="num">Refusals</th><th>In a fresh install?</th></tr></thead>
790
+ <tbody>
791
+ <tr><td class="cell-name">route-dispatch</td><td class="cell-mono">142</td><td><span class="part">▲</span> shipped, but inert unless you opt into cost routing</td></tr>
792
+ <tr><td class="cell-name">verify-interface</td><td class="cell-mono">50</td><td><span class="yes">✓</span> yes</td></tr>
793
+ <tr><td class="cell-name">ground-before-write</td><td class="cell-mono">11</td><td><span class="no">✕</span> <b>no</b> — wired in this machine's own global settings</td></tr>
794
+ <tr><td class="cell-name">design-wall</td><td class="cell-mono">7</td><td><span class="part">▲</span> shipped, fires only inside this repo</td></tr>
795
+ <tr><td class="cell-name">version-bump-gate</td><td class="cell-mono">5</td><td><span class="no">✕</span> <b>no</b> — wired in this repo's project settings</td></tr>
796
+ </tbody>
797
+ </table>
798
+ </div>
799
+
800
+ <div class="why"><b>Two of those five are not part of what you install, and saying otherwise
801
+ would be the exact dishonesty this project exists to kill.</b> A fresh install gets <b>three</b>
802
+ blocking gates: one inert without opt-in, one that only fires inside this repository. In practice
803
+ a new user is subject to exactly one — <code>verify-interface</code>. The <code>version-bump-gate</code>
804
+ lived in <i>global</i> settings until 2026-07-14, taxing every Bash command in 34+ projects with a
805
+ rule that is true in one. A hook belongs in the narrowest scope where it is true.</div>
806
+
807
+ <h3>What their absence costs, specifically</h3>
808
+ <dl class="beat">
809
+ <dt>Grounding</dt><dd>1,986 injections per 10.6 hours become <b>zero</b>. The model consults the brain only when it independently decides to — measured across this project's history, that is the failure mode, not the fallback.</dd>
810
+ <dt>Drift</dt><dd>Four categories of anti-pattern stop watching your writes. Nothing says "you're about to add a server-based vector store; a single RVF file is the database."</dd>
811
+ <dt>Alarm</dt><dd>On 2026-07-12 this brain's dependencies vanished and every search failed <b>for days, invisibly</b> — returning empty results that read as "nothing exists". The four structural checks exist because of that, and they are the only thing that distinguishes a dead brain from a quiet one.</dd>
812
+ <dt>Learning</dt><dd>The queue never fills. It already starved once: the flush originally fired only on a clean session end, so sessions that compacted, crashed, or resumed never reached it. The queue grew to <b>1,884 undelivered events</b> while the learner sat at 5 trajectories, last trained six days earlier. Draining it took the learner to <b>412 / 412</b> in one command.</dd>
813
+ </dl>
814
+ </div>
815
+ </details>
816
+
817
+ <!-- 4 · SPINE -->
818
+ <details class="card rail-prove">
819
+ <summary>
820
+ <span class="card-ic ic-prove" aria-hidden="true">
821
+ <svg viewBox="0 0 24 24"><path d="M20 11.5a8 8 0 1 0-2.3 5.6"/><polyline points="20 5.5 20 11.5 14 11.5"/></svg>
822
+ </span>
823
+ <span class="card-head">
824
+ <h2>4 · The stable spine</h2>
825
+ <span class="q">Lets hook behaviour update without restarting your editor.</span>
826
+ </span>
827
+ <span class="chips"><span class="chip tone-opt">optional</span></span>
828
+ <span class="chev" aria-hidden="true">›</span>
829
+ </summary>
830
+ <div class="body">
831
+ <dl class="beat">
832
+ <dt>What</dt><dd>Immutable copies of the hook bodies under a versions directory, and a pointer file naming the active one. A single shim is the only thing the hook config names; it resolves that pointer once per invocation.</dd>
833
+ <dt>Gives</dt><dd>Behaviour updates the instant the pointer flips — <b>no Claude Code restart</b>. Claude Code freezes the plugin root at boot, so without this every hook is trapped at the version that was on disk when you opened your editor.</dd>
834
+ <dt>Costs</dt><dd>One small JSON read per hook fire. Disk for retained generations — garbage-collected, with leases so a tree still being served by a running MCP process is never collected out from under it.</dd>
835
+ <dt class="lose">Lose</dt><dd class="lose">Update correctness, and the ability to know when you have lost it.</dd>
836
+ </dl>
837
+ <div class="why"><b>Silence has to mean one thing.</b> The shim distinguishes three states and is
838
+ deliberately noisy about two: a spine that resolves but is missing a body file falls back to the
839
+ frozen plugin <i>and prints a line naming the fallback</i>; a previously-seeded-but-now-broken
840
+ spine does the same. Only a genuine first install is quiet. That asymmetry exists so a broken
841
+ spine can never masquerade as health.</div>
842
+ </div>
843
+ </details>
844
+
845
+ <!-- 5 · CONSOLE -->
846
+ <details class="card rail-mirror">
847
+ <summary>
848
+ <span class="card-ic ic-mirror" aria-hidden="true">
849
+ <svg viewBox="0 0 24 24"><rect x="2.5" y="4" width="19" height="13" rx="2"/><line x1="8" y1="20.5" x2="16" y2="20.5"/><line x1="12" y1="17" x2="12" y2="20.5"/></svg>
850
+ </span>
851
+ <span class="card-head">
852
+ <h2>5 · The console</h2>
853
+ <span class="q">Your machine, mirrored — and reversible one-click fixes. The page you are one click from.</span>
854
+ </span>
855
+ <span class="chips"><span class="chip tone-opt">optional</span><span class="chip tone-req">read-only by default</span></span>
856
+ <span class="chev" aria-hidden="true">›</span>
857
+ </summary>
858
+ <div class="body">
859
+ <dl class="beat">
860
+ <dt>What</dt><dd>A local page bound to <code>127.0.0.1</code> with a random per-launch token, opened with <code>/rvbc</code>.</dd>
861
+ <dt>Gives</dt><dd>Which rUv tools are installed and at what version, which capabilities are dormant, memory health, router utilisation — and fixes you can undo.</dd>
862
+ <dt>Costs</dt><dd>A local HTTP server while it is open. Network reads of the npm registry for the version audit. <b>Writes nothing until you click.</b></dd>
863
+ <dt class="lose">Lose</dt><dd class="lose">The ability to <b>see</b> any of this. Every other piece runs headless — the console is the window, not the engine. Its absence costs you discovery, which is precisely the complaint at the top of this page: he owns pieces he has never seen.</dd>
864
+ </dl>
865
+
866
+ <h3>Four laws, encoded in code rather than promised</h3>
867
+ <dl class="beat">
868
+ <dt>Read-only</dt><dd>Rendering the page and building its state writes nothing.</dd>
869
+ <dt>Re-verify</dt><dd>Apply re-measures the world and refuses any item that is no longer true.</dd>
870
+ <dt>Undo first</dt><dd>The inverse is journalled <i>before</i> the change runs.</dd>
871
+ <dt>Never re-do</dt><dd>Every mutation dispatches to a script that already backs up, verifies against disk, and is idempotent.</dd>
872
+ </dl>
873
+
874
+ <div class="why"><b>A recommendation cannot be constructed without evidence, a cost, and an undo.</b>
875
+ The factory throws otherwise — and if the change touches the machine it additionally demands a
876
+ plain-English impact statement of at least 40 characters. That is a type error at construction,
877
+ not a code review someone can forget. Law 2 exists because on 2026-07-12 two concurrent sessions
878
+ both wrote a memory checkpoint and one silently destroyed the other's entire state, with no error
879
+ and only a changed row id as evidence.</div>
880
+ </div>
881
+ </details>
882
+
883
+ <!-- 6 · CAPABILITY REGISTRY -->
884
+ <details class="card rail-mirror">
885
+ <summary>
886
+ <span class="card-ic ic-mirror" aria-hidden="true">
887
+ <svg viewBox="0 0 24 24"><line x1="9" y1="6.5" x2="20.5" y2="6.5"/><line x1="9" y1="12" x2="20.5" y2="12"/><line x1="9" y1="17.5" x2="20.5" y2="17.5"/><circle cx="4.5" cy="6.5" r="1.6"/><circle cx="4.5" cy="12" r="1.6"/><circle cx="4.5" cy="17.5" r="1.6"/></svg>
888
+ </span>
889
+ <span class="card-head">
890
+ <h2>6 · The capability registry</h2>
891
+ <span class="q">Eleven rows answering the flat question: is this one actually on?</span>
892
+ </span>
893
+ <span class="chips"><span class="chip tone-opt">optional</span></span>
894
+ <span class="chev" aria-hidden="true">›</span>
895
+ </summary>
896
+ <div class="body">
897
+ <dl class="beat">
898
+ <dt>What</dt><dd>Eleven capabilities, each with a live probe — learning hooks, memory distillation, workflow learning, cheap-model routing, cross-project lessons, lessons in force, harness self-improvement, write gates, session capture, connected tools, nightly refresh.</dd>
899
+ <dt>Gives</dt><dd>A row per capability whether the news is good, bad, or unavailable. An audit only speaks when something is <i>wrong</i>, so a healthy capability produces no finding at all — and silence is indistinguishable from "I never looked".</dd>
900
+ <dt>Costs</dt><dd>Eleven read-only probes. It observes; it never installs, enables, or writes.</dd>
901
+ <dt class="lose">Lose</dt><dd class="lose">The honest form of "you lose proactivity": <b>you lose the flat inventory that distinguishes <i>off</i> from <i>unmeasurable</i></b>, so a dormant capability and a broken probe look identical. That is exactly how a starving learner sat unnoticed for six days.</dd>
902
+ </dl>
903
+
904
+ <h3>Two rules it exists to enforce</h3>
905
+ <p><b>“Unknown” outranks “off”, always.</b> Reporting "off" for something you failed to measure
906
+ is not a rounding error — it is the precise lie this project was built to kill, and it is
907
+ <i>easy</i> to commit here because every underlying helper has a falsy default. While this file
908
+ was being written, a live probe of this repo's own memory store returned "unable to open database
909
+ file". Ninety seconds later the identical query returned 1,201 memories, 99.8% embedded, 596
910
+ distilled patterns: the store was fully healthy and the first read had simply lost a race with a
911
+ concurrent writer. <b>One transient lock, and the console would have told its owner to fix a
912
+ system that was already working.</b></p>
913
+
914
+ <p><b>No command is offered unless it was run with <code>--help</code> first.</b> Four of the
915
+ eleven therefore offer no turn-on command at all, each recording the negative check that produced
916
+ the null so nobody re-litigates it from memory. A confidently-wrong command is worse than none —
917
+ it sends a person to a shell to be told "unknown subcommand", which costs them trust in every
918
+ other row on the page.</p>
919
+
920
+ <div class="why"><b>The most instructive bug in the repository.</b> Every project-scoped detector
921
+ used to read where the <i>code</i> was installed instead of where the <i>user</i> was standing.
922
+ From an empty folder it reported "write gates: ON — 6 gates can refuse a write, 203 refusals
923
+ recorded" — <i>ruvnet-brain's own numbers, presented as yours</i>. From a real project with a
924
+ healthy 16 MB memory store it reported "memory distillation: ABSENT", with a button offering to
925
+ fix a problem you did not have. Anyone not standing inside a ruvnet-brain checkout — which is
926
+ every user — got one of those two.</div>
927
+ </div>
928
+ </details>
929
+
930
+ <!-- 7 · LESSON STORE -->
931
+ <details class="card rail-act">
932
+ <summary>
933
+ <span class="card-ic ic-act" aria-hidden="true">
934
+ <svg viewBox="0 0 24 24"><path d="M4 4.5h6a3 3 0 0 1 3 3v12a2.4 2.4 0 0 0-2.4-2.4H4Z"/><path d="M22 4.5h-6a3 3 0 0 0-3 3v12a2.4 2.4 0 0 1 2.4-2.4H22Z"/></svg>
935
+ </span>
936
+ <span class="card-head">
937
+ <h2>7 · The lesson store</h2>
938
+ <span class="q">Your corrections, replayed at the moment they apply. It nudges — it does not force.</span>
939
+ </span>
940
+ <span class="chips"><span class="chip tone-opt">optional</span><span class="chip tone-req">nudge by default</span></span>
941
+ <span class="chev" aria-hidden="true">›</span>
942
+ </summary>
943
+ <div class="body">
944
+ <dl class="beat">
945
+ <dt>What</dt><dd>Corrections you have given, structured, and surfaced at the decision point where they apply. <b>15 lessons</b> today — 12 ratified, 3 candidate — across ten decision types: asserting a fact, recommending an architecture, relaying a number, reporting status, writing code, claiming done, shipping, changing the machine, choosing work, finishing.</dd>
946
+ <dt>Gives</dt><dd>You stop re-explaining the same preference. The store currently holds one lesson you have had to give <b>six times</b>.</dd>
947
+ <dt>Costs</dt><dd>One process spawn per matching event, hard-capped at 5 seconds where the timeout utility exists.</dd>
948
+ <dt class="lose">Lose</dt><dd class="lose">Your corrections stay prose in a file nobody reads at the moment they would have mattered.</dd>
949
+ </dl>
950
+
951
+ <h3>One dispatcher, not one hook per lesson</h3>
952
+ <p>Gates scale with <b>decision types</b>, never with lesson count. Adding a lesson requires no
953
+ code change anywhere — and that asymmetry is the whole architecture. Without it, every new rule
954
+ would need its own script, which is the "thousand hooks" outcome the owner explicitly rejected.</p>
955
+
956
+ <h3>It nudges. It does not force.</h3>
957
+ <p>A lesson may <i>declare</i> that it blocks and still arrive as advisory context unless you have
958
+ personally opted that lesson into refusing. That is deliberate, and it is the owner's own
959
+ correction of an earlier design: <b>“Nudging somebody is very fair. Forcing them through a gate is
960
+ not.”</b></p>
961
+ <pre><code>$ bash plugin/scripts/lesson-hooks.sh Stop ; echo $?
962
+ {"hookSpecificOutput":{"hookEventName":"Stop","additionalContext":"…These are
963
+ advisory — they do not refuse anything, and you may proceed…"}}
964
+ 0</code></pre>
965
+
966
+ <div class="why"><b>Why that behaviour is shown as a transcript instead of described.</b> On
967
+ 2026-07-22 the previous version printed <b>“⛔ BLOCKED”</b> and returned exit 0 — three stacked
968
+ defects, each individually sufficient: the gate exited 1 where the harness requires 2, wrote its
969
+ reason to the stream exit-2 ignores, and the dispatcher discarded the code entirely. An ADR
970
+ claimed "five gates refuse the action — proven by exit code". The proof had been obtained by
971
+ running the CLI by hand, <b>which is the one caller that is not a gate</b>. Never verify a hook by
972
+ running it the way a hook doesn't.</div>
973
+
974
+ <div class="why"><b>Fail-open, defined correctly.</b> The old code implemented it as "always
975
+ allow", which is not the same thing. Failing open means an <i>error</i> must not refuse you — a
976
+ missing interpreter, an unreadable store, a timeout. It never meant discarding a <i>deliberate</i>
977
+ refusal. Those are now separated by exit code: one value is a decision and propagates, everything
978
+ else is a malfunction and allows.</div>
979
+ </div>
980
+ </details>
981
+
982
+ <!-- 8 · NIGHTLY -->
983
+ <details class="card rail-prove">
984
+ <summary>
985
+ <span class="card-ic ic-prove" aria-hidden="true">
986
+ <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><polyline points="12 6.8 12 12 15.6 14"/></svg>
987
+ </span>
988
+ <span class="card-head">
989
+ <h2>8 · The nightly refresh</h2>
990
+ <span class="q">Rebuilds the corpus so it does not rot. The only piece that installs a scheduled job.</span>
991
+ </span>
992
+ <span class="chips"><span class="chip tone-opt">optional</span><span class="chip tone-cost">changes your machine</span></span>
993
+ <span class="chev" aria-hidden="true">›</span>
994
+ </summary>
995
+ <div class="body">
996
+ <dl class="beat">
997
+ <dt>What</dt><dd>A scheduled job that rebuilds the corpus from upstream.</dd>
998
+ <dt>Gives</dt><dd>A corpus that does not go stale. rUv ships fast — one of his tools went from 3.26 to 3.28 inside an 18-hour window.</dd>
999
+ <dt>Costs</dt><dd>A launchd plist: a <b>real machine mutation</b>, which is why the installer asks for it behind its own flag and a blanket "yes to everything" cannot silently accept it. Hours of CPU on rebuild nights. Network.</dd>
1000
+ <dt class="lose">Lose</dt><dd class="lose">Freshness, degrading continuously — but <b>visibly</b>. The staleness line on every search response prints the real age of the stores it queried, so you can always see how bad it has become. You can also refresh by hand at any time.</dd>
1001
+ </dl>
1002
+ <div class="why"><b>Its operating discipline: no noise for success or a no-op.</b> Run once → on
1003
+ failure wait and retry <i>once</i> (the only class a blind retry can fix) → on a second failure, a
1004
+ loud alert <i>plus</i> a durable marker file that the session-start hook surfaces unprompted at
1005
+ the top of the very next session. A single-instance lock guards against a rebuild that can run for
1006
+ hours (verified: 2 h 29 m and still embedding) still being alive when the next night's run fires.
1007
+ A reserved exit code marks "skipped, previous run still going" so a skip is never recorded as a
1008
+ failure — <b>silence is never allowed to mean health</b>.</div>
1009
+ </div>
1010
+ </details>
1011
+
1012
+ <!-- 9 · COMMANDS + SKILLS -->
1013
+ <details class="card rail-act">
1014
+ <summary>
1015
+ <span class="card-ic ic-act" aria-hidden="true">
1016
+ <svg viewBox="0 0 24 24"><polyline points="4.5 7 9.5 12 4.5 17"/><line x1="12" y1="17" x2="19.5" y2="17"/></svg>
1017
+ </span>
1018
+ <span class="card-head">
1019
+ <h2>9 · Commands and skills</h2>
1020
+ <span class="q">The doors. Four slash commands, five skills.</span>
1021
+ </span>
1022
+ <span class="chips"><span class="chip tone-opt">optional</span></span>
1023
+ <span class="chev" aria-hidden="true">›</span>
1024
+ </summary>
1025
+ <div class="body">
1026
+ <dl class="beat">
1027
+ <dt>What</dt><dd><b>Four commands:</b> three aliases for the console (people misremember the initials, so all three work) and one for configuration. <b>Five skills</b>, led by the behavioural spine: ground every rUv claim in real source before asserting it.</dd>
1028
+ <dt>Gives</dt><dd>A way in that you do not have to remember a file path for.</dd>
1029
+ <dt>Costs</dt><dd>Nothing at rest.</dd>
1030
+ <dt class="lose">Lose</dt><dd class="lose">The doors. Every capability is still reachable by running its script directly — you just have to know it exists, which returns you to the complaint at the top of this page.</dd>
1031
+ </dl>
1032
+ </div>
1033
+ </details>
1034
+
1035
+ <!-- ======================================================= THE MATRIX -->
1036
+ <div class="sec-head">
1037
+ <p class="eyebrow">the decision</p>
1038
+ <h2>Four configurations, honestly compared</h2>
1039
+ <p>“Brain only” is a legitimate choice, not a broken install. Here is precisely what each shape
1040
+ can and cannot do.</p>
1041
+ </div>
1042
+
1043
+ <div class="scroll-x">
1044
+ <table class="tb">
1045
+ <thead>
1046
+ <tr>
1047
+ <th></th>
1048
+ <th>Brain only</th>
1049
+ <th>Brain + MCP</th>
1050
+ <th>+ hooks</th>
1051
+ <th>Full plugin</th>
1052
+ </tr>
1053
+ </thead>
1054
+ <tbody>
1055
+ <tr><td class="cell-name">Search 69 repos</td>
1056
+ <td><span class="part">▲</span> by hand<small>~110 s / question</small></td>
1057
+ <td><span class="yes">✓</span> in-conversation</td><td><span class="yes">✓</span></td><td><span class="yes">✓</span></td></tr>
1058
+ <tr><td class="cell-name">Model consults it mid-answer</td>
1059
+ <td><span class="no">✕</span><small>you paste results in</small></td>
1060
+ <td><span class="part">▲</span><small>only when it chooses to</small></td>
1061
+ <td><span class="yes">✓</span><small>prompted every turn</small></td><td><span class="yes">✓</span></td></tr>
1062
+ <tr><td class="cell-name">Unprompted grounding</td>
1063
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1064
+ <td><span class="yes">✓</span><small>1,986 injections / 10.6 h</small></td><td><span class="yes">✓</span></td></tr>
1065
+ <tr><td class="cell-name">Drift interception</td>
1066
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1067
+ <td><span class="yes">✓</span><small>4 categories, ~20 patterns</small></td><td><span class="yes">✓</span></td></tr>
1068
+ <tr><td class="cell-name">Brain-health alarm</td>
1069
+ <td><span class="no">✕</span><small>silent death</small></td>
1070
+ <td><span class="no">✕</span><small>silent death</small></td>
1071
+ <td><span class="yes">✓</span><small>4 checks per session</small></td><td><span class="yes">✓</span></td></tr>
1072
+ <tr><td class="cell-name">Blocking gates</td>
1073
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1074
+ <td><span class="yes">✓</span><small>3 shipped: 1 opt-in, 1 repo-scoped</small></td><td><span class="yes">✓</span></td></tr>
1075
+ <tr><td class="cell-name">Learning capture</td>
1076
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td><td><span class="yes">✓</span></td><td><span class="yes">✓</span></td></tr>
1077
+ <tr><td class="cell-name">Lesson nudges</td>
1078
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1079
+ <td><span class="yes">✓</span><small>15 lessons, 10 decision types</small></td><td><span class="yes">✓</span></td></tr>
1080
+ <tr><td class="cell-name">11-capability audit</td>
1081
+ <td><span class="part">▲</span><small>by command</small></td>
1082
+ <td><span class="part">▲</span><small>by command</small></td>
1083
+ <td><span class="part">▲</span><small>by command</small></td>
1084
+ <td><span class="yes">✓</span><small>on screen, in the console</small></td></tr>
1085
+ <tr><td class="cell-name">Console</td>
1086
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1087
+ <td><span class="yes">✓</span></td></tr>
1088
+ <tr><td class="cell-name">Commands &amp; skills</td>
1089
+ <td><span class="no">✕</span></td><td><span class="no">✕</span></td><td><span class="no">✕</span></td>
1090
+ <td><span class="yes">✓</span><small>4 + 5</small></td></tr>
1091
+ <tr><td class="cell-name">Updates without restart</td>
1092
+ <td><span class="no">✕</span></td>
1093
+ <td><span class="part">▲</span><small>retrieval only</small></td>
1094
+ <td><span class="yes">✓</span></td><td><span class="yes">✓</span></td></tr>
1095
+ <tr class="row-cost"><td class="cell-name"><b>Context cost / prompt</b></td>
1096
+ <td class="cell-mono"><b>0</b></td><td class="cell-mono"><b>0</b><small>until called</small></td>
1097
+ <td class="cell-mono"><b>~630 tok</b></td><td class="cell-mono"><b>~630 tok</b></td></tr>
1098
+ <tr class="row-cost"><td class="cell-name"><b>Context cost / session</b></td>
1099
+ <td class="cell-mono"><b>0</b></td><td class="cell-mono"><b>0</b></td>
1100
+ <td class="cell-mono"><b>~2,120 tok</b></td><td class="cell-mono"><b>~2,120 tok</b></td></tr>
1101
+ <tr class="row-cost"><td class="cell-name"><b>Can refuse your action</b></td>
1102
+ <td class="cell-mono"><b>never</b></td><td class="cell-mono"><b>never</b></td>
1103
+ <td class="cell-mono"><b>3 gates</b></td><td class="cell-mono"><b>3 gates</b></td></tr>
1104
+ <tr class="row-cost"><td class="cell-name"><b>Writes in your project dirs</b></td>
1105
+ <td class="cell-mono"><b>none</b></td><td class="cell-mono"><b>none</b></td>
1106
+ <td class="cell-mono"><b>none</b></td><td class="cell-mono"><b>none</b></td></tr>
1107
+ </tbody>
1108
+ </table>
1109
+ </div>
1110
+
1111
+ <div class="why" style="margin-top:18px">
1112
+ <b>Reading this as the power user.</b> Taking only the brain costs you, in numbers: 1,986 grounding
1113
+ injections per 10.6 hours become zero; four categories of drift interception stop watching your
1114
+ writes; the four health checks that catch a silently dead brain stop running — <i>and that failure
1115
+ ran for days on 2026-07-12, returning empty results that read as "nothing exists"</i>; the learning
1116
+ queue never fills; 15 lessons across 10 decision points never surface; and the 11-row audit, the
1117
+ one thing that would have told you any of this was switched off, is not on screen.<br><br>
1118
+ What it <b>saves</b> you: ~630 tokens per prompt, ~2,120 per session, three gates that can refuse a
1119
+ shell or subagent call, and any writes under the cache directory. If you are a heavy rUv user the
1120
+ trade favours the full plugin decisively. <b>If you use one rUv tool occasionally and value an
1121
+ uninterrupted shell, brain-only or brain+MCP is genuinely the right shape — and nothing on this
1122
+ page is trying to talk you out of it.</b>
1123
+ </div>
1124
+
1125
+ <!-- ======================================================= SCOPE -->
1126
+ <div class="sec-head">
1127
+ <p class="eyebrow">the other decision</p>
1128
+ <h2>Per-user or per-project?</h2>
1129
+ </div>
1130
+
1131
+ <div class="scroll-x">
1132
+ <table class="tb">
1133
+ <thead><tr><th></th><th>Per-user <span class="mono muted">— default, recommended</span></th><th>Per-project</th></tr></thead>
1134
+ <tbody>
1135
+ <tr><td class="cell-name">Corpus</td><td>Once, in your cache directory</td><td class="cell-mono">2.7 GB <b>per project</b></td></tr>
1136
+ <tr><td class="cell-name">Learning &amp; lessons</td><td>Compound across <b>every</b> project, including ones you have not created yet</td><td><span class="no">✕</span> cannot compound — there is nothing to compound across</td></tr>
1137
+ <tr><td class="cell-name">Versions</td><td>Stay current everywhere at once</td><td>Updated per project, by hand</td></tr>
1138
+ <tr><td class="cell-name">Isolation</td><td>Shared</td><td><span class="yes">✓</span> complete</td></tr>
1139
+ <tr><td class="cell-name">Choose it when</td><td>You want learning, intelligence, access and versions to stay current across all your work</td><td>You genuinely use this in <b>one</b> project alone</td></tr>
1140
+ </tbody>
1141
+ </table>
1142
+ </div>
1143
+
1144
+ <p style="margin-top:16px">Normally this happens on a <b>per-user</b> basis, which lets learning,
1145
+ intelligence, access and software versions stay updated universally across all your projects. Choose
1146
+ per-project only if this is something you absolutely only use on a per-project basis. Our strong
1147
+ recommendation is per-user — <b>but we always want you to be the arbiter of how things run on your
1148
+ machine.</b></p>
1149
+
1150
+ <p>Nothing below the plugin install is all-or-nothing either. The hook config is a plain file you can
1151
+ trim entry by entry, and the hooks table above tells you exactly what each line does before you
1152
+ delete it.</p>
1153
+
1154
+ <!-- ======================================================= WHAT LANDS -->
1155
+ <div class="sec-head">
1156
+ <p class="eyebrow">full disclosure</p>
1157
+ <h2>What lands on your machine</h2>
1158
+ <p>Everything under your cache and config directories. <b>Nothing is written into your project
1159
+ directories</b> — that is load-bearing, and it was once false.</p>
1160
+ </div>
1161
+
1162
+ <div class="scroll-x">
1163
+ <table class="tb">
1164
+ <thead><tr><th>Path</th><th>Written by</th><th>What</th></tr></thead>
1165
+ <tbody>
1166
+ <tr><td class="cell-name">~/.cache/ruvnet-brain/kb/</td><td>installer</td><td>the corpus (2.7 GB)</td></tr>
1167
+ <tr><td class="cell-name">~/.cache/ruvnet-brain/versions/</td><td>updater</td><td>the stable spine and its pointer</td></tr>
1168
+ <tr><td class="cell-name">…/token-ledger.jsonl</td><td>hooks + MCP</td><td>injected-byte meter — the numbers on this page. <code>RUVNET_BRAIN_METER=0</code> disables it</td></tr>
1169
+ <tr><td class="cell-name">…/learn/session-*.jsonl</td><td>learn-capture</td><td>command verbs and file basenames. <b>Never content, never full paths, never secrets</b></td></tr>
1170
+ <tr><td class="cell-name">…/gate-blocks.jsonl</td><td>the gates</td><td>refusal receipts</td></tr>
1171
+ <tr><td class="cell-name">…/health.json</td><td>brain alarm</td><td>last known retrieval health</td></tr>
1172
+ <tr><td class="cell-name">…/console-undo.jsonl</td><td>console apply</td><td>the inverse of every change, journalled <i>first</i></td></tr>
1173
+ <tr><td class="cell-name">~/.config/ruvnet-brain/lessons.json</td><td>you, ratifying</td><td>your corrections</td></tr>
1174
+ <tr><td class="cell-name">launchd plists</td><td><b>only if you say yes</b></td><td>the nightly refresh and friends</td></tr>
1175
+ </tbody>
1176
+ </table>
1177
+ </div>
1178
+
1179
+ <!-- ======================================================= NOT CLAIMED -->
1180
+ <div class="sec-head">
1181
+ <p class="eyebrow">the limits</p>
1182
+ <h2>What this page does not claim</h2>
1183
+ </div>
1184
+
1185
+ <dl class="beat" style="margin-top:6px">
1186
+ <dt>Not claimed</dt><dd><b>That every gate blocks.</b> Three do. Two of the five in the refusal ledger are wired in this machine's own settings and are not part of a fresh install. The lesson gate nudges.</dd>
1187
+ <dt>Not claimed</dt><dd><b>That search is fast.</b> It is 1.5–2.5 minutes per call on an M3 Max, measured.</dd>
1188
+ <dt>Not claimed</dt><dd><b>That the corpus is current.</b> It is a snapshot, and every response prints its age in days, per store.</dd>
1189
+ <dt>Not claimed</dt><dd><b>That 69 repos is the ecosystem.</b> 69 built, 123 pending, ~248 in the org.</dd>
1190
+ <dt>Not claimed</dt><dd><b>That any of this was verified on Windows or Linux.</b> Every number came from one macOS machine. The hook config declares itself posix-only.</dd>
1191
+ <dt>Not claimed</dt><dd><b>Anything about YOUR machine.</b> This page is static and reads nothing. For live state — what is installed, what is dormant, what is broken right now — that is the console's job, one click away in the header.</dd>
1192
+ </dl>
1193
+
1194
+ </main>
1195
+
1196
+ <footer class="foot">
1197
+ <div class="wrap foot-inner">
1198
+ <p class="foot-line">Every figure on this page was produced by running a command on a real machine
1199
+ on <b>2026-07-22</b>, not recalled. The full write-up, with the command behind each number, is in
1200
+ <code>docs/ARCHITECTURE-MAP.md</code>.</p>
1201
+ <p class="foot-meta mono">RuvNet Brain · static page · reads nothing off your machine</p>
1202
+ </div>
1203
+ </footer>
1204
+
1205
+ <script>
1206
+ /* The only script on the page: the theme toggle, same storage key as the console so the
1207
+ preference follows you between the three pages. No network, no state, no measurement —
1208
+ a page that claims to read nothing must actually read nothing. */
1209
+ (function () {
1210
+ var btn = document.getElementById('theme-toggle');
1211
+ if (!btn) return;
1212
+ btn.addEventListener('click', function () {
1213
+ var next = document.documentElement.dataset.theme === 'light' ? 'dark' : 'light';
1214
+ document.documentElement.dataset.theme = next;
1215
+ try { localStorage.setItem('rbc-theme', next); } catch (e) {}
1216
+ });
1217
+ })();
1218
+ </script>
1219
+
1220
+ </body>
1221
+ </html>