ruvnet-brain 4.3.28 → 4.3.30
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 +1 -1
- package/bin/install.mjs +90 -8
- package/console/app.js +7 -2
- package/kb/corpus-release-identity.mjs +73 -0
- package/package.json +4 -3
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/mcp/managed-cli-interface.mjs +83 -9
- package/plugin/scripts/advocacy-route.mjs +4 -1
- package/plugin/scripts/capacity-aware-parallel-work.mjs +4 -0
- package/plugin/scripts/continuation-gate.mjs +71 -0
- package/plugin/scripts/decision-gate.mjs +29 -57
- package/plugin/scripts/ground-ruvnet.sh +77 -4
- package/plugin/scripts/grounding-stamp.sh +34 -5
- package/plugin/scripts/grounding-turn-gate.mjs +8 -2
- package/plugin/scripts/grounding-turn-mark.mjs +6 -1
- package/plugin/scripts/hook-input.mjs +54 -0
- package/plugin/scripts/hook-shim.mjs +10 -0
- package/plugin/scripts/memory-doctor.mjs +43 -0
- package/plugin/scripts/nightly-controller.mjs +6 -1
- package/plugin/scripts/project-progression-contract.mjs +17 -0
- package/plugin/scripts/project-progression-hook.mjs +18 -5
- package/plugin/scripts/project-progression-producer.mjs +16 -12
- package/plugin/scripts/ruvnet-gate1-pattern.mjs +17 -0
- package/plugin/scripts/session-start-core.mjs +11 -5
- package/plugin/scripts/session-start-update-plane.mjs +5 -1
- package/plugin/scripts/unprompted-runtime.mjs +29 -8
- package/plugin/skills/ruvnet-brain/PLAYBOOK.md +1 -1
- package/scripts/adr-072-completion.mjs +2 -1
- package/scripts/calibrate-router.mjs +6 -6
- package/scripts/corpus-reconcile.mjs +32 -3
- package/scripts/correction-detect.mjs +10 -11
- package/scripts/dispatch-receipt.mjs +2 -2
- package/scripts/dual-host-deliberation.mjs +3 -2
- package/scripts/execution-policy.mjs +10 -2
- package/scripts/gen-console-images.mjs +0 -1
- package/scripts/gen-images.mjs +0 -4
- package/scripts/host-install-matrix.mjs +7 -4
- package/scripts/independent-review-receipt.mjs +7 -8
- package/scripts/ingest-repo.mjs +45 -1
- package/scripts/learning-replay-cli.mjs +2 -1
- package/scripts/learning-replay-fixture.mjs +2 -1
- package/scripts/learnings.mjs +19 -5
- package/scripts/lesson-migrate-agentdb.mjs +635 -0
- package/scripts/loop-checkpoint.mjs +37 -1
- package/scripts/metaharness-receipts.mjs +3 -2
- package/scripts/nightly-two-run-proof.mjs +6 -6
- package/scripts/nightly-watchdog.mjs +10 -2
- package/scripts/onboarding-console.mjs +98 -49
- package/scripts/oracle/producer-hosts.mjs +2 -1
- package/scripts/private-overlay.mjs +31 -9
- package/scripts/public-verification-aggregate.mjs +4 -3
- package/scripts/publication-receipt.mjs +9 -0
- package/scripts/qe/agentic-qe-4.3.mjs +0 -1
- package/scripts/rebuild-gists-from-receipts.mjs +1 -1
- package/scripts/reconcile-project.mjs +0 -0
- package/scripts/release.mjs +34 -82
- package/scripts/retrieval-canary.mjs +3 -2
- package/scripts/review-model-defaults.mjs +26 -0
- package/scripts/route-cheap.mjs +20 -7
- package/scripts/router-utilization.mjs +5 -5
- package/scripts/rvf-generation.mjs +68 -2
- package/scripts/single-source-check.mjs +270 -0
- package/scripts/subscription-hosts.mjs +4 -0
- package/scripts/sync-version.mjs +22 -0
- package/scripts/trismart.mjs +3 -3
- package/scripts/wired-check.mjs +40 -4
- package/console/assets/memory.webp +0 -0
- package/plugin/scripts/version-bump-gate.sh +0 -124
- package/scripts/qe/aggregate-4.3.mjs +0 -42
- package/scripts/release-convergence-watchdog.mjs +0 -112
- package/scripts/stamp-existing-rvf-generations.mjs +0 -53
- package/scripts/verify-channels.mjs +0 -196
package/scripts/release.mjs
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// scripts/release.mjs — the
|
|
2
|
+
// scripts/release.mjs — the ONLY publisher, plus a local preview of the CI qualification gate.
|
|
3
3
|
//
|
|
4
4
|
// WHY (2026-07-17, Stuart): "You should be able to take the applied knowledge and build it into a set
|
|
5
5
|
// of criteria that you always use, not a bunch of suggestions you choose to ignore." Every failure
|
|
6
6
|
// this session was an ASSUMPTION that survived because the check was a suggestion, not a gate. This
|
|
7
|
-
// script turns the checklist into a gate: it runs the criteria in order
|
|
8
|
-
//
|
|
9
|
-
// no "I think it's fine" — there is pass or fail.
|
|
7
|
+
// script turns the checklist into a gate: it runs the criteria in order and STOPS on the first
|
|
8
|
+
// failure. There is no "I think it's fine" — there is pass or fail.
|
|
10
9
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// WHAT EACH MODE ACTUALLY DOES (rewritten 2026-09-26, consolidation/single-source — the previous
|
|
11
|
+
// header called check-only "the DEFINITION OF DONE" and claimed it alone decided "shipped"; that was
|
|
12
|
+
// never true and duplicated a second, drifting gate list alongside CI's real one). Publishing a
|
|
13
|
+
// release — creating the GitHub Release, moving the npm dist-tag — happens ONLY inside the
|
|
14
|
+
// reviewer-protected `protected-release.yml` workflow, against an exact-SHA CI-sealed candidate;
|
|
15
|
+
// `--publish` mode is that workflow's publisher (it validates the protected-invocation receipt before
|
|
16
|
+
// doing anything and refuses outside it), never rebuilds or retests source, and treats the immutable
|
|
17
|
+
// artifact as the evidence boundary. `--check` mode is a read-only LOCAL PREVIEW for a human on a dev
|
|
18
|
+
// branch: it runs the exact same release-qualification gate CI enforces
|
|
19
|
+
// (`scripts/release-qualification.mjs` + `scripts/release-qualification-contract.mjs`, invoked the
|
|
20
|
+
// same way `canonical-qa.yml`/`ci.yml` invoke it) plus the one-publisher check
|
|
21
|
+
// (`scripts/release-authority.mjs`). There is exactly ONE definition of "release-qualified" — the one
|
|
22
|
+
// CI enforces — and this is a preview of it, not a second, independent one.
|
|
14
23
|
//
|
|
15
24
|
// Usage:
|
|
16
|
-
// node scripts/release.mjs --check #
|
|
17
|
-
// node scripts/release.mjs --publish #
|
|
25
|
+
// node scripts/release.mjs --check # preview the SAME qualification gate CI enforces
|
|
26
|
+
// node scripts/release.mjs --publish # the protected workflow's publisher; not for manual use
|
|
18
27
|
// node scripts/release.mjs # same as --check
|
|
19
28
|
//
|
|
20
29
|
// The gates, in order (fail fast):
|
|
21
|
-
// A. version single-source-of-truth agrees (sync-version --check)
|
|
22
|
-
// B.
|
|
23
|
-
// C. narrative + unit gates (vitest) incl. the tag/entity-aware "What's new" check
|
|
30
|
+
// A. version single-source-of-truth agrees (sync-version --check) + one protected publisher
|
|
31
|
+
// B. release qualification — scripts/release-qualification.mjs, invoked exactly as CI invokes it
|
|
24
32
|
// D. [--publish only] stage and promote the exact package plus signed RVF bundle
|
|
25
|
-
// E. [--check only] verify current public channels; publish verifies them inside transaction finalization
|
|
26
33
|
|
|
27
34
|
import path from 'node:path';
|
|
35
|
+
import os from 'node:os';
|
|
28
36
|
import { fileURLToPath } from 'node:url';
|
|
29
37
|
import { spawnSync, execFileSync } from 'node:child_process';
|
|
30
38
|
import fs from 'node:fs';
|
|
@@ -500,71 +508,22 @@ if (!PUBLISH) {
|
|
|
500
508
|
runOrDie('version sync', process.execPath, ['scripts/sync-version.mjs', '--check']);
|
|
501
509
|
runOrDie('one protected publisher', process.execPath, ['scripts/release-authority.mjs']);
|
|
502
510
|
|
|
503
|
-
//
|
|
511
|
+
// RELEASE QUALIFICATION — ONE definition of "release-qualified", the one CI enforces.
|
|
504
512
|
//
|
|
505
|
-
//
|
|
506
|
-
// (
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
runOrDie('wired (no orphan modules)', process.execPath, ['scripts/wired-check.mjs', '--check']);
|
|
514
|
-
|
|
515
|
-
// THE NORTH-STAR PROMOTION VECTOR — strict/check-only releases may not average one broken or
|
|
516
|
-
// unknown invariant into a pass. The separately authorized stabilization class makes no 95 claim;
|
|
517
|
-
// it retains every safety, test, artifact, publication, and post-publication gate below while the
|
|
518
|
-
// promotion program remains open. Derive this only from the already-validated sealed receipt, never
|
|
519
|
-
// from a free-standing environment toggle.
|
|
520
|
-
if (protectedReleaseMode === 'strict') {
|
|
521
|
-
runOrDie('release vector (all critical invariants PASS)', process.execPath, ['scripts/release-vector.mjs']);
|
|
522
|
-
|
|
523
|
-
// The Top-100 corpus spans naive through expert prompts and grades semantic clauses, citations,
|
|
524
|
-
// abstention, and latency. A manual-only benchmark is a report; a strict release-path benchmark
|
|
525
|
-
// is a guarantee. The benchmark itself fails closed unless all 100 canonical questions run.
|
|
526
|
-
runOrDie('Top-100 source-grounded recall contract', process.execPath, ['scripts/top100-benchmark.mjs', '--no-write']);
|
|
527
|
-
} else {
|
|
528
|
-
console.log(c.y(' strict >=95 promotion gates: NOT CLAIMED (sealed stabilization; scoreClaimed:false)'));
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// A2. Stable Spine restart classifier (ADR-023, red-team finding 18): diff the boot-frozen SHELL
|
|
532
|
-
// (hooks.json, hook-shim, MCP server, .mcp.json, skills/, commands/) against the previous release
|
|
533
|
-
// tag and SAY OUT LOUD whether this release needs a restart. The classification is computed, never
|
|
534
|
-
// remembered — the same shellDiff logic runs client-side in update-apply.mjs at every flip, so the
|
|
535
|
-
// user-facing nag stays honest even if this print is ignored. Informational at ship time; the
|
|
536
|
-
// releasing human sees exactly which shell files changed.
|
|
537
|
-
step('A2', 'Stable Spine — does this release change the boot-frozen shell? (requiresRestart classifier)');
|
|
513
|
+
// Rewritten 2026-09-26 (consolidation/single-source). Before this change, check-only mode ran its
|
|
514
|
+
// OWN separate gate list (npm test, vitest tests/unit, release-vector.mjs, top100-benchmark.mjs,
|
|
515
|
+
// wired-check.mjs, a Stable-Spine restart print, and a live verify-channels.mjs walk) that had
|
|
516
|
+
// drifted from — and duplicated — the actual contract CI enforces in
|
|
517
|
+
// scripts/release-qualification-contract.mjs. Two lists of "what counts as qualified" is exactly how
|
|
518
|
+
// a local PASS stops meaning what CI's PASS means. There is now one contract; this runs it locally,
|
|
519
|
+
// read-only, the same way canonical-qa.yml's `qualify-development` job runs it on every push/PR.
|
|
520
|
+
step('B', 'release qualification — the same gate CI enforces (release-qualification.mjs)');
|
|
538
521
|
{
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (!prevTag) {
|
|
544
|
-
console.log(c.dim(' no previous release tag — classifier has no baseline (first spine release: requiresRestart=true by definition)'));
|
|
545
|
-
} else {
|
|
546
|
-
let changed = [];
|
|
547
|
-
try {
|
|
548
|
-
const out = execFileSync('git', ['diff', '--name-only', `${prevTag}..HEAD`, '--', ...SHELL], { encoding: 'utf8' }).trim();
|
|
549
|
-
changed = out ? out.split('\n') : [];
|
|
550
|
-
} catch { /* diff failure = unknown; say so, never guess green */ changed = ['(diff failed — treat as changed)']; }
|
|
551
|
-
if (changed.length) {
|
|
552
|
-
console.log(` ${c.y('requiresRestart: TRUE')} — shell changed vs ${prevTag}:`);
|
|
553
|
-
for (const f of changed) console.log(` · ${f}`);
|
|
554
|
-
console.log(c.dim(' users get ONE honest restart notice (session-start reads active.json.shellChanged); everything else is live.'));
|
|
555
|
-
} else {
|
|
556
|
-
console.log(` ${c.g('requiresRestart: false')} — no shell change vs ${prevTag}; this release goes fully live with zero restarts.`);
|
|
557
|
-
}
|
|
522
|
+
const reportDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ruvnet-brain-release-qualification-'));
|
|
523
|
+
const reportPath = path.join(reportDir, 'source-qualification.json');
|
|
524
|
+
runOrDie('release qualification (source)', process.execPath,
|
|
525
|
+
['scripts/release-qualification.mjs', '--suite', 'source', '--report', reportPath]);
|
|
558
526
|
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// B. the full brain test suite (the 60/60)
|
|
562
|
-
step('B', 'full test suite (npm test)');
|
|
563
|
-
runOrDie('npm test', 'npm', ['test']);
|
|
564
|
-
|
|
565
|
-
// C. unit gates — narrative-version (tag/entity aware), claims, etc.
|
|
566
|
-
step('C', 'unit gates (vitest) — narrative version, claims, guards');
|
|
567
|
-
runOrDie('vitest unit', 'npx', ['vitest', 'run', 'tests/unit']);
|
|
568
527
|
}
|
|
569
528
|
|
|
570
529
|
// D. One remotely durable, staged release transaction (ADR-062 / DDD-0015). GitHub remains a draft
|
|
@@ -634,13 +593,6 @@ if (PUBLISH) {
|
|
|
634
593
|
step('D', 'remote staged release transaction — SKIPPED (check-only; pass --publish to publish)');
|
|
635
594
|
}
|
|
636
595
|
|
|
637
|
-
// Check-only diagnoses the currently public channels. During publication, transaction finalization
|
|
638
|
-
// performs this walk once, then creates and verifies the publication receipt before convergence.
|
|
639
|
-
if (!PUBLISH) {
|
|
640
|
-
step('E', 'verify-channels — the live walk of every user path');
|
|
641
|
-
runOrDie('verify-channels', process.execPath, ['scripts/verify-channels.mjs']);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
596
|
if (PUBLISH) {
|
|
645
597
|
console.log(`\n${c.y(c.b('PUBLISHED, NOT VERIFIED'))}`);
|
|
646
598
|
} else {
|
|
@@ -594,7 +594,7 @@ export function validateRetrievalCanaryReceipt(receipt, { plan, requireAcceptanc
|
|
|
594
594
|
}
|
|
595
595
|
|
|
596
596
|
export async function runRetrievalCanaries({ plan, sourceSha, artifactSha256, candidateArchiveSha256,
|
|
597
|
-
search, citationResolver, concurrency = 3 } = {}) {
|
|
597
|
+
search, citationResolver, concurrency = 3, searchTimeoutMs = null } = {}) {
|
|
598
598
|
validateRetrievalCanaryPlan(plan);
|
|
599
599
|
if (!HEX40.test(String(sourceSha || '')) || !HEX64.test(String(artifactSha256 || ''))
|
|
600
600
|
|| !HEX64.test(String(candidateArchiveSha256 || '')) || sourceSha !== plan.candidate.sourceSha
|
|
@@ -609,7 +609,8 @@ export async function runRetrievalCanaries({ plan, sourceSha, artifactSha256, ca
|
|
|
609
609
|
for (let index = next++; index < plan.cases.length; index = next++) {
|
|
610
610
|
const canary = plan.cases[index];
|
|
611
611
|
try {
|
|
612
|
-
const rows = resultRows(await search({ query: canary.query, k: 10
|
|
612
|
+
const rows = resultRows(await search({ query: canary.query, k: 10,
|
|
613
|
+
...(Number.isFinite(searchTimeoutMs) && searchTimeoutMs > 0 ? { timeoutMs: searchTimeoutMs } : {}) }));
|
|
613
614
|
const top = rows.slice(0, 10);
|
|
614
615
|
const rank = top.findIndex((row) => String(row?.repo || '').toLowerCase() === canary.expected.repo
|
|
615
616
|
&& expectedSources(canary.expected).some((source) => row?.path === source.path));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// scripts/review-model-defaults.mjs — single source for the model IDs this repo SELECTS to run its
|
|
2
|
+
// own release-review and dual/tri-host deliberation pipelines (single-source contract B7, see
|
|
3
|
+
// scripts/single-source-check.mjs). Every value below is copied verbatim from wherever it used to be
|
|
4
|
+
// hard-coded — no ID has changed, so importing these constants changes no behavior.
|
|
5
|
+
//
|
|
6
|
+
// Two independent generations exist because they were pinned on different dates for different gates;
|
|
7
|
+
// they are NOT the same fact and are kept as two separate exports rather than merged into one.
|
|
8
|
+
|
|
9
|
+
// ── Legacy release-review pair (ADR-072 completion gate + public verification aggregate). ─────────
|
|
10
|
+
// Pinned identically, and independently, in three files before this consolidation:
|
|
11
|
+
// adr-072-completion.mjs (REQUIRED_REVIEWERS), independent-review-receipt.mjs
|
|
12
|
+
// (ALLOWED_INDEPENDENT_REVIEWERS) and public-verification-aggregate.mjs (REQUIRED_REVIEW_MODELS).
|
|
13
|
+
export const CLAUDE_FABLE_5_ID = 'claude-fable-5';
|
|
14
|
+
export const GPT_5_6_SOL_ID = 'gpt-5.6-sol';
|
|
15
|
+
export const LEGACY_REVIEW_MODEL_IDS = Object.freeze([CLAUDE_FABLE_5_ID, GPT_5_6_SOL_ID]);
|
|
16
|
+
export const LEGACY_REVIEWER_IDENTITIES = Object.freeze([
|
|
17
|
+
Object.freeze({ identity: CLAUDE_FABLE_5_ID, model: CLAUDE_FABLE_5_ID, provider: 'firstParty' }),
|
|
18
|
+
Object.freeze({ identity: GPT_5_6_SOL_ID, model: GPT_5_6_SOL_ID, provider: 'openai' }),
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
// ── Native subscription-CLI dual/tri-host pair, verified against the live hosts 2026-09-10/13. ─────
|
|
22
|
+
// Pinned identically, and independently, in three files before this consolidation:
|
|
23
|
+
// dual-host-deliberation.mjs (TOP_SUBSCRIPTION_MODELS), oracle/producer-hosts.mjs (PRODUCER_MODELS)
|
|
24
|
+
// and trismart.mjs's --dry-run summary.
|
|
25
|
+
export const DUAL_HOST_MODEL_IDS = Object.freeze({ claude: 'claude-fable-5-1', codex: 'gpt-6-astra' });
|
|
26
|
+
export const TRI_HOST_MODEL_IDS = Object.freeze({ ...DUAL_HOST_MODEL_IDS, grok: 'grok-4.6' });
|
package/scripts/route-cheap.mjs
CHANGED
|
@@ -39,10 +39,23 @@ export const PRICING = {
|
|
|
39
39
|
'deepseek/deepseek-v4-flash': { in: 0.077, out: 0.154 }, // verified 2026-07-12 OpenRouter /models live; successor to deepseek-chat (which resolves to legacy V3)
|
|
40
40
|
'x-ai/grok-4.5': { in: 2.0, out: 6.0 }, // verified 2026-07-12 OpenRouter /models live; mid-priced frontier-adjacent
|
|
41
41
|
};
|
|
42
|
+
// Named handles for the Claude tier ids below, so every OTHER file that needs one of these ids
|
|
43
|
+
// imports it by NAME instead of retyping the literal a second time (single-source contract B7 —
|
|
44
|
+
// calibrate-router.mjs, dispatch-receipt.mjs, router-utilization.mjs and metaharness-receipts.mjs
|
|
45
|
+
// all used to hard-code their own copies of these same strings).
|
|
46
|
+
export const CLAUDE_MODEL_IDS = Object.freeze({
|
|
47
|
+
haiku: 'claude-haiku-4.5',
|
|
48
|
+
sonnet: 'claude-sonnet-5',
|
|
49
|
+
opus: 'claude-opus-4.8',
|
|
50
|
+
fable: 'claude-fable-5',
|
|
51
|
+
opus5: 'claude-opus-5',
|
|
52
|
+
opus5Fast: 'claude-opus-5-fast',
|
|
53
|
+
});
|
|
54
|
+
|
|
42
55
|
// Frontier = the most capable model you'd otherwise reach for. Fable 5 leads the Claude 5 family
|
|
43
56
|
// (2× Opus 4.8 per token — see CLAUDE_TIERS below), so it is the honest "instead of" baseline: every
|
|
44
57
|
// $ the cascade saves is measured against what Fable 5 would have cost on the same tokens.
|
|
45
|
-
export const FRONTIER = { name:
|
|
58
|
+
export const FRONTIER = { name: CLAUDE_MODEL_IDS.fable, in: 10.0, out: 50.0 };
|
|
46
59
|
|
|
47
60
|
// Claude tiers — $/Mtok, verified live from the OpenRouter /models API 2026-07-13.
|
|
48
61
|
// These are NOT routed through here (Claude Code's own Agent/Task tool spawns them). They are priced
|
|
@@ -51,10 +64,10 @@ export const FRONTIER = { name: 'claude-fable-5', in: 10.0, out: 50.0 };
|
|
|
51
64
|
// whole router looked unused. It WAS unused; it was also unmeasurable. Both had to be fixed.
|
|
52
65
|
// The spread is the whole argument: fable-5 costs 10x haiku-4.5 for identical mechanical work.
|
|
53
66
|
export const CLAUDE_TIERS = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
[CLAUDE_MODEL_IDS.haiku]: { in: 1.0, out: 5.0 },
|
|
68
|
+
[CLAUDE_MODEL_IDS.sonnet]: { in: 2.0, out: 10.0 },
|
|
69
|
+
[CLAUDE_MODEL_IDS.opus]: { in: 5.0, out: 25.0 },
|
|
70
|
+
[CLAUDE_MODEL_IDS.fable]: { in: 10.0, out: 50.0 },
|
|
58
71
|
// OPUS 5 ADDED 2026-08-08, and its absence was silently costing every receipt.
|
|
59
72
|
//
|
|
60
73
|
// dispatch-receipt refuses to price an unknown model ("refusing to invent savings"), which is the
|
|
@@ -67,8 +80,8 @@ export const CLAUDE_TIERS = {
|
|
|
67
80
|
// standing rule — never recalled, never inferred from the 4.8 row:
|
|
68
81
|
// anthropic/claude-opus-5 in $5.00/Mtok out $25.00/Mtok
|
|
69
82
|
// anthropic/claude-opus-5-fast in $10.00/Mtok out $50.00/Mtok
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
[CLAUDE_MODEL_IDS.opus5]: { in: 5.0, out: 25.0 },
|
|
84
|
+
[CLAUDE_MODEL_IDS.opus5Fast]: { in: 10.0, out: 50.0 },
|
|
72
85
|
};
|
|
73
86
|
|
|
74
87
|
/** Price lookup across both tables. Unknown model → null (never invent a savings number). */
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// Usage: node scripts/router-utilization.mjs [--json]
|
|
21
21
|
|
|
22
22
|
import fs from 'node:fs';
|
|
23
|
-
import { FRONTIER, priceOf, receiptsPath } from './route-cheap.mjs';
|
|
23
|
+
import { CLAUDE_MODEL_IDS, FRONTIER, priceOf, receiptsPath } from './route-cheap.mjs';
|
|
24
24
|
|
|
25
25
|
// Band = a human-legible grouping over the continuous complexity/cost axis (ADR-149: "tier_label is
|
|
26
26
|
// metadata, not control flow"). The four bands mirror router-optimizer.mjs. Known models are mapped
|
|
@@ -28,15 +28,15 @@ import { FRONTIER, priceOf, receiptsPath } from './route-cheap.mjs';
|
|
|
28
28
|
const BAND_BY_MODEL = {
|
|
29
29
|
'agent-booster': 'mechanical',
|
|
30
30
|
'inclusionai/ling-2.6-flash': 'cheap',
|
|
31
|
-
|
|
31
|
+
[CLAUDE_MODEL_IDS.haiku]: 'cheap',
|
|
32
32
|
'deepseek/deepseek-chat': 'cheap',
|
|
33
33
|
'deepseek/deepseek-v4-flash': 'cheap',
|
|
34
34
|
'meta-llama/llama-3.3-70b-instruct': 'mid',
|
|
35
35
|
'openai/gpt-4.1': 'mid',
|
|
36
36
|
'x-ai/grok-4.5': 'mid',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
[CLAUDE_MODEL_IDS.sonnet]: 'mid',
|
|
38
|
+
[CLAUDE_MODEL_IDS.opus]: 'frontier',
|
|
39
|
+
[CLAUDE_MODEL_IDS.fable]: 'frontier',
|
|
40
40
|
};
|
|
41
41
|
export const BAND_ORDER = ['mechanical', 'cheap', 'mid', 'frontier'];
|
|
42
42
|
const BAND_LABEL = { mechanical: 'Mechanical', cheap: 'Cheap', mid: 'Mid', frontier: 'Frontier' };
|
|
@@ -11,6 +11,29 @@ import path from 'node:path';
|
|
|
11
11
|
import { getVersion, getVersionTag } from './version.mjs';
|
|
12
12
|
|
|
13
13
|
export const RVF_GENERATIONS_FILE = 'RVF-GENERATIONS.json';
|
|
14
|
+
export const RUNTIME_LEDGER_KIND = 'ruvnet-brain-runtime-generation-ledger';
|
|
15
|
+
|
|
16
|
+
// S4 (ONE PROVENANCE RECORD) — RECONCILING THE TWO "schemaVersion 2" MEANINGS.
|
|
17
|
+
//
|
|
18
|
+
// Two independent writers had drifted into calling different shapes "schemaVersion 2":
|
|
19
|
+
// "Schema A" — THIS module's writeRvfGeneration wrote schemaVersion 1, no `kind`, no
|
|
20
|
+
// `sourceSnapshot`, one store touched per call (the dev-time incremental refresh ledger).
|
|
21
|
+
// "Schema B" — scripts/build-bundle.mjs's projectStoreViews independently writes
|
|
22
|
+
// `{ schemaVersion: 2, kind: 'ruvnet-brain-runtime-generation-ledger', brainVersion,
|
|
23
|
+
// releaseTag, sourceSnapshot, stores }` (the release-time ledger) — and
|
|
24
|
+
// plugin/scripts/coverage-integrity.mjs's release validation ALREADY requires exactly this
|
|
25
|
+
// shape (`schemaVersion !== 2` / `kind !== 'ruvnet-brain-runtime-generation-ledger'` are hard
|
|
26
|
+
// failures there, confirmed by reading it directly) for the tree an installed brain carries.
|
|
27
|
+
// Schema B is the one kept: it is a strict superset (Schema A's per-store row shape is a subset
|
|
28
|
+
// of Schema B's), and it is what the release-time validator already enforces on every installed
|
|
29
|
+
// tree. Schema A is retired: writeRvfGeneration below now emits Schema B's envelope, carrying
|
|
30
|
+
// `sourceSnapshot` forward from whatever the ledger already had (null until a release build sets
|
|
31
|
+
// it — build-bundle.mjs's own construction is untouched and still wins at release time; this
|
|
32
|
+
// module's schemaVersion bump changes nothing downstream, since nothing reads the dev-time
|
|
33
|
+
// ledger's schemaVersion — verified against every consumer: validateSelectedRvfGenerations/
|
|
34
|
+
// verifyRvfGenerations below check individual fields, never schemaVersion; build-bundle.mjs
|
|
35
|
+
// reads the dev-time ledger via readRvfGenerations only to look up per-store rows by name, then
|
|
36
|
+
// constructs ITS OWN new ledger object from scratch).
|
|
14
37
|
|
|
15
38
|
export function canonicalRvfStores(dir) {
|
|
16
39
|
return fs.readdirSync(dir)
|
|
@@ -42,7 +65,8 @@ export function sha256File(file) {
|
|
|
42
65
|
export function readRvfGenerations(dir) {
|
|
43
66
|
const file = path.join(dir, RVF_GENERATIONS_FILE);
|
|
44
67
|
if (!fs.existsSync(file)) {
|
|
45
|
-
return { schemaVersion:
|
|
68
|
+
return { schemaVersion: 2, kind: RUNTIME_LEDGER_KIND, brainVersion: getVersion(),
|
|
69
|
+
releaseTag: getVersionTag(), sourceSnapshot: null, stores: {} };
|
|
46
70
|
}
|
|
47
71
|
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
48
72
|
parsed.stores ||= {};
|
|
@@ -56,15 +80,22 @@ export function writeRvfGeneration({
|
|
|
56
80
|
model,
|
|
57
81
|
dimensions,
|
|
58
82
|
sourceCommit = null,
|
|
83
|
+
sourceRepo = null,
|
|
84
|
+
sourceDescribe = null,
|
|
59
85
|
builtUtc = new Date().toISOString(),
|
|
60
86
|
previousDir = dir,
|
|
61
87
|
}) {
|
|
62
88
|
const rvfPath = path.join(dir, rvfFile);
|
|
63
89
|
if (!fs.existsSync(rvfPath)) throw new Error(`cannot stamp missing RVF: ${rvfPath}`);
|
|
64
90
|
const manifest = readRvfGenerations(previousDir);
|
|
65
|
-
manifest.schemaVersion =
|
|
91
|
+
manifest.schemaVersion = 2;
|
|
92
|
+
manifest.kind = RUNTIME_LEDGER_KIND;
|
|
66
93
|
manifest.brainVersion = getVersion();
|
|
67
94
|
manifest.releaseTag = getVersionTag();
|
|
95
|
+
// Carried forward, never invented here: this is the git sha of the CODE checkout that
|
|
96
|
+
// assembles a release (build-bundle.mjs's projectStoreViews sets it fresh at release time).
|
|
97
|
+
// The dev-time incremental refresh this function serves does not know that value yet.
|
|
98
|
+
manifest.sourceSnapshot = manifest.sourceSnapshot ?? null;
|
|
68
99
|
manifest.stores[store] = {
|
|
69
100
|
file: rvfFile,
|
|
70
101
|
sha256: sha256File(rvfPath),
|
|
@@ -72,12 +103,47 @@ export function writeRvfGeneration({
|
|
|
72
103
|
model,
|
|
73
104
|
dimensions,
|
|
74
105
|
sourceCommit,
|
|
106
|
+
// S4 (ONE PROVENANCE RECORD): the repo identity a SOURCE.json entry needs, carried alongside
|
|
107
|
+
// the byte identity this ledger has always recorded, so SOURCE.json can be projected entirely
|
|
108
|
+
// FROM this ledger (projectSourceStore below) rather than a caller's own second copy of the
|
|
109
|
+
// same facts. Optional and omitted (not written as null) so a caller that does not pass them
|
|
110
|
+
// gets a ledger row with no dangling nulls.
|
|
111
|
+
...(sourceRepo !== null ? { sourceRepo } : {}),
|
|
112
|
+
...(sourceDescribe !== null ? { sourceDescribe } : {}),
|
|
75
113
|
builtUtc,
|
|
76
114
|
};
|
|
77
115
|
fs.writeFileSync(path.join(dir, RVF_GENERATIONS_FILE), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
78
116
|
return manifest.stores[store];
|
|
79
117
|
}
|
|
80
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Project ONE SOURCE.json store entry from its ledger row — the ONE place identity fields
|
|
121
|
+
* (sourceRepo, sourceCommit, sourceDescribe, builtUtc) are read FROM the ledger rather than
|
|
122
|
+
* independently recomputed by a caller (S4: ONE PROVENANCE RECORD).
|
|
123
|
+
*
|
|
124
|
+
* Deliberately minimal: it projects ONLY the identity fields the ledger itself carries.
|
|
125
|
+
* `updater` supplies every NON-identity field a caller's SOURCE.json shape wants
|
|
126
|
+
* (canonicalManifestUrl, canonicalBundleUrl, selfUpdate, builder, updateManaged, origin, …) —
|
|
127
|
+
* spread first so identity fields read from `generation` always win, the same adapter pattern
|
|
128
|
+
* scripts/build-bundle.mjs's projectStoreViews already uses for the release-time case ("only its
|
|
129
|
+
* non-identity updater fields are borrowed per store; sourceCommit/builtUtc are always bound from
|
|
130
|
+
* the selected generation record, never independently trusted from the updater's own copy"). This
|
|
131
|
+
* function does not default those non-identity fields itself: a public-bundle store and a private
|
|
132
|
+
* overlay store want genuinely different shapes (a private store has no selfUpdate command at
|
|
133
|
+
* all, since forge-update.mjs never fetches it) — each caller states what IT needs.
|
|
134
|
+
*/
|
|
135
|
+
export function projectSourceStore(name, generation, updater = {}) {
|
|
136
|
+
if (!generation) throw new Error(`projectSourceStore: no ledger generation record for ${name}`);
|
|
137
|
+
return {
|
|
138
|
+
...updater,
|
|
139
|
+
kbName: updater.kbName || name,
|
|
140
|
+
sourceRepo: generation.sourceRepo ?? updater.sourceRepo ?? null,
|
|
141
|
+
sourceCommit: generation.sourceCommit ?? null,
|
|
142
|
+
sourceDescribe: generation.sourceDescribe ?? updater.sourceDescribe ?? null,
|
|
143
|
+
builtUtc: generation.builtUtc,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
81
147
|
export function verifyRvfGenerations(dir, {
|
|
82
148
|
version = getVersion(),
|
|
83
149
|
releaseTag = getVersionTag(),
|