fix(claude-usage): review findings of the #52/#51/#53 stack #56

Merged
maximus merged 4 commits from issue-54-claude-usage-review-findings into main 2026-10-08 00:54:24 +00:00

4 commits

Author SHA1 Message Date
le king fu
f694677a1a test(agent): pin ENOENT as a read error below the root, note the subagent-copy limit
All checks were successful
CI / verification (pull_request) Successful in 29s
CI / verification (push) Successful in 30s
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>
2026-10-07 20:53:27 -04:00
le king fu
ca33f10b85 docs: document the read-error freeze rule and count the #54 cases
All checks were successful
CI / verification (pull_request) Successful in 31s
Refs #54

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:30:20 -04:00
le king fu
123b3c3c8c fix(agent): no double count across midnight, no freeze on a read error, bounded limits read
- 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>
2026-10-07 20:29:29 -04:00
le king fu
e2a24d768b fix(server): require a full ISO receivedAt on read, cover the temp-file cleanup
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>
2026-10-07 20:25:26 -04:00