ruvnet-brain 4.3.1-dev → 4.3.3

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 (38) hide show
  1. package/README.md +12 -13
  2. package/bin/install.mjs +10 -1
  3. package/console/app.js +4 -4
  4. package/kb/verify-citation.mjs +32 -5
  5. package/package.json +12 -9
  6. package/plugin/.claude-plugin/plugin.json +1 -1
  7. package/plugin/.codex-plugin/plugin.json +1 -1
  8. package/plugin/mcp/managed-cli-interface.mjs +4 -2
  9. package/plugin/scripts/codex-hook-adapter.mjs +12 -7
  10. package/plugin/scripts/codex-hook-events.mjs +39 -0
  11. package/plugin/scripts/coverage-integrity.mjs +4 -4
  12. package/plugin/scripts/hook-shim.mjs +9 -7
  13. package/plugin/scripts/learn-capture.sh +20 -1
  14. package/plugin/scripts/learn-flush.mjs +7 -1
  15. package/plugin/scripts/lesson-gate.mjs +17 -2
  16. package/plugin/scripts/lesson-hooks.sh +17 -2
  17. package/plugin/scripts/session-start-core.mjs +47 -11
  18. package/scripts/brain-stamp-resolve.mjs +22 -0
  19. package/scripts/brain-stamp.mjs +5 -3
  20. package/scripts/build-bundle.mjs +41 -4
  21. package/scripts/ci/step-watchdog.mjs +86 -0
  22. package/scripts/convergence-manifest.mjs +66 -0
  23. package/scripts/distill-project.mjs +22 -4
  24. package/scripts/eval-brain.mjs +13 -3
  25. package/scripts/execution-policy.mjs +66 -0
  26. package/scripts/git-hooks/pre-push +16 -0
  27. package/scripts/health-repair.mjs +5 -1
  28. package/scripts/public-verification-aggregate.mjs +3 -1
  29. package/scripts/public-verification-inputs.mjs +5 -3
  30. package/scripts/qa-runner.mjs +71 -0
  31. package/scripts/qe/agentic-qe-4.3.mjs +195 -0
  32. package/scripts/qe/aggregate-4.3.mjs +42 -0
  33. package/scripts/record-lesson.mjs +28 -6
  34. package/scripts/release-projection.mjs +76 -32
  35. package/scripts/release.mjs +6 -11
  36. package/scripts/retrieval-canary.mjs +14 -6
  37. package/scripts/set-version.mjs +31 -0
  38. package/scripts/verify-channels.mjs +1 -1
@@ -108,14 +108,17 @@ export function validateRetrievalQueryEvidence(evidence) {
108
108
  return evidence;
109
109
  }
110
110
 
