Summary
Multiscan receipt recovery handles only one corruption shape: a final line without a terminating newline.
Relevant code: readReceipts lines 312-335.
The complete ledger is read into memory and split. Every newline-terminated line is passed directly to JSON.parse, cast to MultiscanReceipt, and immediately accessed through receipt.id.toLowerCase().
Consequently, any of the following throws on every future resume:
- malformed JSON in the middle of the ledger;
- a malformed final line that does end with a newline;
- valid JSON that is not an object;
- a receipt with a missing or non-string
id;
- structurally invalid attempt, status, or path fields.
The ledger also has no total-size or per-line limit.
Why this matters
A crash, disk fault, partial external write, or manual damage to one durable record can wedge the entire campaign until the user edits internal state. The current recovery behavior may give users the impression that receipt-ledger interruption is generally recoverable, although only a torn final line is repaired.
Expected behavior
Receipt loading should either recover safely from invalid durable records or stop with an actionable corruption diagnostic and a supported recovery path. It should never fail with an incidental JSON/type exception indefinitely.
Suggested direction
Stream the ledger with a maximum line and total size, schema-validate every receipt, and distinguish a recoverable tail from corruption of committed history. Invalid committed lines could be moved to a quarantine file before resuming, with the original ledger retained for diagnosis.
Add tests for malformed newline-terminated JSON, non-object JSON, missing IDs, invalid attempts/statuses, and oversized lines.
Found by static audit of upstream main at 9c7634b.
Summary
Multiscan receipt recovery handles only one corruption shape: a final line without a terminating newline.
Relevant code:
readReceiptslines 312-335.The complete ledger is read into memory and split. Every newline-terminated line is passed directly to
JSON.parse, cast toMultiscanReceipt, and immediately accessed throughreceipt.id.toLowerCase().Consequently, any of the following throws on every future resume:
id;The ledger also has no total-size or per-line limit.
Why this matters
A crash, disk fault, partial external write, or manual damage to one durable record can wedge the entire campaign until the user edits internal state. The current recovery behavior may give users the impression that receipt-ledger interruption is generally recoverable, although only a torn final line is repaired.
Expected behavior
Receipt loading should either recover safely from invalid durable records or stop with an actionable corruption diagnostic and a supported recovery path. It should never fail with an incidental JSON/type exception indefinitely.
Suggested direction
Stream the ledger with a maximum line and total size, schema-validate every receipt, and distinguish a recoverable tail from corruption of committed history. Invalid committed lines could be moved to a quarantine file before resuming, with the original ledger retained for diagnosis.
Add tests for malformed newline-terminated JSON, non-object JSON, missing IDs, invalid attempts/statuses, and oversized lines.
Found by static audit of upstream
mainat9c7634b.