fix(cli): report result sources when Evidence publication fails

A trace keeps linked sources on the result. Use those records for the
undelivered source closure as well as the bundle, so a refused output
does not report them as not captured.
This commit is contained in:
Cursor Agent committed 2026-11-04 17:50:36 +00:00
1 parent bb73c66baf
commit 51cfd08d45
2 files changed
+37 -7

No files matched your search

+17 -7
View File
@@ -250,13 +250,24 @@ export const publishCliAnalysisEvidence = async (
input: CliEvidenceDelivery,
): Promise<JsonValue> => {
const { operation, signal, detail, evidenceOutput } = input;
// Failure reporting uses the same captured sources as publication. A trace
// carries them on the result, while other commands pass them on the input.
const capturedSources = [
...(input.sourceEvidence ?? []),
...(result.sourceEvidence ?? []),
];
const closureInput =
capturedSources.length === 0
? input
: { ...input, sourceEvidence: capturedSources };
const projectFailure = (error: AnalysisError) =>
projectAnalysisError(
redactExplicitFailure(error, input.sensitiveValues ?? []),
);
const undelivered = (error: AnalysisError) =>
withUndeliveredSourceClosure(projectFailure(error), input);
if (!result.ok) return withUndeliveredSourceClosure(result.error, input);
withUndeliveredSourceClosure(projectFailure(error), closureInput);
if (!result.ok)
return withUndeliveredSourceClosure(result.error, closureInput);
const complete = parseEvidence(result.value);
const inputError = cliEvidenceDeliveryInputError(input);
if (inputError !== undefined) {
@@ -285,11 +296,10 @@ export const publishCliAnalysisEvidence = async (
if (!inspectionSources.ok) return undelivered(inspectionSources.error);
const records = [
...new Map(
[
...(input.sourceEvidence ?? []),
...(result.sourceEvidence ?? []),
...inspectionSources.value,
].map((record) => [record.evidence_id, record]),
[...capturedSources, ...inspectionSources.value].map((record) => [
record.evidence_id,
record,
]),
).values(),
];
// Only skip the writer's heap check when every record is already sealed.
@@ -435,6 +435,26 @@ it("reports the closure when an existing output refuses publication", async () =
expect(await readFile(requested.evidenceOutput, "utf8")).toBe("original");
});
it("reports result sources when an existing output refuses publication", async () => {
const root = await createTestTempDirectory("rea-closure-result-sources-");
const held = source("held");
const requested = delivery(root);
await writeFile(requested.evidenceOutput, "original");
const output = await publishCliAnalysisEvidence(
{ ...ok(observed([held.evidence_id])), sourceEvidence: [held] },
requested,
);
expect(output).toMatchObject({ details: { reason: "exists" } });
expect(closureOf(output)).toMatchObject({
publication: "not_published",
complete: true,
omitted_count: 0,
omitted: [],
});
expect(closureOf(output).records).toEqual([held]);
expect(await readFile(requested.evidenceOutput, "utf8")).toBe("original");
});
it("defers to the committed bundle receipt after cancellation wins late", async () => {
const root = await createTestTempDirectory("rea-closure-committed-");
const held = source("held");