ruvnet-brain 4.0.90-dev → 4.3.1-dev

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.
Files changed (74) hide show
  1. package/README.md +31 -7
  2. package/bin/install.mjs +153 -22
  3. package/console/architecture.html +12 -14
  4. package/console/install-mockup.html +3 -3
  5. package/kb/zip-extract.mjs +8 -2
  6. package/package.json +7 -1
  7. package/plugin/.claude-plugin/plugin.json +1 -1
  8. package/plugin/.codex-plugin/plugin.json +1 -1
  9. package/plugin/hooks/codex-hooks.json +1 -1
  10. package/plugin/hooks/hooks.json +10 -0
  11. package/plugin/host-adapters/claude.json +11 -0
  12. package/plugin/host-adapters/codex.json +11 -0
  13. package/plugin/mcp/managed-cli-interface.mjs +77 -10
  14. package/plugin/mcp/server.mjs +2 -1
  15. package/plugin/scripts/anticipate.sh +30 -1
  16. package/plugin/scripts/capability-claim-evidence.mjs +431 -0
  17. package/plugin/scripts/capability-inventory-receipt.mjs +225 -0
  18. package/plugin/scripts/capability-registry.mjs +6 -0
  19. package/plugin/scripts/capability-routing.mjs +70 -0
  20. package/plugin/scripts/continuation-gate.mjs +68 -2
  21. package/plugin/scripts/coverage-integrity.mjs +575 -0
  22. package/plugin/scripts/decision-outcomes.mjs +0 -0
  23. package/plugin/scripts/ground-ruvnet.sh +2 -2
  24. package/plugin/scripts/nightly-controller.mjs +19 -2
  25. package/plugin/scripts/project-progression-contract.mjs +409 -0
  26. package/plugin/scripts/project-progression-hook.mjs +196 -0
  27. package/plugin/scripts/project-progression-outbox.mjs +95 -0
  28. package/plugin/scripts/project-progression-session-start.mjs +183 -0
  29. package/plugin/scripts/project-progression-store.mjs +248 -0
  30. package/plugin/scripts/project-store-resolver.mjs +106 -0
  31. package/plugin/scripts/session-snapshot-hook.mjs +29 -1
  32. package/plugin/scripts/session-start-core.mjs +22 -18
  33. package/plugin/scripts/spend-guard.mjs +6 -1
  34. package/scripts/adr-072-completion.mjs +98 -0
  35. package/scripts/behavioral-l1-l4.mjs +11 -8
  36. package/scripts/brain-score.mjs +23 -9
  37. package/scripts/build-bundle.mjs +94 -8
  38. package/scripts/card-from-source.mjs +140 -0
  39. package/scripts/corpus-aggregates.mjs +194 -0
  40. package/scripts/corpus-candidate.mjs +314 -0
  41. package/scripts/corpus-reconcile.mjs +540 -0
  42. package/scripts/corpus-seed-publish.mjs +110 -0
  43. package/scripts/coverage-integrity.mjs +3 -0
  44. package/scripts/gist-receipts.mjs +183 -0
  45. package/scripts/git-hooks/pre-push +21 -0
  46. package/scripts/host-registry.mjs +118 -0
  47. package/scripts/independent-review-receipt.mjs +499 -0
  48. package/scripts/ingest-gists.mjs +34 -5
  49. package/scripts/ingest-new-repos.mjs +127 -0
  50. package/scripts/metaharness-gate.mjs +58 -0
  51. package/scripts/nightly-gists.sh +3 -3
  52. package/scripts/nightly-wrapper.sh +105 -64
  53. package/scripts/product-integrity-contract.mjs +156 -0
  54. package/scripts/public-inventory.mjs +4 -0
  55. package/scripts/public-verification-aggregate.mjs +342 -0
  56. package/scripts/public-verification-finalizer.mjs +59 -0
  57. package/scripts/public-verification-inputs.mjs +473 -0
  58. package/scripts/public-verification-lane.mjs +244 -0
  59. package/scripts/publication-receipt.mjs +50 -6
  60. package/scripts/rebuild-gists-from-receipts.mjs +291 -0
  61. package/scripts/release-projection.mjs +86 -0
  62. package/scripts/release-transaction-provider.mjs +43 -102
  63. package/scripts/release-transaction.mjs +165 -49
  64. package/scripts/release.mjs +158 -3
  65. package/scripts/restore-local-ingests.mjs +2 -2
  66. package/scripts/retrieval-canary.mjs +533 -0
  67. package/scripts/routing-flywheel.mjs +12 -1
  68. package/scripts/rvf-generation.mjs +50 -0
  69. package/scripts/self-update.mjs +19 -44
  70. package/scripts/source-coverage.mjs +415 -0
  71. package/scripts/source-scope-receipt.mjs +84 -0
  72. package/scripts/sync-census.mjs +0 -0
  73. package/scripts/wired-check.mjs +114 -24
  74. package/scripts/worktree-integrity.mjs +123 -0
