Review of #56: making ENOENT an empty scan on every directory, not only the
root, left the suite green. A vanished subdirectory is now a pinned read
error. The header also names the other known limit of the midnight fix:
the mtime threshold may skip a subagent transcript holding the only copy
of a straddling request's earlier line.
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- aggregate: frozen days are dropped after deduplication, not before. A
request straddling midnight whose best line sat on a day frozen since
was counted again on the open day. On equal output the earliest line now
wins (it was the first one read), so the choice no longer depends on
which files a beat reads.
- aggregate: unreadable directories, failed stats (ENOENT included) and
stream errors are counted in `readErrors`; a missing root is still an
empty scan. The beat then freezes no new day (nextState freeze: false),
still pushes, and logs `read_errors=N` with no path.
- limits: the response is read chunk by chunk and counted in bytes; past
256 KiB the stream is cancelled instead of being read in full and
measured in characters.
- state: a test now covers the chmodSync of a leftover temp file.
Each fix was checked by mutation (13 mutations, 13 red).
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
readClaudeUsage() and readHostSnapshot() validated the stored receivedAt
with new Date(), which accepts "1" or a bare date. Both now go through
isIsoTimestamp(), exported by claude-usage.js and built on the sanitizer's
own check, so no regex is duplicated.
The existing 500 tests failed at mkdir, before the temp file existed, so
removing the unlinkSync() cleanup of either writer left the suite green. Two
new cases make the rename fail after the temp write (the target is a
non-empty directory) and require that no temp file is left.
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>