mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 01:41:21 +00:00
* fix(evidence): bound malformed file field diagnostics `readEvidenceFileSource` projected every validator issue, and the generic error delivery applied the MCP response budget afterwards. A file with many invalid fields therefore produced a transport `resource_constraint` that replaced the typed file error, so the caller lost the selected path entirely. Issue #8453 reports exactly that: 20,000 invalid values still returned a direct `invalid_request` with the path and every field issue, while 125,000 returned `resource_constraint` with neither. `projectInputIssuesWithinBudget` already existed for this but had no production caller. The reader now projects within a 64 KiB allowance and, when the whole list does not fit, keeps the largest leading group that does and appends one explicit omission issue naming how many of how many were dropped. The typed file error and its `path` therefore survive the response boundary, and the diagnostics stay actionable and explicitly incomplete instead of hiding the real failure. Two regressions cover the bound and the fits-in-budget case. With the unbounded projection restored, the bound test and its supporting fixtures fail. * chore: retrigger title and readiness checks after the title was corrected * fix(evidence): bound file diagnostics in one pass and cover the MCP boundary Review findings on the first version. **The prefix was not maximal.** `boundedContentIssues` halved the list and returned the first attempt that fit, so a file just over the allowance lost roughly half its diagnostics. Prefix selection now lives in the shared projector (`projectInputIssuesBounded`), which walks the issues once and stops exactly at the allowance, keeping the largest fitting prefix. **A compound issue could still lose its location.** One `unrecognized_keys` issue naming hundreds of keys expands into hundreds of field diagnostics and can exceed the allowance on its own. The projector now retains a diagnostic at that issue's path instead of returning only the omission notice. **The regression never crossed the MCP boundary.** The issue is about the delivered MCP error, so `evidenceFileSourceMcp.test.ts` sends the 125,000-value fixture through the production `inspect_analysis_view` tool and asserts the delivered error stays `invalid_request`, keeps `details.path`, keeps bounded `content_issues`, and fits the response budget. Both regressions fail without the bound. * test(evidence): move the MCP delivery regression to the adapters project Review finding. `scripts/lib/test-projects.mjs` assigns `src/application/**` to the `services` project, so the delivery regression sat in the wrong project even though it exercises the production MCP tool registration. Moving it to `src/server/` puts it in `adapters`, which runs the other MCP boundary tests and plans the same way.
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { expect, it } from "vitest";
|
|
|
|
import { analysisViewCaptureEvidence } from "../../tests/fixtures/analysisView.js";
|
|
import { createApplicationMcpHarness } from "../../tests/fixtures/applicationMcpHarness.js";
|
|
import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js";
|
|
import type { Evidence } from "../domain/evidence.js";
|
|
|
|
/**
|
|
* Issue #8453 is about the delivered MCP error, not only the reader's result. On
|
|
* unmodified `main`, a malformed Evidence file with 125,000 invalid `limitations`
|
|
* values returned `resource_constraint` at the MCP response boundary with an
|
|
* original-error reference and no file path, because the reader projected every
|
|
* validator issue before the response budget applied.
|
|
*/
|
|
it("delivers a bounded file validation error through inspect_analysis_view", async () => {
|
|
const root = await createTestTempDirectory("rea-mcp-bounded-issues-");
|
|
const evidencePath = join(root, "many.json");
|
|
const source = structuredClone(analysisViewCaptureEvidence());
|
|
const document = {
|
|
...source,
|
|
limitations: Array.from({ length: 125_000 }, (_value, index) => index),
|
|
} as unknown as Evidence;
|
|
await writeFile(evidencePath, JSON.stringify(document));
|
|
|
|
const { client } = await createApplicationMcpHarness();
|
|
const result = await client.callTool({
|
|
name: "inspect_analysis_view",
|
|
arguments: {
|
|
source: { kind: "evidence-file", path: evidencePath },
|
|
view: { kind: "summary" },
|
|
},
|
|
});
|
|
|
|
expect(result.isError).toBe(true);
|
|
const text = (result.content as readonly { type: string; text?: string }[])
|
|
.flatMap(({ type, text: value }) =>
|
|
type === "text" && value !== undefined ? [value] : [],
|
|
)
|
|
.join("");
|
|
// The typed file error and the selected path survive the response boundary.
|
|
const delivered = JSON.parse(text) as {
|
|
error?: {
|
|
code?: string;
|
|
details?: {
|
|
path?: string;
|
|
content_issues?: readonly {
|
|
path?: readonly (string | number)[];
|
|
reason?: string;
|
|
message?: string;
|
|
}[];
|
|
};
|
|
};
|
|
};
|
|
expect(delivered.error?.code).toBe("invalid_request");
|
|
expect(delivered.error?.details?.path).toBe(evidencePath);
|
|
const issues = delivered.error?.details?.content_issues ?? [];
|
|
// Bounded rather than all 125,000, and explicitly incomplete.
|
|
expect(issues.length).toBeGreaterThan(0);
|
|
expect(issues.length).toBeLessThan(1_000);
|
|
expect(issues).toContainEqual(
|
|
expect.objectContaining({
|
|
path: [],
|
|
message: expect.stringContaining("validator issues were omitted"),
|
|
}),
|
|
);
|
|
// The whole delivered response stays well inside the default MCP budget.
|
|
expect(Buffer.byteLength(text, "utf8")).toBeLessThan(1 << 20);
|
|
});
|