111
- export function verifyQueryOracleSource(queryEvidence, candidateSourceSha, { cwd = process.cwd(), run = spawnSync } = {}) {
111
+ export function verifyQueryOracleSource(queryEvidence, candidateSourceSha, {
112
+ cwd = process.cwd(), run = spawnSync, allowSquashedSource = false,
113
+ } = {}) {
112
114
  validateRetrievalQueryEvidence(queryEvidence);
113
115
  if (!HEX40.test(String(candidateSourceSha || '')) || queryEvidence.sourceCommit === candidateSourceSha) {
114
116
  throw new Error('query oracle source identity is invalid');
115
117
  }
116
118
  const ancestor = run('git', ['merge-base', '--is-ancestor', queryEvidence.sourceCommit, candidateSourceSha],
117
119
  { cwd, encoding: 'utf8' });
118
- if (ancestor.error || ancestor.status !== 0) throw new Error('query oracle source is not a strict candidate ancestor');
120
+ const strictAncestor = !ancestor.error && ancestor.status === 0;
121
+ if (!strictAncestor && !allowSquashedSource) throw new Error('query oracle source is not a strict candidate ancestor');
119
122
  const shown = run('git', ['show', `${queryEvidence.sourceCommit}:${queryEvidence.sourcePath}`],
120
123
  { cwd, encoding: null, maxBuffer: 64 * 1024 * 1024 });
121
124
  let sourcePayload;
@@ -184,7 +187,8 @@ export function validateRetrievalCanaryPlan(plan) {
184
187
  throw new Error('oracle denominator differs from eligible coverage');
185
188
  }
186
189
  if (new Set(ids).size !== ids.length) throw new Error('retrieval canary plan has duplicate case ids');
187
- if (!plan.cases.some(({ cohort }) => cohort === 'delta') || !plan.cases.some(({ cohort }) => cohort === 'legacy')) {
190
+ if ((!plan.cases.some(({ cohort }) => cohort === 'delta') && plan.noDelta !== true)
191
+ || !plan.cases.some(({ cohort }) => cohort === 'legacy')) {
188
192
  throw new Error('retrieval canary plan requires delta and legacy cohorts');
189
193
  }
190
194
  for (const row of plan.cases) {
@@ -236,7 +240,7 @@ export function validatePlanAgainstCoverage(plan, coverage, { allowObservedBasel
236
240
  return plan;
237
241
  }
238
242
  export function buildRetrievalCanaryPlan({ coverage, baseline, candidate, coverageIdentity = null, queryEvidence, assetsDir = '.',
239
- readPassages = defaultReadPassages, legacySampleSize } = {}) {
243
+ readPassages = defaultReadPassages, legacySampleSize, allowNoDelta = false } = {}) {
240
244
  const checked = validateCoverageLedger(coverage);
241
245
  if (!checked.valid) throw new Error(`coverage ledger is invalid: ${checked.failures.join('; ')}`);
242
246
  const coverageGeneration = coverage.kind === 'ruvnet-brain-release-coverage'
@@ -294,12 +298,15 @@ export function buildRetrievalCanaryPlan({ coverage, baseline, candidate, covera
294
298
  const eligibleStores = ordered(eligible.map(storeOf));
295
299
  if (queryEvidence.queryStoreSetSha256 !== setDigest(eligibleStores)
296
300
  || canonicalJson(ordered(Object.keys(queryEvidence.queries))) !== canonicalJson(eligibleStores)) {
297
- throw new Error('independent query oracle does not cover the exact eligible store set');
301
+ const oracleStores = new Set(Object.keys(queryEvidence.queries));
302
+ const missing = eligibleStores.filter((store) => !oracleStores.has(store));
303
+ const extra = [...oracleStores].filter((store) => !eligibleStores.includes(store)).sort();
304
+ throw new Error(`independent query oracle does not cover the exact eligible store set (eligible=${eligibleStores.length}, oracle=${oracleStores.size}, missing=${missing.join(',') || 'none'}, extra=${extra.join(',') || 'none'})`);
298
305
  }
299
306
  const baselineStores = new Set(baseline.stores.map((name) => String(name).toLowerCase()));
300
307
  const delta = eligible.filter((row) => !baselineStores.has(storeOf(row)));
301
308
  const legacyPool = eligible.filter((row) => baselineStores.has(storeOf(row)));
302
- if (!delta.length || !legacyPool.length) throw new Error('coverage does not establish both failed-seed delta and legacy cohorts');
309
+ if ((!delta.length && !allowNoDelta) || !legacyPool.length) throw new Error('coverage does not establish both failed-seed delta and legacy cohorts');
303
310
  const passages = new Map(eligible.map((row) => [storeOf(row), readPassages(assetsDir, storeOf(row))]));
304
311
  const rankedLegacy = legacyPool.map((row) => ({ row, count: passages.get(storeOf(row)).length }))
305
312
  .sort((a, b) => a.count - b.count || storeOf(a.row).localeCompare(storeOf(b.row)));
@@ -376,6 +383,7 @@ export function buildRetrievalCanaryPlan({ coverage, baseline, candidate, covera
376
383
  queryStoreSetSha256: queryEvidence.queryStoreSetSha256,
377
384
  sourceCommit: queryEvidence.sourceCommit, sourceBlobSha256: queryEvidence.sourceBlobSha256,
378
385
  evidence: structuredClone(queryEvidence) },
386
+ noDelta: delta.length === 0,
379
387
  cohorts: { delta: delta.length, legacy: legacy.length,
380
388
  legacyStrata: [...strata.keys()].sort().map((stratum) => ({ stratum,
381
389
  population: rankedLegacy.filter((_entry, index) => Math.min(3, Math.floor(index * 4 / rankedLegacy.length)) === stratum).length,
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ // One release version command. The plugin manifest is the only editable source of truth;
3
+ // propagation and verification happen in the same process so a package-only bump cannot drift.
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { spawnSync } from 'node:child_process';
8
+
9
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
10
+ const version = process.argv[2];
11
+ if (!version || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
12
+ console.error('usage: npm run version:set -- X.Y.Z[-channel]');
13
+ process.exit(2);
14
+ }
15
+ const manifest = path.join(root, 'plugin/.claude-plugin/plugin.json');
16
+ const doc = JSON.parse(fs.readFileSync(manifest, 'utf8'));
17
+ const previous = doc.version;
18
+ doc.version = version;
19
+ fs.writeFileSync(manifest, `${JSON.stringify(doc, null, 2)}\n`);
20
+ const sync = spawnSync(process.execPath, [path.join(root, 'scripts/sync-version.mjs')], {
21
+ cwd: root, encoding: 'utf8', stdio: 'inherit',
22
+ });
23
+ if (sync.status !== 0) {
24
+ console.error(`[version:set] propagation failed; source changed ${previous} -> ${version}, run version:sync after fixing the reported error`);
25
+ process.exit(sync.status || 1);
26
+ }
27
+ const check = spawnSync(process.execPath, [path.join(root, 'scripts/sync-version.mjs'), '--check'], {
28
+ cwd: root, encoding: 'utf8', stdio: 'inherit',
29
+ });
30
+ if (check.status !== 0) process.exit(check.status || 1);
31
+ console.log(`[version:set] ${previous} -> ${version}; all release surfaces agree`);
@@ -97,7 +97,7 @@ async function run() {
97
97
  }
98
98
  check('npm latest matches shipping version', ok,
99
99
  ok ? `npm latest=${latest} · shipping=${V}`
100
- : `npm latest=${latest || '(view failed: ' + lastErr + ')'} · shipping=${V}${latest && latest !== V ? ' ← STILL STALE after ~' + (ATTEMPTS * 5) + 's: run `npm publish` + `npm dist-tag add ' + NPM_PKG + '@' + V + ' latest`' : ''}`);
100
+ : `npm latest=${latest || '(view failed: ' + lastErr + ')'} · shipping=${V}${latest && latest !== V ? ' ← STILL STALE after ~' + (ATTEMPTS * 5) + 's: dispatch the protected-release workflow for the exact candidate SHA' : ''}`);
101
101
  }
102
102
 
103
103
  // 2) the self-update manifest URL actually resolves (the 404 that stranded users)