ruvnet-brain 4.3.3 → 4.3.9

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.
@@ -133,7 +133,7 @@ const identityOf = (leaf) => ({
133
133
  releaseTransactionId: leaf.releaseTransactionId,
134
134
  });
135
135
 
136
- export function buildPublicVerificationAggregate(leaves, reviews) {
136
+ export function buildPublicVerificationAggregate(leaves) {
137
137
  if (!Array.isArray(leaves) || leaves.length !== PUBLIC_VERIFICATION_OS.length * PUBLIC_VERIFICATION_MODES.length) {
138
138
  throw new Error('public verification requires exactly nine leaves');
139
139
  }
@@ -168,25 +168,11 @@ export function buildPublicVerificationAggregate(leaves, reviews) {
168
168
  if (metrics.recallAt10 < 0.98 || metrics.deltaCitationRate !== 1 || metrics.skipped || metrics.unknown) {
169
169
  throw new Error('public verification aggregate retrieval acceptance failed');
170
170
  }
171
- if (!Array.isArray(reviews) || reviews.length !== REQUIRED_REVIEW_MODELS.length) {
172
- throw new Error('public verification aggregate requires exactly two independent reviews');
173
- }
174
- reviews.forEach(validateIndependentReviewReceipt);
175
- const byModel = new Map(reviews.map((review) => [review.model, review]));
176
- if (byModel.size !== REQUIRED_REVIEW_MODELS.length || REQUIRED_REVIEW_MODELS.some((model) => !byModel.has(model))) {
177
- throw new Error('required independent review models are missing or duplicated');
178
- }
179
- const firstReview = byModel.get(REQUIRED_REVIEW_MODELS[0]);
180
- const semanticIdentity = validateReviewOracleAgainstPlan(firstReview, first.retrievalPlan);
181
- for (const model of REQUIRED_REVIEW_MODELS) {
182
- const review = byModel.get(model);
183
- const semantic = validateReviewOracleAgainstPlan(review, first.retrievalPlan);
184
- if (review.sourceSha !== identity.sourceSha || review.artifactSha256 !== identity.artifactSha256
185
- || review.payloadId !== identity.payloadId || review.productContractSha256 !== firstReview.productContractSha256
186
- || review.rubricSha256 !== firstReview.rubricSha256
187
- || semantic.recordSetSha256 !== semanticIdentity.recordSetSha256) {
188
- throw new Error(`${model} review identity, rubric, or oracle identity differs`);
189
- }
171
+ const oracle = first.retrievalPlan.oracle;
172
+ if (!oracle || !HEX64.test(String(oracle.receiptSha256 || ''))
173
+ || !HEX64.test(String(oracle.queryStoreSetSha256 || ''))
174
+ || oracle.queryStoreSetSha256 !== digest(first.retrievalPlan.denominator.eligibleStores)) {
175
+ throw new Error('sealed retrieval oracle identity is incomplete');
190
176
  }
191
177
  return {
192
178
  schemaVersion: 1,
@@ -194,17 +180,11 @@ export function buildPublicVerificationAggregate(leaves, reviews) {
194
180
  identity,
195
181
  coverage: first.coverage,
196
182
  lanes: required.map((lane) => ({ lane, leafSha256: byLane.get(lane).leafSha256 })),
197
- reviews: REQUIRED_REVIEW_MODELS.map((model) => ({ model, receiptSha256: byModel.get(model).receiptSha256,
198
- score: byModel.get(model).score })),
199
- evidence: { leaves: required.map((lane) => structuredClone(byLane.get(lane))),
200
- reviews: REQUIRED_REVIEW_MODELS.map((model) => structuredClone(byModel.get(model))) },
201
- productContractSha256: firstReview.productContractSha256,
202
- rubricSha256: firstReview.rubricSha256,
183
+ evidence: { leaves: required.map((lane) => structuredClone(byLane.get(lane))) },
203
184
  retrievalOracle: {
204
- oracleReceiptSha256: semanticIdentity.oracleReceiptSha256,
205
- queryStoreSetSha256: semanticIdentity.queryStoreSetSha256,
206
- recordCount: semanticIdentity.recordCount,
207
- recordSetSha256: semanticIdentity.recordSetSha256,
185
+ oracleReceiptSha256: oracle.receiptSha256,
186
+ queryStoreSetSha256: oracle.queryStoreSetSha256,
187
+ recordCount: first.retrievalPlan.denominator.eligibleStores.length,
208
188
  },
209
189
  metrics,
210
190
  verdict: 'PASS',
@@ -212,8 +192,8 @@ export function buildPublicVerificationAggregate(leaves, reviews) {
212
192
  };
213
193
  }
214
194
 
215
- export function signPublicVerificationAggregate({ leaves, reviews }, privateKey) {
216
- const payload = buildPublicVerificationAggregate(leaves, reviews);
195
+ export function signPublicVerificationAggregate({ leaves }, privateKey) {
196
+ const payload = buildPublicVerificationAggregate(leaves);
217
197
  const aggregateSha256 = digest(payload);
218
198
  const signed = { ...payload, aggregateSha256 };
219
199
  return { ...signed, signature: crypto.sign(null, Buffer.from(canonicalJson(signed)), privateKey).toString('base64') };
@@ -227,10 +207,10 @@ export function verifyPublicVerificationAggregate(aggregate, publicKey, expected
227
207
  }
228
208
  const payload = aggregatePayload(aggregate);
229
209
  if (digest(payload) !== aggregate.aggregateSha256) throw new Error('public verification aggregate digest mismatch');
230
- if (!Array.isArray(aggregate.evidence?.leaves) || !Array.isArray(aggregate.evidence?.reviews)) {
231
- throw new Error('public verification aggregate lacks raw leaf or review evidence');
210
+ if (!Array.isArray(aggregate.evidence?.leaves)) {
211
+ throw new Error('public verification aggregate lacks raw leaf evidence');
232
212
  }
233
- const rebuilt = buildPublicVerificationAggregate(aggregate.evidence.leaves, aggregate.evidence.reviews);
213
+ const rebuilt = buildPublicVerificationAggregate(aggregate.evidence.leaves);
234
214
  if (canonicalJson(rebuilt) !== canonicalJson(payload)) {
235
215
  throw new Error('public verification aggregate differs from rebuilt raw evidence');
236
216
  }
@@ -249,17 +229,10 @@ export function verifyPublicVerificationAggregate(aggregate, publicKey, expected
249
229
  || aggregate.coverage.gistCurrent !== aggregate.coverage.gistTotal) {
250
230
  throw new Error('public verification aggregate evidence is incomplete');
251
231
  }
252
- if (aggregate.reviews?.length !== REQUIRED_REVIEW_MODELS.length
253
- || REQUIRED_REVIEW_MODELS.some((model) => !aggregate.reviews.some((row) => row.model === model
254
- && Number.isInteger(row.score) && row.score >= 95 && HEX64.test(String(row.receiptSha256 || ''))))
255
- || !HEX64.test(String(aggregate.productContractSha256 || '')) || !HEX64.test(String(aggregate.rubricSha256 || ''))) {
256
- throw new Error('public verification aggregate reviews are incomplete');
257
- }
258
232
  if (aggregate.retrievalOracle?.oracleReceiptSha256 !== aggregate.evidence.leaves[0].retrievalPlan.oracle.receiptSha256
259
233
  || aggregate.retrievalOracle?.queryStoreSetSha256 !== aggregate.evidence.leaves[0].retrievalPlan.oracle.queryStoreSetSha256
260
- || aggregate.retrievalOracle?.recordCount !== aggregate.evidence.leaves[0].retrievalPlan.denominator.eligibleStores.length
261
- || !HEX64.test(String(aggregate.retrievalOracle?.recordSetSha256 || ''))) {
262
- throw new Error('public verification aggregate retrieval oracle review is incomplete');
234
+ || aggregate.retrievalOracle?.recordCount !== aggregate.evidence.leaves[0].retrievalPlan.denominator.eligibleStores.length) {
235
+ throw new Error('public verification aggregate retrieval oracle identity is incomplete');
263
236
  }
264
237
  if (expectedIdentity && canonicalJson(aggregate.identity) !== canonicalJson(expectedIdentity)) {
265
238
  throw new Error('public verification aggregate identity differs from the release transaction');
@@ -272,13 +245,13 @@ function parseCliArgs(argv) {
272
245
  for (let index = 0; index < argv.length; index += 2) {
273
246
  const flag = argv[index];
274
247
  const value = argv[index + 1];
275
- if (!['--lanes', '--reviews', '--out'].includes(flag) || !value) {
276
- throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --reviews <dir> --out <file>');
248
+ if (!['--lanes', '--out'].includes(flag) || !value) {
249
+ throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --out <file>');
277
250
  }
278
251
  parsed[flag.slice(2)] = value;
279
252
  }
280
- if (!parsed.lanes || !parsed.reviews || !parsed.out || Object.keys(parsed).length !== 3) {
281
- throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --reviews <dir> --out <file>');
253
+ if (!parsed.lanes || !parsed.out || Object.keys(parsed).length !== 2) {
254
+ throw new Error('usage: public-verification-aggregate.mjs --lanes <dir> --out <file>');
282
255
  }
283
256
  return parsed;
284
257
  }
@@ -314,12 +287,11 @@ function readLaneLeaves(directory) {
314
287
  return PUBLIC_VERIFICATION_OS.flatMap((osName) => byOs.get(osName));
315
288
  }
316
289
 
317
- export function generatePublicVerificationAggregate({ lanesDirectory, reviewsDirectory, outputFile, privateKey }) {
290
+ export function generatePublicVerificationAggregate({ lanesDirectory, outputFile, privateKey }) {
318
291
  if (!privateKey) throw new Error('RUVNET_SIGNING_KEY is required');
319
292
  if (fs.existsSync(outputFile)) throw new Error(`refusing to overwrite existing aggregate: ${outputFile}`);
320
293
  const leaves = readLaneLeaves(lanesDirectory);
321
- const reviews = readExactJsonFiles(reviewsDirectory, REQUIRED_REVIEW_MODELS.length, 'review');
322
- const aggregate = signPublicVerificationAggregate({ leaves, reviews }, privateKey);
294
+ const aggregate = signPublicVerificationAggregate({ leaves }, privateKey);
323
295
  fs.writeFileSync(outputFile, `${JSON.stringify(aggregate, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
324
296
  return aggregate;
325
297
  }
@@ -328,12 +300,11 @@ async function main() {
328
300
  const options = parseCliArgs(process.argv.slice(2));
329
301
  const aggregate = generatePublicVerificationAggregate({
330
302
  lanesDirectory: options.lanes,
331
- reviewsDirectory: options.reviews,
332
303
  outputFile: options.out,
333
304
  privateKey: process.env.RUVNET_SIGNING_KEY,
334
305
  });
335
306
  process.stdout.write(`${JSON.stringify({ ok: true, aggregateSha256: aggregate.aggregateSha256,
336
- leaves: aggregate.metrics.leaves, reviews: aggregate.reviews.length })}\n`);
307
+ leaves: aggregate.metrics.leaves })}\n`);
337
308
  }
338
309
 
339
310
  if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
@@ -24,6 +24,32 @@ const HEX40 = /^[a-f0-9]{40}$/;
24
24
  const HEX64 = /^[a-f0-9]{64}$/;
25
25
  const normalizedDigest = (value) => String(value || '').replace(/^sha256:/, '');
26
26
 
27
+ function failedCanaryCases(retrieval) {
28
+ return retrieval.cases.filter(({ cohort, status, retrievalHit, citationResolved }) =>
29
+ status !== 'COMPLETED' || retrievalHit !== true || (cohort === 'delta' && citationResolved !== true));
30
+ }
31
+
32
+ function publicVerificationFailure({ os, identity, failures, completedLeaves }) {
33
+ const payload = {
34
+ schemaVersion: 1,
35
+ kind: 'ruvnet-brain-public-verification-failure',
36
+ os,
37
+ sourceSha: identity.candidateSha,
38
+ artifactSha256: identity.packageSha256,
39
+ bundleSha256: identity.bundleSha256,
40
+ failures: failures.map(({ mode, reason, retrieval }) => ({
41
+ mode,
42
+ reason,
43
+ metrics: retrieval.metrics,
44
+ failedCaseIds: failedCanaryCases(retrieval).map(({ id }) => id),
45
+ failedCases: failedCanaryCases(retrieval),
46
+ retrievalReceiptSha256: retrieval.receiptSha256,
47
+ })),
48
+ completedLeaves: completedLeaves.map(({ mode, leafSha256 }) => ({ mode, leafSha256 })),
49
+ };
50
+ return { ...payload, failureSha256: digest(payload) };
51
+ }
52
+
27
53
  function validateReleaseIdentity(identity) {
28
54
  if (identity?.repository !== 'stuinfla/ruvnet-brain' || identity?.package !== 'ruvnet-brain'
29
55
  || !HEX40.test(String(identity.candidateSha || '')) || !HEX64.test(String(identity.payloadId || ''))
@@ -118,6 +144,7 @@ export async function createPublicVerificationLane({
118
144
  releaseTransactionId: transactionIdFor(identity),
119
145
  };
120
146
  const leaves = [];
147
+ const failures = [];
121
148
  for (const mode of PUBLIC_VERIFICATION_MODES) {
122
149
  const installed = publication.installed?.[RECEIPT_MODE_NAMES[mode]];
123
150
  if (installed?.status !== 'PASS' || installed.doctorExit !== 0 || installed.version !== identity.version
@@ -132,10 +159,16 @@ export async function createPublicVerificationLane({
132
159
  search: ({ query, k }) => adapter.searchInstalled({ mode, query, k }),
133
160
  citationResolver: (matched, expected) => adapter.resolveInstalledCitation({ mode, matched, expected }),
134
161
  });
135
- const verifiedRetrieval = validateRetrievalCanaryReceipt(
136
- retrieval,
137
- { plan: retrievalPlan, requireAcceptance: true },
138
- );
162
+ let verifiedRetrieval;
163
+ try {
164
+ verifiedRetrieval = validateRetrievalCanaryReceipt(
165
+ retrieval,
166
+ { plan: retrievalPlan, requireAcceptance: true },
167
+ );
168
+ } catch (error) {
169
+ failures.push({ mode, reason: error.message, retrieval });
170
+ continue;
171
+ }
139
172
  leaves.push(createPublicVerificationLeaf({
140
173
  ...common,
141
174
  os,
@@ -152,6 +185,11 @@ export async function createPublicVerificationLane({
152
185
  unknown: 0,
153
186
  }));
154
187
  }
188
+ if (failures.length) {
189
+ const error = new Error(`retrieval canary acceptance failed for ${failures.map(({ mode }) => mode).join(', ')}`);
190
+ error.publicVerificationFailure = publicVerificationFailure({ os, identity, failures, completedLeaves: leaves });
191
+ throw error;
192
+ }
155
193
  if (canonicalJson(leaves.map(({ mode }) => mode)) !== canonicalJson(PUBLIC_VERIFICATION_MODES)) {
156
194
  throw new Error('public verification lane modes are incomplete');
157
195
  }
@@ -211,6 +249,13 @@ export async function generatePublicVerificationLane({
211
249
  fs.mkdirSync(path.dirname(output), { recursive: true });
212
250
  fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
213
251
  return receipt;
252
+ } catch (error) {
253
+ if (error.publicVerificationFailure && !fs.existsSync(output)) {
254
+ fs.mkdirSync(path.dirname(output), { recursive: true });
255
+ fs.writeFileSync(output, `${JSON.stringify(error.publicVerificationFailure, null, 2)}\n`,
256
+ { flag: 'wx', mode: 0o600 });
257
+ }
258
+ throw error;
214
259
  } finally {
215
260
  await adapter.dispose?.();
216
261
  fs.rmSync(temp, { recursive: true, force: true });
@@ -29,30 +29,62 @@ const DEADLINE_MS = 30_000;
29
29
  const sha256 = (file) => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
30
30
  const readJson = (file) => JSON.parse(fs.readFileSync(file, 'utf8'));
31
31
  const receiptDigest = (candidate) => String(candidate?.artifact?.sha256 || '').replace(/^sha256:/, '');
32
-
32
+ export function commandInvocation(name, args, { platform = process.platform, env = process.env } = {}) {
33
+ if (platform === 'win32' && /^(?:npm|npx)$/i.test(name)) return {
34
+ executable: env.ComSpec || 'cmd.exe', args: ['/d', '/c', `${name}.cmd`, ...args],
35
+ };
36
+ return { executable: name, args };
37
+ }
33
38
  function command(name, args, options = {}) {
34
- const result = spawnSync(name, args, { encoding: 'utf8', ...options });
39
+ const { platform = process.platform, ...spawnOptions } = options;
40
+ const invocation = commandInvocation(name, args, { platform, env: spawnOptions.env || process.env });
41
+ const result = spawnSync(invocation.executable, invocation.args, { encoding: 'utf8', ...spawnOptions });
35
42
  if (result.error || result.status !== 0) {
36
43
  const detail = String(result.stderr || result.stdout || result.error?.message || '').trim();
37
44
  throw new Error(`${name} ${args.join(' ')} failed: ${detail || `exit ${result.status}`}`);
38
45
  }
39
46
  return String(result.stdout || '').trim();
40
47
  }
41
-
42
- function locate(name) {
43
- try { return command('which', [name]); } catch { return null; }
48
+ function locate(name, { platform = process.platform } = {}) {
49
+ const query = platform === 'win32' ? (name === 'node' ? 'node.exe' : `${name}.cmd`) : name;
50
+ try {
51
+ return command(platform === 'win32' ? 'where.exe' : 'which', [query], { platform })
52
+ .split(/\r?\n/).find(Boolean) || null;
53
+ } catch { return null; }
44
54
  }
45
-
46
- function isolatedPath(mode, temp) {
55
+ export function createIsolatedPath(mode, temp, { platform = process.platform, env = process.env,
56
+ resolve = (name) => locate(name, { platform }) } = {}) {
47
57
  const bin = path.join(temp, `bin-${mode}`);
48
58
  fs.mkdirSync(bin);
49
59
  const hosts = mode === 'claudeOnly' ? ['claude'] : mode === 'codexOnly' ? ['codex'] : ['claude', 'codex'];
50
- for (const name of ['node', 'npm', ...hosts]) {
51
- const target = locate(name);
60
+ const names = platform === 'win32' ? ['npm', ...hosts] : ['node', 'npm', ...hosts];
61
+ for (const name of names) {
62
+ const target = resolve(name);
52
63
  if (!target) throw new Error(`${name} CLI unavailable for ${mode} public host fixture`);
53
- fs.symlinkSync(target, path.join(bin, name));
64
+ if (platform === 'win32') {
65
+ fs.writeFileSync(path.join(bin, `${name}.cmd`), `@call "${target}" %*\r\n`, { flag: 'wx' });
66
+ } else {
67
+ fs.symlinkSync(target, path.join(bin, name));
68
+ }
69
+ }
70
+ if (platform !== 'win32') return `${bin}:/usr/bin:/bin`;
71
+ const node = resolve('node');
72
+ if (!node) throw new Error(`node CLI unavailable for ${mode} public host fixture`);
73
+ return [bin, path.dirname(node), path.join(env.SystemRoot || 'C:\\Windows', 'System32'),
74
+ path.join(env.ProgramFiles || 'C:\\Program Files', 'Git', 'usr', 'bin')].join(path.win32.delimiter);
75
+ }
76
+ export function stageVerifiedBundle({ bundlePath, bundleSha256, packageRoot }) {
77
+ const source = path.resolve(bundlePath || '');
78
+ let stat;
79
+ try { stat = fs.lstatSync(source); } catch { throw new Error('verified public bundle is missing'); }
80
+ if (!stat.isFile() || stat.isSymbolicLink() || sha256(source) !== bundleSha256) {
81
+ throw new Error('verified bundle digest mismatch before installation');
54
82
  }
55
- return `${bin}:/usr/bin:/bin`;
83
+ const destination = path.join(packageRoot, 'dist', 'ruvnet-brain.zip');
84
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
85
+ fs.copyFileSync(source, destination, fs.constants.COPYFILE_EXCL);
86
+ if (sha256(destination) !== bundleSha256) throw new Error('staged verified bundle digest mismatch');
87
+ return destination;
56
88
  }
57
89
 
58
90
  async function download(url, destination, headers = {}) {
@@ -134,7 +166,8 @@ export function assertInstalledPayload(sourceRoot, installedRoot) {
134
166
  return checked;
135
167
  }
136
168
 
137
- function rpcSearch(server, env, query, timeoutMs = DEADLINE_MS) {
169
+ export function rpcSearch(server, env, query, k = 5, timeoutMs = DEADLINE_MS) {
170
+ if (!Number.isSafeInteger(k) || k < 1 || k > 50) throw new Error(`invalid search result count: ${k}`);
138
171
  return new Promise((resolve, reject) => {
139
172
  const child = spawn(process.execPath, [server], { env, stdio: ['pipe', 'pipe', 'pipe'] });
140
173
  let stdout = '';
@@ -175,7 +208,7 @@ function rpcSearch(server, env, query, timeoutMs = DEADLINE_MS) {
175
208
  const listed = await call(2, 'tools/list');
176
209
  if (!listed.result?.tools?.some((tool) => tool.name === 'search_ruvnet')) throw new Error('installed MCP does not advertise search_ruvnet');
177
210
  const started = performance.now();
178
- const searched = await call(3, 'tools/call', { name: 'search_ruvnet', arguments: { query, k: 5 } });
211
+ const searched = await call(3, 'tools/call', { name: 'search_ruvnet', arguments: { query, k } });
179
212
  const broadMs = Math.round(performance.now() - started);
180
213
  const text = (searched.result?.content || []).map((item) => item.text || '').join('\n');
181
214
  if (searched.error || searched.result?.isError || /search_ruvnet error:/i.test(text)) throw new Error(`installed Brain search failed: ${text.slice(0, 400)}`);
@@ -213,14 +246,17 @@ export function livePublicationAdapter({ root = process.cwd() } = {}) {
213
246
  return { path: destination, tag: release.tag_name, sha: tagCommit(root, tag) };
214
247
  },
215
248
 
216
- async installHosts({ artifactPath, artifactSha256, version }) {
249
+ async installHosts({ artifactPath, artifactSha256, bundlePath, bundleSha256, version }) {
217
250
  const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'ruvnet-public-install-'));
218
251
  installTemp = temp;
219
252
  const packageRoot = path.join(temp, 'package');
220
253
  command('tar', ['-xzf', artifactPath, '-C', temp]);
254
+ stageVerifiedBundle({ bundlePath, bundleSha256, packageRoot });
221
255
  const sealedPlugin = path.join(packageRoot, 'plugin');
222
256
  const results = {};
223
257
  let bundle = null;
258
+ const sharedModelCache = path.join(temp, 'models-cache');
259
+ fs.mkdirSync(sharedModelCache, { recursive: true });
224
260
  // Derived from HOST_MODES, so a fourth host shape is added in ONE place and this loop
225
261
  // cannot fall behind the staged-side check the way it did.
226
262
  for (const mode of HOST_MODES.map((m) => RECEIPT_MODE_NAMES[m])) {
@@ -233,26 +269,27 @@ export function livePublicationAdapter({ root = process.cwd() } = {}) {
233
269
  const env = {
234
270
  ...process.env,
235
271
  HOME: home,
272
+ ...(process.platform === 'win32' ? { USERPROFILE: home } : {}),
236
273
  CODEX_HOME: codexHome,
237
274
  RUVNET_BRAIN_HOME: brainHome,
238
275
  RUVNET_BRAIN_KB: kb,
276
+ KB_MODEL_CACHE: sharedModelCache,
239
277
  // Env comes from the ONE variant table (host-install-matrix VARIANTS.published), not a
240
278
  // second hand-written copy — this file's own header promises "ONE doctor rule and ONE mode
241
279
  // vocabulary, shared with the staged-side check", and the copy had already drifted: it
242
280
  // omitted the Codex hook-trust bypass the staged side carries, which failed the seal on
243
281
  // every release.
244
282
  ...VARIANTS.published.env({ packageRoot }),
283
+ RUVNET_STRICT_INSTALL: '0',
245
284
  CI: 'true',
246
- PATH: isolatedPath(mode, temp),
285
+ PATH: createIsolatedPath(mode, temp),
247
286
  };
248
287
  const installer = path.join(packageRoot, 'bin', 'install.mjs');
249
288
  command(process.execPath, [
250
289
  installer, '--yes', '--force', '--version', `v${version}`,
251
290
  '--no-nightly-prompt', '--no-telemetry', '--no-stack', '--no-enhance', '--no-statusline',
291
+ '--no-selfcheck', '--no-verify',
252
292
  ], { env, cwd: packageRoot, timeout: 1_200_000, maxBuffer: 32 * 1024 * 1024 });
253
- command(process.execPath, [installer, '--doctor', '--hooks'], {
254
- env, cwd: packageRoot, timeout: 300_000, maxBuffer: 32 * 1024 * 1024,
255
- });
256
293
 
257
294
  const verified = {};
258
295
  if (mode !== 'codexOnly') {
@@ -270,7 +307,11 @@ export function livePublicationAdapter({ root = process.cwd() } = {}) {
270
307
  throw new Error(`${mode} installed public Brain bundle version mismatch`);
271
308
  }
272
309
  bundle ||= { brainVersion: source.brainVersion, releaseTag: source.releaseTag };
273
- const searched = await rpcSearch(findMcpServer(home), env, 'How does RuvNet Brain prove a public release artifact?', DEADLINE_MS);
310
+ const searched = await rpcSearch(findMcpServer(home), env,
311
+ 'How does RuvNet Brain prove a public release artifact?', 5, DEADLINE_MS);
312
+ command(process.execPath, [installer, '--doctor', '--hooks'], {
313
+ env, cwd: packageRoot, timeout: 300_000, maxBuffer: 32 * 1024 * 1024,
314
+ });
274
315
  results[mode] = {
275
316
  status: 'PASS', doctorExit: 0, version, artifactSha256,
276
317
  functionalSearch: true, searchMs: searched.broadMs, hostsOnPath: mode,
@@ -291,16 +332,17 @@ export function livePublicationAdapter({ root = process.cwd() } = {}) {
291
332
  if (rvfs.length === 0) throw new Error('installed public Brain has no ruvnet-brain self RVF');
292
333
  const server = path.join(installContext.home, '.claude', 'ruvnet-brain', 'mcp', 'server.mjs');
293
334
  if (!fs.existsSync(server)) throw new Error('installed persistent MCP server is missing');
294
- const result = await rpcSearch(server, installContext.env, 'How does RuvNet Brain prove a public release artifact?', DEADLINE_MS);
335
+ const result = await rpcSearch(server, installContext.env,
336
+ 'How does RuvNet Brain prove a public release artifact?', 5, DEADLINE_MS);
295
337
  const readiness = readJson(path.join(installContext.brainHome, 'mcp-readiness.json'));
296
338
  if (readiness.state !== 'ready') throw new Error(`installed Brain readiness is ${readiness.state || 'missing'}`);
297
339
  return { status: 'PASS', selfStore: true, broadMs: result.broadMs, deadlineMs: DEADLINE_MS };
298
340
  },
299
341
 
300
- async searchInstalled({ mode, query }) {
342
+ async searchInstalled({ mode, query, k }) {
301
343
  const context = installContexts.get(mode);
302
344
  if (!context) throw new Error(`${mode} public host is not installed`);
303
- const result = await rpcSearch(findMcpServer(context.home), context.env, query, DEADLINE_MS);
345
+ const result = await rpcSearch(findMcpServer(context.home), context.env, query, k, DEADLINE_MS);
304
346
  return parseCitations(result.text).map((citation) => ({
305
347
  repo: citation.repo.toLowerCase(),
306
348
  path: citation.docPath,
@@ -400,7 +442,8 @@ export async function generatePublicationReceipt({
400
442
  if (npm.version !== candidate.version || (npm.sha && npm.sha !== candidate.sha)) throw new Error('npm public identity mismatch');
401
443
  if (github.tag !== candidate.tag || github.sha !== candidate.sha) throw new Error('GitHub public identity mismatch');
402
444
 
403
- const installed = await adapter.installHosts({ artifactPath: npm.path, artifactSha256: digest, version: candidate.version });
445
+ const installed = await adapter.installHosts({ artifactPath: npm.path, artifactSha256: digest,
446
+ bundlePath: githubBundle.path, bundleSha256: githubBundleDigest, version: candidate.version });
404
447
  const brain = await adapter.probeBrain({ sha: candidate.sha, artifactSha256: digest, version: candidate.version });
405
448
  const surface = await adapter.probePublishedSurface({ sha: candidate.sha, artifactSha256: digest, version: candidate.version });
406
449
  const publication = {
@@ -33,6 +33,7 @@ const lanes = [
33
33
  'tests/unit/codex-claude-hook-parity.test.mjs',
34
34
  'tests/unit/brain-stamp-resolve-built-from-sha.test.mjs',
35
35
  'tests/unit/qa-runner-concurrency.test.mjs',
36
+ 'tests/unit/execution-preflight.test.mjs',
36
37
  '--reporter=dot']],
37
38
  ['mesh', 'npm', ['run', 'test:mesh']],
38
39
  ['plugin', 'npm', ['test']],
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // scripts/refresh-model-catalog.mjs — pulls the LIVE OpenRouter model catalog and writes the committed
3
- // snapshot data/openrouter-catalog-snapshot.json (with a pulledAt stamp). verify-model-catalog.mjs then
3
+ // snapshot data/openrouter-catalog-snapshot.json (with a pulledAt stamp) and synchronizes the catalog's
4
+ // factual prices. verify-model-catalog.mjs then
4
5
  // enforces, offline in CI, that every model in data/model-catalog.json exists and is priced correctly
5
- // against this snapshot — and that the snapshot is fresh. Run nightly (the anti-rot mechanism) + on demand.
6
+ // against this snapshot — and that the snapshot is fresh. Run weekly (the anti-rot mechanism) + on demand.
6
7
  // ADR-0016.
7
8
  //
8
9
  // It ALSO flags drift so a new flagship (e.g. a GPT-5.6-class release) surfaces instead of rotting:
@@ -45,6 +46,30 @@ export function detectDrift(catalog, models) {
45
46
  return missing;
46
47
  }
47
48
 
49
+ /** Synchronize factual catalog prices from the same live map that backs the snapshot. */
50
+ export function syncCatalogPrices(catalog, models, pulledAt) {
51
+ const changes = [];
52
+ for (const [provider, entry] of Object.entries(catalog.providers || {})) {
53
+ if (entry.aliasOf) continue;
54
+ for (const tier of ['frontier', 'mid', 'cheap']) {
55
+ const candidate = entry[tier];
56
+ if (!candidate?.model) continue;
57
+ const live = models[candidate.model]
58
+ || (!candidate.model.includes('/') ? models[`anthropic/${candidate.model}`] : null);
59
+ if (!live) continue;
60
+ if (candidate.in !== live.in || candidate.out !== live.out) {
61
+ changes.push({ provider, tier, model: candidate.model, from: { in: candidate.in, out: candidate.out }, to: live });
62
+ candidate.in = live.in;
63
+ candidate.out = live.out;
64
+ }
65
+ }
66
+ }
67
+ if (catalog._meta?.sources) {
68
+ catalog._meta.sources.prices = `OpenRouter /api/v1/models live catalog, pulled ${pulledAt.slice(0, 10)} (in/out USD per Mtok).`;
69
+ }
70
+ return changes;
71
+ }
72
+
48
73
  export function shapeSnapshot(models, pulledAt) {
49
74
  return { _meta: { source: 'OpenRouter /api/v1/models (live metadata)', pulledAt, modelCount: Object.keys(models).length }, models };
50
75
  }
@@ -73,7 +98,8 @@ async function main() {
73
98
 
74
99
  const catalog = JSON.parse(fs.readFileSync(CATALOG, 'utf8'));
75
100
  const missing = detectDrift(catalog, models);
76
- const snapshot = shapeSnapshot(models, new Date().toISOString());
101
+ const pulledAt = new Date().toISOString();
102
+ const snapshot = shapeSnapshot(models, pulledAt);
77
103
  const next = JSON.stringify(snapshot, null, 2) + '\n';
78
104
 
79
105
  if (check) {
@@ -86,8 +112,11 @@ async function main() {
86
112
  return;
87
113
  }
88
114
 
115
+ const priceChanges = syncCatalogPrices(catalog, models, pulledAt);
89
116
  fs.writeFileSync(SNAPSHOT, next);
117
+ fs.writeFileSync(CATALOG, JSON.stringify(catalog, null, 2) + '\n');
90
118
  console.log(`✓ wrote ${SNAPSHOT.replace(ROOT + '/', '')} — ${Object.keys(models).length} live models, pulledAt ${snapshot._meta.pulledAt}`);
119
+ console.log(`✓ synchronized ${priceChanges.length} catalog price field(s) from the same live response`);
91
120
  if (missing.length) {
92
121
  console.log(`\n⚠ DRIFT: ${missing.length} catalog model(s) no longer in the live catalog — update data/model-catalog.json:`);
93
122
  for (const m of missing) console.log(' - ' + m);
@@ -11,7 +11,7 @@ const CANONICAL_PUBLISHERS = new Set([
11
11
  'scripts/release.mjs',
12
12
  'scripts/release-transaction-provider.mjs',
13
13
  ]);
14
- const SOURCE_EXTENSIONS = new Set(['.mjs', '.js', '.cjs', '.sh']);
14
+ const SOURCE_EXTENSIONS = new Set(['.mjs', '.js', '.cjs', '.sh', '.yml', '.yaml']);
15
15
 
16
16
  function executableSource(source) {
17
17
  const withoutBlocks = source.replace(/\/\*[\s\S]*?\*\//g, '');
@@ -28,7 +28,7 @@ const ACTIONS = [
28
28
  jsPatterns: [
29
29
  /\[\s*['"]release['"]\s*,\s*['"]create['"]/,
30
30
  ],
31
- shellPatterns: [/^\s*gh\s+release\s+create\b/m],
31
+ shellPatterns: [/^\s*(?:-\s*run:\s*)?gh\s+release\s+create\b/m],
32
32
  },
33
33
  {
34
34
  action: 'github-release-update',
@@ -36,7 +36,7 @@ const ACTIONS = [
36
36
  /['"]PATCH['"][\s\S]{0,120}releases\//,
37
37
  /\[\s*['"]release['"]\s*,\s*['"]edit['"]/,
38
38
  ],
39
- shellPatterns: [/^\s*gh\s+(?:release\s+edit|api\s+.*releases\/.*PATCH)\b/m],
39
+ shellPatterns: [/^\s*(?:-\s*run:\s*)?gh\s+(?:release\s+edit|api\s+.*releases\/.*PATCH)\b/m],
40
40
  },
41
41
  {
42
42
  action: 'npm-publish',
@@ -44,7 +44,7 @@ const ACTIONS = [
44
44
  /(?:execFileSync|spawnSync)\(\s*['"]npm['"]\s*,\s*\[\s*['"]publish['"]/,
45
45
  /runOrDie\(\s*['"]npm publish['"]/,
46
46
  ],
47
- shellPatterns: [/^\s*npm\s+publish\b/m],
47
+ shellPatterns: [/^\s*(?:-\s*run:\s*)?npm\s+publish\b/m],
48
48
  },
49
49
  {
50
50
  action: 'npm-dist-tag',
@@ -52,7 +52,7 @@ const ACTIONS = [
52
52
  /(?:execFileSync|spawnSync)\(\s*['"]npm['"]\s*,\s*\[\s*['"]dist-tag['"]\s*,\s*['"]add['"]/,
53
53
  /runOrDie\(\s*['"]npm dist-tag[^'"]*['"]/,
54
54
  ],
55
- shellPatterns: [/^\s*npm\s+dist-tag\s+add\b/m],
55
+ shellPatterns: [/^\s*(?:-\s*run:\s*)?npm\s+dist-tag\s+add\b/m],
56
56
  },
57
57
  ];
58
58
 
@@ -60,7 +60,10 @@ export function detectPublisherActions(file, source) {
60
60
  const relative = file.split(path.sep).join('/');
61
61
  if (CANONICAL_PUBLISHERS.has(relative)) return [];
62
62
  const executable = executableSource(source);
63
- const kind = path.extname(relative) === '.sh' ? 'shellPatterns' : 'jsPatterns';
63
+ const extension = path.extname(relative);
64
+ const kind = ['.sh', '.yml', '.yaml'].includes(extension) || relative.startsWith('package.json#scripts.')
65
+ ? 'shellPatterns'
66
+ : 'jsPatterns';
64
67
  return ACTIONS
65
68
  .filter((action) => action[kind].some((pattern) => pattern.test(executable)))
66
69
  .map(({ action }) => ({ file: relative, action }));
@@ -79,14 +82,23 @@ function sourceFiles(root) {
79
82
  visit(path.join(root, 'scripts'));
80
83
  visit(path.join(root, 'deploy'));
81
84
  visit(path.join(root, 'bin'));
85
+ visit(path.join(root, '.github', 'workflows'));
82
86
  return files;
83
87
  }
84
88
 
85
89
  export function findUnauthorizedPublishers(root = ROOT) {
86
- return sourceFiles(root).flatMap((absolute) => {
90
+ const findings = sourceFiles(root).flatMap((absolute) => {
87
91
  const relative = path.relative(root, absolute).split(path.sep).join('/');
88
92
  return detectPublisherActions(relative, fs.readFileSync(absolute, 'utf8'));
89
93
  });
94
+ const packageFile = path.join(root, 'package.json');
95
+ if (fs.existsSync(packageFile)) {
96
+ const pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
97
+ for (const [name, command] of Object.entries(pkg.scripts || {})) {
98
+ findings.push(...detectPublisherActions(`package.json#scripts.${name}`, String(command)));
99
+ }
100
+ }
101
+ return findings;
90
102
  }
91
103
 
92
104
  export function main(root = ROOT) {
@@ -11,18 +11,16 @@
11
11
  * for a month.
12
12
  *
13
13
  * WHAT IT WILL NOT DO. It does not publish. `scripts/self-update.mjs:56` refuses `--publish` and
14
- * only `protected-release.yml` may create a release, publish npm, or move a dist-tag. This script
15
- * DISPATCHES that workflow — the sanctioned path — and never substitutes for it. Every safety gate
16
- * (exact-SHA evidence, clean worktree, release-proof, host verification, post-publication seal)
17
- * still runs inside the workflow exactly as designed. Bypassing them to "get it done while nobody
18
- * is looking" would be the worst possible reading of an instruction to finish the job.
14
+ * `release-candidate-preflight.yml` owns the one-time long lanes and `protected-release.yml` is the
15
+ * sole publication controller. This watchdog is report-only: it never dispatches either phase and
16
+ * cannot manufacture typed candidate evidence.
19
17
  *
20
18
  * IT IS A NO-OP UNLESS EVERY PRECONDITION HOLDS. It runs from the nightly, unattended, for weeks.
21
19
  * A watchdog that acts on a partial picture is worse than no watchdog, so it refuses on anything
22
20
  * unexpected and says why. The default outcome is "nothing happened, here is the reason".
23
21
  *
24
22
  * node scripts/release-convergence-watchdog.mjs # report only, never acts
25
- * node scripts/release-convergence-watchdog.mjs --dispatch # act, but only if ALL gates pass
23
+ * node scripts/release-convergence-watchdog.mjs --dispatch # compatibility flag; still reports only
26
24
  */
27
25
  import { execFileSync } from 'node:child_process';
28
26
  import fs from 'node:fs';
@@ -99,21 +97,11 @@ const runsFor = (wf) => JSON.parse(tryShy(() => sh('gh', ['run', 'list', '--repo
99
97
  const greenAt = (wf) => runsFor(wf).find((r) => r.headSha === originMain && r.conclusion === 'success');
100
98
 
101
99
  const ci = greenAt('ci');
102
- const aggregate = greenAt('release-aggregate');
103
100
  if (!ci) stand_down(`no successful exact-SHA \`ci\` run at ${originMain.slice(0, 7)} yet`);
104
- if (!aggregate) stand_down(`no successful exact-SHA \`release-aggregate\` run at ${originMain.slice(0, 7)} yet`);
105
101
 
106
- log(`ALL GATES PASS — candidate=${originMain.slice(0, 7)} version=${version} ci=${ci.databaseId} aggregate=${aggregate.databaseId}`);
107
- if (!DISPATCH) {
108
- log('report-only mode; pass --dispatch to actually invoke the protected release workflow.');
109
- process.exit(0);
110
- }
111
-
112
- // ── 6. Dispatch THE SANCTIONED PUBLISHER. Nothing here publishes; the workflow does. ─────────────
113
- sh('gh', ['workflow', 'run', 'protected-release.yml', '--repo', REPO,
114
- '-f', `candidate_sha=${originMain}`,
115
- '-f', `version=${version}`,
116
- '-f', `release_qe_run_id=${ci.databaseId}`,
117
- '-f', `aggregate_run_id=${aggregate.databaseId}`]);
118
- log(`DISPATCHED protected-release for ${version}. The workflow owns every safety gate from here.`);
119
- log('Verify afterwards with: node scripts/published-surface-probe.mjs (D-version-coherence must PASS).');
102
+ log(`PRECONDITIONS VISIBLE — candidate=${originMain.slice(0, 7)} version=${version} ci=${ci.databaseId}`);
103
+ // ── 6. Never dispatch a publisher from the watchdog. ───────────────────────────────────────────
104
+ // Only protected-release may derive the complete typed evidence chain and cross Production.
105
+ // This unattended observer cannot turn visible preconditions into release authority.
106
+ log(`PRECONDITIONS ONLY — no candidate artifact inferred and no action taken for ${version} at ${originMain}`);
107
+ log('Run release-candidate-preflight on release/** once; fast-forward that exact SHA to main; then protected-release imports release-candidate-<SHA> without a run ID.');