ruvnet-brain 4.6.0 → 4.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,7 +80,11 @@ try {
80
80
  if (registration.mode === 'developer-suite') {
81
81
  const coordinator = await import(pathToFileURL(modules['developer-update.mjs'].path).href);
82
82
  const receipt = await coordinator.runDeveloperUpdate({ mode: 'apply', env: { ...process.env,
83
- RUVNET_NIGHTLY_IDENTITY: registration.identity, RUVNET_NIGHTLY: '1' } });
83
+ RUVNET_NIGHTLY_IDENTITY: registration.identity, RUVNET_NIGHTLY: '1',
84
+ RUVNET_NIGHTLY_REGISTRATION: registrationPath,
85
+ RUVNET_NIGHTLY_NODE_PATH: registration.nodePath,
86
+ RUVNET_NIGHTLY_RUNNER_PATH: registration.runnerPath,
87
+ RUVNET_NIGHTLY_RUNNER_SHA256: registration.runnerSha256 } });
84
88
  console.log(JSON.stringify(receipt));
85
89
  process.exit(0);
86
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
- "version": "4.6.0",
3
+ "version": "4.6.2",
4
4
  "description": "One-command installer for RuvNet Brain — a portable, source-grounded brain over rUv's RuvNet building blocks, delivered as a Claude Code plugin so Claude uses the stack instead of fighting it.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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.6.0",