@@ -32,11 +32,12 @@ import crypto from 'node:crypto';
32
32
  import { validateProtectedPublishInvocation } from './protected-release-invocation.mjs';
33
33
  import { runReleaseTransaction } from './release-transaction.mjs';
34
34
  import { liveReleaseProvider } from './release-transaction-provider.mjs';
35
- import { stagedHostVerifier } from './staged-host-verifier.mjs';
36
35
  import { verifyPayload } from './release-payload.mjs';
37
36
 
38
37
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
38
+ const IS_MAIN = path.resolve(process.argv[1] || '') === fileURLToPath(import.meta.url);
39
39
  const PUBLISH = process.argv.includes('--publish');
40
+ const CORPUS_SEED = process.argv.includes('--corpus-seed');
40
41
  let protectedCandidate = null;
41
42
  let sealedPackageArtifact = null;
42
43
  let publicationReceiptPath = null;
@@ -56,6 +57,149 @@ function runOrDie(label, cmd, args, opts = {}) {
56
57
  }
57
58
  }
58
59
 
60
+ const cliArg = (argv, name) => {
61
+ const index = argv.indexOf(name);
62
+ return index >= 0 ? argv[index + 1] : undefined;
63
+ };
64
+ const isHex = (value, length) => new RegExp(`^[a-f0-9]{${length}}$`).test(String(value || ''));
65
+ const sha256File = (file) => {
66
+ const hash = crypto.createHash('sha256');
67
+ const fd = fs.openSync(file, 'r');
68
+ const buffer = Buffer.allocUnsafe(1024 * 1024);
69
+ try {
70
+ let bytes;
71
+ while ((bytes = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) hash.update(buffer.subarray(0, bytes));
72
+ } finally {
73
+ fs.closeSync(fd);
74
+ }
75
+ return hash.digest('hex');
76
+ };
77
+ const exactFileIdentity = (value) => value && typeof value.file === 'string'
78
+ && isHex(value.sha256, 64) && Number.isSafeInteger(value.bytes) && value.bytes >= 0;
79
+
80
+ function corpusFailure(message) {
81
+ throw new Error(`[corpus-seed] ${message}`);
82
+ }
83
+
84
+ export function runProtectedCorpusSeed({
85
+ argv = process.argv.slice(2),
86
+ env = process.env,
87
+ root = ROOT,
88
+ run = (command, args, options) => spawnSync(command, args, { encoding: 'utf8', ...options }),
89
+ } = {}) {
90
+ if (env.GITHUB_ACTIONS !== 'true' || env.GITHUB_WORKFLOW !== 'protected-release'
91
+ || env.GITHUB_REPOSITORY !== 'stuinfla/ruvnet-brain') {
92
+ corpusFailure('publication is allowed only inside the protected-release GitHub workflow for stuinfla/ruvnet-brain');
93
+ }
94
+
95
+ const tag = cliArg(argv, '--corpus-tag');
96
+ const bundleFile = cliArg(argv, '--corpus-bundle');
97
+ const receiptFile = cliArg(argv, '--corpus-receipt');
98
+ const target = cliArg(argv, '--target');
99
+ const repo = cliArg(argv, '--repo') || env.GITHUB_REPOSITORY;
100
+ const digestMatch = String(tag || '').match(/^corpus-sha256-([a-f0-9]{64})$/);
101
+ if (!digestMatch) corpusFailure('corpus tag must be corpus-sha256- followed by 64 lowercase hex characters');
102
+ if (repo !== env.GITHUB_REPOSITORY || repo !== 'stuinfla/ruvnet-brain') corpusFailure('repository does not match the protected workflow');
103
+
104
+ for (const [label, file] of [['bundle', bundleFile], ['receipt', receiptFile]]) {
105
+ if (!file || !path.isAbsolute(file)) corpusFailure(`${label} must be an absolute regular file`);
106
+ try {
107
+ const stat = fs.lstatSync(file);
108
+ if (!stat.isFile() || stat.isSymbolicLink()) corpusFailure(`${label} must be an absolute regular file`);
109
+ } catch (error) {
110
+ if (String(error.message).startsWith('[corpus-seed]')) throw error;
111
+ corpusFailure(`${label} must be an absolute regular file (${error.message})`);
112
+ }
113
+ }
114
+
115
+ const headResult = run('git', ['rev-parse', 'HEAD'], {
116
+ cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
117
+ });
118
+ if (headResult.error || headResult.status !== 0) corpusFailure('cannot resolve release HEAD');
119
+ const head = String(headResult.stdout || '').trim();
120
+
121
+ let receipt;
122
+ try {
123
+ receipt = JSON.parse(fs.readFileSync(receiptFile, 'utf8'));
124
+ } catch (error) {
125
+ corpusFailure(`corpus receipt is unreadable/corrupt (${error.message})`);
126
+ }
127
+ if (!isHex(target, 40) || target !== head || target !== env.GITHUB_SHA || target !== receipt.builderSourceSha) {
128
+ corpusFailure('target must exactly equal HEAD, GITHUB_SHA, and the corpus receipt builderSourceSha');
129
+ }
130
+
131
+ const boundaryIdentities = [receipt.privateFence, receipt.eligibilityPolicy, receipt.generationLedger];
132
+ const storeBindingsValid = Number.isSafeInteger(receipt.storeCount) && receipt.storeCount > 0
133
+ && Array.isArray(receipt.stores) && receipt.stores.length === receipt.storeCount
134
+ && receipt.stores.every((store) => typeof store?.name === 'string' && store.name.length > 0
135
+ && /^[a-f0-9]{7,64}$/.test(String(store.sourceCommit || ''))
136
+ && typeof store.builtUtc === 'string' && Number.isFinite(Date.parse(store.builtUtc))
137
+ && typeof store.model === 'string' && store.model.length > 0
138
+ && Number.isSafeInteger(store.dimensions) && store.dimensions > 0
139
+ && Array.isArray(store.files) && store.files.length > 0 && store.files.every(exactFileIdentity));
140
+ const emptyFailureArrays = ['duplicateRvfDigests', 'unreceiptedRvfFiles', 'missingSidecars']
141
+ .every((key) => Array.isArray(receipt[key]) && receipt[key].length === 0);
142
+ const privateExclusionsValid = Array.isArray(receipt.excludedPrivateStores)
143
+ && receipt.excludedPrivateStores.every((name) => typeof name === 'string' && name.length > 0);
144
+ const generatorFile = path.join(root, 'scripts/corpus-candidate.mjs');
145
+ const generatorValid = fs.existsSync(generatorFile)
146
+ && receipt.generator?.corpusCandidateSha256 === sha256File(generatorFile);
147
+ if (receipt.schemaVersion !== 1 || receipt.kind !== 'ruvnet-brain-corpus-candidate'
148
+ || !receipt.createdAt || !receipt.coverageGeneration || !storeBindingsValid || !emptyFailureArrays
149
+ || !privateExclusionsValid || !boundaryIdentities.every(exactFileIdentity) || !exactFileIdentity(receipt.archive)
150
+ || !generatorValid
151
+ || receipt.archive.file !== path.basename(bundleFile)) {
152
+ corpusFailure('corpus receipt bindings are incomplete or invalid');
153
+ }
154
+
155
+ const archiveSha256 = sha256File(bundleFile);
156
+ if (archiveSha256 !== receipt.archive.sha256 || fs.statSync(bundleFile).size !== receipt.archive.bytes) {
157
+ corpusFailure('archive bytes do not match the corpus receipt');
158
+ }
159
+ if (digestMatch[1] !== archiveSha256) corpusFailure('corpus tag digest does not match the receipt and archive');
160
+
161
+ const viewArgs = ['release', 'view', tag, '--json', 'tagName', '--repo', repo];
162
+ const ghCommand = env.RUVNET_GH_COMMAND || 'gh';
163
+ const ghPrefix = env.RUVNET_GH_SCRIPT ? [env.RUVNET_GH_SCRIPT] : [];
164
+ const view = run(ghCommand, [...ghPrefix, ...viewArgs], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
165
+ if (!view.error && view.status === 0) corpusFailure(`release ${tag} already exists; refusing to overwrite immutable corpus seed`);
166
+ const viewError = String(view.error?.message || view.stderr || view.stdout || '');
167
+ if (!/(release not found|no release found)/i.test(viewError)) corpusFailure(`cannot prove ${tag} is absent (${viewError.trim() || `gh exited ${view.status}`})`);
168
+
169
+ const notes = [
170
+ 'Content-addressed RuvNet Brain corpus seed.',
171
+ `Archive SHA-256: ${archiveSha256}`,
172
+ `Receipt SHA-256: ${sha256File(receiptFile)}`,
173
+ `Stores: ${receipt.storeCount}`,
174
+ `Coverage generation: ${receipt.coverageGeneration}`,
175
+ 'This published prerelease is immutable and must never be replaced.',
176
+ ].join('\n');
177
+ const createArgs = [
178
+ 'release', 'create', tag,
179
+ '--prerelease', '--latest=false',
180
+ '--target', target,
181
+ '--repo', repo,
182
+ '--title', `Immutable corpus seed ${archiveSha256.slice(0, 16)}`,
183
+ '--notes', notes,
184
+ bundleFile, receiptFile,
185
+ ];
186
+ const create = run(ghCommand, [...ghPrefix, ...createArgs], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
187
+ if (create.error || create.status !== 0) {
188
+ corpusFailure(`protected corpus publication failed (${String(create.error?.message || create.stderr || create.stdout || '').trim()})`);
189
+ }
190
+ return { tag, target, repository: repo, archiveSha256, receiptSha256: sha256File(receiptFile) };
191
+ }
192
+
193
+ if (IS_MAIN && CORPUS_SEED) {
194
+ try {
195
+ const result = runProtectedCorpusSeed();
196
+ console.log(JSON.stringify({ ok: true, mode: 'corpus-seed', ...result }, null, 2));
197
+ } catch (error) {
198
+ console.error(error.message);
199
+ process.exitCode = 1;
200
+ }
201
+ } else if (IS_MAIN) {
202
+
59
203
  console.log(`\n${c.b('RuvNet Brain — release / definition-of-done')} ${c.dim('· ' + (PUBLISH ? 'PUBLISH' : 'check-only') + ' · shipping ' + V())}\n`);
60
204
 
61
205
  // The local CLI remains useful as a read-only preflight, but publication authority lives only in
@@ -195,6 +339,8 @@ if (PUBLISH) {
195
339
  bundleSignaturePath: path.join(payloadRoot, 'ruvnet-brain.zip.sig'),
196
340
  bundleDigestPath: path.join(payloadRoot, 'ruvnet-brain.zip.sha256'),
197
341
  packagePath: byRole.get('npm'),
342
+ corpusSeedPath: byRole.get('corpus-seed'),
343
+ generationLedgerPath: byRole.get('generation-ledger'),
198
344
  };
199
345
  for (const asset of Object.values(assets)) {
200
346
  if (!fs.existsSync(asset)) {
@@ -224,7 +370,12 @@ if (PUBLISH) {
224
370
  bundleSha256,
225
371
  bundleSignatureSha256: crypto.createHash('sha256').update(fs.readFileSync(assets.bundleSignaturePath)).digest('hex'),
226
372
  bundleDigestSha256: crypto.createHash('sha256').update(fs.readFileSync(assets.bundleDigestPath)).digest('hex'),
373
+ corpusSeedSha256: crypto.createHash('sha256').update(fs.readFileSync(assets.corpusSeedPath)).digest('hex'),
374
+ generationLedgerSha256: crypto.createHash('sha256').update(fs.readFileSync(assets.generationLedgerPath)).digest('hex'),
227
375
  };
376
+ const identityPath = path.resolve(ROOT, process.env.RUVNET_RELEASE_IDENTITY || 'release-evidence/release-identity.json');
377
+ if (fs.existsSync(identityPath)) throw new Error(`refusing to overwrite release identity: ${identityPath}`);
378
+ fs.writeFileSync(identityPath, `${JSON.stringify(identity, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
228
379
  const privatePem = process.env.RUVNET_SIGNING_KEY;
229
380
  if (!privatePem) throw new Error('RUVNET_SIGNING_KEY is required for signed transaction receipts');
230
381
  const finalReceipt = await runReleaseTransaction({
@@ -235,9 +386,12 @@ if (PUBLISH) {
235
386
  }),
236
387
  privateKey: crypto.createPrivateKey(privatePem),
237
388
  publicKey: crypto.createPublicKey(fs.readFileSync(path.join(ROOT, 'keys/ruvnet-brain-signing.pub.pem'), 'utf8')),
238
- hostVerifier: stagedHostVerifier({ assets, identity }),
239
389
  });
240
390
  if (finalReceipt.state !== 'channels-converged') throw new Error(`release transaction stopped at ${finalReceipt.state}`);
391
+ const channelReceiptPath = path.resolve(ROOT,
392
+ process.env.RUVNET_CHANNEL_RECEIPT || 'release-evidence/channels-converged-receipt.json');
393
+ if (fs.existsSync(channelReceiptPath)) throw new Error(`refusing to overwrite channel receipt: ${channelReceiptPath}`);
394
+ fs.writeFileSync(channelReceiptPath, `${JSON.stringify(finalReceipt, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
241
395
  } else {
242
396
  step('D', 'remote staged release transaction — SKIPPED (check-only; pass --publish to publish)');
243
397
  }
@@ -250,7 +404,8 @@ if (!PUBLISH) {
250
404
  }
251
405
 
252
406
  if (PUBLISH) {
253
- console.log(`\n${c.g(c.b('✓✓✓ SHIPPED'))} — every gate passed and every live channel is current. ${c.dim('A user on any path (npm, npx, explainer, --update) gets the working, current build.')}\n`);
407
+ console.log(`\n${c.y(c.b('PUBLISHED, NOT VERIFIED'))} — npm and GitHub expose the sealed candidate; public install verification is still required.\n`);
254
408
  } else {
255
409
  console.log(`\n${c.g(c.b('✓✓✓ PREFLIGHT PASS — NOT PUBLISHED'))} — the committed candidate passed every check-only gate.\n`);
256
410
  }
411
+ }
@@ -42,7 +42,7 @@ import fs from 'node:fs';
42
42
  import path from 'node:path';
43
43
  import { execFileSync } from 'node:child_process';
44
44
  import { fileURLToPath } from 'node:url';
45
- import { storeRoot, storesAt } from '../kb/store-root.mjs';
45
+ import { storeRoot, storesAt, rootNeverMaterialized } from '../kb/store-root.mjs';
46
46
 
47
47
  const ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
48
48
  const LEDGER = path.join(ROOT, 'kb', 'local-ingests.json');
@@ -71,7 +71,7 @@ export function missingIngests({ ledgerFile = LEDGER, root = storeRoot() } = {})
71
71
  export function classify({ ledgerFile = LEDGER, root = storeRoot() } = {}) {
72
72
  const missing = missingIngests({ ledgerFile, root });
73
73
  if (!missing.length) return { state: OK, missing };
74
- if (!fs.existsSync(root)) return { state: NEVER_MATERIALIZED, missing };
74
+ if (rootNeverMaterialized(root)) return { state: NEVER_MATERIALIZED, missing };
75
75
  return { state: WIPED, missing };
76
76
  }
77
77