fix(cli): preserve literal Markdown table facts (#7073)

* fix(cli): preserve literal Markdown table facts

* test(cli): retain capacity diagnostics through Markdown escaping

* fix(cli): keep intraword underscores readable in Markdown tables

Underscore runs between letters or digits cannot open or close CommonMark emphasis, so escaping them only adds noise to identifiers such as source_index and invalid_request in raw Markdown. Escape underscore runs only at word boundaries and restore the original diagnostic assertions.
This commit is contained in:
RRFZ
2026-10-25 01:55:18 -04:00
committed by GitHub
parent fead4fdaa7
commit cc146042d1
5 changed files with 137 additions and 15 deletions
+4 -2
View File
@@ -924,8 +924,10 @@ redirected, which keeps large results smaller for agents and scripts; pipe
through `jq .` for indented files. Output selection and formatting do not change
operation status.
`--format md` escapes backslashes and pipes in table headers and cells, and
renders cell line breaks as `<br>`. These are presentation changes; retained
`--format md` escapes backslashes, pipes, and inline Markdown syntax in table
headers and cells so source text is displayed literally, and renders cell line
breaks as `<br>`. Underscores between letters or digits, as in `source_index`,
cannot form emphasis and stay unescaped. These are presentation changes; retained
Evidence and JSON output keep the original values. Nested objects and arrays
use JSON inside their owning Markdown cells, retaining the field names and
values instead of replacing them with an object label. Table widths are measured
+16 -2
View File
@@ -43,13 +43,27 @@ const isObjectArray = (value: unknown): value is Record<string, unknown>[] =>
) &&
value.some((item) => Object.keys(item).length > 0);
const isWordCharacter = (character: string | undefined): boolean =>
character !== undefined && /[\p{L}\p{N}]/u.test(character);
/** Underscore runs between word characters cannot open or close emphasis. */
const escapeInlineSyntax = (
match: string,
offset: number,
text: string,
): string =>
match.startsWith("_") &&
isWordCharacter(text[offset - 1]) &&
isWordCharacter(text[offset + match.length])
? match
: match.replace(/./g, "\\$&");
const escapeTableCell = (value: string): string =>
value
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\\/g, "\\\\")
.replace(/\|/g, "\\|")
.replace(/[\\|`*[\]~]|_+/g, escapeInlineSyntax)
.replace(/\r\n|\r|\n/g, "<br>");
const renderTable = (
+11 -10
View File
@@ -4,7 +4,10 @@ import { Cli, Formatter } from "incur";
import { estimateTokenCount } from "tokenx";
import { expect, it } from "vitest";
import { parseCliOutputArguments } from "../../../src/cliOutput.js";
import {
parseCliOutputArguments,
renderCliDocument,
} from "../../../src/cliOutput.js";
import {
createCliResultOutput,
streamCliCommandResults,
@@ -80,7 +83,7 @@ it("renders only each record's own fields in heterogeneous Markdown tables", asy
"name",
"constructor",
"toString",
"__proto__",
"\\_\\_proto\\_\\_",
"enabled",
"count",
]);
@@ -346,10 +349,9 @@ it.each(["json", "jsonl", "yaml", "toon", "md"] as const)(
.split("\n")
.map((line) => JSON.parse(line)),
).toEqual(expected);
else if (format === "md") {
expect(outputs[0]).toContain(`| ${JSON.stringify(expected[0]!.rows)} |`);
expect(outputs[0]).not.toContain("[object Object]");
} else expect(outputs[0]).toBe(`${Formatter.format(expected, format)}\n`);
else if (format === "md")
expect(outputs[0]).toBe(renderCliDocument(expected, format, false));
else expect(outputs[0]).toBe(`${Formatter.format(expected, format)}\n`);
expect(projectionSource[0]?.rows[0]?.retained).toEqual([0, 10]);
},
);
@@ -406,10 +408,9 @@ it.each(["json", "jsonl", "yaml", "toon", "md"] as const)(
.split("\n")
.map((line) => JSON.parse(line)),
).toEqual(expected);
else if (format === "md") {
expect(outputs[0]).toContain(`| ${JSON.stringify(expected[0]!.rows)} |`);
expect(outputs[0]).not.toContain("[object Object]");
} else expect(outputs[0]).toBe(`${Formatter.format(expected, format)}\n`);
else if (format === "md")
expect(outputs[0]).toBe(renderCliDocument(expected, format, false));
else expect(outputs[0]).toBe(`${Formatter.format(expected, format)}\n`);
expect(projectionSource).toEqual(before);
},
);
@@ -0,0 +1,105 @@
import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { parseFragment, type DefaultTreeAdapterTypes } from "parse5";
import { createMarkdownRenderer, disposeMdItInstance } from "vitepress";
import { afterAll, expect } from "vitest";
import { createEvidence } from "../../../src/domain/evidence.js";
import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
import { renderCliDocument } from "../../../src/cliOutput.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
afterAll(() => disposeMdItInstance());
const renderedCells = (html: string): string[] => {
const text = (node: DefaultTreeAdapterTypes.Node): string =>
node.nodeName === "#text" && "value" in node
? node.value
: "childNodes" in node
? node.childNodes.map(text).join("")
: "";
const cells: string[] = [];
const visit = (node: DefaultTreeAdapterTypes.Node): void => {
if ("tagName" in node && (node.tagName === "th" || node.tagName === "td"))
cells.push(text(node));
else if ("childNodes" in node) node.childNodes.forEach(visit);
};
visit(parseFragment(html));
return cells;
};
cliTest(
"preserves literal Evidence keys and values when Markdown tables are rendered",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-markdown-literal-cells-");
const path = join(root, "bundle.json");
const literal =
"`code` **bold** _name_ ~~deleted~~ [label](target) ![alt](image)";
const punctuation = "\\`tick` \\*star* \\[label](target) a|b <T> &copy;";
const identifiers = "x__y__z _a_b_ 中_文 snake_case_";
const row = {
[literal]: literal,
punctuation,
nested: { text: literal, values: ["__name__", "plain"] },
empty: "",
zero: 0,
disabled: false,
absent: null,
source_index: identifiers,
};
const result = { scalar: { [literal]: literal }, rows: [row] };
const record = createEvidence(
undefined,
{ id: "fixture", name: "Fixture", version: "1" },
{ operation: "ordinary-json", parameters: {}, result },
);
const saved = JSON.stringify(createEvidenceBundle([record]));
await writeFile(path, saved);
const base = [
"inspect-evidence-bundle",
JSON.stringify({ path, detail: "complete" }),
"--filter-output",
"records.0.normalized_result",
];
const json = await cli.run({ arguments: [...base, "--json"] });
const markdown = await cli.run({ arguments: [...base, "--format", "md"] });
expect(json.exitCode, json.stderr).toBe(0);
expect(markdown.exitCode, markdown.stderr).toBe(0);
expect(JSON.parse(json.stdout)).toEqual({
records: { "0": { normalized_result: result } },
});
expect(await readFile(path, "utf8")).toBe(saved);
// Intraword underscores cannot form emphasis, so identifiers stay readable.
expect(markdown.stdout).toContain(" source_index ");
expect(markdown.stdout).toContain(
" x__y__z \\_a_b\\_ 中_文 snake_case\\_ ",
);
const renderer = await createMarkdownRenderer(root, { linkify: false });
const html = renderer.render(markdown.stdout);
const expectedCells = [
"Key",
"Value",
literal,
literal,
...Object.keys(row),
literal,
punctuation,
JSON.stringify(row.nested),
"",
"0",
"false",
"null",
identifiers,
];
expect(renderedCells(html)).toEqual(expectedCells);
expect(html).not.toMatch(/<(?:em|strong|s|code|img)(?:\s|>)/u);
// Standalone reports share the same table owner as command output.
expect(
renderedCells(renderer.render(renderCliDocument(result, "md", false))),
).toEqual(expectedCells);
},
);
@@ -16,7 +16,7 @@ it("retains nested facts in standalone Markdown table cells and mixed arrays", (
];
const markdown = renderCliDocument(value, "md", false);
expect(markdown).toContain('{"path":"alpha.txt"}');
expect(markdown).toContain('[{"path":"sub/beta.txt"}]');
expect(markdown).toContain('\\[{"path":"sub/beta.txt"}\\]');
expect(markdown).toContain("caller\\|value");
expect(markdown).not.toContain("[object Object]");
expect(