fix(cli): retain Evidence receipts on format refusal (#9943)

This commit is contained in:
morluto authored and GitHub committed 2026-11-11 00:22:30 -05:00
1 parent a54e848302
commit 7aaff2e4e4
2 files changed
+59 -20

No files matched your search

+24 -20
View File
@@ -556,28 +556,32 @@ export const createCliResultOutput = (
const selected = selection.value;
const refuseUnrepresentableText = async (): Promise<true> => {
error = projectAnalysisError(
new AnalysisInputError(
metadata.command,
{
userMessage:
format === "md"
? "Markdown cannot preserve NUL or unpaired UTF-16 surrogates in rendered output."
: "TOON cannot preserve unpaired UTF-16 surrogates in UTF-8 output.",
remediationAction:
"Use --format json or --format yaml to retain the exact code units.",
...(completed?.originalFailure === undefined
? {}
: {
diagnostics: { original_failure: completed.originalFailure },
}),
},
[
retainCliPublicationOnError(
new AnalysisInputError(
metadata.command,
{
path: ["--format"],
reason: "invalid_value",
message: "The selected format cannot preserve this value.",
userMessage:
format === "md"
? "Markdown cannot preserve NUL or unpaired UTF-16 surrogates in rendered output."
: "TOON cannot preserve unpaired UTF-16 surrogates in UTF-8 output.",
remediationAction:
"Use --format json or --format yaml to retain the exact code units.",
...(completed?.originalFailure === undefined
? {}
: {
diagnostics: {
original_failure: completed.originalFailure,
},
}),
},
],
[
{
path: ["--format"],
reason: "invalid_value",
message: "The selected format cannot preserve this value.",
},
],
),
),
);
const document = options.fullOutput
@@ -72,6 +72,41 @@ it("retains a publication receipt when formatting fails after the invocation con
expect(Buffer.concat(chunks).toString("utf8")).toContain("/tmp/result.json");
});
it.each([
{ format: "md", value: "\u0000", window: false },
{ format: "toon", value: "\ud800", window: false },
{ format: "toon", value: "\ud800", window: true },
] as const)(
"retains the committed receipt when $format cannot encode output (token window: $window)",
async ({ format, value, window }) => {
const chunks: Buffer[] = [];
const receipt = { path: "/authored/retained.json", bytes: 123 };
const output = createCliResultOutput(
parseCliOutputArguments([
"--format",
format,
...(window ? ["--token-limit", "1000"] : []),
]),
new Writable({
write(chunk: Buffer, _encoding, callback) {
chunks.push(chunk);
callback();
},
}),
);
if (output === undefined) throw new Error("Missing output owner");
await withCliEvidencePublication(async () => {
recordCliEvidencePublication(receipt);
expect(await output.write({ value }, { ...metadata, format })).toBe(true);
});
expect(output.error).toMatchObject({
code: "invalid_request",
details: { diagnostics: { evidence_bundle: receipt } },
});
expect(Buffer.concat(chunks).toString("utf8")).toContain(receipt.path);
},
);
describe("CLI formatting stage admission", () => {
it("preserves native CTA sentinel output", async () => {
const replies: string[] = [];