ruvnet-brain 3.9.134-dev → 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.
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +5 -5
- package/bin/install.mjs +382 -36
- package/console/CONTRACT.md +172 -0
- package/console/activity.js +753 -0
- package/console/app.js +4189 -0
- package/console/architecture.html +1221 -0
- package/console/assets/depth-1.webp +0 -0
- package/console/assets/depth-2.webp +0 -0
- package/console/assets/depth-3.webp +0 -0
- package/console/assets/harness-vs-plain.svg +259 -0
- package/console/assets/hero.webp +0 -0
- package/console/assets/memory.webp +0 -0
- package/console/assets/metaharness.svg +247 -0
- package/console/index.html +777 -0
- package/console/install-architecture.html +162 -0
- package/console/install-mockup.html +543 -0
- package/console/style.css +2144 -0
- package/console/tips.css +926 -0
- package/console/tips.html +858 -0
- package/console/tips.js +128 -0
- package/docs/RELEASE-NOTES-4.0.md +88 -0
- package/kb/model-requirements.mjs +37 -6
- package/kb/zip-extract.mjs +53 -14
- package/keys/ruvnet-brain-signing.pub.pem +3 -0
- package/package.json +14 -22
- package/plugin/.claude-plugin/marketplace.json +14 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/.codex-plugin/plugin.json +21 -0
- package/plugin/.mcp.json +8 -0
- package/plugin/commands/brain-console.md +16 -0
- package/plugin/commands/configure.md +33 -0
- package/plugin/commands/rvbc.md +79 -0
- package/plugin/commands/rvcb.md +16 -0
- package/plugin/commands/whats-new.md +57 -0
- package/plugin/hooks/codex-hooks.json +160 -0
- package/plugin/hooks/hook-contracts.json +77 -0
- package/plugin/hooks/hooks.json +202 -0
- package/plugin/mcp/managed-cli-interface.mjs +47 -4
- package/plugin/mcp/server.mjs +56 -6
- package/plugin/scripts/anticipate.sh +534 -0
- package/plugin/scripts/codex-hook-adapter.mjs +96 -0
- package/plugin/scripts/continuation-gate.mjs +267 -0
- package/plugin/scripts/design-wall.sh +137 -0
- package/plugin/scripts/detach.mjs +182 -0
- package/plugin/scripts/first-session-worker.mjs +38 -0
- package/plugin/scripts/gate-receipt.sh +35 -0
- package/plugin/scripts/ground-before-write.sh +199 -0
- package/plugin/scripts/ground-ruvnet.sh +517 -0
- package/plugin/scripts/grounding-stamp.sh +113 -0
- package/plugin/scripts/grounding-substance.mjs +595 -0
- package/plugin/scripts/hijack-ruvnet.sh +81 -0
- package/plugin/scripts/hook-input.mjs +558 -0
- package/plugin/scripts/hook-shim-bash.mjs +55 -0
- package/plugin/scripts/hook-shim.mjs +303 -0
- package/plugin/scripts/host-update.mjs +58 -0
- package/plugin/scripts/kling-preflight.sh +146 -0
- package/plugin/scripts/learn-capture.sh +173 -0
- package/plugin/scripts/learn-flush.mjs +155 -0
- package/plugin/scripts/lesson-hooks.sh +213 -0
- package/plugin/scripts/md-stamp.mjs +219 -0
- package/plugin/scripts/protect-brain-state.sh +84 -0
- package/plugin/scripts/route-dispatch.sh +147 -0
- package/plugin/scripts/routing-outcome-capture.mjs +89 -0
- package/plugin/scripts/runtime-preferences.mjs +269 -0
- package/plugin/scripts/session-start-core.mjs +477 -0
- package/plugin/scripts/session-start.sh +13 -0
- package/plugin/scripts/signal-watch.mjs +193 -0
- package/plugin/scripts/unprompted-runtime.mjs +377 -0
- package/plugin/scripts/update-apply.mjs +419 -0
- package/plugin/scripts/verify-interface.sh +53 -0
- package/plugin/scripts/version-bump-gate.sh +112 -0
- package/plugin/skills/brain-build/SKILL.md +123 -0
- package/plugin/skills/brain-console/SKILL.md +22 -0
- package/plugin/skills/brain-prompt/SKILL.md +83 -0
- package/plugin/skills/brain-score/SKILL.md +101 -0
- package/plugin/skills/release-proof/SKILL.md +81 -0
- package/plugin/skills/release-proof/agents/openai.yaml +4 -0
- package/plugin/skills/release-proof/references/receipt-contract.md +38 -0
- package/plugin/skills/release-proof/scripts/release-proof.mjs +210 -0
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +121 -0
- package/plugin/skills/ruvnet-brain/SKILL.md +234 -0
- package/plugin/skills/rvbc/SKILL.md +23 -0
- package/plugin/skills/savings/SKILL.md +46 -0
- package/plugin/skills/whats-new/SKILL.md +22 -0
- package/scripts/adr-backfill.mjs +107 -0
- package/scripts/advocacy-outcomes.mjs +808 -0
- package/scripts/agentdb-context.mjs +216 -0
- package/scripts/agentdb-fleet-doctor.mjs +101 -0
- package/scripts/ascii-drift.mjs +236 -0
- package/scripts/behavioral-l1-l4.mjs +210 -0
- package/scripts/brain-capability-check.mjs +72 -0
- package/scripts/brain-grade-groundtruth.mjs +100 -0
- package/scripts/brain-latency-50.mjs +227 -0
- package/scripts/brain-novice-50.mjs +189 -0
- package/scripts/brain-stamp.mjs +94 -0
- package/scripts/brain-state.mjs +212 -0
- package/scripts/build-bundle.mjs +522 -0
- package/scripts/build-concepts.mjs +132 -0
- package/scripts/build-l2.mjs +71 -0
- package/scripts/build-primer.mjs +73 -0
- package/scripts/build-symbols.mjs +68 -0
- package/scripts/calibrate-router.mjs +97 -0
- package/scripts/capability-audit.mjs +321 -0
- package/scripts/capability-registry.mjs +876 -0
- package/scripts/check-indexation.mjs +108 -0
- package/scripts/check-legibility.mjs +189 -0
- package/scripts/ci/build-fixture-kb.mjs +67 -0
- package/scripts/ci/learning-replay-codex-adapter.mjs +62 -0
- package/scripts/ci/learning-replay-recorder.mjs +59 -0
- package/scripts/ci/mutate-hook-timeout.mjs +70 -0
- package/scripts/ci/stranger-fixture-stage.mjs +17 -0
- package/scripts/ci/stranger-scenario.mjs +228 -0
- package/scripts/ci/stranger-timeout.mjs +25 -0
- package/scripts/ci-verdict.mjs +29 -0
- package/scripts/claims-verify.mjs +710 -0
- package/scripts/clear-claude-tmp.sh +31 -0
- package/scripts/console-engine.mjs +434 -0
- package/scripts/console-engine.test.mjs +125 -0
- package/scripts/corpus-qa.mjs +250 -0
- package/scripts/correction-detect-embed.mjs +346 -0
- package/scripts/correction-detect-measure.mjs +270 -0
- package/scripts/correction-detect.mjs +686 -0
- package/scripts/count-chunks.mjs +54 -0
- package/scripts/described-questions.json +30 -0
- package/scripts/design-grade.mjs +58 -0
- package/scripts/dev-plugin-link.sh +105 -0
- package/scripts/distill-project.mjs +200 -0
- package/scripts/doc-currency.mjs +801 -0
- package/scripts/eval-brain.mjs +244 -0
- package/scripts/fix-metaharness-memretrieve.mjs +121 -0
- package/scripts/full-hints.mjs +87 -0
- package/scripts/gate.sh +39 -0
- package/scripts/gates.mjs +146 -0
- package/scripts/gen-console-images.mjs +54 -0
- package/scripts/gen-images.mjs +47 -0
- package/scripts/git-clone-refresh.mjs +52 -0
- package/scripts/git-hooks/pre-push +126 -0
- package/scripts/goal-match.mjs +398 -0
- package/scripts/goldie-research.mjs +223 -0
- package/scripts/goldie-weekly.sh +67 -0
- package/scripts/health-repair.mjs +250 -0
- package/scripts/helix-scenario-questions.json +10 -0
- package/scripts/ingest-gists.mjs +230 -0
- package/scripts/ingest-meeting.mjs +115 -0
- package/scripts/ingest-repo.mjs +79 -0
- package/scripts/install-npx-witness.sh +49 -0
- package/scripts/issue-fix.mjs +639 -0
- package/scripts/issue-watch.mjs +276 -0
- package/scripts/issue4-close-note.md +31 -0
- package/scripts/key-canary.mjs +91 -0
- package/scripts/latency-to-surface.mjs +233 -0
- package/scripts/learning-enable.mjs +380 -0
- package/scripts/learning-replay.mjs +1570 -0
- package/scripts/learnings.mjs +62 -0
- package/scripts/lesson-gate.mjs +680 -0
- package/scripts/lesson-lifecycle.mjs +449 -0
- package/scripts/lesson-promote.mjs +262 -0
- package/scripts/lesson-ratify.mjs +98 -0
- package/scripts/lesson-seed.mjs +252 -0
- package/scripts/lesson-store.mjs +447 -0
- package/scripts/loop-checkpoint.mjs +86 -0
- package/scripts/memdb-health.sh +14 -0
- package/scripts/memory-doctor.mjs +271 -0
- package/scripts/model-catalog.mjs +79 -0
- package/scripts/nightly-controller.mjs +66 -0
- package/scripts/nightly-gists.sh +72 -0
- package/scripts/nightly-wrapper.sh +180 -0
- package/scripts/notify.sh +12 -0
- package/scripts/npx-witness.sh +56 -0
- package/scripts/onboarding-console.mjs +2749 -0
- package/scripts/private-fence.mjs +69 -0
- package/scripts/proactivity-metrics.mjs +118 -0
- package/scripts/proof-questions.json +56 -0
- package/scripts/prove.mjs +95 -0
- package/scripts/proxy/claude-proxied.sh +57 -0
- package/scripts/proxy/proxy-revert.sh +59 -0
- package/scripts/proxy/proxy-up.sh +60 -0
- package/scripts/proxy/proxy-verify.mjs +142 -0
- package/scripts/published-surface-probe.mjs +241 -0
- package/scripts/qe/card-lane-gate.mjs +162 -0
- package/scripts/qe/session-start-gate.mjs +229 -0
- package/scripts/qe/ux-suite.mjs +323 -0
- package/scripts/reconcile-project.mjs +0 -0
- package/scripts/record-lesson.mjs +113 -0
- package/scripts/refresh-model-catalog.mjs +99 -0
- package/scripts/release-proof.mjs +9 -0
- package/scripts/release-vector.mjs +281 -0
- package/scripts/release.mjs +395 -0
- package/scripts/remedy-registry.mjs +247 -0
- package/scripts/rerank-cap-eval.mjs +265 -0
- package/scripts/rerank-cap-warm-ab.mjs +129 -0
- package/scripts/route-cheap.mjs +20 -15
- package/scripts/router-utilization.mjs +182 -0
- package/scripts/routing-flywheel.mjs +596 -0
- package/scripts/rvf-generation.mjs +104 -0
- package/scripts/rvf-index-audit.mjs +138 -0
- package/scripts/self-update.mjs +508 -0
- package/scripts/selfcheck.mjs +7 -1
- package/scripts/sign-bundle.mjs +69 -0
- package/scripts/signal-watch.mjs +171 -0
- package/scripts/stack-sync.mjs +469 -0
- package/scripts/stamp-existing-rvf-generations.mjs +53 -0
- package/scripts/stamp-sweep.mjs +144 -0
- package/scripts/status-honesty.mjs +102 -0
- package/scripts/sync-version.mjs +217 -0
- package/scripts/token-report.mjs +102 -0
- package/scripts/top100-benchmark.mjs +479 -0
- package/scripts/top100-corpus.mjs +112 -0
- package/scripts/top100-semantic-assertions.mjs +449 -0
- package/scripts/update-apply.mjs +9 -0
- package/scripts/upgrade-notice.mjs +14 -0
- package/scripts/verify-bundle.mjs +51 -0
- package/scripts/verify-channels.mjs +184 -0
- package/scripts/verify-model-catalog.mjs +104 -0
- package/scripts/verify-nightly-close-issue4.sh +31 -0
- package/scripts/version.mjs +40 -0
- package/scripts/wired-check.mjs +864 -0
package/console/tips.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* RuvNet Brain — How to use it (page two).
|
|
2
|
+
Two behaviors, both progressive enhancement — with JS off the page reads exactly as before:
|
|
3
|
+
1. The same theme toggle as the console (same 'rbc-theme' key, same data-theme mechanism).
|
|
4
|
+
2. Collapsible sections (owner, 2026-07-24: the page "gets too overwhelming as a wall of
|
|
5
|
+
text"). Every .depth.card collapses behind its own header; only the inventory — the
|
|
6
|
+
section that IS the page — starts open. Open state persists per browser; a #hash
|
|
7
|
+
deep-link always opens its target. */
|
|
8
|
+
(function () {
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/* ── theme toggle (unchanged) ─────────────────────────────────────────────── */
|
|
12
|
+
var KEY = 'rbc-theme';
|
|
13
|
+
var btn = document.getElementById('theme-toggle');
|
|
14
|
+
function applyTheme(t) {
|
|
15
|
+
document.documentElement.dataset.theme = t;
|
|
16
|
+
try { localStorage.setItem(KEY, t); } catch (e) { /* private mode is fine */ }
|
|
17
|
+
}
|
|
18
|
+
if (btn) {
|
|
19
|
+
btn.addEventListener('click', function () {
|
|
20
|
+
applyTheme(document.documentElement.dataset.theme === 'light' ? 'dark' : 'light');
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ── collapsible sections ─────────────────────────────────────────────────── */
|
|
25
|
+
var OPEN_KEY = 'rbc-tips-open';
|
|
26
|
+
var DEFAULT_OPEN = ['inventory']; // the lead section IS the page; everything else waits
|
|
27
|
+
var sections = Array.prototype.slice.call(document.querySelectorAll('section.depth.card'));
|
|
28
|
+
if (!sections.length) return;
|
|
29
|
+
|
|
30
|
+
function savedOpen() {
|
|
31
|
+
try {
|
|
32
|
+
var raw = localStorage.getItem(OPEN_KEY);
|
|
33
|
+
return raw ? JSON.parse(raw) : null;
|
|
34
|
+
} catch (e) { return null; }
|
|
35
|
+
}
|
|
36
|
+
function persist() {
|
|
37
|
+
try {
|
|
38
|
+
var open = sections.filter(function (s) { return !s.classList.contains('is-collapsed'); })
|
|
39
|
+
.map(function (s) { return s.id; });
|
|
40
|
+
localStorage.setItem(OPEN_KEY, JSON.stringify(open));
|
|
41
|
+
} catch (e) { /* private mode is fine */ }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function setOpen(sec, open) {
|
|
45
|
+
sec.classList.toggle('is-collapsed', !open);
|
|
46
|
+
var head = sec.querySelector(':scope > .depth-head');
|
|
47
|
+
if (head) head.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
var open = savedOpen() || DEFAULT_OPEN;
|
|
51
|
+
sections.forEach(function (sec) {
|
|
52
|
+
var head = sec.querySelector(':scope > .depth-head');
|
|
53
|
+
if (!head || !sec.id) return;
|
|
54
|
+
|
|
55
|
+
// Wrap EVERYTHING after the header — several sections carry an art <figure> as a sibling of
|
|
56
|
+
// .depth-body, and a collapsed section that still shows its full-bleed art has not collapsed.
|
|
57
|
+
// grid-template-rows animates the height without measuring it (no reflow jank).
|
|
58
|
+
var dc = document.createElement('div'); dc.className = 'dc';
|
|
59
|
+
var dci = document.createElement('div'); dci.className = 'dci';
|
|
60
|
+
var after = [];
|
|
61
|
+
var walk = head.nextElementSibling;
|
|
62
|
+
while (walk) { after.push(walk); walk = walk.nextElementSibling; }
|
|
63
|
+
if (!after.length) return;
|
|
64
|
+
sec.appendChild(dc); dc.appendChild(dci);
|
|
65
|
+
after.forEach(function (n) { dci.appendChild(n); });
|
|
66
|
+
|
|
67
|
+
// The header is the disclosure control: chevron, ARIA, keyboard.
|
|
68
|
+
var chev = document.createElement('span');
|
|
69
|
+
chev.className = 'dc-chev';
|
|
70
|
+
chev.setAttribute('aria-hidden', 'true');
|
|
71
|
+
var svgNS = 'http://www.w3.org/2000/svg';
|
|
72
|
+
var csvg = document.createElementNS(svgNS, 'svg'); csvg.setAttribute('viewBox', '0 0 24 24');
|
|
73
|
+
var cpath = document.createElementNS(svgNS, 'path'); cpath.setAttribute('d', 'M6 9l6 6 6-6');
|
|
74
|
+
csvg.appendChild(cpath); chev.appendChild(csvg);
|
|
75
|
+
head.appendChild(chev);
|
|
76
|
+
head.setAttribute('role', 'button');
|
|
77
|
+
head.setAttribute('tabindex', '0');
|
|
78
|
+
head.setAttribute('aria-controls', sec.id + '-body');
|
|
79
|
+
dc.id = sec.id + '-body';
|
|
80
|
+
sec.classList.add('dc-ready');
|
|
81
|
+
|
|
82
|
+
function toggle() { setOpen(sec, sec.classList.contains('is-collapsed')); persist(); }
|
|
83
|
+
head.addEventListener('click', function (e) {
|
|
84
|
+
if (e.target.closest && e.target.closest('a, button')) return; // links in headers stay links
|
|
85
|
+
toggle();
|
|
86
|
+
});
|
|
87
|
+
head.addEventListener('keydown', function (e) {
|
|
88
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); }
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
setOpen(sec, open.indexOf(sec.id) !== -1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// One small master switch, right-aligned above the first section.
|
|
95
|
+
var all = document.createElement('p');
|
|
96
|
+
all.className = 'dc-all';
|
|
97
|
+
var allBtn = document.createElement('button');
|
|
98
|
+
allBtn.type = 'button';
|
|
99
|
+
function anyCollapsed() {
|
|
100
|
+
return sections.some(function (s) { return s.classList.contains('is-collapsed'); });
|
|
101
|
+
}
|
|
102
|
+
function labelAll() { allBtn.textContent = anyCollapsed() ? 'expand all' : 'collapse all'; }
|
|
103
|
+
allBtn.addEventListener('click', function () {
|
|
104
|
+
var opening = anyCollapsed();
|
|
105
|
+
sections.forEach(function (s) { setOpen(s, opening); });
|
|
106
|
+
persist(); labelAll();
|
|
107
|
+
});
|
|
108
|
+
all.appendChild(allBtn);
|
|
109
|
+
sections[0].parentNode.insertBefore(all, sections[0]);
|
|
110
|
+
labelAll();
|
|
111
|
+
sections.forEach(function (s) {
|
|
112
|
+
var h = s.querySelector(':scope > .depth-head');
|
|
113
|
+
if (!h) return;
|
|
114
|
+
h.addEventListener('click', function () { setTimeout(labelAll, 0); });
|
|
115
|
+
h.addEventListener('keydown', function () { setTimeout(labelAll, 0); });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// A #hash deep-link must always land on an OPEN section — from the console's cards, the
|
|
119
|
+
// metaharness cross-reference, or a shared URL.
|
|
120
|
+
function openHashTarget() {
|
|
121
|
+
var id = (location.hash || '').replace(/^#/, '');
|
|
122
|
+
if (!id) return;
|
|
123
|
+
var sec = document.getElementById(id);
|
|
124
|
+
if (sec && sec.classList.contains('depth')) { setOpen(sec, true); persist(); labelAll(); }
|
|
125
|
+
}
|
|
126
|
+
window.addEventListener('hashchange', openHashTarget);
|
|
127
|
+
openHashTarget();
|
|
128
|
+
}());
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# RuvNet-Brain 4.0 line — what's new (the major-release highlights)
|
|
2
|
+
|
|
3
|
+
Updated: 2026-07-30
|
|
4
|
+
|
|
5
|
+
> **Source of truth** for the `/whats-new` command and the first-run upgrade message. Curated, honest,
|
|
6
|
+
> major-only — not the point-release churn. If a claim here isn't true of the shipping build, it does not
|
|
7
|
+
> belong here. The self-measurement claims are deliberately hedged: they are *new and filling*, not
|
|
8
|
+
> *proven*.
|
|
9
|
+
>
|
|
10
|
+
> **VERSION STATUS:** the public release line is now 4.x. Publication does not erase the remaining
|
|
11
|
+
> acceptance obligations: the exact public artifact must still pass the fail-closed release contract
|
|
12
|
+
> through fresh Claude Code and Codex hosts. Anything not proven through that boundary is listed as a
|
|
13
|
+
> limitation, never converted into a capability claim.
|
|
14
|
+
|
|
15
|
+
**One line:** the 4.0 line is where the brain got **honest, legible, fast, and self-measuring** — and
|
|
16
|
+
it's landing now.
|
|
17
|
+
|
|
18
|
+
## The big things
|
|
19
|
+
|
|
20
|
+
### Release proof is fail-closed
|
|
21
|
+
The 4.0 release path now separates a clean candidate seal from a post-publication seal. Dirty
|
|
22
|
+
lineage, zero/skipped/todo tests, open issues, red or pending exact-SHA workflows, a missing
|
|
23
|
+
`ruvnet-brain` self-RVF store, weak query-deadline margin, missing independent graders,
|
|
24
|
+
host/artifact mismatches, and public-byte drift are release failures rather than warnings.
|
|
25
|
+
`npm run release:proof -- --status --quick` shows the current live blockers.
|
|
26
|
+
|
|
27
|
+
### 1. The Console is the front door
|
|
28
|
+
Type `/rvbc` and your whole RuvNet stack is on one live local page: what's installed, what the AI has
|
|
29
|
+
actually learned from *your* projects (real memories + distilled lessons, drill-down to the verbatim
|
|
30
|
+
cards), which subscription pays for what, and one-click **reversible** fixes for anything stale. New in
|
|
31
|
+
4.0:
|
|
32
|
+
- a plain-English **explainer on every card** (no more guessing what "trust & provenance" means),
|
|
33
|
+
- every suggestion carries its **blast radius** — *just this project* vs *every project · this machine*,
|
|
34
|
+
- **safe on/off checkboxes** that appear *only* where the undo is proven,
|
|
35
|
+
- a **terminal-first install** — the granular "here's exactly what I'll do, uncheck any of it" flow runs
|
|
36
|
+
in your terminal, where an `npx` user expects it.
|
|
37
|
+
|
|
38
|
+
### 2. It will not lie about your machine
|
|
39
|
+
Every number is measured live from your setup. **"We couldn't check" never renders as "off."** One
|
|
40
|
+
project's state can never leak into another project's view (a real bug 4.0 fixed in the console itself).
|
|
41
|
+
Empty-first, honest-always.
|
|
42
|
+
|
|
43
|
+
### 3. Fast — and it tells you when it's ready
|
|
44
|
+
The console and tips page paint in **well under a second** (measured, with a QE suite that runs every
|
|
45
|
+
time). On a first scan it shows a **countdown** and then says *"it's live — take a look at your page,"*
|
|
46
|
+
so you're never staring at a blank screen wondering if it hung.
|
|
47
|
+
|
|
48
|
+
### 4. It measures itself now
|
|
49
|
+
The brain records when it offered help and whether you acted on it — so over time it can **prove** it's
|
|
50
|
+
improving instead of asserting it. **Honest caveat:** this instrumentation is *new* and has only just
|
|
51
|
+
started collecting. 4.0 is not a claim of "proven better in the field" — it is the release that makes
|
|
52
|
+
that proof *possible*, and the evidence accrues as you use it.
|
|
53
|
+
|
|
54
|
+
### 5. It learns across your projects
|
|
55
|
+
A lesson proven in one project can be **promoted to your global brain** and applied everywhere — and it
|
|
56
|
+
now **survives an update** (tested against the real updater, not argued).
|
|
57
|
+
|
|
58
|
+
### 6. Runs on your account, cheapest capable model
|
|
59
|
+
The QE suite and model routing use **your Claude account, not an API key**, at the least-powerful model
|
|
60
|
+
that does the job. Nothing bills silently.
|
|
61
|
+
|
|
62
|
+
### 7. Claude Code and Codex share one active runtime
|
|
63
|
+
Both hosts are wired to the same Stable Spine generation: the MCP search shell, lifecycle hooks,
|
|
64
|
+
skills and update behavior advance as one versioned runtime instead of being installed as unrelated
|
|
65
|
+
copies.
|
|
66
|
+
|
|
67
|
+
### 8. Source grounding is an RVF-native product surface
|
|
68
|
+
The Brain searches per-repository RuVector RVF stores, joins hits to their source passages, and
|
|
69
|
+
returns cited repository paths. Model memory is not accepted as evidence for rUv-stack claims.
|
|
70
|
+
|
|
71
|
+
### 9. Quality and harness workflows are explicit
|
|
72
|
+
Agentic-QE is the testing fleet. Harness scoring evaluates the orchestration layer, and cost-aware
|
|
73
|
+
routing can select cheaper capable models when the required provider access is configured. These are
|
|
74
|
+
named workflows a user can request, not silent substitutes.
|
|
75
|
+
|
|
76
|
+
## What 4.0 deliberately does NOT claim
|
|
77
|
+
Stated up front because overclaiming is the one thing this product cannot do:
|
|
78
|
+
- **Not** "proven X% better" — the outcome ledger is still filling (see #4).
|
|
79
|
+
- **Not** "fully proactive / anticipatory" — the brain still mostly speaks when you open the console or
|
|
80
|
+
ask; the in-session, unprompted surface is the next frontier, not a shipped 4.0 guarantee.
|
|
81
|
+
- **Not** independently graded ≥95 on the exact public artifact. A score is not a release substitute.
|
|
82
|
+
- **Not** fully accepted while any critical exact-artifact, host, retrieval, version-convergence or
|
|
83
|
+
recovery invariant is FAIL, UNKNOWN, skipped or mocked-only.
|
|
84
|
+
|
|
85
|
+
## For upgraders
|
|
86
|
+
On the first real major-version transition, the installer/session experience presents the concise
|
|
87
|
+
highlights once. Run `npx ruvnet-brain --whats-new` at any time to read this full list again, or
|
|
88
|
+
`npx ruvnet-brain --what-changed` to inspect the exact machine footprint and undo path for each piece.
|
|
@@ -19,19 +19,50 @@ export function modelCacheReady(modelCache, model, revision = null) {
|
|
|
19
19
|
].every((file) => fs.existsSync(file));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function acquireMaterializeLock(destination, timeoutMs = 30_000) {
|
|
23
|
+
const lock = `${destination}.materialize-lock`;
|
|
24
|
+
const started = Date.now();
|
|
25
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
26
|
+
while (true) {
|
|
27
|
+
try {
|
|
28
|
+
fs.mkdirSync(lock);
|
|
29
|
+
return () => fs.rmSync(lock, { recursive: true, force: true });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
32
|
+
try {
|
|
33
|
+
const ageMs = Date.now() - fs.statSync(lock).mtimeMs;
|
|
34
|
+
if (ageMs > timeoutMs) {
|
|
35
|
+
fs.rmSync(lock, { recursive: true, force: true });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
} catch { /* another process released it between checks */ }
|
|
39
|
+
if (Date.now() - started >= timeoutMs) {
|
|
40
|
+
throw new Error(`timed out waiting to materialize model cache: ${destination}`);
|
|
41
|
+
}
|
|
42
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
22
47
|
// Revision-pinned downloads live below <model>/<revision>/, while strict offline reads resolve
|
|
23
48
|
// <model>/ directly. Promote only the exact pinned files into that canonical offline location.
|
|
24
49
|
export function materializeModelRevision(modelCache, model, revision) {
|
|
25
50
|
if (!revision || !modelCacheReady(modelCache, model, revision)) return false;
|
|
26
51
|
const source = path.join(modelPath(modelCache, model), revision);
|
|
27
52
|
const destination = modelPath(modelCache, model);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
const release = acquireMaterializeLock(destination);
|
|
54
|
+
try {
|
|
55
|
+
if (modelCacheReady(modelCache, model)) return true;
|
|
56
|
+
for (const entry of fs.readdirSync(source)) {
|
|
57
|
+
fs.cpSync(path.join(source, entry), path.join(destination, entry), {
|
|
58
|
+
recursive: true,
|
|
59
|
+
force: true,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return modelCacheReady(modelCache, model);
|
|
63
|
+
} finally {
|
|
64
|
+
release();
|
|
33
65
|
}
|
|
34
|
-
return modelCacheReady(modelCache, model);
|
|
35
66
|
}
|
|
36
67
|
|
|
37
68
|
// The RVF sidecar is the source of truth for the query embedder. Only sidecars with a matching
|
package/kb/zip-extract.mjs
CHANGED
|
@@ -47,15 +47,28 @@ const SIG_EOCD64_LOC = 0x07064b50;
|
|
|
47
47
|
const SIG_CENTRAL = 0x02014b50;
|
|
48
48
|
const SIG_LOCAL = 0x04034b50;
|
|
49
49
|
|
|
50
|
+
export const ZIP_LIMITS = Object.freeze({
|
|
51
|
+
archiveBytes: 2 * 1024 ** 3,
|
|
52
|
+
entries: 100_000,
|
|
53
|
+
centralDirectoryBytes: 128 * 1024 ** 2,
|
|
54
|
+
entryBytes: 2 * 1024 ** 3,
|
|
55
|
+
totalBytes: 8 * 1024 ** 3,
|
|
56
|
+
compressionRatio: 1_000,
|
|
57
|
+
});
|
|
58
|
+
|
|
50
59
|
// zlib.crc32 landed in Node 20.15.0 / 22.2.0. package.json engines allow >=18, so it is OPTIONAL —
|
|
51
60
|
// present it is used, absent the size check still stands and the caller is told which happened.
|
|
52
61
|
// Reimplementing CRC-32 here would be hand-rolling something the runtime ships.
|
|
53
62
|
const crc32 = typeof zlib.crc32 === 'function' ? zlib.crc32 : null;
|
|
54
63
|
|
|
55
64
|
class Tally extends Transform {
|
|
56
|
-
constructor() { super(); this.bytes = 0; this.crc = 0; }
|
|
65
|
+
constructor(maxBytes) { super(); this.bytes = 0; this.crc = 0; this.maxBytes = maxBytes; }
|
|
57
66
|
_transform(chunk, _enc, cb) {
|
|
58
67
|
this.bytes += chunk.length;
|
|
68
|
+
if (this.bytes > this.maxBytes) {
|
|
69
|
+
cb(new Error(`uncompressed entry exceeded the ${this.maxBytes}-byte safety limit`));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
59
72
|
if (crc32) this.crc = crc32(chunk, this.crc);
|
|
60
73
|
this.push(chunk);
|
|
61
74
|
cb();
|
|
@@ -181,10 +194,40 @@ export async function extractZip(zipPath, destDir) {
|
|
|
181
194
|
try {
|
|
182
195
|
const size = fs.fstatSync(fd).size;
|
|
183
196
|
if (size === 0) throw new Error('archive is empty (0 bytes)');
|
|
197
|
+
if (size > ZIP_LIMITS.archiveBytes) {
|
|
198
|
+
throw new Error(`archive is ${size} bytes, above the ${ZIP_LIMITS.archiveBytes}-byte safety limit`);
|
|
199
|
+
}
|
|
184
200
|
const { cdSize, cdOffset } = centralDirectoryLocation(fd, size);
|
|
201
|
+
if (cdSize > ZIP_LIMITS.centralDirectoryBytes) {
|
|
202
|
+
throw new Error(`central directory is ${cdSize} bytes, above the ${ZIP_LIMITS.centralDirectoryBytes}-byte safety limit`);
|
|
203
|
+
}
|
|
185
204
|
if (cdOffset + cdSize > size) throw new Error(`central directory runs past end of file (offset ${cdOffset} + ${cdSize} > ${size})`);
|
|
186
205
|
const entries = parseCentralDirectory(readAt(fd, cdSize, cdOffset));
|
|
187
206
|
if (entries.length === 0) throw new Error('central directory contains no entries');
|
|
207
|
+
if (entries.length > ZIP_LIMITS.entries) {
|
|
208
|
+
throw new Error(`archive has ${entries.length} entries, above the ${ZIP_LIMITS.entries}-entry safety limit`);
|
|
209
|
+
}
|
|
210
|
+
let declaredTotal = 0;
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
const unixType = (entry.externalAttrs >>> 16) & 0xf000;
|
|
213
|
+
if ((entry.versionMadeBy >> 8) === 3 && unixType === 0xa000) {
|
|
214
|
+
throw new Error(`entry "${entry.name}" is a symbolic link — downloaded bundles may contain regular files and directories only`);
|
|
215
|
+
}
|
|
216
|
+
if (!Number.isSafeInteger(entry.uncompSize) || entry.uncompSize < 0) {
|
|
217
|
+
throw new Error(`entry "${entry.name}" has an unsafe uncompressed size`);
|
|
218
|
+
}
|
|
219
|
+
if (entry.uncompSize > ZIP_LIMITS.entryBytes) {
|
|
220
|
+
throw new Error(`entry "${entry.name}" claims ${entry.uncompSize} bytes, above the ${ZIP_LIMITS.entryBytes}-byte safety limit`);
|
|
221
|
+
}
|
|
222
|
+
declaredTotal += entry.uncompSize;
|
|
223
|
+
if (!Number.isSafeInteger(declaredTotal) || declaredTotal > ZIP_LIMITS.totalBytes) {
|
|
224
|
+
throw new Error(`archive claims ${declaredTotal} uncompressed bytes, above the ${ZIP_LIMITS.totalBytes}-byte safety limit`);
|
|
225
|
+
}
|
|
226
|
+
const ratio = entry.uncompSize / Math.max(entry.compSize, 1);
|
|
227
|
+
if (ratio > ZIP_LIMITS.compressionRatio) {
|
|
228
|
+
throw new Error(`entry "${entry.name}" has compression ratio ${ratio.toFixed(1)}, above the ${ZIP_LIMITS.compressionRatio}:1 safety limit`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
188
231
|
|
|
189
232
|
fs.mkdirSync(destDir, { recursive: true });
|
|
190
233
|
let files = 0;
|
|
@@ -234,17 +277,6 @@ export async function extractZip(zipPath, destDir) {
|
|
|
234
277
|
throw new Error(`entry "${e.name}" uses unsupported compression method ${e.method} (only stored=0 and deflate=8 are implemented)`);
|
|
235
278
|
}
|
|
236
279
|
|
|
237
|
-
const isSymlink = (e.versionMadeBy >> 8) === 3 && ((e.externalAttrs >>> 16) & 0xf000) === 0xa000;
|
|
238
|
-
if (isSymlink) {
|
|
239
|
-
// A symlink's payload is its target path — always tiny, so it is read whole rather than streamed.
|
|
240
|
-
const comp = e.compSize === 0 ? Buffer.alloc(0) : readAt(fd, e.compSize, dataStart);
|
|
241
|
-
const linkTarget = (e.method === 8 ? zlib.inflateRawSync(comp) : comp).toString('utf8');
|
|
242
|
-
fs.rmSync(target, { recursive: true, force: true });
|
|
243
|
-
fs.symlinkSync(linkTarget, target);
|
|
244
|
-
files++;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
280
|
// A zero-length entry has no data range to stream (start > end would be a nonsense read), so
|
|
249
281
|
// it is written directly. Its size/CRC assertions below still apply.
|
|
250
282
|
if (e.compSize === 0) {
|
|
@@ -254,25 +286,32 @@ export async function extractZip(zipPath, destDir) {
|
|
|
254
286
|
continue;
|
|
255
287
|
}
|
|
256
288
|
|
|
257
|
-
const tally = new Tally();
|
|
289
|
+
const tally = new Tally(Math.min(e.uncompSize, ZIP_LIMITS.entryBytes));
|
|
290
|
+
const partial = path.join(path.dirname(target), `.ruvnet-extract-${process.pid}-${entryIndex}.tmp`);
|
|
291
|
+
try { fs.rmSync(partial, { force: true }); } catch { /* absent */ }
|
|
258
292
|
const stages = [fs.createReadStream(zipPath, { start: dataStart, end: dataStart + e.compSize - 1 })];
|
|
259
293
|
if (e.method === 8) stages.push(zlib.createInflateRaw());
|
|
260
|
-
stages.push(tally, fs.createWriteStream(
|
|
294
|
+
stages.push(tally, fs.createWriteStream(partial, { flags: 'wx' }));
|
|
261
295
|
|
|
262
296
|
try {
|
|
263
297
|
await pipeline(...stages);
|
|
264
298
|
} catch (err) {
|
|
299
|
+
try { fs.rmSync(partial, { force: true }); } catch { /* best effort */ }
|
|
265
300
|
throw new Error(`entry "${e.name}" failed to inflate (${err && err.code ? err.code : ''}${err && err.message ? ` ${err.message}` : ''}) — the archive is corrupt or truncated`);
|
|
266
301
|
}
|
|
267
302
|
if (tally.bytes !== e.uncompSize) {
|
|
303
|
+
fs.rmSync(partial, { force: true });
|
|
268
304
|
throw new Error(`entry "${e.name}" is corrupt: expected ${e.uncompSize} bytes, extracted ${tally.bytes}`);
|
|
269
305
|
}
|
|
270
306
|
if (crc32 && (tally.crc >>> 0) !== (e.crc >>> 0)) {
|
|
307
|
+
fs.rmSync(partial, { force: true });
|
|
271
308
|
// Zero-padded to 8 hex digits so the value is directly comparable to the one Info-ZIP's
|
|
272
309
|
// own `unzip` prints for the same damaged entry ("bad CRC 811e0dd1 (should be 0f6994e4)").
|
|
273
310
|
const hex = (n) => (n >>> 0).toString(16).padStart(8, '0');
|
|
274
311
|
throw new Error(`entry "${e.name}" failed its CRC-32 check (expected ${hex(e.crc)}, got ${hex(tally.crc)}) — the archive is corrupt`);
|
|
275
312
|
}
|
|
313
|
+
if (process.platform === 'win32') fs.rmSync(target, { recursive: true, force: true });
|
|
314
|
+
fs.renameSync(partial, target);
|
|
276
315
|
|
|
277
316
|
// Unix permission bits, when the archive carries them. Skipped on Windows, which has no
|
|
278
317
|
// POSIX mode and where chmod is a no-op at best.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "One-command installer for RuvNet Brain — a portable, source-grounded brain over rUv's RuvNet building blocks, delivered as a Claude Code plugin so Claude uses the stack instead of fighting it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "node plugin/test/run-tests.mjs",
|
|
11
|
+
"release:proof": "node scripts/release-proof.mjs",
|
|
12
|
+
"benchmark:brain50": "node scripts/brain-latency-50.mjs",
|
|
13
|
+
"benchmark:novice50": "node scripts/brain-novice-50.mjs",
|
|
11
14
|
"version:check": "node scripts/sync-version.mjs --check",
|
|
12
15
|
"claims:verify": "node scripts/claims-verify.mjs",
|
|
13
16
|
"claims:fix": "node scripts/claims-verify.mjs --fix",
|
|
@@ -49,39 +52,28 @@
|
|
|
49
52
|
"cap:ab": "node scripts/rerank-cap-warm-ab.mjs"
|
|
50
53
|
},
|
|
51
54
|
"files": [
|
|
55
|
+
".claude-plugin/marketplace.json",
|
|
52
56
|
"bin/install.mjs",
|
|
57
|
+
"plugin/",
|
|
53
58
|
"plugin/mcp/managed-cli-interface.mjs",
|
|
54
59
|
"plugin/mcp/server.mjs",
|
|
55
60
|
"plugin/scripts/codex-hook-wrapper.mjs",
|
|
61
|
+
"plugin/scripts/host-update.mjs",
|
|
62
|
+
"plugin/scripts/update-apply.mjs",
|
|
56
63
|
"!plugin/README.md",
|
|
64
|
+
"!plugin/scripts/.ruvnet-brain/",
|
|
65
|
+
"!plugin/test/",
|
|
57
66
|
"README.md",
|
|
67
|
+
"docs/RELEASE-NOTES-4.0.md",
|
|
58
68
|
"LICENSE",
|
|
69
|
+
"console/",
|
|
59
70
|
"config/model-router/",
|
|
71
|
+
"scripts/",
|
|
60
72
|
"kb/verify-citation.mjs",
|
|
61
73
|
"kb/zip-extract.mjs",
|
|
62
74
|
"kb/brain-profile.mjs",
|
|
63
75
|
"kb/model-requirements.mjs",
|
|
64
|
-
"
|
|
65
|
-
"scripts/model-router-setup.mjs",
|
|
66
|
-
"scripts/model-router-status.mjs",
|
|
67
|
-
"scripts/model-router-outcome.mjs",
|
|
68
|
-
"scripts/subscription-hosts.mjs",
|
|
69
|
-
"scripts/dual-host-deliberation.mjs",
|
|
70
|
-
"scripts/dual-host-suggest.mjs",
|
|
71
|
-
"scripts/route-cheap.mjs",
|
|
72
|
-
"scripts/dispatch-receipt.mjs",
|
|
73
|
-
"scripts/metaharness-receipts.mjs",
|
|
74
|
-
"scripts/codex-routed.sh",
|
|
75
|
-
"scripts/metaharness-router.mjs",
|
|
76
|
-
"scripts/no-silent-substitution.mjs",
|
|
77
|
-
"scripts/user-settings.mjs",
|
|
78
|
-
"scripts/selfcheck.mjs",
|
|
79
|
-
"scripts/hook-registry.mjs",
|
|
80
|
-
"scripts/install-scope.mjs",
|
|
81
|
-
"scripts/upgrade-notice.mjs",
|
|
82
|
-
"scripts/falsify.mjs",
|
|
83
|
-
"scripts/nightly-watchdog.mjs",
|
|
84
|
-
"scripts/job-heartbeat.sh"
|
|
76
|
+
"keys/ruvnet-brain-signing.pub.pem"
|
|
85
77
|
],
|
|
86
78
|
"engines": {
|
|
87
79
|
"node": ">=18"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ruvnet-brain",
|
|
3
|
+
"description": "Source-grounded RuvNet Brain integrations for Claude Code and Codex.",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Stuart Kerr"
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
{
|
|
9
|
+
"name": "ruvnet-brain",
|
|
10
|
+
"source": "./",
|
|
11
|
+
"description": "RuvNet brain transplant for Claude Code: source-grounded knowledge of the rUv ecosystem (19+ repos) + enforced grounding so Claude stops arguing about RuvNet and actually uses it + on-demand repo ingestion."
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ruvnet-brain",
|
|
3
|
+
"description": "RuvNet brain transplant for Claude Code — grounds every RuvNet decision in real source across 69 rUv repositories, prefers Ruflo / RuVector-RVF / AgentDB over training-prior defaults (pgvector, Pinecone, hand-rolled cosine), and can pull in any RuvNet repo on demand. Ships an enforced UserPromptSubmit retrieve-and-inject grounding hook that sharply reduces drift.",
|
|
4
|
+
"version": "4.0.2",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Stuart Kerr"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/stuinfla/ruvnet-brain",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"ruvnet",
|
|
12
|
+
"ruv",
|
|
13
|
+
"ruflo",
|
|
14
|
+
"ruvector",
|
|
15
|
+
"rvf",
|
|
16
|
+
"agentdb",
|
|
17
|
+
"grounding",
|
|
18
|
+
"brain",
|
|
19
|
+
"mcp",
|
|
20
|
+
"claude-code"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ruvnet-brain",
|
|
3
|
+
"version": "4.0.2",
|
|
4
|
+
"description": "Source-grounded RuvNet knowledge, lifecycle enforcement, and learning for Codex.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Stuart Kerr"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/stuinfla/ruvnet-brain",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"ruvnet",
|
|
12
|
+
"ruflo",
|
|
13
|
+
"ruvector",
|
|
14
|
+
"rvf",
|
|
15
|
+
"agentdb",
|
|
16
|
+
"grounding"
|
|
17
|
+
],
|
|
18
|
+
"skills": "./skills/",
|
|
19
|
+
"mcpServers": "./.mcp.json",
|
|
20
|
+
"hooks": "./hooks/codex-hooks.json"
|
|
21
|
+
}
|
package/plugin/.mcp.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Brain Console — RuvNet Brain Console (same console as /rvbc, /brain-console, and /ruvnet-brain:configure — every spelling works). Opens the live console page: your whole RuvNet stack on one page. Read-only until you click."
|
|
3
|
+
updated: 2026-07-20
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`/rvbc`, `/rvcb`, `/brain-console`, and `/ruvnet-brain:configure` are equally valid names. Never
|
|
7
|
+
correct the user's spelling.
|
|
8
|
+
|
|
9
|
+
1. Say: "Opening it now; it scans live while you watch."
|
|
10
|
+
2. Use `${RUVNET_BRAIN_KB:-$HOME/.cache/ruvnet-brain/kb}/.console-runtime/scripts/onboarding-console.mjs`.
|
|
11
|
+
A current-repository copy is allowed only for an explicit developer checkout.
|
|
12
|
+
3. Run `node <resolved-script> --serve --open` in the background.
|
|
13
|
+
4. Give the URL immediately. Never promise a duration; the page reports its own progress.
|
|
14
|
+
|
|
15
|
+
An already-running server is success. It is read-only until the user clicks; changes must be
|
|
16
|
+
explained and reversible. If the script or server is unavailable, report the exact failure.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Open the RuvNet Brain Onboarding Console — a local web page that mirrors YOUR machine's RuvNet setup, explains it in plain English, and lets you safely configure and fix it. Read-only until you click; every machine change is explained first and is reversible.
|
|
3
|
+
updated: 2026-07-15
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Launch the **RuvNet Brain Onboarding Console** for the user — a locally-served web page that reads
|
|
7
|
+
their actual machine and lets them configure and (with consent) fix it.
|
|
8
|
+
|
|
9
|
+
Do this:
|
|
10
|
+
|
|
11
|
+
1. **Find the installed runtime.** Use
|
|
12
|
+
`${RUVNET_BRAIN_KB:-$HOME/.cache/ruvnet-brain/kb}/.console-runtime/scripts/onboarding-console.mjs`.
|
|
13
|
+
A current-repository copy is allowed only for an explicit developer checkout.
|
|
14
|
+
|
|
15
|
+
2. **Start the server in the BACKGROUND** (it is a long-running process) so this turn keeps going, and
|
|
16
|
+
open the browser:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
node <repo>/scripts/onboarding-console.mjs --serve --open
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Run it with `run_in_background: true`. It binds `127.0.0.1` only and prints a line like
|
|
23
|
+
`http://127.0.0.1:7411/`.
|
|
24
|
+
|
|
25
|
+
3. **Give the user the link** as a clickable `http://127.0.0.1:<port>/`, with one warm sentence:
|
|
26
|
+
the page is **read-only until you click**, everything it can change to your computer is **explained
|
|
27
|
+
in plain English and confirmed first**, and **Settings save at your user level**
|
|
28
|
+
(`~/.claude/ruvnet-brain/config.json`) without touching how anything else on your machine runs.
|
|
29
|
+
|
|
30
|
+
4. If the server errors (e.g. `sqlite3` not found, or a bad node version), report it plainly and offer
|
|
31
|
+
to fix it — don't leave them staring at a dead tab.
|
|
32
|
+
|
|
33
|
+
Do **not** narrate the machine yourself — the page is the mirror. Your job is just to get it open.
|