4
+ "version": "4.6.2",
5
5
  "author": {
6
6
  "name": "Stuart Kerr"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-brain",
3
- "version": "4.6.0",
3
+ "version": "4.6.2",
4
4
  "description": "Source-grounded RuvNet knowledge, lifecycle enforcement, and learning for Codex.",
5
5
  "author": {
6
6
  "name": "Stuart Kerr"
@@ -3,6 +3,114 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import os from 'node:os';
5
5
  import { cmpVersion as compare } from './developer-update-policy.mjs';
6
+ import crypto from 'node:crypto';
7
+ import { pathToFileURL } from 'node:url';
8
+ const sha = bytes => crypto.createHash('sha256').update(bytes).digest('hex');
9
+ const quote = text => `'${String(text).replaceAll("'", "'\\''")}'`;
10
+ function regular(file) {
11
+ const stat = fs.lstatSync(file);
12
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid?.()
13
+ || (stat.mode & 0o022)) throw Error('Codex launcher requires owned regular files');
14
+ return fs.readFileSync(file);
15
+ }
16
+ function runtimeIdentity(root) {
17
+ const entries = [];
18
+ function visit(relative) {
19
+ const file = path.join(root, relative), stat = fs.lstatSync(file);
20
+ if (stat.isDirectory() && !stat.isSymbolicLink() && stat.uid === process.getuid?.()) {
21
+ for (const name of fs.readdirSync(file)) visit(path.join(relative, name));
22
+ } else entries.push([relative.split(path.sep).join('/'), sha(regular(file))]);
23
+ }
24
+ visit('');
25
+ return sha(JSON.stringify(entries.sort(([a], [b]) => a.localeCompare(b))));
26
+ }
27
+ function nativeOwner(file, home) {
28
+ const real = fs.realpathSync(file), root = path.join(home, '.codex/packages/standalone/releases') + path.sep;
29
+ if (!real.startsWith(root) || path.basename(real) !== 'codex') throw Error('Codex native owner outside standalone releases');
30
+ const bytes = regular(real), magic = bytes.subarray(0, 4).toString('hex');
31
+ if (!['7f454c46', 'cffaedfe', 'feedfacf', 'cafebabe', 'bebafeca'].includes(magic)) throw Error('Codex native executable identity unavailable');
32
+ fs.accessSync(real, fs.constants.X_OK);
33
+ return real;
34
+ }
35
+ export function inspectCodexLauncher(file, home) {
36
+ if (file !== path.join(home, '.local/bin/codex') || fs.lstatSync(file).isSymbolicLink()) return null;
37
+ const bytes = regular(file);
38
+ // A direct native binary is handled by the ordinary owner gate; unknown scripts never run.
39
+ if (!bytes.subarray(0, 2).equals(Buffer.from('#!'))) {
40
+ if (!['7f454c46', 'cffaedfe', 'feedfacf', 'cafebabe', 'bebafeca'].includes(bytes.subarray(0, 4).toString('hex'))) throw Error('Unknown Codex native launcher refused');
41
+ return null;
42
+ }
43
+ const configPath = path.join(home, '.cache/ruvnet-brain/model-routing/terminal-launcher-config.json');
44
+ const configBytes = regular(configPath), config = JSON.parse(configBytes);
45
+ const runtimeRoot = path.join(home, '.cache/ruvnet-brain/model-routing/versions', config.runtimeDigest || '');
46
+ const keys = ['schemaVersion', 'managedBy', 'runtimeDigest', 'runtimeRoot', 'nodeBinary', 'runner', 'realCodex', 'gatewayPath',
47
+ ...(config.realClaude ? ['realClaude', 'claudeHelperPath', 'enginePath'] : [])];
48
+ if (Object.keys(config).sort().join(',') !== keys.sort().join(',')) throw Error('Codex launcher config shape mismatch');
49
+ if (config.schemaVersion !== 1 || config.managedBy !== 'ruvnet-brain-terminal-launchers'
50
+ || !/^[a-f0-9]{64}$/.test(config.runtimeDigest || '') || config.runtimeRoot !== runtimeRoot
51
+ || fs.realpathSync(runtimeRoot) !== runtimeRoot || config.runner !== path.join(runtimeRoot, 'scripts/model-terminal-launchers.mjs')
52
+ || config.gatewayPath !== path.join(runtimeRoot, 'scripts/model-terminal-gateway.mjs')
53
+ || (config.realClaude && (config.claudeHelperPath !== path.join(runtimeRoot, 'scripts/claude-terminal-mod.mjs')
54
+ || config.enginePath !== path.join(runtimeRoot, 'scripts/model-router-engine.mjs')))
55
+ || runtimeIdentity(runtimeRoot) !== config.runtimeDigest) throw Error('Codex launcher runtime/config identity mismatch');
56
+ const expected = `#!/bin/sh\n# Managed RuvNet Brain terminal launcher\nexec ${quote(config.nodeBinary)} ${quote(config.runner)} --launch 'codex' --config ${quote(configPath)} -- "$@"\n`;
57
+ if (!bytes.equals(Buffer.from(expected))) throw Error('Preserving unknown Codex terminal wrapper');
58
+ regular(config.nodeBinary); fs.accessSync(config.nodeBinary, fs.constants.X_OK);
59
+ // The shared installer also visits registered Claude launchers; require exact existing bytes.
60
+ const claudeEntries = [];
61
+ if (config.realClaude) {
62
+ const claudeBytes = Buffer.from(expected.replace("--launch 'codex'", "--launch 'claude'"));
63
+ for (const entry of ['claude', 'ruvnet-brain-claude-terminal']) {
64
+ const claudeFile = path.join(home, '.local/bin', entry);
65
+ if (!regular(claudeFile).equals(claudeBytes)) throw Error('Preserving changed Claude terminal wrapper');
66
+ claudeEntries.push({ file: claudeFile, bytes: claudeBytes });
67
+ }
68
+ }
69
+ return { file, home, configPath, configBytes, config, bytes, claudeEntries, native: nativeOwner(config.realCodex, home) };
70
+ }
71
+ export function updatedCodexNative(proof) {
72
+ if (fs.lstatSync(proof.file).uid !== process.getuid?.()) throw Error('Codex canonical owner changed during update');
73
+ if (!fs.lstatSync(proof.file).isSymbolicLink() && regular(proof.file).equals(proof.bytes)) {
74
+ return nativeOwner(path.join(proof.home, '.codex/packages/standalone/current/bin/codex'), proof.home);
75
+ }
76
+ return nativeOwner(proof.file, proof.home);
77
+ }
78
+ export async function restoreCodexLauncher(proof, { retainOriginalNative = false } = {}) {
79
+ const { file, home, config, configPath } = proof;
80
+ if (!regular(configPath).equals(proof.configBytes) || runtimeIdentity(config.runtimeRoot) !== config.runtimeDigest) {
81
+ throw Error('Codex launcher identity changed during native update');
82
+ }
83
+ for (const entry of proof.claudeEntries) if (!regular(entry.file).equals(entry.bytes)) throw Error('Claude wrapper changed during Codex update');
84
+ const candidate = updatedCodexNative(proof);
85
+ const native = retainOriginalNative ? nativeOwner(proof.native, home) : candidate;
86
+ const installed = await import(pathToFileURL(config.runner).href);
87
+ if (typeof installed.installTerminalLaunchers !== 'function') throw Error('Codex launcher installer unavailable');
88
+ // Installer accepts only same-owner native links. On failure retain the known original
89
+ // wrapper first, backing up the verified vendor link, then let its installer reconcile config.
90
+ if (retainOriginalNative && fs.lstatSync(file).isSymbolicLink()) {
91
+ const link = fs.readlinkSync(file), backup = `${file}.${sha(link)}.original`;
92
+ try { fs.symlinkSync(link, backup); }
93
+ catch (error) {
94
+ if (error.code !== 'EEXIST' || !fs.lstatSync(backup).isSymbolicLink()
95
+ || fs.lstatSync(backup).uid !== process.getuid?.() || fs.readlinkSync(backup) !== link) throw Error('Codex vendor link backup mismatch');
96
+ }
97
+ const temporary = `${file}.${crypto.randomUUID()}.tmp`;
98
+ try { fs.writeFileSync(temporary, proof.bytes, { flag: 'wx', mode: 0o755 }); fs.renameSync(temporary, file); }
99
+ finally { fs.rmSync(temporary, { force: true }); }
100
+ }
101
+ installed.installTerminalLaunchers({ home, runtimeRoot: config.runtimeRoot, runtimeDigest: config.runtimeDigest,
102
+ nodeBinary: config.nodeBinary, realCodex: native, realClaude: config.realClaude, apply: true, manageZsh: false });
103
+ if (!regular(file).equals(proof.bytes)) throw Error('Codex wrapper restoration verification failed');
104
+ const after = JSON.parse(regular(configPath));
105
+ const expectedConfig = { ...config, realCodex: native };
106
+ const sameConfig = Object.keys(after).sort().join(',') === Object.keys(expectedConfig).sort().join(',')
107
+ && Object.entries(expectedConfig).every(([key, value]) => after[key] === value);
108
+ if (!sameConfig || nativeOwner(after.realCodex, home) !== native) {
109
+ throw Error('Codex restored owner verification failed');
110
+ }
111
+ return native;
112
+ }
113
+
6
114
  const HOME = os.homedir();
7
115
  const read = p => JSON.parse(fs.readFileSync(p, 'utf8'));
8
116
  function required(p) { fs.accessSync(p, fs.constants.X_OK); return p; }
@@ -66,14 +174,33 @@ export async function maintenance(config, run, dryRun, { home = HOME, brainHome,
66
174
  const resolved = fs.realpathSync(file);
67
175
  if (resolved.split(path.sep).includes('node_modules')) { result.exclusions.push(`npm owns native alias: ${file}`); continue; }
68
176
  required(file);
69
- const owner = fs.realpathSync(file), stage = { owner: file, originalTarget: owner, state: 'running', commands: [] }; result.stages.push(stage);
70
- const before = commandOutput(run, file, ['--version'], { timeout: 30_000 }, stage); stage.before = before;
71
- if (!dryRun) commandOutput(run, file, args, { timeout: 900_000 }, stage);
72
- const after = commandOutput(run, file, ['--version'], { timeout: 30_000 }, stage);
177
+ const launcher = inspectCodexLauncher(file, home);
178
+ const command = launcher?.native || file;
179
+ const owner = launcher?.native || fs.realpathSync(file), stage = { owner: file, originalTarget: owner, state: 'running', commands: [] }; result.stages.push(stage);
180
+ if (launcher) stage.launcherProof = { kind: 'brain-terminal-wrapper', runtimeDigest: launcher.config.runtimeDigest, wrapperSha256: sha(launcher.bytes) };
181
+ const before = commandOutput(run, command, ['--version'], { timeout: 30_000 }, stage); stage.before = before;
73
182
  const version = text => text.match(/\b(\d+\.\d+\.\d+(?:-[\w.-]+)?)/)?.[1];
74
- const versionVerified = !!version(before) && !!version(after) && compare(version(after), version(before)) >= 0;
183
+ let after, afterOwner, versionVerified, restorationAttempted = false;
184
+ try {
185
+ if (!dryRun) commandOutput(run, command, args, { timeout: 900_000 }, stage);
186
+ const afterCommand = launcher && !dryRun ? updatedCodexNative(launcher) : command;
187
+ after = commandOutput(run, afterCommand, ['--version'], { timeout: 30_000 }, stage);
188
+ versionVerified = !!version(before) && !!version(after) && compare(version(after), version(before)) >= 0;
189
+ if (launcher && !dryRun) {
190
+ restorationAttempted = true;
191
+ if (!versionVerified) stage.rejectedNativeTarget = afterCommand;
192
+ afterOwner = await restoreCodexLauncher(launcher, { retainOriginalNative: !versionVerified });
193
+ }
194
+ else afterOwner = launcher?.native || fs.realpathSync(file);
195
+ } catch (error) {
196
+ stage.state = 'failed';
197
+ if (launcher && !dryRun && !restorationAttempted) {
198
+ try { stage.restoredTarget = await restoreCodexLauncher(launcher, { retainOriginalNative: true }); }
199
+ catch (restoreError) { stage.restorationError = restoreError.message; }
200
+ }
201
+ throw error;
202
+ }
75
203
  // Native launchers may re-point to a newer version, but stay in the same established root.
76
- const afterOwner = fs.realpathSync(file);
77
204
  const ownerPreserved = nativeOwnerPreserved(file, owner, afterOwner, home);
78
205
  settleStage(stage, after, { versionVerified, ownerPreserved }, { currentTarget: afterOwner });
79
206
  }
@@ -310,7 +310,7 @@ function knowledge(run, dryRun, runId, { brainHome, root, node }) {
310
310
  }
311
311
  export function developerUpdatePaths({ home = os.homedir(), brainHome = process.env.RUVNET_BRAIN_HOME || path.join(home, '.cache/ruvnet-brain') } = {}) {
312
312
  return { home, brainHome, config: path.join(brainHome, 'developer-update-config.json'),
313
- receipt: path.join(brainHome, 'nightly-suite-update.json'), lock: path.join(brainHome, 'developer-update.lock') };
313
+ receipt: path.join(brainHome, 'nightly-suite-update.json'), scheduledReceipt: path.join(brainHome, 'scheduler/last-suite-attempt.json'), lock: path.join(brainHome, 'developer-update.lock') };
314
314
  }
315
315
  export function validateDeveloperUpdateConfig(value = {}) {
316
316
  const defaults = { schemaVersion: 1, channel: 'latest', scope: 'ruvnet', homebrew: false, uv: false, cargo: false, native: false, managedCallback: null, cleanup: false, preservePackages: [] };
@@ -371,12 +371,42 @@ export async function runDeveloperUpdate({ mode = 'check', home = os.homedir(),
371
371
  run.receipt = (command, args, options = {}) => dispatch(command, args, { ...options, capture: true });
372
372
  const locate = name => locateExecutable(name, { env: childEnv });
373
373
  const receipt = { schemaVersion: 1, kind: 'nightly-suite-update', runId: crypto.randomUUID(), pid: process.pid,
374
- startedAt: new Date().toISOString(), mode, state: 'running', ok: false, config, steps: [], notes: [],
374
+ startedAt: new Date().toISOString(), mode, scheduled: mode === 'apply' && env.RUVNET_NIGHTLY === '1' && !!env.RUVNET_NIGHTLY_IDENTITY, state: 'running', ok: false, config, steps: [], notes: [],
375
375
  sourceSha256: null, ownerToken: lock.token, ownerPid: lock.ownerPid, schedulerIdentity: env.RUVNET_NIGHTLY_IDENTITY || null };
376
+ const scheduledAttempt = value => value?.mode === 'apply' && value.scheduled !== false && !!value.schedulerIdentity;
377
+ const readAttempt = file => {
378
+ try {
379
+ const stat = fs.lstatSync(file);
380
+ if (!stat.isFile() || stat.isSymbolicLink()) throw Error('scheduled receipt must be a regular file');
381
+ return read(file);
382
+ } catch (error) { if (error.code === 'ENOENT') return null; throw error; }
383
+ };
384
+ const persistReceipt = () => {
385
+ // Write Activity first: if the second write fails, health still sees this newer failed attempt.
386
+ atomic(paths.receipt, receipt);
387
+ if (receipt.scheduled) {
388
+ try { atomic(paths.scheduledReceipt, receipt); }
389
+ catch (error) {
390
+ receipt.state = 'failed'; receipt.ok = false; receipt.error = `Scheduled receipt persistence failed: ${error.message}`;
391
+ atomic(paths.receipt, receipt);
392
+ throw error;
393
+ }
394
+ }
395
+ };
396
+ let latestMayBeReplaced = receipt.scheduled;
376
397
  try {
398
+ if (!receipt.scheduled) {
399
+ const latest = readAttempt(paths.receipt), retained = readAttempt(paths.scheduledReceipt);
400
+ const latestTime = Date.parse(latest?.startedAt || latest?.finishedAt || ''), retainedTime = Date.parse(retained?.startedAt || retained?.finishedAt || '');
401
+ if (scheduledAttempt(latest) && (!retained || !Number.isFinite(latestTime) || !Number.isFinite(retainedTime) || latestTime >= retainedTime)) {
402
+ // Preserve real legacy/failed evidence before a check or manual run replaces Activity.
403
+ atomic(paths.scheduledReceipt, latest);
404
+ }
405
+ latestMayBeReplaced = true;
406
+ }
377
407
  receipt.sourceSha256 = hash(fs.readFileSync(new URL(import.meta.url)));
378
408
  receipt.sourceSnapshot = Object.fromEntries(EXECUTION_MODULES.map(name => [name, hash(fs.readFileSync(new URL(name, import.meta.url)))]));
379
- atomic(paths.receipt, receipt);
409
+ persistReceipt();
380
410
  const npm = locate('npm');
381
411
  if (!npm) throw Error('existing npm owner absent; fresh install refused');
382
412
  const prefix = run(npm, ['prefix', '-g'], { timeout: 30_000 }).trim();
@@ -386,16 +416,16 @@ export async function runDeveloperUpdate({ mode = 'check', home = os.homedir(),
386
416
  childEnv.npm_config_prefix = prefix;
387
417
  receipt.npmIdentity = { npm, prefix, root, node: process.execPath };
388
418
  receipt.before = discover(root, prefix, config.scope);
389
- atomic(paths.receipt, receipt);
419
+ persistReceipt();
390
420
  for (const before of receipt.before) {
391
421
  const tags = before.localSource ? {} : JSON.parse(run(npm, ['view', before.name, 'dist-tags', '--json'], { timeout: 90_000 }));
392
422
  receipt.steps.push(upgradePackage(before, tags, REVIEWED_INSTALL_SCRIPTS, { run, dryRun: mode === 'check', channel: config.channel, npm, preservePackages: config.preservePackages }));
393
- atomic(paths.receipt, receipt);
423
+ persistReceipt();
394
424
  }
395
425
  receipt.plugins = await synchronizePlugins(run, mode === 'check', receipt.notes, { home, prefix, scope: config.scope, locate, unattended: !!env.RUVNET_NIGHTLY_IDENTITY });
396
426
  if (exists(path.join(paths.brainHome, 'kb/forge-update.mjs'))) receipt.knowledge = knowledge(run, mode === 'check', receipt.runId, { brainHome: paths.brainHome, root, node: process.execPath });
397
427
  else receipt.notes.push('Brain corpus absent; fresh knowledge install excluded');
398
- receipt.maintenance = await maintenance(config, run, mode === 'check', { home, brainHome: paths.brainHome, locate, node: process.execPath, progress: result => { receipt.maintenance = result; atomic(paths.receipt, receipt); } });
428
+ receipt.maintenance = await maintenance(config, run, mode === 'check', { home, brainHome: paths.brainHome, locate, node: process.execPath, progress: result => { receipt.maintenance = result; persistReceipt(); } });
399
429
  receipt.cleanup = cleanupNpxDuplicates({ home, globalRoot: root, enabled: config.cleanup && mode === 'apply', run });
400
430
  receipt.after = discover(root, prefix, config.scope);
401
431
  receipt.state = mode === 'check' ? 'checked' : 'completed'; receipt.ok = true;
@@ -412,7 +442,7 @@ export async function runDeveloperUpdate({ mode = 'check', home = os.homedir(),
412
442
  throw error;
413
443
  } finally {
414
444
  receipt.finishedAt = new Date().toISOString();
415
- try { atomic(paths.receipt, receipt); } finally { lock.release(); }
445
+ try { if (latestMayBeReplaced) persistReceipt(); } finally { lock.release(); }
416
446
  }
417
447
  }
418
448
  export async function developerUpdateCli(args = process.argv.slice(2)) {
@@ -252,24 +252,45 @@ export function resolveNightlyProofBundle({ brainHome, env = process.env } = {})
252
252
  }
253
253
 
254
254
  export function developerRunHealth({ brainHome, registration, now = Date.now(), maxAgeHours = 30 }) {
255
- const file = path.join(brainHome, 'nightly-suite-update.json');
256
- if (!fs.existsSync(file)) return { state: 'never-ran', evidence: 'No coordinated update receipt exists yet.', receipt: null };
255
+ const scheduled = value => value?.mode === 'apply' && value.scheduled !== false && !!value.schedulerIdentity;
256
+ const readReceipt = file => {
257
+ try {
258
+ const stat = fs.lstatSync(file);
259
+ if (!stat.isFile() || stat.isSymbolicLink()) throw Error('receipt is not a regular file');
260
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
261
+ } catch (error) { if (error.code === 'ENOENT') return null; throw error; }
262
+ };
257
263
  let receipt;
258
- try { receipt = JSON.parse(fs.readFileSync(file, 'utf8')); }
259
- catch (error) { return { state: 'failed', evidence: `Coordinated update receipt unreadable: ${error.message}`, receipt: null }; }
264
+ try {
265
+ const retained = readReceipt(path.join(brainHome, 'scheduler/last-suite-attempt.json'));
266
+ const latest = readReceipt(path.join(brainHome, 'nightly-suite-update.json'));
267
+ if (retained && !scheduled(retained)) throw Error('retained receipt is not a scheduled apply attempt');
268
+ receipt = retained || latest;
269
+ if (scheduled(latest)) {
270
+ const latestTime = Date.parse(latest.startedAt || latest.finishedAt || '');
271
+ const retainedTime = Date.parse(retained?.startedAt || retained?.finishedAt || '');
272
+ if (!retained || !Number.isFinite(latestTime) || !Number.isFinite(retainedTime) || latestTime >= retainedTime) receipt = latest;
273
+ }
274
+ } catch (error) { return { state: 'failed', evidence: `Coordinated update receipt unreadable or refused: ${error.message}`, receipt: null }; }
275
+ if (!receipt) return { state: 'never-ran', evidence: 'No coordinated update receipt exists yet.', receipt: null };
260
276
  const validDigest = value => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
261
277
  const bound = validDigest(receipt.sourceSha256) && receipt.kind === 'nightly-suite-update' && receipt.schemaVersion === 1
262
278
  && receipt.sourceSha256 === registration.updateModules?.['developer-update.mjs']?.sha256
279
+ && Object.entries(receipt.sourceSnapshot || {}).every(([name, digest]) => validDigest(digest) && digest === registration.updateModules?.[name]?.sha256)
263
280
  && EXECUTION_MODULES.filter(name => name !== 'plugin-artifact-proof.mjs' || registration.updateModules?.[name]).every(name => validDigest(receipt.sourceSnapshot?.[name]) && receipt.sourceSnapshot[name] === registration.updateModules?.[name]?.sha256);
264
281
  const ageHours = (now - Date.parse(receipt.finishedAt || receipt.startedAt || '')) / 3_600_000;
265
- if (!bound || !Number.isFinite(ageHours)) return { state: 'failed', evidence: 'Coordinated update receipt is not bound to registered source.', receipt };
282
+ const times = [receipt.startedAt, receipt.finishedAt].filter(value => value !== undefined).map(value => Date.parse(value));
283
+ const timestampsValid = times.length > 0 && times.every(value => Number.isFinite(value) && value <= now)
284
+ && !(receipt.startedAt && receipt.finishedAt && Date.parse(receipt.finishedAt) < Date.parse(receipt.startedAt));
285
+ if (!bound) return { state: 'failed', evidence: 'Coordinated update receipt is not bound to registered source.', receipt };
286
+ if (!Number.isFinite(ageHours) || !timestampsValid) return { state: 'failed', evidence: 'Coordinated update receipt timestamps are invalid or future-dated.', receipt };
287
+ if (!scheduled(receipt) || receipt.schedulerIdentity !== registration.identity) return { state: 'never-ran', ageHours, receipt, evidence: 'No scheduled apply attempt is bound to the current registration.' };
266
288
  if (receipt.state === 'running') {
267
289
  const lock = sharedLockStatus({ brainHome });
268
290
  const live = lock.state === 'running' && lock.owner.pid === (receipt.ownerPid || receipt.pid) && lock.owner.token === receipt.ownerToken;
269
291
  return { state: live ? 'running' : 'failed', ageHours, receipt, evidence: live ? 'Coordinated updates have a live exact owner.' : 'Coordinated update owner is absent or unverified.' };
270
292
  }
271
293
  if (receipt.state === 'failed' || receipt.ok !== true) return { state: 'failed', ageHours, receipt, evidence: receipt.error || 'Coordinated updates failed.' };
272
- if (receipt.mode !== 'apply' || receipt.schedulerIdentity !== registration.identity) return { state: 'never-ran', ageHours, receipt, evidence: 'Latest receipt is manual or a currency check; nightly execution remains unproven.' };
273
294
  if (receipt.state !== 'completed') return { state: 'failed', ageHours, receipt, evidence: 'Coordinated update terminal state is unverified.' };
274
295
  if (ageHours > maxAgeHours) return { state: 'stale', ageHours, receipt, evidence: `Last coordinated update is ${ageHours.toFixed(1)}h old.` };
275
296
  const verification = { ok: bound && receipt.mode === 'apply' && receipt.schedulerIdentity === registration.identity && receipt.state === 'completed' && receipt.ok === true && ageHours >= 0 && ageHours <= maxAgeHours };
@@ -1,13 +1,18 @@
1
1
  // Reviewed stabilization release boundaries. Legacy tests remain developer diagnostics.
2
2
  export const RELEASE_REQUIREMENTS = Object.freeze({
3
3
  "source": [
4
+ {
5
+ "id": "public-admin-release-and-engineering-status",
6
+ "reason": "Latest code release stays separate from corpus identity; issue and pull-request pagination preserves the complete engineering queue and failed probes remain unknown without inventing owner action",
7
+ "files": ["tests/unit/explainer-api.test.mjs", "tests/unit/admin-render.test.mjs"]
8
+ },
4
9
  {
5
10
  "id": "source-bound-installed-update-coordinator",
6
11
  "reason": "Allowed channels and immutable Claude artifact paths reject downgrades, ambiguous registry records, changed bytes, extra active resources and escape paths; Console success requires the exact launched source closure. POSIX owner fixtures additionally enforce original-prefix installs, existing-tool-only policy, shared locks and source-bound scheduler receipts",
7
- "files": ["tests/unit/developer-update-policy-portable.test.mjs", "tests/unit/plugin-artifact-proof.test.mjs", "tests/unit/console-suite-update.test.mjs"],
12
+ "files": ["tests/unit/developer-update-policy-portable.test.mjs", "tests/unit/plugin-artifact-proof.test.mjs", "tests/unit/console-suite-update.test.mjs", "tests/unit/developer-update-scheduled-receipt.test.mjs"],
8
13
  "platformFiles": {
9
- "linux": ["tests/unit/developer-update.test.mjs"],
10
- "macos": ["tests/unit/developer-update.test.mjs"]
14
+ "linux": ["tests/unit/developer-update.test.mjs", "tests/unit/developer-update-codex-launcher.test.mjs"],
15
+ "macos": ["tests/unit/developer-update.test.mjs", "tests/unit/developer-update-codex-launcher.test.mjs"]
11
16
  }
12
17
  },
13
18
  {