ruvnet-brain 4.5.17 โ 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -19
- package/bin/developer-update.mjs +3 -0
- package/bin/install.mjs +14 -2
- package/bin/nightly-refresh.mjs +17 -1
- package/console/CONTRACT.md +47 -3
- package/console/app.js +112 -20
- package/package.json +4 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/commands/brain-console.md +13 -1
- package/plugin/commands/configure.md +2 -2
- package/plugin/commands/rnb.md +13 -1
- package/plugin/commands/rnbc.md +13 -1
- package/plugin/commands/rvbc.md +13 -1
- package/plugin/commands/rvcb.md +13 -1
- package/plugin/scripts/automatic-update.mjs +11 -2
- package/plugin/scripts/continuation-gate.mjs +2 -1
- package/plugin/scripts/decision-gate.mjs +0 -16
- package/plugin/scripts/developer-update-cleanup.mjs +38 -0
- package/plugin/scripts/developer-update-lock.mjs +30 -0
- package/plugin/scripts/developer-update-maintenance.mjs +178 -0
- package/plugin/scripts/developer-update-owner.mjs +24 -0
- package/plugin/scripts/developer-update-policy.mjs +48 -0
- package/plugin/scripts/developer-update.mjs +435 -0
- package/plugin/scripts/host-update.mjs +6 -3
- package/plugin/scripts/nightly-scheduler.mjs +55 -16
- package/plugin/scripts/plugin-artifact-proof.mjs +135 -0
- package/plugin/skills/ruvnet-brain/SKILL.md +2 -2
- package/scripts/codex-managed-terminal.mjs +1 -1
- package/scripts/console-runtime-identity.mjs +2 -0
- package/scripts/console-suite-update.mjs +117 -0
- package/scripts/hook-qualify-core.mjs +29 -0
- package/scripts/onboarding-console.mjs +41 -12
- package/scripts/release-qualification-contract.mjs +14 -0
- package/scripts/remedy-registry.mjs +1 -0
- package/scripts/stack-sync.mjs +19 -132
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Updated: 2026-
|
|
1
|
+
Updated: 2026-10-10 05:06:39 EDT | Version 4.6.0
|
|
2
2
|
Created: 2026-06-29 22:36:38 EDT
|
|
3
3
|
|
|
4
4
|
<div align="center">
|
|
@@ -7,25 +7,23 @@ Created: 2026-06-29 22:36:38 EDT
|
|
|
7
7
|
|
|
8
8
|
# ๐ง RuvNet Brain
|
|
9
9
|
|
|
10
|
-
### ๐ง RuvNet Brain โ [](https://github.com/stuinfla/ruvnet-brain/blob/main/plugin/.claude-plugin/plugin.json)
|
|
11
11
|
|
|
12
12
|
**A portable, source-grounded brain over Reuven Cohen's (rUv's) RuvNet stack โ delivered as a Claude Code plugin that makes Claude _use_ the stack instead of fighting it.**
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## 4.6.0 coordinated updates and proof boundaries
|
|
17
17
|
|
|
18
|
-
The 4.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
The 4.6.0 source introduces one coordinated updater for installed tools, host plugins and the
|
|
19
|
+
Brain's signed knowledge bundle. RNBC presents **Keep all tools updated** with **Latest
|
|
20
|
+
(recommended)** or **Alpha**. The public default covers the RuvNet suite; wider developer-tool
|
|
21
|
+
maintenance requires an explicit choice. See the update policy in `CONTRIBUTING.md` and the
|
|
22
|
+
4.6.0 release notes in `docs/RELEASE-NOTES-4.6.0.md`.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
safe inverse must not be advertised as a one-click undo. See the
|
|
27
|
-
[QA execution contract](docs/qa-execution-contract.md) and
|
|
28
|
-
[ADR-072 stabilization boundary](docs/adr/0072-whole-product-integrity-conformance.md).
|
|
24
|
+
A saved choice or registered schedule proves configuration. Completed runs, installed versions
|
|
25
|
+
and the protected release's public installation receipt prove different things. This source
|
|
26
|
+
version alone does not establish publication, all-tool freshness or a new product score.
|
|
29
27
|
|
|
30
28
|
> ## ๐งญ North Star
|
|
31
29
|
>
|
|
@@ -53,12 +51,10 @@ safe inverse must not be advertised as a one-click undo. See the
|
|
|
53
51
|
> - **`plugin`** (badge above) โ the Claude Code plugin itself: SKILL.md, the grounding hooks, the MCP server. Read live from [`plugin/.claude-plugin/plugin.json`](plugin/.claude-plugin/plugin.json). Updates often โ this is where behavior fixes land.
|
|
54
52
|
> - **`installer (npm)`** (badge above) โ the setup script's published version, read live from the [npm registry](https://www.npmjs.com/package/ruvnet-brain). It is a release surface of the same product, not an independent version track.
|
|
55
53
|
> - **Brain Release** โ [`releases/latest`](https://github.com/stuinfla/ruvnet-brain/releases/latest) exposes the published knowledge bundle. It must agree with that release's npm, plugin, and signed artifact identities. A moving source badge does not establish publication.
|
|
56
|
-
> - **
|
|
57
|
-
>
|
|
58
|
-
>
|
|
59
|
-
>
|
|
60
|
-
> `--update` requests the published plugin and knowledge. Adding `--auto` requests **Evergreen** scheduling; successful registration alone is not proof a scheduled run completed. Drop `--auto` for a one-time update: `npx ruvnet-brain@latest --update`.
|
|
61
|
-
> - **Disable scheduled updates:** `npx ruvnet-brain --disable-nightly`. Platform behavior is owned by the native scheduler adapter, not a hand-maintained cron recipe.
|
|
54
|
+
> - **Update an existing Brain installation:** invoke its existing `ruvnet-brain --update` command. For an explicit developer checkout, use `node bin/install.mjs --update`. Initial installation may use `npx ruvnet-brain@latest`; routine updates preserve the installed owner instead of creating a second tool installation.
|
|
55
|
+
> - **Check installed tools:** `ruvnet-brain-update --check`. Apply the saved policy with `ruvnet-brain-update --apply`; inspect the receipt for exclusions and failures.
|
|
56
|
+
> - **Coordinate installed tools:** open RNBC and use **Keep all tools updated**. Choose **Latest (recommended)** or **Alpha**, and explicitly choose wider tool maintenance when wanted. The schedule runs at **03:30 in the user's local time** through the same coordinator as a manual run.
|
|
57
|
+
> - **Disable scheduled updates:** use the existing `ruvnet-brain --disable-nightly` command. Registration and run health are reported separately.
|
|
62
58
|
> - **Publication, local activation, and loaded session version are distinct observations.** An unchanged release does not by itself prove local health, freshness, or a successful update.
|
|
63
59
|
|
|
64
60
|
<sub>Built by **[Stuart Kerr](https://isovision.ai)** at [Isovision.ai](https://isovision.ai) ยท free & fair use, to help everyone leverage the high end of agentic coding.</sub>
|
package/bin/install.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import { acquireRefreshLock, finishRefreshReceipt, openRefreshReceipt, physicalP
|
|
|
27
27
|
import { assessLifecycleEvidence, pruneLifecycleEvidence } from '../kb/lifecycle-evidence-retention.mjs';
|
|
28
28
|
import { checkDiskSpace, recoverIncompleteStorageTransactions } from '../kb/update-storage-transaction.mjs';
|
|
29
29
|
import { saveUpdateSource, stableNode } from '../plugin/scripts/automatic-update.mjs';
|
|
30
|
+
import { acquireDeveloperLock } from '../plugin/scripts/developer-update-lock.mjs';
|
|
30
31
|
import { footprintRoots, inventoryFootprint, sweepFootprint } from '../plugin/scripts/brain-footprint.mjs';
|
|
31
32
|
import { kbCopyProof } from '../plugin/scripts/kb-copy-proof.mjs';
|
|
32
33
|
import { assessMoveLeftovers, isVolumeMetadata } from '../plugin/scripts/footprint-io.mjs';
|
|
@@ -4435,7 +4436,7 @@ function enableNightly() {
|
|
|
4435
4436
|
testMode: TEST_MODE });
|
|
4436
4437
|
if (status.state !== 'on') throw new Error(status.evidence);
|
|
4437
4438
|
ok(`nightly updates enabled โ ${status.evidence}`);
|
|
4438
|
-
info(c.dim(`identity ${NIGHTLY_LABEL}; immutable runner ${registration.runnerSha256.slice(0, 16)}โฆ; 03:
|
|
4439
|
+
info(c.dim(`identity ${NIGHTLY_LABEL}; immutable runner ${registration.runnerSha256.slice(0, 16)}โฆ; 03:30 local`));
|
|
4439
4440
|
if (TEST_MODE) warn('RUVNET_BRAIN_TEST=1 โ scheduler registration was written but OS activation was safely simulated');
|
|
4440
4441
|
} catch (error) {
|
|
4441
4442
|
console.error(`\n${c.red('โ nightly updates were NOT enabled:')} ${error.message}`);
|
|
@@ -6331,7 +6332,18 @@ the installer reports that boot-level declarations changed.
|
|
|
6331
6332
|
}
|
|
6332
6333
|
if (FLAG_DEMO) return runDemo();
|
|
6333
6334
|
if (FLAG_FEEDBACK) return runFeedback();
|
|
6334
|
-
if (FLAG_UPDATE)
|
|
6335
|
+
if (FLAG_UPDATE) {
|
|
6336
|
+
if (refuseOverSetAsideBrain()) return;
|
|
6337
|
+
const developerLock = acquireDeveloperLock({ brainHome: process.env.RUVNET_BRAIN_HOME || path.dirname(resolvedKbDir()) });
|
|
6338
|
+
const previousToken = process.env.RUVNET_DEVELOPER_UPDATE_TOKEN;
|
|
6339
|
+
process.env.RUVNET_DEVELOPER_UPDATE_TOKEN = developerLock.token;
|
|
6340
|
+
try { return await runUpdate(); }
|
|
6341
|
+
finally {
|
|
6342
|
+
if (previousToken === undefined) delete process.env.RUVNET_DEVELOPER_UPDATE_TOKEN;
|
|
6343
|
+
else process.env.RUVNET_DEVELOPER_UPDATE_TOKEN = previousToken;
|
|
6344
|
+
developerLock.release();
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6335
6347
|
if (FLAG_ENABLE_NIGHTLY) return enableNightly();
|
|
6336
6348
|
if (FLAG_DISABLE_NIGHTLY) return disableNightly();
|
|
6337
6349
|
// Standalone, like the nightly pair above. Without these, the flags existed only as a way to
|
package/bin/nightly-refresh.mjs
CHANGED
|
@@ -61,13 +61,29 @@ Object.assign(process.env, registration.environment || {});
|
|
|
61
61
|
let invocation;
|
|
62
62
|
try {
|
|
63
63
|
const modules = registration.updateModules;
|
|
64
|
-
|
|
64
|
+
const legacy = ['automatic-update.mjs', 'ruvnet-gate1-pattern.mjs', 'user-settings.mjs'];
|
|
65
|
+
const bridged = [...legacy, 'developer-update-owner.mjs'];
|
|
66
|
+
const currentLegacy = [...bridged, 'developer-update-policy.mjs'];
|
|
67
|
+
if (!modules?.['developer-update-policy.mjs'] && registration.mode === 'developer-suite') throw new Error('suite policy closure is missing');
|
|
68
|
+
if (registration.mode === 'developer-suite' && (!path.isAbsolute(modules['developer-update-policy.mjs'].path) || path.basename(modules['developer-update-policy.mjs'].path) !== 'developer-update-policy.mjs' || path.dirname(modules['developer-update-policy.mjs'].path) !== path.dirname(modules['automatic-update.mjs'].path) || !fs.lstatSync(modules['developer-update-policy.mjs'].path).isFile())) throw new Error('suite policy path identity mismatch');
|
|
69
|
+
if (registration.mode === 'developer-suite' && crypto.createHash('sha256').update(fs.readFileSync(modules['developer-update-policy.mjs'].path)).digest('hex') !== modules['developer-update-policy.mjs'].sha256) throw new Error('suite policy digest mismatch');
|
|
70
|
+
const executionPolicy = registration.mode === 'developer-suite' ? await import(pathToFileURL(modules['developer-update-policy.mjs'].path).href) : null;
|
|
71
|
+
const suite = executionPolicy ? [...new Set([...currentLegacy, ...executionPolicy.EXECUTION_MODULES])] : [];
|
|
72
|
+
const expected = registration.mode === 'developer-suite' ? [suite] : [legacy, bridged, currentLegacy];
|
|
73
|
+
if (!modules || !expected.some(names => Object.keys(modules).sort().join(',') === names.sort().join(','))) throw new Error('registered update module closure is missing');
|
|
65
74
|
for (const [name, item] of Object.entries(modules)) {
|
|
66
75
|
if (!path.isAbsolute(item.path) || path.basename(item.path) !== name
|
|
67
76
|
|| path.dirname(item.path) !== path.dirname(modules['automatic-update.mjs'].path)
|
|
68
77
|
|| !fs.lstatSync(item.path).isFile()
|
|
69
78
|
|| crypto.createHash('sha256').update(fs.readFileSync(item.path)).digest('hex') !== item.sha256) throw new Error('update module digest mismatch');
|
|
70
79
|
}
|
|
80
|
+
if (registration.mode === 'developer-suite') {
|
|
81
|
+
const coordinator = await import(pathToFileURL(modules['developer-update.mjs'].path).href);
|
|
82
|
+
const receipt = await coordinator.runDeveloperUpdate({ mode: 'apply', env: { ...process.env,
|
|
83
|
+
RUVNET_NIGHTLY_IDENTITY: registration.identity, RUVNET_NIGHTLY: '1' } });
|
|
84
|
+
console.log(JSON.stringify(receipt));
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
71
87
|
const policy = await import(pathToFileURL(modules['automatic-update.mjs'].path).href);
|
|
72
88
|
process.env.PATH = policy.automaticPath({ nodePath: registration.nodePath });
|
|
73
89
|
invocation = policy.automaticInvocation(['--update', '--no-nightly-prompt'], {
|
package/console/CONTRACT.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Updated: 2026-07-17
|
|
1
|
+
Updated: 2026-10-10 04:37:54 EDT | Version 1.1.2
|
|
4
2
|
Created: 2026-07-15
|
|
5
3
|
|
|
4
|
+
# Onboarding Console โ API + data contract (v1)
|
|
5
|
+
|
|
6
6
|
The single source of truth both the backend (`scripts/onboarding-console.mjs`) and the
|
|
7
7
|
frontend (`console/index.html` + `app.js` + `style.css`) build against. Implements ADR-0013
|
|
8
8
|
and DDD-0002. **The ordering is the design: Mirror โ Explain โ Recommend โ (consent) Apply โ Undo.**
|
|
@@ -13,6 +13,50 @@ A GET with a wrong/absent token still serves read-only state; a POST with a wron
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## Coordinated developer updates
|
|
17
|
+
|
|
18
|
+
`GET /api/suite-update` reads the installed coordinator's canonical policy, latest receipt,
|
|
19
|
+
and shared ownership lock on every request. It never starts an update or writes a policy.
|
|
20
|
+
The same live `suiteUpdate` field decorates `/api/state` and `/api/activity`; it is not persisted
|
|
21
|
+
inside their heavier measurement caches.
|
|
22
|
+
|
|
23
|
+
The response distinguishes `never-run`, `running`, `succeeded`, `failed`, `interrupted`,
|
|
24
|
+
`checked`, and `unavailable`. A scheduled job or a saved setting cannot prove a completed run.
|
|
25
|
+
A check-only receipt cannot prove an apply. The public card shows the saved release policy and
|
|
26
|
+
scope; Latest is recommended, Alpha uses published Ruv alpha tags with Latest fallback, and
|
|
27
|
+
other providers retain their release policies. Newer installed versions remain preserved.
|
|
28
|
+
|
|
29
|
+
`POST /api/suite-update` requires the existing per-launch token and rejects a supplied Origin
|
|
30
|
+
that differs from this loopback server. The body contains only `channel: "latest" | "alpha"`.
|
|
31
|
+
An explicit **Keep all tools updated** click saves that channel and opts existing installations
|
|
32
|
+
into scope `all`, including supported Homebrew, uv, Cargo, and native inventories. It enables the
|
|
33
|
+
existing canonical nightly job through the same installer scheduler adapter, verifies enforcement,
|
|
34
|
+
mirrors the nightly choice, and then runs one immediate update. Reads never enroll a scheduler. It preserves
|
|
35
|
+
cleanup and managed-callback preferences, never installs a missing provider, and never changes
|
|
36
|
+
project dependencies. An invalid channel or non-idle shared lock returns a refusal before
|
|
37
|
+
policy mutation or child execution.
|
|
38
|
+
|
|
39
|
+
The server starts the shipped coordinator with its own Node interpreter, a fixed user-home
|
|
40
|
+
working directory and explicit Brain home. The coordinator acquires the shared lock. The adapter
|
|
41
|
+
records an asynchronous launch receipt and accepts success only from a completed apply receipt
|
|
42
|
+
matching the child's PID, exact coordinator source hash, and complete five-module execution snapshot. Activity polls this live read model;
|
|
43
|
+
launch failure, interrupted execution, and a failed provider are distinct from success.
|
|
44
|
+
|
|
45
|
+
The nightly Settings control uses the same coordinator through the installer's existing scheduler
|
|
46
|
+
adapter. Schedule enforcement and the last completed receipt remain separate measured facts.
|
|
47
|
+
|
|
48
|
+
## Refresh settlement
|
|
49
|
+
|
|
50
|
+
A fresh state response is painted directly from the snapshot that satisfied the freshness poller;
|
|
51
|
+
no second fetch can substitute an older or withdrawn state. Stack, capability, and memory cards
|
|
52
|
+
keep waiting when their own cache is warming or stale. Each fresh recommendation source replaces
|
|
53
|
+
its previous source snapshot, so resolved proposals disappear and changed evidence is repainted.
|
|
54
|
+
The header describes the machine reading's age, without claiming all independent cards finished.
|
|
55
|
+
A forced refresh during an active measurement queues one follow-up in the same project scope.
|
|
56
|
+
While it waits, caches from the earlier writer remain stale. When that writer exits, its late
|
|
57
|
+
snapshots are withdrawn before the follow-up begins, so a pre-mutation scan cannot settle a
|
|
58
|
+
post-mutation request. Repeated requests coalesce into that one follow-up.
|
|
59
|
+
|
|
16
60
|
## GET `/api/state` โ fast sections (no network)
|
|
17
61
|
|
|
18
62
|
Returns in well under a second. `memory.fleet` is **`null` here by design** โ scanning every memory
|
package/console/app.js
CHANGED
|
@@ -25,6 +25,7 @@ let preStateHash = null; // echoed on /api/apply so the server can refu
|
|
|
25
25
|
let lastMemory = null; // last rendered memory card, so the late fleet scan can merge into it
|
|
26
26
|
const renderedRecIds = new Set();
|
|
27
27
|
const renderedRecommendations = new Map();
|
|
28
|
+
const recommendationSources = new Map();
|
|
28
29
|
let stateRecsSettled = false;
|
|
29
30
|
let stackRecsSettled = false;
|
|
30
31
|
// Health advocacy (ADR-027) hydrates from /api/memory, the slowest source. Until it has answered,
|
|
@@ -1374,7 +1375,7 @@ async function loadCapabilities(attempt = 0) {
|
|
|
1374
1375
|
const r = await fetchCapabilities();
|
|
1375
1376
|
if (r.status === 404) { capsMissingEndpoint(); return; }
|
|
1376
1377
|
if (!r.ok) throw new Error(`/api/capabilities answered HTTP ${r.status}`);
|
|
1377
|
-
if (r.data && r.data.warming) {
|
|
1378
|
+
if (r.data && (r.data.warming || r.data.stale === true)) {
|
|
1378
1379
|
// "Not measured yet" is NOT "off" โ this card's entire reason for existing. Rendering a
|
|
1379
1380
|
// warming answer would produce an empty row list, which on this surface says "you own
|
|
1380
1381
|
// nothing", the single most damaging thing this page could get wrong.
|
|
@@ -1720,14 +1721,26 @@ function addRecommendations(recs, source) {
|
|
|
1720
1721
|
'Ordered by what youโre working on โ machine-wide updates first (they affect every project), then your most recently active projects.'));
|
|
1721
1722
|
}
|
|
1722
1723
|
let dropped = 0;
|
|
1723
|
-
const
|
|
1724
|
+
const next = new Map();
|
|
1724
1725
|
for (const rec of Array.isArray(recs) ? recs : []) {
|
|
1725
|
-
if (!rec || rec.id == null
|
|
1726
|
-
//
|
|
1726
|
+
if (!rec || rec.id == null) continue;
|
|
1727
|
+
// A fresh audit replaces this source, including an empty result after an update.
|
|
1727
1728
|
if (!Array.isArray(rec.evidence) || !rec.evidence.length || !rec.cost || !rec.undo) { dropped += 1; continue; }
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1729
|
+
next.set(rec.id, rec);
|
|
1730
|
+
}
|
|
1731
|
+
const previous = recommendationSources.get(source) || new Map();
|
|
1732
|
+
recommendationSources.set(source, next);
|
|
1733
|
+
const nodes = [];
|
|
1734
|
+
for (const id of new Set([...previous.keys(), ...next.keys()])) {
|
|
1735
|
+
const rec = next.get(id) || [...recommendationSources.values()].map(rows => rows.get(id)).find(Boolean);
|
|
1736
|
+
const existing = document.getElementById(`rec-${id}`);
|
|
1737
|
+
if (!rec) {
|
|
1738
|
+
existing?.remove(); renderedRecIds.delete(id); renderedRecommendations.delete(id);
|
|
1739
|
+
continue;
|
|
1740
|
+
}
|
|
1741
|
+
renderedRecIds.add(id); renderedRecommendations.set(id, rec);
|
|
1742
|
+
const card = buildRecCard(rec);
|
|
1743
|
+
if (existing) existing.replaceWith(card); else nodes.push(card);
|
|
1731
1744
|
}
|
|
1732
1745
|
// Ordering is by BLAST RADIUS, not arrival time โ the slow sources arrive last and matter most.
|
|
1733
1746
|
//
|
|
@@ -2954,6 +2967,83 @@ function nightlyFactsLine(f) {
|
|
|
2954
2967
|
f.agree === false ? ' โ these disagree, so the toggle above shows no single state until they do.' : '');
|
|
2955
2968
|
}
|
|
2956
2969
|
|
|
2970
|
+
let suiteUpdateTimer = null;
|
|
2971
|
+
let suiteUpdateBusy = false;
|
|
2972
|
+
let suiteUpdateWasActive = false;
|
|
2973
|
+
function suiteUpdateSummary(state) {
|
|
2974
|
+
const labels = { running: 'Update in progress', succeeded: 'Last update completed successfully',
|
|
2975
|
+
failed: 'Last update failed', interrupted: 'Last update has no completed result',
|
|
2976
|
+
checked: 'Last run checked for updates; it did not apply them', 'never-run': 'No update run has been recorded' };
|
|
2977
|
+
const label = state.status === 'succeeded' && state.unverified?.length ? 'Update finished; some checks remain unverified' : labels[state.status];
|
|
2978
|
+
return `${label || 'Update status unknown'}${state.finishedAt ? ` ยท ${fmtDate(state.finishedAt)}` : ''}${state.error ? ` ยท ${state.error}` : ''}`;
|
|
2979
|
+
}
|
|
2980
|
+
function renderSuiteUpdate(state) {
|
|
2981
|
+
if (!state) return;
|
|
2982
|
+
const updateFinished = suiteUpdateWasActive && !state.active && ['succeeded', 'failed'].includes(state.status);
|
|
2983
|
+
suiteUpdateWasActive = !!state.active;
|
|
2984
|
+
if (updateFinished) void recheckMachine(); // Measure the changed machine once, including partial failures.
|
|
2985
|
+
let card = document.getElementById('card-suite-update');
|
|
2986
|
+
if (!card) {
|
|
2987
|
+
card = el('section', { class: 'card sub-card rail-act', id: 'card-suite-update', 'aria-labelledby': 'suite-update-heading' },
|
|
2988
|
+
el('div', { class: 'body' },
|
|
2989
|
+
el('h2', { id: 'suite-update-heading' }, 'Keep all tools updated'),
|
|
2990
|
+
chip('recommended', 'cyan'),
|
|
2991
|
+
el('p', { class: 'fineprint' }, 'One coordinated update for your installed developer tools, plugins and Brain knowledge. Enable nightly updates and run the same coordinator now.'),
|
|
2992
|
+
el('label', { for: 'suite-update-channel' }, 'Release policy '),
|
|
2993
|
+
el('select', { id: 'suite-update-channel' },
|
|
2994
|
+
el('option', { value: '', disabled: true }, 'Policy not measured'),
|
|
2995
|
+
el('option', { value: 'latest' }, 'Latest (recommended)'),
|
|
2996
|
+
el('option', { value: 'alpha' }, 'Alpha')),
|
|
2997
|
+
el('p', { class: 'fineprint' }, 'Alpha uses published alpha releases for supported Ruv tools, with Latest as the fallback. Other providers keep their own release channels; installed newer versions are preserved.'),
|
|
2998
|
+
el('button', { class: 'btn btn-apply', id: 'suite-update-run', type: 'button', onclick: async () => {
|
|
2999
|
+
if (suiteUpdateBusy) return;
|
|
3000
|
+
const button = document.getElementById('suite-update-run');
|
|
3001
|
+
const channel = document.getElementById('suite-update-channel').value;
|
|
3002
|
+
suiteUpdateBusy = true; button.disabled = true;
|
|
3003
|
+
try {
|
|
3004
|
+
const result = await postJSON('/api/suite-update', { channel });
|
|
3005
|
+
if (!result.ok || !result.data.ok) throw new Error(result.status === 403 ? TOKEN_MSG : result.data.error || 'The update could not start.');
|
|
3006
|
+
renderSuiteUpdate(result.data.state);
|
|
3007
|
+
} catch (error) { document.getElementById('suite-update-result').textContent = String(error.message || error); }
|
|
3008
|
+
finally { suiteUpdateBusy = false; void pollSuiteUpdate(); }
|
|
3009
|
+
} }, 'Keep all tools updated'),
|
|
3010
|
+
el('p', { id: 'suite-update-result', role: 'status', 'aria-live': 'polite' }),
|
|
3011
|
+
el('p', { id: 'suite-update-nightly', class: 'fineprint' })));
|
|
3012
|
+
document.getElementById('card-settings')?.before(card);
|
|
3013
|
+
}
|
|
3014
|
+
const select = document.getElementById('suite-update-channel');
|
|
3015
|
+
// Preserve an unsaved policy choice during receipt polling.
|
|
3016
|
+
if (!select.dataset.initialized) {
|
|
3017
|
+
select.value = state.channel || '';
|
|
3018
|
+
select.dataset.initialized = '1';
|
|
3019
|
+
}
|
|
3020
|
+
select.disabled = !!state.active;
|
|
3021
|
+
const button = document.getElementById('suite-update-run');
|
|
3022
|
+
button.disabled = suiteUpdateBusy || !!state.active || !state.available || state.lockState !== 'idle';
|
|
3023
|
+
button.textContent = state.active ? 'Updatingโฆ' : 'Keep all tools updated';
|
|
3024
|
+
document.getElementById('suite-update-result').textContent = state.available ? suiteUpdateSummary(state) : state.policyError || 'The coordinated updater is unavailable.';
|
|
3025
|
+
document.getElementById('suite-update-nightly').textContent = `Nightly updates: ${state.nightlyState === 'on' ? 'enabled' : state.nightlyState === 'off' ? 'not scheduled' : 'not verified'}. Saved scope: ${state.scope === 'all' ? 'all installed tools' : state.scope === 'ruvnet' ? 'Ruv tools' : 'not measured'}. Clicking this button includes existing npm, Homebrew, uv, Cargo and native tools. It does not install missing tools or change project dependencies.`;
|
|
3026
|
+
let activity = document.getElementById('suite-update-activity');
|
|
3027
|
+
if (!activity) {
|
|
3028
|
+
activity = el('section', { class: 'body', id: 'suite-update-activity', 'aria-label': 'Tool update activity' });
|
|
3029
|
+
document.getElementById('card-activity')?.append(activity);
|
|
3030
|
+
}
|
|
3031
|
+
activity.replaceChildren(el('h3', {}, 'Tool update activity'),
|
|
3032
|
+
el('p', { role: 'status' }, suiteUpdateSummary(state)),
|
|
3033
|
+
state.startedAt ? el('p', { class: 'fineprint' }, `Started ${fmtDate(state.startedAt)} ยท ${state.mode === 'check' ? 'check only' : 'apply'}`) : '',
|
|
3034
|
+
state.steps?.length ? el('ul', {}, state.steps.map(step => el('li', {}, `${step.name}: ${step.state}`))) : '',
|
|
3035
|
+
state.unverified?.length ? el('details', { class: 'sub' }, el('summary', {}, 'Checks not verified'),
|
|
3036
|
+
el('ul', {}, state.unverified.map(note => el('li', {}, note)))) : '',
|
|
3037
|
+
state.exclusions?.length ? el('details', { class: 'sub' }, el('summary', {}, `${state.exclusions.length} tools preserved`),
|
|
3038
|
+
el('ul', {}, state.exclusions.map(item => el('li', {}, `${item.name}: ${item.reason}`)))) : '',
|
|
3039
|
+
state.sourceSha256 ? el('details', { class: 'sub' }, el('summary', {}, 'Update receipt'), el('p', { class: 'fineprint' }, `Updater source: ${state.sourceSha256.slice(0, 16)}`)) : '');
|
|
3040
|
+
}
|
|
3041
|
+
async function pollSuiteUpdate() {
|
|
3042
|
+
try { renderSuiteUpdate(await getJSON('/api/suite-update')); }
|
|
3043
|
+
catch { const result = document.getElementById('suite-update-result'); if (result) result.textContent = 'Update status could not be measured. Reload to reconnect.'; }
|
|
3044
|
+
if (!suiteUpdateTimer) suiteUpdateTimer = setInterval(() => { if (!document.hidden) void pollSuiteUpdate(); }, 3000);
|
|
3045
|
+
}
|
|
3046
|
+
|
|
2957
3047
|
function renderSettings(cfg, us, bp) {
|
|
2958
3048
|
const body = $('#body-settings');
|
|
2959
3049
|
// UNSAVED CHOICES SURVIVE A REPAINT. RNBC QA 2026-10-01: a background measurement landing while
|
|
@@ -3350,7 +3440,7 @@ function bpParts(bp) {
|
|
|
3350
3440
|
'Version updates and the health alarm keep running even when the brain is off โ an off machine ',
|
|
3351
3441
|
'has to be able to receive the fix for an off-state bug. The nightly refresh is the part of that ',
|
|
3352
3442
|
'you can pause: it is the ',
|
|
3353
|
-
el('button', { class: 'btn btn-ghost btn-sm', type: 'button', onclick: () => jumpToSetting('nightly') }, 'Nightly
|
|
3443
|
+
el('button', { class: 'btn btn-ghost btn-sm', type: 'button', onclick: () => jumpToSetting('nightly') }, 'Nightly coordinated updates'),
|
|
3354
3444
|
' switch in Settings.'),
|
|
3355
3445
|
|
|
3356
3446
|
part('Installed knowledge',
|
|
@@ -3683,16 +3773,16 @@ function renderFreshness(state, { polling = false, justLanded = false } = {}) {
|
|
|
3683
3773
|
}
|
|
3684
3774
|
const at = state && (state.measuredAt || state.cachedAt || state.generatedAt);
|
|
3685
3775
|
if (at) FRESH_BASE = at;
|
|
3686
|
-
if (justLanded) {
|
|
3687
|
-
pill.className = 'chip tone-green';
|
|
3688
|
-
pill.textContent = 'measured just now';
|
|
3689
|
-
return;
|
|
3690
|
-
}
|
|
3691
3776
|
if (!at || stampWithdrawn(at)) {
|
|
3692
3777
|
pill.className = polling ? 'chip tone-cyan' : 'chip tone-warn';
|
|
3693
3778
|
pill.textContent = polling ? 're-measuring your machineโฆ' : 'this reading was cleared โ click to measure again';
|
|
3694
3779
|
return;
|
|
3695
3780
|
}
|
|
3781
|
+
if (justLanded) {
|
|
3782
|
+
pill.className = 'chip tone-green';
|
|
3783
|
+
pill.textContent = `machine reading updated ยท as of ${fmtAge(at)}`;
|
|
3784
|
+
return;
|
|
3785
|
+
}
|
|
3696
3786
|
pill.className = polling ? 'chip tone-cyan' : (state && state.stale ? 'chip tone-warn' : 'chip tone-grey');
|
|
3697
3787
|
pill.textContent = `as of ${fmtAge(at)}${polling ? ' ยท re-measuringโฆ' : ''}`;
|
|
3698
3788
|
}
|
|
@@ -3738,7 +3828,7 @@ function startFreshnessPolling() {
|
|
|
3738
3828
|
// ONE painter โ the same loadState() the first paint uses, so a polled repaint can never
|
|
3739
3829
|
// drift from an initial one. It also refreshes preStateHash and the recommendations, which a
|
|
3740
3830
|
// sections-only repaint silently left pointing at the pre-measurement machine.
|
|
3741
|
-
await loadState({ landed: true });
|
|
3831
|
+
await loadState({ landed: true, snapshot: st });
|
|
3742
3832
|
// The other cards measure on their own clocks (stack, capabilities, fleet). Re-ask them here
|
|
3743
3833
|
// rather than letting the page claim page-wide freshness on the strength of state alone.
|
|
3744
3834
|
void loadStack();
|
|
@@ -3790,10 +3880,12 @@ async function doManualRefresh() {
|
|
|
3790
3880
|
that painted the same bytes twice and cost a round-trip on every open. There is now one call, and
|
|
3791
3881
|
the "live" reading arrives the only way it can without freezing the server โ from the detached
|
|
3792
3882
|
child, via the freshness poller. */
|
|
3793
|
-
async function loadState({ landed = false } = {}) {
|
|
3883
|
+
async function loadState({ landed = false, snapshot = null } = {}) {
|
|
3794
3884
|
try {
|
|
3795
|
-
const state = await getJSON('/api/state');
|
|
3885
|
+
const state = snapshot || await getJSON('/api/state');
|
|
3796
3886
|
$('#global-error').hidden = true;
|
|
3887
|
+
renderSuiteUpdate(state.suiteUpdate);
|
|
3888
|
+
void pollSuiteUpdate();
|
|
3797
3889
|
|
|
3798
3890
|
// WARMING: NOTHING HAS BEEN MEASURED FOR THIS PROJECT YET. Paint NOTHING. Every render* below
|
|
3799
3891
|
// would turn a missing section into a positive claim โ renderWiring(undefined) prints "No wiring
|
|
@@ -3968,7 +4060,7 @@ const WARM_RETRY_MAX = 60;
|
|
|
3968
4060
|
async function loadMemoryFleet(attempt = 0) {
|
|
3969
4061
|
try {
|
|
3970
4062
|
const m = await getJSON('/api/memory');
|
|
3971
|
-
if (m && m.warming) {
|
|
4063
|
+
if (m && (m.warming || m.stale === true)) {
|
|
3972
4064
|
// The fleet scan is the slowest thing the console does. Until it lands, the memory card keeps
|
|
3973
4065
|
// whatever /api/state already gave it and simply does not claim a fleet โ it must never render
|
|
3974
4066
|
// "0 projects", which on this card would read as "nothing on your machine learns anything".
|
|
@@ -3999,8 +4091,8 @@ async function loadMemoryFleet(attempt = 0) {
|
|
|
3999
4091
|
async function loadStack(attempt = 0) {
|
|
4000
4092
|
try {
|
|
4001
4093
|
const stack = await getJSON('/api/stack');
|
|
4002
|
-
if (stack && stack.warming) {
|
|
4003
|
-
setChips('chips-stack', [chip('auditing your stackโฆ', 'wait')]);
|
|
4094
|
+
if (stack && (stack.warming || stack.stale === true)) {
|
|
4095
|
+
setChips('chips-stack', [chip(stack.warming ? 'auditing your stackโฆ' : 're-measuring your stackโฆ', 'wait')]);
|
|
4004
4096
|
if (attempt < WARM_RETRY_MAX) setTimeout(() => { void loadStack(attempt + 1); }, WARM_RETRY_MS);
|
|
4005
4097
|
else { setChips('chips-stack', [chip('audit is taking unusually long', 'warn')]); recsSettled('stack', false); }
|
|
4006
4098
|
return; // skeleton stays: an empty package list would read as "nothing is installed"
|
|
@@ -4076,7 +4168,7 @@ const MOCK_STATE = {
|
|
|
4076
4168
|
values: { openrouterKey: true, nightly: true, routing: 'auto', qeFleet: false },
|
|
4077
4169
|
schema: [
|
|
4078
4170
|
{ key: 'openrouterKey', label: 'OpenRouter API key', type: 'secret', help: 'Unlocks cheap-model routing + the self-improvement loop', secret: true },
|
|
4079
|
-
{ key: 'nightly', label: 'Nightly
|
|
4171
|
+
{ key: 'nightly', label: 'Nightly coordinated updates', type: 'bool', help: 'Rebuild the KB from pinned SHAs overnight' },
|
|
4080
4172
|
{ key: 'routing', label: 'Token-smart routing', type: 'enum', options: ['auto', 'off'], help: 'Route cheap tasks to smaller models' },
|
|
4081
4173
|
{ key: 'qeFleet', label: 'On-demand QE fleet', type: 'bool', help: 'Agentic-QE test fleet, spun up on request' },
|
|
4082
4174
|
],
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
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": {
|
|
7
|
-
"ruvnet-brain": "bin/install.mjs"
|
|
7
|
+
"ruvnet-brain": "bin/install.mjs",
|
|
8
|
+
"ruvnet-brain-update": "bin/developer-update.mjs"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"prepublishOnly": "node scripts/protected-release-invocation.mjs --prepublish-only",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
".claude-plugin/marketplace.json",
|
|
84
85
|
"bin/install.mjs",
|
|
85
86
|
"bin/nightly-refresh.mjs",
|
|
87
|
+
"bin/developer-update.mjs",
|
|
86
88
|
"plugin/",
|
|
87
89
|
"plugin/mcp/managed-cli-interface.mjs",
|
|
88
90
|
"plugin/mcp/server.mjs",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruvnet-brain",
|
|
3
3
|
"description": "RuvNet brain transplant for Claude Code โ grounds every RuvNet decision in real source across 77 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 a UserPromptSubmit retrieve-and-inject grounding hook and a PreToolUse write gate that refuses ungrounded rUv-product code until search_ruvnet has been consulted (ADR-0012 / ADR-067).",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.6.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Stuart Kerr"
|
|
7
7
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Brain Console โ the RuvNet Brain Console, also opened by /rnbc (same console as /rnb, /rvbc, /rvcb and /ruvnet-brain:configure โ every spelling works). Your whole RuvNet stack on one page. Read-only until you click."
|
|
3
|
-
updated: 2026-10-
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
|
@@ -64,6 +65,17 @@ header chip says how fresh the reading is and re-measures if they click it.
|
|
|
64
65
|
Report it plainly โ the real error, not a guess โ and offer to fix it. Never leave them staring at
|
|
65
66
|
a dead tab wondering whether it is still loading.
|
|
66
67
|
|
|
68
|
+
## Promise capture preference
|
|
69
|
+
|
|
70
|
+
If the user asks to stop automatic promise capture, explain the supported setting:
|
|
71
|
+
`RUVNET_PROMISE_CAPTURE=off` in the hook process environment. It can be supplied for the machine or
|
|
72
|
+
one project through the host's environment configuration. The exact value `off` stops capturing new
|
|
73
|
+
"I'll โฆ" promises; unset it or set `on` to resume capture. This preference is separate from the Brain
|
|
74
|
+
on/off switch and is not currently a Console toggle. Existing promises remain visible and forceable,
|
|
75
|
+
and close only on verified evidence. Capability and completion integrity checks keep running. Do not
|
|
76
|
+
disable the Stop hook or edit the work ledger to apply this preference. Reopen the host if its current
|
|
77
|
+
session has not picked up the changed environment.
|
|
78
|
+
|
|
67
79
|
---
|
|
68
80
|
|
|
69
81
|
## Things NOT to do before the page is open
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Open the RuvNet Brain Console (/rnbc) โ 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-10-
|
|
4
|
-
version: 1.0.
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
package/plugin/commands/rnb.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "RNB โ opens the RuvNet Brain Console (same console as /rnbc, /rvbc, /brain-console and /ruvnet-brain:configure โ every spelling works). Your whole RuvNet stack on one page. Read-only until you click."
|
|
3
|
-
updated: 2026-10-
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
|
@@ -64,6 +65,17 @@ header chip says how fresh the reading is and re-measures if they click it.
|
|
|
64
65
|
Report it plainly โ the real error, not a guess โ and offer to fix it. Never leave them staring at
|
|
65
66
|
a dead tab wondering whether it is still loading.
|
|
66
67
|
|
|
68
|
+
## Promise capture preference
|
|
69
|
+
|
|
70
|
+
If the user asks to stop automatic promise capture, explain the supported setting:
|
|
71
|
+
`RUVNET_PROMISE_CAPTURE=off` in the hook process environment. It can be supplied for the machine or
|
|
72
|
+
one project through the host's environment configuration. The exact value `off` stops capturing new
|
|
73
|
+
"I'll โฆ" promises; unset it or set `on` to resume capture. This preference is separate from the Brain
|
|
74
|
+
on/off switch and is not currently a Console toggle. Existing promises remain visible and forceable,
|
|
75
|
+
and close only on verified evidence. Capability and completion integrity checks keep running. Do not
|
|
76
|
+
disable the Stop hook or edit the work ledger to apply this preference. Reopen the host if its current
|
|
77
|
+
session has not picked up the changed environment.
|
|
78
|
+
|
|
67
79
|
---
|
|
68
80
|
|
|
69
81
|
## Things NOT to do before the page is open
|
package/plugin/commands/rnbc.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "RNBC โ RuvNet Brain Console. Opens the live console page: your whole RuvNet stack on one page โ what's installed, what your AI learned, one-click reversible fixes. Read-only until you click. (Also: /rnb, /rvbc, /rvcb, /brain-console โ all the same thing.)"
|
|
3
|
-
updated: 2026-10-
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
|
@@ -64,6 +65,17 @@ header chip says how fresh the reading is and re-measures if they click it.
|
|
|
64
65
|
Report it plainly โ the real error, not a guess โ and offer to fix it. Never leave them staring at
|
|
65
66
|
a dead tab wondering whether it is still loading.
|
|
66
67
|
|
|
68
|
+
## Promise capture preference
|
|
69
|
+
|
|
70
|
+
If the user asks to stop automatic promise capture, explain the supported setting:
|
|
71
|
+
`RUVNET_PROMISE_CAPTURE=off` in the hook process environment. It can be supplied for the machine or
|
|
72
|
+
one project through the host's environment configuration. The exact value `off` stops capturing new
|
|
73
|
+
"I'll โฆ" promises; unset it or set `on` to resume capture. This preference is separate from the Brain
|
|
74
|
+
on/off switch and is not currently a Console toggle. Existing promises remain visible and forceable,
|
|
75
|
+
and close only on verified evidence. Capability and completion integrity checks keep running. Do not
|
|
76
|
+
disable the Stop hook or edit the work ledger to apply this preference. Reopen the host if its current
|
|
77
|
+
session has not picked up the changed environment.
|
|
78
|
+
|
|
67
79
|
---
|
|
68
80
|
|
|
69
81
|
## Things NOT to do before the page is open
|
package/plugin/commands/rvbc.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "RvBC โ the older spelling of /rnbc, the RuvNet Brain Console (same console as /rnbc, /rnb, /brain-console and /ruvnet-brain:configure โ every spelling works). Your whole RuvNet stack on one page. Read-only until you click."
|
|
3
|
-
updated: 2026-10-
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
|
@@ -64,6 +65,17 @@ header chip says how fresh the reading is and re-measures if they click it.
|
|
|
64
65
|
Report it plainly โ the real error, not a guess โ and offer to fix it. Never leave them staring at
|
|
65
66
|
a dead tab wondering whether it is still loading.
|
|
66
67
|
|
|
68
|
+
## Promise capture preference
|
|
69
|
+
|
|
70
|
+
If the user asks to stop automatic promise capture, explain the supported setting:
|
|
71
|
+
`RUVNET_PROMISE_CAPTURE=off` in the hook process environment. It can be supplied for the machine or
|
|
72
|
+
one project through the host's environment configuration. The exact value `off` stops capturing new
|
|
73
|
+
"I'll โฆ" promises; unset it or set `on` to resume capture. This preference is separate from the Brain
|
|
74
|
+
on/off switch and is not currently a Console toggle. Existing promises remain visible and forceable,
|
|
75
|
+
and close only on verified evidence. Capability and completion integrity checks keep running. Do not
|
|
76
|
+
disable the Stop hook or edit the work ledger to apply this preference. Reopen the host if its current
|
|
77
|
+
session has not picked up the changed environment.
|
|
78
|
+
|
|
67
79
|
---
|
|
68
80
|
|
|
69
81
|
## Things NOT to do before the page is open
|
package/plugin/commands/rvcb.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "RvCB โ another spelling of /rnbc, the RuvNet Brain Console (same console as /rnbc, /rnb, /rvbc, /brain-console and /ruvnet-brain:configure โ every spelling works). Read-only until you click."
|
|
3
|
-
updated: 2026-10-
|
|
3
|
+
updated: 2026-10-10 06:00:22 EDT
|
|
4
|
+
version: 1.0.2
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
Launch the **RuvNet Brain Console** (RNBC) for the user. `/rnbc` is its name; `/rnb`, `/rvbc`,
|
|
@@ -64,6 +65,17 @@ header chip says how fresh the reading is and re-measures if they click it.
|
|
|
64
65
|
Report it plainly โ the real error, not a guess โ and offer to fix it. Never leave them staring at
|
|
65
66
|
a dead tab wondering whether it is still loading.
|
|
66
67
|
|
|
68
|
+
## Promise capture preference
|
|
69
|
+
|
|
70
|
+
If the user asks to stop automatic promise capture, explain the supported setting:
|
|
71
|
+
`RUVNET_PROMISE_CAPTURE=off` in the hook process environment. It can be supplied for the machine or
|
|
72
|
+
one project through the host's environment configuration. The exact value `off` stops capturing new
|
|
73
|
+
"I'll โฆ" promises; unset it or set `on` to resume capture. This preference is separate from the Brain
|
|
74
|
+
on/off switch and is not currently a Console toggle. Existing promises remain visible and forceable,
|
|
75
|
+
and close only on verified evidence. Capability and completion integrity checks keep running. Do not
|
|
76
|
+
disable the Stop hook or edit the work ledger to apply this preference. Reopen the host if its current
|
|
77
|
+
session has not picked up the changed environment.
|
|
78
|
+
|
|
67
79
|
---
|
|
68
80
|
|
|
69
81
|
## Things NOT to do before the page is open
|