fix(evidence): require a complete record before the single-record hint

A malformed bundle that merely has an evidence_id field was described
as one Evidence record. Use the single-record message only when the
input matches the Evidence record schema; otherwise report the bundle
schema failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822 2026-10-08 14:09:28 +00:00
parent cf96ceb211
commit 38bbb656da
2 changed files with 18 additions and 9 deletions

View File

@ -190,15 +190,9 @@ export const describeEvidenceBundleFailure = (
input: unknown,
cause: unknown,
): string => {
if (
typeof input === "object" &&
input !== null &&
!Array.isArray(input) &&
"evidence_id" in input &&
typeof input.evidence_id === "string" &&
!("records" in input)
)
return `Expected an Evidence bundle with records and manifest arrays, but this JSON is one Evidence record (${input.evidence_id}). Export a session bundle with the MCP export_evidence_bundle tool and supply that file.`;
const record = evidenceRecordSchema.safeParse(input);
if (record.success)
return `Expected an Evidence bundle with records and manifest arrays, but this JSON is one Evidence record (${record.data.evidence_id}). Export a session bundle with the MCP export_evidence_bundle tool and supply that file.`;
return cause instanceof z.ZodError
? `Evidence bundle does not match the bundle schema ${describeValidationFailure(cause)}. Supply an unmodified bundle written by export_evidence_bundle or rea evidence-export.`
: `Evidence bundle validation failed: ${describeValidationFailure(cause)}. Recreate or re-export the bundle, then try again.`;

View File

@ -160,6 +160,21 @@ describe("evidence bundle filesystem adapter", () => {
},
});
const partial = join(directory, "partial.json");
await writeFile(partial, JSON.stringify({ evidence_id: "typo" }));
const rejected = await readEvidenceBundle(partial);
expect(rejected).toMatchObject({
ok: false,
error: {
userMessage: expect.stringContaining(
"does not match the bundle schema",
),
},
});
expect(rejected).not.toMatchObject({
error: { userMessage: expect.stringContaining("one Evidence record") },
});
const mismatched = join(directory, "mismatched.json");
await writeFile(mismatched, JSON.stringify({ ...bundle(), records: {} }));
expect(await readEvidenceBundle(mismatched)).toMatchObject({