mirror of
https://github.com/morluto/rea
synced 2026-10-28 15:07:26 -04:00
ce1f4b1bba
fix(artifacts): keep unknown inventory traversal out of complete inspection coverage Artifact inspection derived its coverage status only from always-zero omission counts, so an inventory substep with unknown container-entry traversal (for example, an omitted FIFO) yielded complete-within-substeps. Analysis-view summaries also exposed the inventory's typed source_coverage only for PCK and PyInstaller roots. Derive the inspection status from the inventory substep's coverage (unknown traversal is partial), and carry the producer's source_coverage plus the traversal-unknown caveat in every artifact summary. Hash-count fields keep their existing format gating. Fixes #5837 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
5.1 KiB
TypeScript
131 lines
5.1 KiB
TypeScript
import { execFileSync } from "node:child_process";
|
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
|
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { createArtifactBinarySession } from "../../../src/composition/binary.js";
|
|
import { analysisViewResultSchema } from "../../../src/domain/analysisView/analysisView.js";
|
|
import { artifactInspectionResultSchema } from "../../../src/domain/artifactInspection.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { createServer } from "../../../src/server/createServer.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
const TRAVERSAL_UNKNOWN =
|
|
"Producer traversal coverage is unknown; missing matches do not prove source absence.";
|
|
|
|
const writeBundle = async (withFifo: boolean): Promise<string> => {
|
|
const root = await createTestTempDirectory("rea-inspection-coverage-");
|
|
const bundle = join(root, "Fixture.app");
|
|
await mkdir(bundle);
|
|
await writeFile(
|
|
join(bundle, "Info.plist"),
|
|
"<plist><dict><key>CFBundleExecutable</key><string>main.js</string></dict></plist>",
|
|
);
|
|
await writeFile(join(bundle, "main.js"), "export const value = 1;\n");
|
|
if (withFifo) execFileSync("mkfifo", [join(bundle, "omitted.fifo")]);
|
|
return bundle;
|
|
};
|
|
|
|
const connect = async () => {
|
|
const session = await createArtifactBinarySession(process.env);
|
|
const server = createServer({ kind: "session", session });
|
|
const client = new Client({ name: "inspection-coverage", version: "1" });
|
|
const [clientTransport, serverTransport] =
|
|
InMemoryTransport.createLinkedPair();
|
|
await server.connect(serverTransport);
|
|
await client.connect(clientTransport);
|
|
const definition = (await client.listTools()).tools.find(
|
|
(tool) => tool.name === "inspect_analysis_view",
|
|
);
|
|
expect(definition?.outputSchema).toBeDefined();
|
|
const outputSchema: Record<string, unknown> = definition!.outputSchema!;
|
|
const viewOutput = new Ajv2020({
|
|
strict: false,
|
|
validateFormats: false,
|
|
}).compile(outputSchema);
|
|
return { client, server, viewOutput };
|
|
};
|
|
|
|
describe.skipIf(process.platform === "win32")(
|
|
"inspect_artifact traversal coverage",
|
|
() => {
|
|
it.each([
|
|
{
|
|
withFifo: true,
|
|
status: "partial",
|
|
sourceCoverage: { scope: "container-entries", status: "unknown" },
|
|
},
|
|
{
|
|
withFifo: false,
|
|
status: "complete-within-substeps",
|
|
sourceCoverage: { scope: "container-entries", status: "complete" },
|
|
},
|
|
] as const)(
|
|
"derives inspection and summary coverage from inventory traversal (FIFO: $withFifo)",
|
|
async ({ withFifo, status, sourceCoverage }) => {
|
|
const path = await writeBundle(withFifo);
|
|
const { client, server, viewOutput } = await connect();
|
|
try {
|
|
const opened = await client.callTool({
|
|
name: "open_binary",
|
|
arguments: { path },
|
|
});
|
|
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
|
|
const inspected = await client.callTool({
|
|
name: "inspect_artifact",
|
|
arguments: {},
|
|
});
|
|
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
|
|
const parent = parseEvidence(inspected.structuredContent);
|
|
const inspection = artifactInspectionResultSchema.parse(
|
|
parent.normalized_result,
|
|
);
|
|
const inventory = inspection.substeps[0]!.evidence.normalized_result;
|
|
expect(inventory.coverage).toEqual(sourceCoverage);
|
|
expect(inspection.coverage.status).toBe(status);
|
|
|
|
const summaries = [
|
|
await client.callTool({
|
|
name: "inspect_analysis_view",
|
|
arguments: {
|
|
source: {
|
|
kind: "retained-evidence",
|
|
evidence_id: parent.evidence_id,
|
|
},
|
|
view: { kind: "summary" },
|
|
},
|
|
}),
|
|
await client.callTool({
|
|
name: "inspect_artifact",
|
|
arguments: { detail: "summary" },
|
|
}),
|
|
];
|
|
for (const reply of summaries) {
|
|
expect(reply.isError, JSON.stringify(reply)).not.toBe(true);
|
|
expect(
|
|
viewOutput(reply.structuredContent),
|
|
JSON.stringify(viewOutput.errors),
|
|
).toBe(true);
|
|
const view = analysisViewResultSchema.parse(
|
|
parseEvidence(reply.structuredContent).normalized_result,
|
|
);
|
|
if (view.kind !== "summary" || !("source_complete" in view.summary))
|
|
throw new Error("Expected an artifact inventory summary");
|
|
expect(view.summary.source_complete).toBe(true);
|
|
expect(view.summary.source_coverage).toEqual(sourceCoverage);
|
|
expect(view.summary.inspection?.coverage.status).toBe(status);
|
|
expect(view.unknowns.includes(TRAVERSAL_UNKNOWN)).toBe(withFifo);
|
|
}
|
|
} finally {
|
|
await client.close();
|
|
await server.close();
|
|
}
|
|
},
|
|
30_000,
|
|
);
|
|
},
|
|
);
|