perf(config): scope dry-run legacy validation

This commit is contained in:
Vincent Koc
2026-04-13 20:40:52 +01:00
parent e02c6ca82a
commit 25a2ea4480
7 changed files with 200 additions and 10 deletions

View File

@@ -11,6 +11,7 @@ const tempDirs: string[] = [];
const mocks = getRegistryJitiMocks();
let clearPluginDoctorContractRegistryCache: typeof import("./doctor-contract-registry.js").clearPluginDoctorContractRegistryCache;
let collectRelevantDoctorPluginIdsForTouchedPaths: typeof import("./doctor-contract-registry.js").collectRelevantDoctorPluginIdsForTouchedPaths;
let listPluginDoctorLegacyConfigRules: typeof import("./doctor-contract-registry.js").listPluginDoctorLegacyConfigRules;
function makeTempDir(): string {
@@ -25,8 +26,11 @@ describe("doctor-contract-registry getJiti", () => {
beforeEach(async () => {
resetRegistryJitiMocks();
vi.resetModules();
({ clearPluginDoctorContractRegistryCache, listPluginDoctorLegacyConfigRules } =
await import("./doctor-contract-registry.js"));
({
clearPluginDoctorContractRegistryCache,
collectRelevantDoctorPluginIdsForTouchedPaths,
listPluginDoctorLegacyConfigRules,
} = await import("./doctor-contract-registry.js"));
clearPluginDoctorContractRegistryCache();
});
@@ -56,4 +60,49 @@ describe("doctor-contract-registry getJiti", () => {
}),
);
});
it("narrows touched-path doctor ids for scoped dry-run validation", () => {
expect(
collectRelevantDoctorPluginIdsForTouchedPaths({
raw: {
channels: {
discord: {},
telegram: {},
},
plugins: {
entries: {
"memory-wiki": {},
},
},
talk: {
voiceId: "legacy-voice",
},
},
touchedPaths: [
["channels", "discord", "token"],
["plugins", "entries", "memory-wiki", "enabled"],
["talk", "voiceId"],
],
}),
).toEqual(["discord", "elevenlabs", "memory-wiki"]);
});
it("falls back to the full doctor-id set when touched paths are too broad", () => {
expect(
collectRelevantDoctorPluginIdsForTouchedPaths({
raw: {
channels: {
discord: {},
telegram: {},
},
plugins: {
entries: {
"memory-wiki": {},
},
},
},
touchedPaths: [["channels"]],
}),
).toEqual(["discord", "memory-wiki", "telegram"]);
});
});

View File

@@ -161,6 +161,42 @@ export function collectRelevantDoctorPluginIds(raw: unknown): string[] {
return [...ids].toSorted();
}
export function collectRelevantDoctorPluginIdsForTouchedPaths(params: {
raw: unknown;
touchedPaths: ReadonlyArray<ReadonlyArray<string>>;
}): string[] {
const root = asNullableRecord(params.raw);
if (!root) {
return [];
}
const ids = new Set<string>();
for (const touchedPath of params.touchedPaths) {
const [first, second, third] = touchedPath;
if (first === "channels") {
if (!second) {
return collectRelevantDoctorPluginIds(params.raw);
}
if (second !== "defaults") {
ids.add(second);
}
continue;
}
if (first === "plugins") {
if (second !== "entries" || !third) {
return collectRelevantDoctorPluginIds(params.raw);
}
ids.add(third);
continue;
}
if (first === "talk" && hasLegacyElevenLabsTalkFields(root)) {
ids.add("elevenlabs");
}
}
return [...ids].toSorted();
}
function getDoctorContractRecordCache(
baseCacheKey: string,
): Map<string, PluginDoctorContractEntry | null> {