fix(formatter): emit verbatim content in default vault.read (PR #205 blocking review)

formatFileRead truncated the string branch to a 50-line preview; since raw
defaults false, the DEFAULT vault.read was that truncated view — an agent
reading any >50-line file could not derive a byte-matching edit.window
oldText without raw:true, the exact #133 friction ADR-203 retires. The data
layer was already byte-faithful; the presentation default undercut it.

- string branch: emit full content verbatim, no 50-line slice, no wrapping
  code fence (body may contain fences — a wrapper would corrupt round-trip)
- add non-text/binary passthrough guard (closes a pre-existing latent
  formatter-error path; reviewer non-blocking note)
- test now asserts the FORMATTED (raw:false) output is byte-exact, not just
  not.toThrow

make check green: 0 errors, baseline 5 warnings, 244/244.
This commit is contained in:
Aaron Bockelie 2026-05-19 00:38:29 -05:00
parent f3dac1847e
commit 1f83465d3b
2 changed files with 42 additions and 21 deletions

View file

@ -238,6 +238,17 @@ export function formatFileRead(response: FileReadResponse): string {
// Content - handle both string and fragment array formats
lines.push('');
if (content != null && typeof content !== 'string' && !Array.isArray(content)) {
// Unrecognized/binary structured passthrough — render a safe note
// instead of falling through to the _Formatter error_ fallback.
lines.push(header(2, 'Content'));
lines.push('');
lines.push('_(non-text content; use `raw: true` for the structured payload)_');
lines.push(divider());
lines.push(summaryFooter());
return joinLines(lines);
}
if (Array.isArray(content)) {
// Fragment-based response
const fragments = content;
@ -260,18 +271,17 @@ export function formatFileRead(response: FileReadResponse): string {
lines.push(`... and ${fragments.length - 5} more fragments`);
}
} else {
// Simple string content
// Verbatim string content (ADR-203: content reads are faithful by
// default — the formatted default path must NOT truncate, or an agent
// cannot derive a byte-matching edit.window oldText without raw:true,
// which is the exact #133 friction this ADR retires). The data layer
// already bounds size to READ_PAGE_CHARS (or it's an explicit
// returnFullFile override), so emitting the full block is safe. No
// wrapping code fence: the body may itself contain ``` fences, and a
// wrapper would corrupt round-trip fidelity.
lines.push(header(2, 'Content'));
lines.push('');
const contentLines = content.split('\n');
const previewLines = contentLines.slice(0, 50);
lines.push('```markdown');
lines.push(previewLines.join('\n'));
if (contentLines.length > 50) {
lines.push(`\n... (${contentLines.length - 50} more lines)`);
}
lines.push('```');
lines.push(content);
}
if (pagination && pagination.paginated) {

View file

@ -102,22 +102,33 @@ describe('vault.read fidelity & pagination (ADR-203)', () => {
expect(r.fragmentMetadata).toBeDefined();
});
test('formatter renders all shapes without the _Formatter error_ crash', () => {
// whole-file string shape
expect(() => formatFileRead({
path: 's.md', content: TRICKY,
metadata: { totalLines: 14, bytes: TRICKY.length },
pagination: { paginated: false, page: 1, pageLineStart: 1, pageLineEnd: 14, totalLines: 14, bytes: TRICKY.length, hasMore: false, nextPage: null },
} as any)).not.toThrow();
// paginated shape
test('formatted (non-raw) default output is byte-faithful — no truncation, no fence corruption', () => {
// The blocking #133 case: an agent reading the DEFAULT (raw:false)
// formatted output must be able to lift a byte-exact edit.window oldText.
const out = formatFileRead({
path: 's.md', content: TRICKY,
metadata: { totalLines: TRICKY.split('\n').length, bytes: TRICKY.length },
pagination: { paginated: false, page: 1, pageLineStart: 1, pageLineEnd: TRICKY.split('\n').length, totalLines: TRICKY.split('\n').length, bytes: TRICKY.length, hasMore: false, nextPage: null },
} as any);
// Verbatim body present in full, including the inner ```python fence,
// the indented code line, and the trailing-space + tab line.
expect(out).toContain(TRICKY);
expect(out).toContain(' return x*2 # indented');
expect(out).toContain('\ttab-line ');
expect(out).not.toMatch(/more lines\)/); // no truncation marker
});
test('formatter renders paginated + edge shapes without the _Formatter error_ crash', () => {
const out2 = formatFileRead({
path: 'big.md', content: 'line 1 ...\nline 2 ...',
metadata: { totalLines: 4000, bytes: 90000 },
pagination: { paginated: true, page: 1, pageLineStart: 1, pageLineEnd: 1800, totalLines: 4000, bytes: 90000, hasMore: true, nextPage: "vault.read(path='big.md', page=2)" },
warning: 'Large file …',
} as any);
expect(out).toContain('Pagination');
expect(out).toContain('page=2');
expect(out).not.toContain('Formatter error');
expect(out2).toContain('Pagination');
expect(out2).toContain('page=2');
expect(out2).not.toContain('Formatter error');
// non-text passthrough must not crash
expect(() => formatFileRead({ path: 'x.bin', content: { binary: true } } as any)).not.toThrow();
});
});