fix(coverage): consolidate request validation ownership

Route CLI and MCP coverage requests through the shared strict workflow boundary before authenticating source Evidence. Preserve canonical advertised schemas, use integrity errors for malformed source bundles, and use the workflow operation for input diagnostics.

Remove the unused throwing domain facade and keep semantic fixture assertions on the parsed Result APIs. Validate compiled CLI and stdio MCP parity, strict request rejection, domain authority/time semantics, generated schemas, and unexpected failure propagation. No external providers execute in these checks.
This commit is contained in:
morluto committed 2026-10-23 09:29:30 +08:00
1 parent da9a580a0d
commit ed069bb9d4
8 files changed
+148 -43

No files matched your search

+1 -2
View File
@@ -1,4 +1,3 @@
import { reconstructionCoverageEvaluationInputSchema } from "../domain/reconstructionCoverageInput.js";
import { Cli, z } from "incur";
import { CLI_COMMANDS } from "../cliCommandNames.js";
@@ -145,7 +144,7 @@ const registerCoverageCommand = (cli: CliInstance, logger: Logger): void =>
logger,
name: CLI_COMMANDS.evaluateReconstructionCoverage,
description: "Evaluate inline fail-closed reconstruction coverage",
inputSchema: reconstructionCoverageEvaluationInputSchema,
inputSchema: z.unknown(),
workflow: async (input) => {
const { evaluateReconstructionCoverage } =
await import("../application/ReconstructionCoverageService.js");
@@ -1,8 +1,12 @@
import { expect } from "vitest";
import { createEvidence } from "./evidence.js";
import { createEvidenceBundle } from "./evidenceBundle.js";
import {
createReconstructionCoverageData,
createReconstructionVerifierContract,
evaluateParsedReconstructionClosure,
readReconstructionCoverageData,
} from "./reconstructionCoverage.js";
const digest = (character: string): string => character.repeat(64);
@@ -149,6 +153,31 @@ export const completeReconstructionCoverageData = () => {
});
};
/** Assert successful domain parsing and evaluation for semantic test fixtures. */
export const assertReconstructionCoverageEvaluation = (
input: unknown,
boundaryId: string,
nowEpochMs: number,
) => {
const parsed = readReconstructionCoverageData(input, ["coverage"]);
expect(parsed.ok).toBe(true);
if (!parsed.ok)
throw new Error("Expected valid reconstruction coverage fixture", {
cause: parsed.error,
});
const result = evaluateParsedReconstructionClosure(
parsed.value,
boundaryId,
nowEpochMs,
);
expect(result.ok).toBe(true);
if (!result.ok)
throw new Error("Expected reconstruction coverage evaluation success", {
cause: result.error,
});
return result.value;
};
/** Complete workspace with its first verifier result and boundary narrowed once. */
export const completeReconstructionCoverageFixture = () => {
const workspace = completeReconstructionCoverageData();
+28 -20
View File
@@ -3,13 +3,14 @@ import { describe, expect, it } from "vitest";
import {
completeReconstructionCoverageData,
completeReconstructionCoverageFixture,
assertReconstructionCoverageEvaluation,
RECONSTRUCTION_COVERAGE_NOW,
reconstructionCoverageEvidenceId,
} from "./reconstructionCoverage.fixture.js";
import {
createReconstructionCoverageData,
createReconstructionVerifierContract,
evaluateReconstructionClosure,
evaluateParsedReconstructionClosure,
readReconstructionCoverageData,
type ReconstructionCoverageData,
} from "./reconstructionCoverage.js";
@@ -20,7 +21,7 @@ it("preserves duplicate coverage owners instead of overwriting them in shared in
const workspace = completeReconstructionCoverageData();
const owner = workspace.owners[0];
if (owner === undefined) throw new Error("Expected fixture owner");
const result = evaluateReconstructionClosure(
const result = assertReconstructionCoverageEvaluation(
{
...workspace,
owners: [owner, owner],
@@ -44,7 +45,7 @@ describe("reconstruction coverage closure", () => {
const workspace = completeReconstructionCoverageData();
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
workspace,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -78,7 +79,7 @@ describe("reconstruction coverage closure", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
incomplete,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -115,7 +116,7 @@ describe("reconstruction coverage closure", () => {
boundaries: [{ ...boundary, required_surface_ids: missingSurfaces }],
});
const result = evaluateReconstructionClosure(
const result = assertReconstructionCoverageEvaluation(
incomplete,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -147,7 +148,7 @@ describe("reconstruction coverage closure", () => {
],
});
const result = evaluateReconstructionClosure(
const result = assertReconstructionCoverageEvaluation(
changed,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -173,7 +174,7 @@ describe("reconstruction coverage closure", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
stale,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -233,7 +234,7 @@ describe("reconstruction coverage verifier observations", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
shared,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -281,7 +282,7 @@ describe("reconstruction coverage verifier selection", () => {
],
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
ambiguous,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -298,7 +299,7 @@ describe("reconstruction coverage verifier selection", () => {
verifier_results: [],
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
missing,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -330,12 +331,12 @@ describe("reconstruction coverage conflicting latest verifier observations", ()
verifier_results: [failed, result],
});
const firstPassResult = evaluateReconstructionClosure(
const firstPassResult = assertReconstructionCoverageEvaluation(
firstPass,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
);
const firstFailResult = evaluateReconstructionClosure(
const firstFailResult = assertReconstructionCoverageEvaluation(
firstFail,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -384,12 +385,12 @@ describe("reconstruction coverage equivalent latest verifier observations", () =
...workspace,
verifier_results: [equivalent, first],
});
const forwardResult = evaluateReconstructionClosure(
const forwardResult = assertReconstructionCoverageEvaluation(
forward,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
);
const reversedResult = evaluateReconstructionClosure(
const reversedResult = assertReconstructionCoverageEvaluation(
reversed,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -421,7 +422,7 @@ describe("reconstruction coverage verifier timestamp selection", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
changed,
boundary.boundary_id,
RECONSTRUCTION_COVERAGE_NOW,
@@ -441,7 +442,7 @@ describe("reconstruction coverage verifier timestamp selection", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
changed,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -478,7 +479,7 @@ describe("reconstruction coverage verifier future timestamps", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
changed,
"replacement.cli",
Date.parse("2026-07-16T12:00:00.123Z") + 0.5,
@@ -491,8 +492,15 @@ describe("reconstruction coverage verifier future timestamps", () => {
it("rejects a non-finite evaluation clock", () => {
const workspace = completeReconstructionCoverageData();
const parsed = readReconstructionCoverageData(workspace, ["coverage"]);
expect(parsed.ok).toBe(true);
if (!parsed.ok) throw parsed.error;
expect(() =>
evaluateReconstructionClosure(workspace, "replacement.cli", Number.NaN),
evaluateParsedReconstructionClosure(
parsed.value,
"replacement.cli",
Number.NaN,
),
).toThrow("Expected finite epoch milliseconds.");
});
@@ -507,7 +515,7 @@ describe("reconstruction coverage verifier future timestamps", () => {
});
expect(
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
changed,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -533,7 +541,7 @@ describe("reconstruction coverage verifier future timestamps", () => {
})),
});
const result = evaluateReconstructionClosure(
const result = assertReconstructionCoverageEvaluation(
incomplete,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
-17
View File
@@ -325,23 +325,6 @@ export const createReconstructionVerifierContract = (
});
};
/** Evaluate inline coverage data against one named fail-closed boundary. */
export const evaluateReconstructionClosure = (
coverageInput: unknown,
boundaryId: string,
nowEpochMs: number,
): ReconstructionClosureResult => {
const parsed = readReconstructionCoverageData(coverageInput, ["coverage"]);
if (!parsed.ok) throw parsed.error;
const result = evaluateParsedReconstructionClosure(
parsed.value,
boundaryId,
nowEpochMs,
);
if (!result.ok) throw result.error;
return result.value;
};
/** Evaluate the owner's authenticated workspace and lookup facts. */
export const evaluateParsedReconstructionClosure = (
parsed: ParsedReconstructionCoverage,
@@ -4,12 +4,12 @@ import { createEvidence, type Evidence } from "./evidence.js";
import { createEvidenceBundle } from "./evidenceBundle.js";
import {
completeReconstructionCoverageFixture,
assertReconstructionCoverageEvaluation,
RECONSTRUCTION_COVERAGE_NOW,
} from "./reconstructionCoverage.fixture.js";
import {
createReconstructionCoverageData,
createReconstructionVerifierContract,
evaluateReconstructionClosure,
type ReconstructionCoverageData,
} from "./reconstructionCoverage.js";
@@ -79,7 +79,7 @@ const withPackageProof = (
};
const evaluate = (coverage: ReconstructionCoverageData) =>
evaluateReconstructionClosure(
assertReconstructionCoverageEvaluation(
coverage,
"replacement.cli",
RECONSTRUCTION_COVERAGE_NOW,
@@ -4,7 +4,7 @@ import { runAdmittedToolOperation } from "../admittedToolOperation.js";
import { evaluateReconstructionCoverage } from "../../application/ReconstructionCoverageService.js";
import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
import { logToolExecution } from "../toolLogging.js";
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
import { applicationToolRegistrationOptions } from "../toolRegistrationOptions.js";
import type { ApplicationToolRegistration } from "./types.js";
const contract = applicationToolContract("evaluate_reconstruction_coverage");
@@ -16,7 +16,7 @@ export const registerCoverageTools = (
): void => {
server.registerTool(
contract.name,
toolRegistrationOptions(contract),
applicationToolRegistrationOptions(contract),
async (input, context) =>
runAdmittedToolOperation(
server,
@@ -3,6 +3,7 @@ import { join } from "node:path";
import { expect } from "vitest";
import { completeReconstructionCoverageData } from "../../../src/domain/reconstructionCoverage.fixture.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
@@ -65,3 +66,52 @@ cliTest(
});
},
);
cliTest(
"preserves shared integrity diagnostics for malformed source Evidence",
async ({ cli }) => {
const coverage = currentCoverage();
const malformed = {
...coverage,
evidence_bundle: {
...coverage.evidence_bundle,
records: coverage.evidence_bundle.records.map((record, index) =>
index === 0
? { ...record, provider: { ...record.provider, name: 3 } }
: record,
),
},
};
const call = await cli.sharedMcpCall?.();
if (call === undefined) throw new Error("Expected file MCP session");
const mcp = await call("evaluate_reconstruction_coverage", {
coverage: malformed,
boundary_id: "replacement.cli",
});
expect(mcp.isError).toBe(true);
expect(parseMcpToolError(mcp)).toMatchObject({
error: { code: "evidence_integrity_mismatch" },
});
const result = await evaluate(cli, malformed);
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "evidence_integrity_mismatch",
category: "integrity_mismatch",
});
},
);
cliTest(
"locates malformed coverage through its shared workflow owner",
async ({ cli }) => {
const result = await evaluate(cli, { ...currentCoverage(), owners: 3 });
expect(result.exitCode).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
details: {
operation: "evaluate_reconstruction_coverage",
issues: [{ path: ["coverage", "owners"], reason: "invalid_type" }],
},
});
},
);
@@ -123,6 +123,42 @@ describe("reconstruction coverage inline Evidence", () => {
});
describe("reconstruction coverage selection", () => {
it.each([
{
label: "unknown root argument",
selection: { boundary_id: "replacement.cli", unexpected: true },
path: ["unexpected"],
reason: "unknown_argument",
},
{
label: "missing boundary",
selection: {},
path: ["boundary_id"],
reason: "missing_argument",
},
])(
"rejects $label through the shared owner",
async ({ selection, path, reason }) => {
await withCoverageMcp(async ({ client, session }) => {
const result = await client.callTool({
name: "evaluate_reconstruction_coverage",
arguments: { coverage: currentFixtureCoverage(), ...selection },
});
expect(result.isError).toBe(true);
expect(parseMcpToolError(result)).toMatchObject({
error: {
code: "invalid_request",
details: {
operation: "evaluate_reconstruction_coverage",
issues: [{ path, reason }],
},
},
});
expect(session.exportEvidenceBundle().records).toEqual([]);
});
},
);
it("reports ambiguity when multiple verifier results cover one claim", async () => {
await withCoverageMcp(async ({ client }) => {
const coverage = currentFixtureCoverage();