xff v0.8.0

Structured output

A format selects the representation of built-in records. It does not rewrite explicit printf
or grep templates, or reinterpret child-process stdout. Authored output remains responsible for
its own syntax, including when combined with JSONL summaries.

Producer contracts

Producer Plain JSONL Other formats
Default listing Path records Objects with a path NUL, CSV, TSV, aligned, Markdown, tree
Comparison status Tab-separated status/path Comparison objects Rejected when per-path statuses are selected
Comparison patch Unified patch Rejected when patches are selected Rejected when patches are selected
Built-in grep Match/context lines or counts Grep objects Rejected before actions run
Summary Tables Grouping and metric objects Aligned, Markdown, and CSV/TSV summary exports
Histogram Bars Bucket/value objects Aligned bars and Markdown tables; CSV/TSV/NUL/tree rejected
Explicit printf/grep template Authored output Authored output Existing action/format validation applies
Child process Child stdout Child stdout Existing action/format validation applies

Comparison restrictions concern selected per-path output. With --compare-select=none, the
requested reductions' format rules apply. --compare=summary --summary=ext --format=md can
therefore render tables without per-path records.

CSV and TSV use the listing schema for ordinary listings. With summaries, they use a single
summary export schema across requests, with explicit grouping, scope, and
aggregate-row identity. Summary exports reject action output and per-path comparison records;
histograms reject CSV and TSV.

To consume a whole command as JSONL, use producers with JSONL representations and ensure any
authored output is valid JSONL too. Escaped newlines inside strings do not split records.

Listing records contain path; comparison, grep, and summary records carry their documented
record tag. Histogram rows contain histogram, bucket, and value; they retain that existing
schema. Summary rows retain request, grouping/scope/root identity, and is_total.
A built-in grep action can emit records followed by summary and histogram rows in one JSONL
stream. An ordinary summary suppresses the default listing rather than duplicating it.

String and byte values

Text values remain JSON strings when their original bytes are valid UTF-8. Otherwise the
value is a lossless object such as {"encoding":"base64","data":"eP8="} (the bytes x
and 0xff). Base64 uses the standard alphabet with padding and encodes the entire original
value. Consumers should handle this union for paths, roots, group labels, templates, patterns,
and grep text. No replacement characters or invalid UTF-8 are silently inserted.

This rule applies to built-in JSONL producers. Explicit templates and child stdout retain
their authored-output contract. is_total remains the authority for summary aggregates,
regardless of whether a group value is text or bytes.

Comparison status

{
  "record": "comparison",
  "status": "different",
  "path": "src/main.cc",
  "left_root": "LEFT",
  "right_root": "RIGHT"
}

status is left-only, right-only, different, or identical. path is relative to the
root operands; the root entry uses .. Records retain relative-path order. Selection filters
per-path records, not summary populations.

Grep

{
  "record": "grep",
  "kind": "match",
  "path": "src/main.cc",
  "root": ".",
  "pattern": "TODO",
  "line": 12,
  "group": 0,
  "text": "// TODO: document this"
}

kind is match or context. line is one-based. group is zero-based within the file and
grep evaluation, incrementing at gaps between emitted lines. JSONL never emits raw -- group
separators. text excludes the line terminator. Quotes, backslashes, and controls are escaped.

With --count, each file with matching lines produces:

{
  "record": "grep",
  "kind": "count",
  "path": "src/main.cc",
  "root": ".",
  "pattern": "TODO",
  "count": 2
}

Count mode ignores context and supersedes an attached template. Files without matching lines
produce no count record. Repeating a grep action repeats its output, retaining its pattern.
An explicit -grep:FORMAT without --count retains authored output even with --format=jsonl.

Validation

Unsupported built-in comparison and grep formats fail before traversal or expression actions,
including actions appearing earlier in the expression. Validation does not wait until a match
reaches the output action, so a format error cannot leave earlier deletions or commands executed.