ruvnet-brain 4.5.16 → 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 +15 -3
- package/bin/nightly-refresh.mjs +17 -1
- package/config/practical-rule-catalog.json +111 -0
- package/console/CONTRACT.md +47 -3
- package/console/app.js +112 -20
- package/package.json +5 -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/hooks/codex-hooks.json +13 -1
- package/plugin/hooks/hook-contracts.json +20 -4
- package/plugin/scripts/adr-currency-gate.mjs +75 -31
- package/plugin/scripts/advocacy-outcomes.mjs +18 -4
- package/plugin/scripts/agentdb-recall.mjs +161 -26
- package/plugin/scripts/automatic-update.mjs +11 -2
- package/plugin/scripts/capability-inventory-receipt.mjs +16 -7
- package/plugin/scripts/capability-registry.mjs +55 -114
- package/plugin/scripts/capacity-aware-parallel-work.mjs +1 -1
- package/plugin/scripts/codex-hook-adapter.mjs +33 -6
- package/plugin/scripts/codex-hook-wrapper.mjs +23 -3
- package/plugin/scripts/continuation-gate.mjs +2 -1
- package/plugin/scripts/continuity-brief.mjs +55 -22
- package/plugin/scripts/continuity-events.mjs +93 -17
- package/plugin/scripts/continuity-hook-policy.mjs +1 -1
- package/plugin/scripts/continuity-journal.mjs +77 -29
- package/plugin/scripts/decision-gate.mjs +62 -37
- package/plugin/scripts/decision-outcomes.mjs +192 -199
- package/plugin/scripts/detach.mjs +53 -13
- 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/doc-currency.mjs +993 -0
- package/plugin/scripts/ground-ruvnet.sh +16 -3
- package/plugin/scripts/grounding-answer.mjs +18 -16
- package/plugin/scripts/grounding-stamp.sh +3 -0
- package/plugin/scripts/grounding-turn-evidence.mjs +60 -65
- package/plugin/scripts/grounding-turn-gate.mjs +34 -29
- package/plugin/scripts/grounding-turn-mark.mjs +101 -17
- package/plugin/scripts/hook-context-budget.mjs +90 -0
- package/plugin/scripts/hook-registry.mjs +129 -1
- package/plugin/scripts/hook-shim.mjs +20 -2
- package/plugin/scripts/host-update.mjs +6 -3
- package/plugin/scripts/lesson-gate.mjs +1 -1
- package/plugin/scripts/nightly-scheduler.mjs +55 -16
- package/plugin/scripts/plugin-artifact-proof.mjs +135 -0
- package/plugin/scripts/project-capture-queue.mjs +22 -15
- package/plugin/scripts/project-progression-producer.mjs +15 -8
- package/plugin/scripts/project-progression-reader.mjs +14 -4
- package/plugin/scripts/project-progression-session-start.mjs +29 -17
- package/plugin/scripts/project-progression-sources.mjs +24 -10
- package/plugin/scripts/project-progression-store.mjs +7 -2
- package/plugin/scripts/project-store-resolver.mjs +10 -7
- package/plugin/scripts/project-transition-hook.mjs +20 -12
- package/plugin/scripts/session-snapshot-hook.mjs +20 -9
- package/plugin/scripts/session-start-budget.mjs +18 -0
- package/plugin/scripts/session-start-core.mjs +32 -6
- package/plugin/scripts/session-start-trace.mjs +2 -1
- package/plugin/scripts/turn-outcome-capture.mjs +7 -5
- package/plugin/scripts/unprompted-runtime.mjs +36 -10
- package/plugin/scripts/user-settings.mjs +7 -1
- 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/doc-currency.mjs +6 -990
- package/scripts/hook-qualify-core.mjs +30 -1
- package/scripts/model-managed-prompt.mjs +1 -1
- package/scripts/model-managed-workflow-service.mjs +113 -14
- package/scripts/model-routing-controller.mjs +112 -21
- package/scripts/onboarding-console.mjs +41 -12
- package/scripts/practical-rule-selector.mjs +51 -0
- package/scripts/product-integrity-contract.mjs +11 -4
- package/scripts/release-qualification-contract.mjs +14 -0
- package/scripts/remedy-registry.mjs +1 -0
- package/scripts/source-scope-receipt.mjs +9 -4
- package/scripts/stack-sync.mjs +19 -132
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",
|
|
@@ -97,6 +99,7 @@
|
|
|
97
99
|
"LICENSE",
|
|
98
100
|
"console/",
|
|
99
101
|
"config/model-router/",
|
|
102
|
+
"config/practical-rule-catalog.json",
|
|
100
103
|
"tri-smart-skill/",
|
|
101
104
|
"data/model-catalog.json",
|
|
102
105
|
"scripts/",
|
|
@@ -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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "RuvNet Brain continuity plane for Codex. Broad legacy automatic gates remain retired. Registrations here are MEASURED, not assumed: a probe hook was registered on all twelve event names the installed codex-cli 0.154.0 binary declares and a real `codex exec` run was observed (2026-09-11). SessionStart, UserPromptSubmit and SessionEnd FIRED and carry handlers. Stop keeps the pre-existing, project-scoped continuation gate; session-snapshot was added at Stop on 2026-09-29 to record each turn's outcome (turn-outcome-capture.mjs)
|
|
2
|
+
"description": "RuvNet Brain continuity plane for Codex. Broad legacy automatic gates remain retired. Registrations here are MEASURED, not assumed: a probe hook was registered on all twelve event names the installed codex-cli 0.154.0 binary declares and a real `codex exec` run was observed (2026-09-11). SessionStart, UserPromptSubmit and SessionEnd FIRED and carry handlers. Stop keeps the pre-existing, project-scoped continuation gate; session-snapshot was added at Stop on 2026-09-29 to record each turn's outcome (turn-outcome-capture.mjs) — Codex SessionEnd carries no last_assistant_message, so Stop is the only boundary that can. Its delivery rests on codex-cli 0.158.0's declared stop.command.input schema and the other Codex Stop handlers; it has NOT been live-observed and fails open. PreCompact uses the existing session-snapshot capture handler: Codex0.160.1 declares this native boundary. Registration is not proof of native delivery; manual/automatic delivery remains separately evidenced. All handlers are bounded and fail open. Amended 2026-09-11: UserPromptSubmit also runs ground-ruvnet (grounding injection, ADR-040 §Amendment). Amended 2026-09-12: the 2026-09-11 PreToolUse/PostToolUse \"not observed\" note was measured with a prompt (`codex exec \"reply OK\"`) that never invoked a tool, so neither event had anything to fire on — that was an untested path, not a failing one. Re-probed with prompts that actually call a tool: a real apply_patch write fired PreToolUse/PostToolUse with tool_name \"apply_patch\", and a real MCP call to this repo's own search_ruvnet server fired both with tool_name \"mcp__ruvnet_brain__search_ruvnet\". Both are now registered below: decision-gate's write route (matcher includes apply_patch, Codex's raw write-tool name) and grounding-stamp (unchanged matcher already recognizes the mcp__..__search_ruvnet shape). The bash route (exec_command) remains unregistered — today's measurement covered a write and an MCP call, not exec_command, and extending on that evidence would be the same unproven leap this note replaces. Also added 2026-09-12: grounding-turn-mark (UserPromptSubmit) and grounding-turn-gate (Stop), the \"answered without searching\" pair — a prompt-level grounding directive is advisory, so this records whether it fired and forces continuation at Stop if no search_ruvnet call was recorded since (reusing grounding-stamp's own stamp evidence). Both are registered on Codex identically to Claude: the Stop-block contract (hookSpecificOutput.additionalContext) already has a proven Codex translation via codex-hook-adapter.mjs's Stop branch (see tests/unit/codex-lifecycle-hooks.test.mjs), the same path continuation-gate already uses. Capacity-aware parallel-work guidance is also registered at UserPromptSubmit: it advises the coordinator to launch only real independent workers within sampled resource headroom and the live runtime/tool cap; it never spawns workers or claims execution. Automatic normalized project observations are captured at prompt, pre-tool, post-tool and child completion; Claude also observes PostToolUseFailure. Pending and degraded memory are disclosed; this does not establish synchronous ADR-073 conformance or native Grok recall. SessionStart has an 8-second measured replay/restore envelope. Issue377: consent-eligible learning captures only fixed workflow metadata; lifecycle recovery schedules one finite worker. Original records remain retained with hash-bound acknowledgments. Native delivery must be separately evidenced; registration is not proof.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
@@ -164,6 +164,18 @@
|
|
|
164
164
|
}
|
|
165
165
|
]
|
|
166
166
|
}
|
|
167
|
+
],
|
|
168
|
+
"PreCompact": [
|
|
169
|
+
{
|
|
170
|
+
"hooks": [
|
|
171
|
+
{
|
|
172
|
+
"type": "command",
|
|
173
|
+
"command": "node -e \"const f=require('node:fs'),o=require('node:os'),p=require('node:path'),c=require('node:child_process'),d=process.env.CODEX_HOME||p.join(o.homedir(),'.codex'),b=process.env.RUVNET_BRAIN_HOME||p.join(p.dirname(d),'.cache','ruvnet-brain'),w=p.join(b,'codex-hook.mjs');let s;try{s=f.statSync(w)}catch{}if(!s?.isFile())process.exit(0);const r=c.spawnSync(process.execPath,[w,...process.argv.slice(2)],{stdio:['inherit','pipe','pipe'],encoding:'utf8',env:process.env,timeout:Number(process.argv[1]),killSignal:'SIGKILL'});if(r.status===0||r.status===2){if(r.stdout)process.stdout.write(r.stdout);if(r.stderr)process.stderr.write(r.stderr)}process.exit(r.status===2?2:0)\" 9000 session-snapshot PreCompact",
|
|
174
|
+
"timeout": 10
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
"matcher": "*"
|
|
178
|
+
}
|
|
167
179
|
]
|
|
168
180
|
}
|
|
169
181
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "The legacy automatic gate collection remains retired. What is permitted is the continuity plane below and nothing else: SessionStart restores the canonical project checkpoint; UserPromptSubmit runs the single unprompted-speech chokepoint; Stop may nudge one explicitly authorized project-scoped objective AND capture a project snapshot; PreCompact and SessionEnd capture a project snapshot. Version 3 of this file permitted exactly two handlers, which was not a safety property but a contradiction: it demanded a restore while forbidding any event that could WRITE the journal the restore reads, so the canonical store held zero progression rows. Adding to this list is still a deliberate act that must pass `npm run hooks:check`; what changed is that the shape can now express the plane that actually works. Amended 2026-09-11 (Stuart): the continuity-only charter had retired the ONLY enforcement of ADR-0012 \u2014 never write rUv-product code the brain has not seen \u2014 and the failure it exists to prevent recurred the day it was measured absent. The plane now also carries three grounding registrations: ground-ruvnet (UserPromptSubmit, grounding injection \u2014 a second owner of that event, scoped by ADR-040 \u00a7Amendment 2026-09-11 to directives rather than speech), decision-gate\u2019s write route (PreToolUse, the one refuser, ADR-067), and grounding-stamp (PostToolUse on a successful search_ruvnet, the receipt that opens the write gate). Amended 2026-09-12: the 2026-09-11 claim that Codex PreToolUse/PostToolUse delivery had never been observed was measured with a prompt that never invoked a tool (`codex exec \"reply OK\"`), so it was an untested path, not a failing one. Re-measured with prompts that actually call a tool (a real apply_patch write, a real MCP search_ruvnet call against this repo's own server): both events FIRED on codex-cli 0.154.0 with real payloads. decision-gate's write route and grounding-stamp are now dual-host (see _codexCapture and contracts below); the bash route (exec_command) remains Claude-only \u2014 today's measurement did not exercise it. Also amended 2026-09-12: added grounding-turn-mark (UserPromptSubmit) and grounding-turn-gate (Stop), the \"answered without searching\" pair \u2014 ground-ruvnet's Gate 1 directive is advisory, so nothing previously checked whether the model complied before the turn ended. grounding-turn-mark records that Gate 1 fired for a turn; grounding-turn-gate forces continuation at Stop if grounding-stamp's own evidence shows no search_ruvnet call happened since. Both dual-host from the start (the Stop-block contract is already proven on Codex via continuation-gate). Added capacity-aware-parallel-work at UserPromptSubmit on both measured hosts: it is context-only, resource-bounded, and requires the coordinator to check actual tool/runtime slots; it does not spawn or claim workers. Amended 2026-09-30 (ADR-0030 decision point #1, no new registration): grounding-turn-mark also arms a turn when the prompt asks a capability/feasibility/architecture question about any tool or platform, and grounding-turn-gate then requires every capability claim in the final answer to be bound to a relevant STRONG source read this turn after the last weak one (a WebFetch body is a small model's summary: weak); on Claude it reads the transcript instead of stamp mtimes, which removed the measured 'no search recorded' false alarms. ADR-0030 #2/#3 run in shadow only. Amendment 2026-10-03: normalized prompt/tool/failure/child observations are automatic; startup replays before restoring. Queued and unavailable evidence remain distinct from exact committed readback. Native Grok prompt recall is unsupported.",
|
|
3
|
-
"_version":
|
|
3
|
+
"_version": 11,
|
|
4
4
|
"_eventOwners": [
|
|
5
5
|
{
|
|
6
6
|
"event": "SessionStart",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"owner": "session-snapshot",
|
|
48
48
|
"class": "continuity capture",
|
|
49
49
|
"hosts": [
|
|
50
|
-
"claude"
|
|
50
|
+
"claude",
|
|
51
|
+
"codex"
|
|
51
52
|
],
|
|
52
53
|
"responsibility": "The boundary at which context is about to be lost \u2014 capture before it is."
|
|
53
54
|
},
|
|
@@ -233,7 +234,14 @@
|
|
|
233
234
|
"Stop": "The 2026-09-11 probe turn never completed, so run_turn_stop_hooks had no completion to fire on. The binary declares the event and StopCommandOutputWire, so this is not-proven rather than unsupported. Stop keeps only the pre-existing continuation gate; no capture handler was added to an event whose delivery has not been seen.",
|
|
234
235
|
"PreCompact": "A one-line turn never approaches a compaction threshold. The binary declares PreCompact and PreCompactCommandOutputWire. No capture handler registered."
|
|
235
236
|
},
|
|
236
|
-
"consequence": "Codex captures at SessionEnd only; Stop/PreCompact remain unproven and unregistered as above (unchanged by this measurement). PreToolUse/PostToolUse are now proven for a write (apply_patch) and an MCP tool call (search_ruvnet) specifically, and decision-gate's write route plus grounding-stamp are registered on Codex accordingly (see contracts below). Bash-class tool calls (exec_command) were not exercised by this measurement and remain unregistered \u2014 extending on unexercised evidence would repeat the exact mistake this record corrects. Amended 2026-09-29: session-snapshot is registered on Codex Stop so each Codex turn's outcome is recorded (SessionEnd carries no last_assistant_message). Stop delivery is still NOT live-observed; the registration rests on codex-cli 0.158.0's declared stop.command.input schema and fails open."
|
|
237
|
+
"consequence": "Codex captures at SessionEnd only; Stop/PreCompact remain unproven and unregistered as above (unchanged by this measurement). PreToolUse/PostToolUse are now proven for a write (apply_patch) and an MCP tool call (search_ruvnet) specifically, and decision-gate's write route plus grounding-stamp are registered on Codex accordingly (see contracts below). Bash-class tool calls (exec_command) were not exercised by this measurement and remain unregistered \u2014 extending on unexercised evidence would repeat the exact mistake this record corrects. Amended 2026-09-29: session-snapshot is registered on Codex Stop so each Codex turn's outcome is recorded (SessionEnd carries no last_assistant_message). Stop delivery is still NOT live-observed; the registration rests on codex-cli 0.158.0's declared stop.command.input schema and fails open.",
|
|
238
|
+
"preCompact": "Registered using supported Codex0.160.1 native event and existing session-snapshot handler; native delivery remains separately unproven.",
|
|
239
|
+
"currentRegistration": {
|
|
240
|
+
"checkedOn": "2026-10-07",
|
|
241
|
+
"host": "codex-cli0.160.1",
|
|
242
|
+
"PreCompact": "Existing session-snapshot registered; native hooks/list accepts declaration. Actual auto/manual compaction dispatch remains unproven.",
|
|
243
|
+
"failedPostToolUse": "Existing transition observation retains explicit is_error, success:false and ok:false as failure; no unsupported PostToolUseFailure event is registered."
|
|
244
|
+
}
|
|
237
245
|
},
|
|
238
246
|
"contracts": [
|
|
239
247
|
{
|
|
@@ -288,7 +296,8 @@
|
|
|
288
296
|
"id": "session-snapshot",
|
|
289
297
|
"event": "PreCompact",
|
|
290
298
|
"hosts": [
|
|
291
|
-
"claude"
|
|
299
|
+
"claude",
|
|
300
|
+
"codex"
|
|
292
301
|
],
|
|
293
302
|
"mode": "advisory",
|
|
294
303
|
"offBehavior": "run",
|
|
@@ -629,6 +638,13 @@
|
|
|
629
638
|
"matcher": "^(?:Bash|Write|Edit|MultiEdit|exec_command|apply_patch|functions(?:\\.|__)(?:exec_command|apply_patch))$",
|
|
630
639
|
"reason": "Consent-eligible normalized fixed workflow vocabulary, privacy-minimized before write.",
|
|
631
640
|
"retiredBy": "reviewed issue377 learning repair"
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
"layer": "codex",
|
|
644
|
+
"event": "PreCompact",
|
|
645
|
+
"matcher": "*",
|
|
646
|
+
"reason": "PreCompact is a lifecycle event; wildcard is the host contract.",
|
|
647
|
+
"retiredBy": "continuity-only lifecycle plane"
|
|
632
648
|
}
|
|
633
649
|
]
|
|
634
650
|
}
|