fix(ci): base-reds round 3b — stale sibling tests + mode-pack weight contract

- check-docs-counts-sync.test.ts: drop the imports/subtests of the four helpers
  #10196 removed from the gate script (readMcpFactsFromSource, listLocalizedDocs,
  makeRequiredCountsValidator, checkFreeTierInventory) — the new-API tests that
  #10196 added stay; the file now loads again under the node runner
- quota-connection-recovery.test.ts: convert from vitest APIs to node:test —
  the file lives in tests/unit/*.test.ts (node-runner glob) and the vitest
  runtime crashes when imported outside vitest, killing the whole shard entry
- modePacks.ts: re-normalize all six mode packs to sum 1.0 — #8940 added
  sessionAvailability: 0.05 to every pack without rebalancing (1.05 total);
  ratios preserved exactly (÷1.05), so post-normalizeScoringWeights behavior
  is unchanged; restores the declared sum-to-1.0 contract the 4235 test pins

Refs #9985
This commit is contained in:
backryun
2026-08-12 18:17:17 -03:00
parent 24c95aac78
commit 76d825ce76
3 changed files with 76 additions and 123 deletions

View File

@@ -14,84 +14,85 @@ export const MODE_PACKS: Record<string, ScoringWeights> = {
// Prioritize latency → health. tierPriority replaces 0.05 from stability.
// tierAffinity/specificityMatch stay at 0 (manifest-routing-only weights).
"ship-fast": {
quota: 0.14,
health: 0.28,
costInv: 0.05,
latencyInv: 0.32,
taskFit: 0.1,
stability: 0.0,
tierPriority: 0.05,
quota: 0.1333,
health: 0.2667,
costInv: 0.0476,
latencyInv: 0.3048,
taskFit: 0.0952,
stability: 0,
tierPriority: 0.0476,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.01,
sessionAvailability: 0.05,
contextAffinity: 0.0095,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
// Prioritize cost. tierPriority replaces 0.05 from stability.
"cost-saver": {
quota: 0.14,
health: 0.19,
costInv: 0.37,
latencyInv: 0.05,
taskFit: 0.1,
stability: 0.05,
tierPriority: 0.05,
quota: 0.1333,
health: 0.181,
costInv: 0.3524,
latencyInv: 0.0476,
taskFit: 0.0952,
stability: 0.0476,
tierPriority: 0.0476,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.0,
sessionAvailability: 0.05,
contextAffinity: 0,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
// Prioritize task fitness. tierPriority replaces 0.05 from latencyInv.
"quality-first": {
quota: 0.1,
health: 0.18,
costInv: 0.05,
latencyInv: 0.05,
taskFit: 0.37,
stability: 0.15,
tierPriority: 0.05,
quota: 0.0952,
health: 0.1714,
costInv: 0.0476,
latencyInv: 0.0476,
taskFit: 0.3524,
stability: 0.1429,
tierPriority: 0.0476,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.0,
sessionAvailability: 0.05,
contextAffinity: 0,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
// Prioritize quota availability. tierPriority replaces 0.05 from taskFit.
"offline-friendly": {
quota: 0.37,
health: 0.28,
costInv: 0.1,
latencyInv: 0.05,
taskFit: 0.0,
stability: 0.1,
tierPriority: 0.05,
quota: 0.3524,
health: 0.2667,
costInv: 0.0952,
latencyInv: 0.0476,
taskFit: 0,
stability: 0.0952,
tierPriority: 0.0476,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.0,
sessionAvailability: 0.05,
contextAffinity: 0,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
// #4235 `:reliable` — prioritize healthy, low-variance providers (high availability).
// health (circuit-breaker) + stability (latency std-dev) dominate; weights sum to 1.0.
// health (circuit-breaker) + stability (latency std-dev) dominate; weights sum to ~1.0
// (re-normalized after #8940 added sessionAvailability without rebalancing — #9985).
"reliability-first": {
quota: 0.14,
health: 0.37,
costInv: 0.04,
latencyInv: 0.05,
taskFit: 0.1,
stability: 0.2,
tierPriority: 0.05,
quota: 0.1333,
health: 0.3524,
costInv: 0.0381,
latencyInv: 0.0476,
taskFit: 0.0952,
stability: 0.1905,
tierPriority: 0.0476,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.0,
sessionAvailability: 0.05,
contextAffinity: 0,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
// Chaos mode — priority: health > stability > taskFit > latency.
// Selects top-N healthy providers for parallel dispatch. Favors providers with
@@ -100,19 +101,19 @@ export const MODE_PACKS: Record<string, ScoringWeights> = {
// to picking the most stable providers); connectionDensity boosted slightly to
// prefer providers with multiple accounts (more resilient to per-account rate limits).
"chaos-mode": {
quota: 0.05,
health: 0.42,
costInv: 0.02,
latencyInv: 0.03,
taskFit: 0.2,
stability: 0.18,
tierPriority: 0.02,
quota: 0.0476,
health: 0.4,
costInv: 0.019,
latencyInv: 0.0286,
taskFit: 0.1905,
stability: 0.1714,
tierPriority: 0.019,
tierAffinity: 0,
specificityMatch: 0,
contextAffinity: 0.03,
sessionAvailability: 0.05,
contextAffinity: 0.0286,
sessionAvailability: 0.0476,
resetWindowAffinity: 0,
connectionDensity: 0.05,
connectionDensity: 0.0476,
},
};

View File

@@ -8,9 +8,6 @@ import {
tallyDrift,
readProviderTotal,
countLocales,
readMcpFactsFromSource,
listLocalizedDocs,
makeRequiredCountsValidator,
} from "../../scripts/check/check-docs-counts-sync.mjs";
// Explicit types for the .mjs exports — keep the test at 0 no-explicit-any warnings.
@@ -27,11 +24,6 @@ const tally = tallyDrift as (
) => { strict: number; soft: number; lines: string[] };
const readTotal = readProviderTotal as () => number;
const locales = countLocales as () => number;
const mcpFacts = readMcpFactsFromSource as () => { tools: number; scopes: number } | null;
const localizedDocs = listLocalizedDocs as (relativePath: string) => string[];
const requireCounts = makeRequiredCountsValidator as (
requirements: { label: string; value: number }[]
) => (content: string) => { ok: boolean; detail: string };
const here = path.dirname(fileURLToPath(import.meta.url));
const GATE = path.resolve(here, "../../scripts/check/check-docs-counts-sync.mjs");
@@ -100,31 +92,6 @@ test("countLocales reads a real, positive locale count from config/i18n.json", (
assert.ok(locales() >= 40, "i18n config should define at least 40 locales");
});
test("source-only MCP fallback matches the canonical inventory and scope union", () => {
assert.deepEqual(mcpFacts(), { tools: 107, scopes: 32 });
});
test("localized-doc discovery returns every locale root document", () => {
const readmes = localizedDocs("README.md");
assert.equal(readmes.length, 42);
assert.ok(readmes.includes("docs/i18n/pt-BR/README.md"));
assert.ok(readmes.includes("docs/i18n/zh-CN/README.md"));
});
test("required-count validator reports precisely which live markers are missing", () => {
const validate = requireCounts([
{ label: "providers", value: 327 },
{ label: "MCP tools", value: 107 },
{ label: "MCP scopes", value: 32 },
]);
assert.equal(validate("327 providers; 107 tools; 32 scopes").ok, true);
const stale = validate("290 providers; 104 tools; 31 scopes");
assert.equal(stale.ok, false);
assert.match(stale.detail, /providers=327/);
assert.match(stale.detail, /MCP tools=107/);
assert.match(stale.detail, /MCP scopes=32/);
});
// --- live gate smoke -----------------------------------------------------------------
test("the gate exits 0 against the current (synced) repo state", () => {
@@ -138,7 +105,6 @@ test("the gate exits 0 against the current (synced) repo state", () => {
// down to 1.37B, because no gate watched that number.
import {
checkFreeTierHeadline,
checkFreeTierInventory,
extractHeadlineClaims,
} from "../../scripts/check/check-docs-counts-sync.mjs";
@@ -180,23 +146,6 @@ test("free-tier gate passes when a file carries no headline at all", () => {
assert.equal(checkHeadline("no figures here", TOTALS).ok, true);
});
const checkInventory = checkFreeTierInventory as (
content: string,
totals: { pools: number; models: number }
) => { ok: boolean; detail: string };
test("free-tier inventory gate accepts the live pool/model counts", () => {
assert.equal(
checkInventory("43 provider pools / 522 model budget entries", { pools: 43, models: 522 }).ok,
true
);
});
test("free-tier inventory gate rejects stale model counts", () => {
const result = checkInventory("43 provider pools / 516 models", { pools: 43, models: 522 });
assert.equal(result.ok, false);
assert.match(result.detail, /live catalog has 43 pools \/ 522 model budget entries/);
});
// --- Generic numeric-claim gate (engines / MCP tools / scopes / CLI) --------
// Extends the same drift guard to the counts that silently drifted in v3.8.49:

View File

@@ -1,4 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { describe, it, mock } from "node:test";
import assert from "node:assert/strict";
import {
CREDITS_EXHAUSTED_STATUS,
isCreditsExhaustedReprobeCandidate,
@@ -17,7 +18,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
testStatus: CREDITS_EXHAUSTED_STATUS,
rateLimitedUntil: new Date(nowMs - 5000).toISOString(),
};
expect(isRecoverableCooldownConnection(conn, nowMs)).toBe(false);
assert.equal(isRecoverableCooldownConnection(conn, nowMs), false);
});
it("should reprobe credits_exhausted when >30m has elapsed since lastErrorAt", () => {
@@ -27,7 +28,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
testStatus: CREDITS_EXHAUSTED_STATUS,
lastErrorAt: thirtyOneMinAgo,
};
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(true);
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), true);
});
it("should NOT reprobe credits_exhausted when <30m has elapsed since lastErrorAt", () => {
@@ -37,7 +38,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
testStatus: CREDITS_EXHAUSTED_STATUS,
lastErrorAt: tenMinAgo,
};
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(false);
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), false);
});
it("should reprobe credits_exhausted if no timestamp is present (first tick after startup)", () => {
@@ -45,7 +46,7 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
id: "conn-1",
testStatus: CREDITS_EXHAUSTED_STATUS,
};
expect(isCreditsExhaustedReprobeCandidate(conn, nowMs)).toBe(true);
assert.equal(isCreditsExhaustedReprobeCandidate(conn, nowMs), true);
});
it("selectRecoverableConnections includes both transient cooldowns and expired credits_exhausted", () => {
@@ -69,18 +70,18 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
[activeTransient, expiredCredits, freshCredits],
nowMs
);
expect(selected.map((c) => c.id)).toEqual(["t-1", "c-1"]);
assert.deepEqual(selected.map((c) => c.id), ["t-1", "c-1"]);
});
it("runConnectionRecoveryTick calls clearConnectionError for reprobe candidates", async () => {
const loadConnections = vi.fn().mockResolvedValue([
const loadConnections = mock.fn(async () => [
{
id: "c-1",
testStatus: CREDITS_EXHAUSTED_STATUS,
lastErrorAt: new Date(nowMs - thirtyMinMs - 1000).toISOString(),
},
]);
const clearConnectionError = vi.fn().mockResolvedValue(undefined);
const clearConnectionError = mock.fn(async () => undefined);
const res = await runConnectionRecoveryTick({
nowMs,
@@ -88,8 +89,10 @@ describe("connectionRecovery — credits_exhausted reprobe", () => {
clearConnectionError,
});
expect(res.recovered).toBe(1);
expect(res.recoveredIds).toEqual(["c-1"]);
expect(clearConnectionError).toHaveBeenCalledWith("c-1", expect.anything());
assert.equal(res.recovered, 1);
assert.deepEqual(res.recoveredIds, ["c-1"]);
assert.equal(clearConnectionError.mock.callCount(), 1);
assert.equal(clearConnectionError.mock.calls[0].arguments[0], "c-1");
assert.notEqual(clearConnectionError.mock.calls[0].arguments[1], undefined);
});
});