One physical line that carried a parsed record, in file order. Archive needs
the line numbers and per-ID groupings the fold already walks past; carrying
them out of the fold is what keeps plan_archive to a single parse.
The one choke point every read path calls immediately after read_bytes,
under the lock it already holds and before the fold, any tear-heal byte, any
append, and any copy-and-swap.
The same fold, additionally carrying the (line, id, ts) tuple of every
physical line that parsed into a record. Only archive needs them, and the
tuples cost one owned ID per physical line, so every other caller keeps the
cheaper fold_bytes. Collecting them changes no fold verdict: the tuples are
written from the scanner’s own output and nothing reads them back.
Inspect each scanned line’s raw JSON, before and independently of the
scan’s classification (r50): a v1 cut is a record missing required fields,
which the scan calls malformed, so keying on classification would exempt the
exact file this probe exists to catch. A log holding no line with a known raw
kind passes.
Resolve a symlinked log path to its target before a copy-and-swap, so the
backup, sidecar, and atomic replacement all act on the real file and the
link survives. Only final-component links are chased; parent components
keep their spelling so envelope paths stay stable for regular files.
The sources[] of every promotion in a folded log, keyed by promotion ID.
Rules (5) and (6) below join against it, and doctor builds the same map
while it scans so both answer a hand-edited log identically.