mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
ci(quality): enforce stale-allowlist on check-known-symbols (Fase 6A.3 follow-up) (#3899)
Integrated into release/v3.8.26
This commit is contained in:
committed by
GitHub
parent
f8131c8a19
commit
be82146d37
@@ -12,7 +12,8 @@
|
||||
// (2) COMBO STRATEGIES — a cadeia de despacho `strategy === "..."` em
|
||||
// open-sse/services/combo.ts DEVE tratar exatamente o conjunto canônico de
|
||||
// ROUTING_STRATEGY_VALUES (src/shared/constants/routingStrategies.ts), exceto
|
||||
// as estratégias-default implícitas (priority não tem branch; cai no
|
||||
// as estratégias-default implícitas documentadas em IMPLICIT_DEFAULT_STRATEGIES
|
||||
// (estratégias canônicas sem NENHUMA referência `strategy === "..."`; caem no
|
||||
// ordenamento padrão). Adicionar um valor canônico sem fiá-lo no despacho, ou
|
||||
// fiar uma string de estratégia que não é canônica (inventada), falha aqui.
|
||||
//
|
||||
@@ -38,10 +39,21 @@
|
||||
// Catraca: cada divergência pré-existente fica numa allowlist documentada e sai 0 hoje.
|
||||
// Padrão herdado de scripts/check/check-provider-consistency.ts (gate .ts via
|
||||
// `node --import tsx` que IMPORTA módulos reais + funções puras + main() guardado).
|
||||
//
|
||||
// Stale-enforcement (6A.3): a ÚNICA allowlist de SUPRESSÃO deste gate é
|
||||
// IMPLICIT_DEFAULT_STRATEGIES — cada entrada suprime uma violação `canonicalNotHandled`
|
||||
// (estratégia canônica sem branch de despacho). Uma entrada que não suprime mais
|
||||
// nenhuma violação real (porque a estratégia ganhou um branch `strategy === "..."`)
|
||||
// é obsoleta → o gate falha com instrução de remoção, fechando o furo de regressão
|
||||
// silenciosa. As demais listas (KNOWN_TRANSLATOR_PAIRS, KNOWN_MCP_TOOL_NAMES) NÃO são
|
||||
// allowlists de supressão e sim snapshots-catraca (falham na REMOÇÃO, não na presença):
|
||||
// uma entrada nelas exige que o par/tool continue VIVO no registry — o oposto de
|
||||
// supressão — então a semântica de stale-enforcement não se aplica a elas.
|
||||
|
||||
import { readFileSync, readdirSync } from "node:fs";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { dirname, resolve as resolvePath, basename, extname } from "node:path";
|
||||
import { assertNoStale } from "./lib/allowlist.mjs";
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = resolvePath(HERE, "..", "..");
|
||||
@@ -53,12 +65,18 @@ const REPO_ROOT = resolvePath(HERE, "..", "..");
|
||||
/**
|
||||
* Estratégias canônicas que NÃO têm um branch `strategy === "..."` na cadeia de
|
||||
* despacho porque são o comportamento padrão (sem reordenamento explícito). Cada
|
||||
* uma documentada. Remover daqui se um branch dedicado for adicionado.
|
||||
* uma documentada. Adicionar aqui se uma estratégia canônica não tiver NENHUMA
|
||||
* referência `strategy === "..."` em combo.ts (do contrário extractHandledStrategies
|
||||
* já a considera tratada e a entrada vira obsoleta — stale-enforcement abaixo falha).
|
||||
*
|
||||
* Atualmente vazio: a entrada `priority` foi removida porque combo.ts passou a
|
||||
* referenciar `strategy === "priority"` (pre-screen de latência em resolveComboTargets),
|
||||
* o que torna `priority` já-tratada por extractHandledStrategies — a supressão não
|
||||
* suprimia mais nenhuma violação `canonicalNotHandled` (era stale). Se o pre-screen
|
||||
* for removido no futuro, `priority` reaparecerá como `canonicalNotHandled` e o gate
|
||||
* pedirá para refiá-la no despacho OU redocumentá-la aqui.
|
||||
*/
|
||||
export const IMPLICIT_DEFAULT_STRATEGIES: Record<string, string> = {
|
||||
priority:
|
||||
'Default sem branch: combo.ts não tem `strategy === "priority"`; cai no ordenamento padrão de resolveComboTargets (ordem de prioridade declarada). É o fallback de normalizeRoutingStrategy.',
|
||||
};
|
||||
export const IMPLICIT_DEFAULT_STRATEGIES: Record<string, string> = {};
|
||||
|
||||
/** Extrai todas as strings literais de `strategy === "..."` da fonte do combo. */
|
||||
export function extractHandledStrategies(comboSource: string): Set<string> {
|
||||
@@ -458,6 +476,16 @@ async function main(): Promise<void> {
|
||||
const canonical = strategiesMod.ROUTING_STRATEGY_VALUES as readonly string[];
|
||||
const comboSource = readFileSync(resolvePath(REPO_ROOT, "open-sse/services/combo.ts"), "utf8");
|
||||
const handled = extractHandledStrategies(comboSource);
|
||||
|
||||
// Stale-enforcement (6A.3): IMPLICIT_DEFAULT_STRATEGIES is a suppression allowlist —
|
||||
// each entry exists ONLY to suppress a `canonicalNotHandled` violation (a canonical
|
||||
// strategy with no `strategy === "..."` dispatch reference). The live violations it
|
||||
// suppresses are the canonical strategies NOT already in `handled` (computed with an
|
||||
// EMPTY implicit-defaults map). An entry whose key IS already in `handled` suppresses
|
||||
// nothing → it is stale and the gate must fail asking for its removal.
|
||||
const liveImplicitNeeded = diffComboStrategies(canonical, handled, {}).canonicalNotHandled;
|
||||
assertNoStale(Object.keys(IMPLICIT_DEFAULT_STRATEGIES), liveImplicitNeeded, "known-symbols:combo");
|
||||
|
||||
const { canonicalNotHandled, handledNotCanonical } = diffComboStrategies(
|
||||
canonical,
|
||||
handled,
|
||||
@@ -635,6 +663,9 @@ async function main(): Promise<void> {
|
||||
console.error(`[known-symbols] ${failures.length} sub-checagem(ns) falharam:\n\n${failures.join("\n\n")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
// assertNoStale (combo) seta process.exitCode=1 sem lançar — não imprima o OK
|
||||
// enganoso; a mensagem de stale já foi logada no stderr pelo helper.
|
||||
if (process.exitCode === 1) return;
|
||||
|
||||
const newPairsNote = newPairs.length
|
||||
? ` (${newPairs.length} par(es) novo(s) não-congelado(s): ${newPairs.join(", ")} — atualize KNOWN_TRANSLATOR_PAIRS se intencional)`
|
||||
|
||||
@@ -20,6 +20,13 @@ import {
|
||||
diffCloudAgents,
|
||||
type ExecutorLike,
|
||||
} from "../../scripts/check/check-known-symbols.ts";
|
||||
import { reportStaleEntries } from "../../scripts/check/lib/allowlist.mjs";
|
||||
import { ROUTING_STRATEGY_VALUES } from "../../src/shared/constants/routingStrategies.ts";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), "../../..");
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
// (2) COMBO STRATEGIES — extractHandledStrategies + diffComboStrategies
|
||||
@@ -174,9 +181,57 @@ test("findNewTranslatorPairs returns [] when live is a subset of frozen", () =>
|
||||
// Allowlist / snapshot sanity (documented frozen sets stay well-formed)
|
||||
// ───────────────────────────────────────────────────────────────────────────
|
||||
|
||||
test("IMPLICIT_DEFAULT_STRATEGIES documents `priority` with a justification", () => {
|
||||
assert.ok(Object.prototype.hasOwnProperty.call(IMPLICIT_DEFAULT_STRATEGIES, "priority"));
|
||||
assert.ok(IMPLICIT_DEFAULT_STRATEGIES.priority.length > 20);
|
||||
test("IMPLICIT_DEFAULT_STRATEGIES: every documented key carries a non-trivial justification", () => {
|
||||
// The map may be empty (all canonical strategies are referenced in combo.ts), but any
|
||||
// entry that DOES exist must explain why the strategy has no explicit dispatch branch.
|
||||
for (const [key, justification] of Object.entries(IMPLICIT_DEFAULT_STRATEGIES)) {
|
||||
assert.ok(typeof justification === "string" && justification.length > 20, `weak/missing justification for "${key}"`);
|
||||
}
|
||||
});
|
||||
|
||||
// --- stale-allowlist enforcement (6A.3) ---
|
||||
|
||||
test("stale-enforcement: an IMPLICIT_DEFAULT_STRATEGIES key now referenced in dispatch is reported as stale", () => {
|
||||
// "priority" gained a `strategy === "priority"` reference in combo.ts, so it is now in
|
||||
// the handled set and no longer in canonicalNotHandled — an implicit-default for it would
|
||||
// suppress nothing → stale.
|
||||
const liveImplicitNeeded: string[] = []; // canonicalNotHandled WITHOUT the allowlist
|
||||
const stale = (reportStaleEntries as (a: string[], l: string[], g: string) => string[])(
|
||||
["priority"],
|
||||
liveImplicitNeeded,
|
||||
"known-symbols:combo"
|
||||
);
|
||||
assert.deepEqual(stale, ["priority"]);
|
||||
});
|
||||
|
||||
test("stale-enforcement: an IMPLICIT_DEFAULT_STRATEGIES key still uncovered by dispatch is NOT stale", () => {
|
||||
// A canonical strategy with no `strategy === "..."` reference IS still suppressed by the
|
||||
// implicit-default, so the entry is live (must stay).
|
||||
const liveImplicitNeeded = ["priority"]; // priority would be canonicalNotHandled without it
|
||||
const stale = (reportStaleEntries as (a: string[], l: string[], g: string) => string[])(
|
||||
["priority"],
|
||||
liveImplicitNeeded,
|
||||
"known-symbols:combo"
|
||||
);
|
||||
assert.deepEqual(stale, []);
|
||||
});
|
||||
|
||||
test("stale-enforcement: live repo IMPLICIT_DEFAULT_STRATEGIES has no stale entries", () => {
|
||||
// Every key must still be a canonical strategy that lacks a `strategy === "..."` reference
|
||||
// in combo.ts; otherwise the suppression is dead and must be removed.
|
||||
const comboSource = fs.readFileSync(path.join(REPO_ROOT, "open-sse/services/combo.ts"), "utf8");
|
||||
const handled = extractHandledStrategies(comboSource);
|
||||
const liveImplicitNeeded = diffComboStrategies(
|
||||
ROUTING_STRATEGY_VALUES as readonly string[],
|
||||
handled,
|
||||
{}
|
||||
).canonicalNotHandled;
|
||||
const stale = (reportStaleEntries as (a: string[], l: string[], g: string) => string[])(
|
||||
Object.keys(IMPLICIT_DEFAULT_STRATEGIES),
|
||||
liveImplicitNeeded,
|
||||
"known-symbols:combo"
|
||||
);
|
||||
assert.deepEqual(stale, [], `IMPLICIT_DEFAULT_STRATEGIES has stale entries: ${stale.join(", ")}`);
|
||||
});
|
||||
|
||||
test("KNOWN_TRANSLATOR_PAIRS is a non-empty, well-formed, deduped from:to snapshot", () => {
|
||||
|
||||
Reference in New Issue
Block a user