mirror of
https://github.com/SikongJueluo/pi-extensions.git
synced 2026-10-05 11:52:55 +08:00
feat(ai-judge): add advisory model catalog and strict corpus replay
- add versioned advisory model catalog shipped with the package and a fail-closed loader - annotate the enforce session notice for untested, deprecated, and revoked models - add --strict to corpus-replay with 0/1/2 exit codes and reject strict subset runs - extract replay qualification into a pure module that recomputes matches and validates latencies - remove the documented-but-unimplemented --thinking flag and stamp reports with a corpus version - qualify gpt-5.6-sol as the first recommended entry and archive three real replay reports - revise the corpus to 2026-08-21.2 changing unclear-forward expected defer to deny
This commit is contained in:
@@ -32,6 +32,25 @@ vi.mock("@earendil-works/pi-coding-agent", async (importOriginal) => {
|
||||
getAgentDir: () => mockAgentDir.dir || "/nonexistent-ai-judge-test",
|
||||
};
|
||||
});
|
||||
// Catalog seam (PIEXTENSIO-24): index.ts reads the advisory catalog once
|
||||
// per session; tests inject entries through this hoisted holder while
|
||||
// keeping the real classifyModel (pure lookup).
|
||||
const { mockCatalog } = vi.hoisted(() => ({
|
||||
mockCatalog: {
|
||||
entries: [] as Array<Record<string, unknown>>,
|
||||
diagnostics: [] as Array<Record<string, unknown>>,
|
||||
},
|
||||
}));
|
||||
vi.mock("../src/catalog", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../src/catalog")>();
|
||||
return {
|
||||
...actual,
|
||||
loadModelCatalog: () => ({
|
||||
catalog: { version: 1, entries: mockCatalog.entries },
|
||||
diagnostics: mockCatalog.diagnostics,
|
||||
}),
|
||||
};
|
||||
});
|
||||
import type { AssistantMessage, Context, Model } from "@earendil-works/pi-ai";
|
||||
import type {
|
||||
ExtensionAPI,
|
||||
@@ -50,6 +69,7 @@ import {
|
||||
unpublishPermissionsService,
|
||||
} from "@gotgenes/pi-permission-system";
|
||||
import extension from "../src/index";
|
||||
import type { ModelCatalogEntry } from "../src/catalog";
|
||||
import { PROMPT_VERSION, TOOL_SCHEMA_VERSION } from "../src/prompt";
|
||||
|
||||
function createFakePi(): {
|
||||
@@ -191,6 +211,8 @@ afterEach(() => {
|
||||
publishedService = undefined;
|
||||
}
|
||||
cleanupMockAgentDir();
|
||||
mockCatalog.entries = [];
|
||||
mockCatalog.diagnostics = [];
|
||||
});
|
||||
|
||||
describe("AI judge lifecycle", () => {
|
||||
@@ -875,4 +897,72 @@ describe("AI judge Enforce authority seam (PIEXTENSIO-23, ADR 0008)", () => {
|
||||
notify.mock.calls.filter((call) => /Enforce/i.test(String(call[0]))),
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("appends an untested-model note to the enforce notice for an out-of-catalog model", async () => {
|
||||
const { notify } = await runAsk({
|
||||
config: { version: 2, mode: "enforce" },
|
||||
});
|
||||
const enforceNotice = notify.mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.find((message) => /Enforce/i.test(message));
|
||||
expect(enforceNotice).toBeDefined();
|
||||
expect(enforceNotice).toMatch(/untested/i);
|
||||
expect(enforceNotice).toMatch(/advisory catalog/i);
|
||||
});
|
||||
|
||||
it("adds catalog-status notes for deprecated and revoked enforce models", async () => {
|
||||
for (const status of ["deprecated", "revoked"] as const) {
|
||||
mockCatalog.entries = [
|
||||
{
|
||||
provider: "session-provider",
|
||||
model: "session-model",
|
||||
api: "openai-codex-responses",
|
||||
status,
|
||||
promptVersion: "v",
|
||||
corpusVersion: "v",
|
||||
testedAt: "2026-01-01T00:00:00Z",
|
||||
corpusCases: 21,
|
||||
matched: 21,
|
||||
infrastructureFailures: 0,
|
||||
latencyMs: { p50: 1, p95: 2, max: 3 },
|
||||
reportPath: "reports/x.json",
|
||||
} satisfies ModelCatalogEntry,
|
||||
];
|
||||
const { notify } = await runAsk({
|
||||
config: { version: 2, mode: "enforce" },
|
||||
});
|
||||
const enforceNotice = notify.mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.find((message) => /Enforce/i.test(message));
|
||||
expect(enforceNotice).toMatch(new RegExp(`catalog status: ${status}`, "i"));
|
||||
}
|
||||
});
|
||||
|
||||
it("omits the untested note when the enforce model is catalog-recommended", async () => {
|
||||
mockCatalog.entries = [
|
||||
{
|
||||
provider: "session-provider",
|
||||
model: "session-model",
|
||||
api: "openai-codex-responses",
|
||||
status: "recommended",
|
||||
promptVersion: "v",
|
||||
corpusVersion: "v",
|
||||
testedAt: "2026-01-01T00:00:00Z",
|
||||
corpusCases: 21,
|
||||
matched: 21,
|
||||
infrastructureFailures: 0,
|
||||
latencyMs: { p50: 1, p95: 2, max: 3 },
|
||||
reportPath: "reports/x.json",
|
||||
} satisfies ModelCatalogEntry,
|
||||
];
|
||||
const { notify } = await runAsk({
|
||||
config: { version: 2, mode: "enforce" },
|
||||
});
|
||||
const enforceNotice = notify.mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.find((message) => /Enforce/i.test(message));
|
||||
expect(enforceNotice).toBeDefined();
|
||||
expect(enforceNotice).not.toMatch(/untested/i);
|
||||
expect(enforceNotice).not.toMatch(/catalog status/i);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user