mirror of
https://github.com/morluto/rea.git
synced 2026-11-08 07:46:28 +00:00
Keep full schemas and canonical analysis unchanged while loading the cross-domain view through existing named discovery. Qualify real recent-tool workflows with token ledgers, latency, CPU, family RSS and retained-follow-up controls. BREAKING CHANGE: The opt-in hot-cold catalog no longer preloads inspect_analysis_view. Discover it before calling it, batch with its producer when needed, or use the unchanged default full profile.
99 lines
4.0 KiB
TypeScript
99 lines
4.0 KiB
TypeScript
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it, vi } from "vitest";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { evidenceBundleSchema } from "../../../src/domain/evidenceBundle.js";
|
|
import { apiContractSchema } from "../../../src/domain/apiContract/apiContract.js";
|
|
import { analysisViewResultSchema } from "../../../src/domain/analysisView/analysisView.js";
|
|
import { toolContract } from "../../../src/contracts/toolContracts.js";
|
|
import { toolOutputSchemaWithMetadata } from "../../../src/contracts/toolSchemaMetadata.js";
|
|
|
|
it("loads retained analysis views only when needed, preserving complete schemas, source facts and Evidence", async () => {
|
|
const root = await createTestTempDirectory("rea-progressive-api-");
|
|
const path = join(root, "api.json");
|
|
await writeFile(
|
|
path,
|
|
JSON.stringify({
|
|
openapi: "3.1.0",
|
|
info: { title: "Fixture", version: "1" },
|
|
paths: { "/items": { get: { operationId: "listItems" } } },
|
|
}),
|
|
);
|
|
const { client } = await connectLocalToolsMcp({
|
|
REA_MCP_TOOL_CATALOG_PROFILE: "hot-cold",
|
|
REA_MCP_TOOL_FAMILIES: "application,session",
|
|
});
|
|
const names = async () =>
|
|
(await client.listTools()).tools.map((tool) => tool.name);
|
|
expect(await names()).not.toContain("inspect_analysis_view");
|
|
const changed = vi.fn();
|
|
client.setNotificationHandler("notifications/tools/list_changed", changed);
|
|
const call = async (name: string, args: Record<string, unknown>) => {
|
|
const result = await client.callTool({ name, arguments: args });
|
|
expect(result.isError, JSON.stringify(result.content)).not.toBe(true);
|
|
return result;
|
|
};
|
|
await call("discover_tools", { names: ["inspect_api_contract"] });
|
|
expect(await names()).toContain("inspect_api_contract");
|
|
const view = {
|
|
kind: "api-contract",
|
|
collection: "operations",
|
|
paths: ["/items"],
|
|
fields: ["method", "path"],
|
|
};
|
|
const first = parseEvidence(
|
|
(await call("inspect_api_contract", { path, view })).structuredContent,
|
|
);
|
|
const selected = analysisViewResultSchema.parse(first.normalized_result);
|
|
expect(selected).toMatchObject({
|
|
kind: "api-contract",
|
|
items: [{ method: "GET", path: "/items", state: "declared" }],
|
|
});
|
|
expect(await names()).not.toContain("inspect_analysis_view");
|
|
expect(changed).toHaveBeenCalledTimes(1);
|
|
await rm(path);
|
|
await call("discover_tools", {
|
|
names: ["inspect_analysis_view", "export_evidence_bundle"],
|
|
});
|
|
const loaded = (await client.listTools()).tools.find(
|
|
(tool) => tool.name === "inspect_analysis_view",
|
|
);
|
|
expect(loaded?.outputSchema).toEqual(
|
|
toolOutputSchemaWithMetadata(toolContract("inspect_analysis_view"))[
|
|
"~standard"
|
|
].jsonSchema.output({ target: "draft-2020-12" }),
|
|
);
|
|
expect(changed).toHaveBeenCalledTimes(2);
|
|
const source = {
|
|
kind: "retained-evidence",
|
|
evidence_id: selected.parent_evidence_id,
|
|
};
|
|
const follow = await call("inspect_analysis_view", { source, view });
|
|
expect(parseEvidence(follow.structuredContent)).toEqual(first);
|
|
const bundlePath = join(root, "complete.json");
|
|
await call("export_evidence_bundle", { path: bundlePath });
|
|
const bundle = evidenceBundleSchema.parse(
|
|
JSON.parse(await readFile(bundlePath, "utf8")),
|
|
);
|
|
const parent = bundle.records.find(
|
|
(record) => record.evidence_id === selected.parent_evidence_id,
|
|
);
|
|
expect(parent).toBeDefined();
|
|
const report = apiContractSchema.parse(parent?.normalized_result);
|
|
expect(report.operations).toMatchObject([
|
|
{
|
|
method: "GET",
|
|
path: "/items",
|
|
declared_id: "listItems",
|
|
source_pointer: "/paths/~1items/get",
|
|
},
|
|
]);
|
|
expect(follow.structuredContent).toHaveProperty("evidence_links", [
|
|
selected.parent_evidence_id,
|
|
]);
|
|
await call("discover_tools", { names: ["inspect_analysis_view"] });
|
|
expect(changed).toHaveBeenCalledTimes(2);
|
|
});
|