fix(claude-usage): review findings of the #52/#51/#53 stack #56
No reviewers
Labels
No labels
autopilot:pending-human
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-clarification
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/vps-health-api#56
Loading…
Reference in a new issue
No description provided.
Delete branch "issue-54-claude-usage-review-findings"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #54
The six non-blocking findings from the reviews of the claude-usage stack (#52, #51, #53), with the decisions recorded on the issue.
Server (
e2a24d7)readClaudeUsage()andreadHostSnapshot()now require a full ISO timestamp with an offset for the storedreceivedAt, throughisIsoTimestamp()exported byclaude-usage.js(built on the sanitizer's own check, no duplicated regex)."1", a date alone and a time without an offset now degrade tono_data/neverSeen(6 new cases, red before the fix).renamefail after the temp file is written (the target is a non-empty directory) and require that no.tmpis left, for/claude-usageand/hosts/<id>. The existing tests failed atmkdir, so the cleanup was never exercised.Agent (
123b3c3)outputthe earliest line now wins (before, the first line read won), so the choice no longer depends on which files a beat reads. Known limit, documented in the header comment: the window start is still filtered per line.aggregate()returnsreadErrors(unlistable directory, failedstatincludingENOENT, stream or opener error; a missing root is still an empty scan). A beat withreadErrors > 0freezes no new day (nextState(..., { freeze: false })), still pushes, and logsclaude-usage: warning: read_errors=N, no day frozen this beat, with no path.readBody()reads the response chunk by chunk, counts bytes and cancels the stream past 256 KiB. It used to read everything, then compare a character count.chmodSyncof a temp file left at mode 644.Docs (
ca33f10)CLAUDE.md(case counts, gotcha on freezing),agent/README.md(new section "Unreadable transcripts").Verification
npm test: 634/634 (611 + 23).bash test-curl.sh: 45/45.sh scripts/boot-from-copy.sh: PASS.unlinkSync(tmpPath)inwriteHostSnapshotunlinkSync(tmpPath)inwriteClaudeUsagereadLinesresolves true on a stream errorENOENTcountedread_errorslog linenextStateignoresfreezechmodSyncreader.cancel()ENOENTignored on every directory, not only the root (from the review, fixed inf694677)S2 was proven by test-first: 6 red before the fix.
Post-merge checklist (hence
Refs, not a closing keyword)GET /claude-usageandGET /hostsstill return content.agent/claude-usage/*to~/.local/share/maximus-claude-usage/on the ThinkPad (thecpfromagent/README.md), then checkjournalctl -t claude-usageon the next beat.🤖 Generated with Claude Code
Revue adversariale — PR #56
Verdict : APPROVE
Les six findings de #54 sont traites comme annonce, chacun avec un test qui rougit sous la mutation du defaut. Suite rejouee et mutations du relecteur executees sur une copie hors depot de la tete
ca33f10.Ce qui a ete execute
npx vitest runsur la tete : 633/633. CI de la tete (CI / verification (pull_request)) : success.entry.ts > prev.ts, la ligne la plus tardive gagne)statenENOENTnon compte (catch (err) { if (err.code !== "ENOENT") onError(); })readBody()en>=au lieu de>readHostSnapshot()revenu anew Date(receivedAt)stream.on("error", finish)au lieu defailENOENTignore sur tout dossier, pas seulement la racinerl.on("error", finish)au lieu defailfailedavant)Points verifies
isIsoTimestamp()reutilisecleanTimestamp()(regex +Date.parse), pas de regex dupliquee ;"1", date seule et heure sans decalage degradent enno_data/neverSeen.renameSync(cible = dossier non vide, echec meme en root en CI) et exigent l'absence de.tmp.tsest un nombre (Date.parse), donc la comparaison du tie-break est numerique et non lexicographique. Le filtre des jours figes apres deduplication est correct.nextState(..., { freeze: false })conserve les jours deja figes et ne fige rien de neuf. Le cas du fichier durablement illisible est documente dansagent/README.md(rescan complet a chaque battement, ligne de log seule alerte). La ligne de log ne porte qu'un compte, aucun chemin.reader.cancel()au-dela de 256 Kio ; le timeout dufetchcouvre aussi la lecture du corps.closes(ref_action: none), coherent avec leRefs #54et la fermeture manuelle annoncee.Suggestions (non bloquantes)
agent/claude-usage/aggregate.js(listTranscripts) : la distinction «ENOENTsur la racine = scan vide, sur un sous-dossier = erreur » n'est pas epinglee. Ma mutation qui ignoreENOENTsur tout dossier laisse les 633 cas verts. Un cas ou un sous-dossier disparait entre lereaddirdu parent et le sien (opener ou double dereaddir) fermerait le trou. Le code est correct : epingle manquante, pas defaut.mtimesuppose que toutes les lignes d'une requete vivent dans un fichier ecrit au plus tot le jour de sa derniere ligne. Une requete dupliquee dans un transcript de sous-agent dont le dernier ecrit precede le seuil pourrait encore etre vue sans sa ligne figee. Cas tres improbable ; une phrase dans l'en-tete suffirait si on veut l'acter.output. En pratique c'est la ligne la plus ancienne du meme fichier, donc le meme choix ; rien a faire.Actions faites
scratchpad/pr56) supprimee en fin de revue ; arbre du depot inchange (?? reports/seul, preexistant).status:review->status:approved.Merged fast-forward at
f694677, CI green on that head.f694677was added after the APPROVE and has not been reviewed: it applies suggestions 1 and 2 of the review (a test pinning ENOENT as a read error below the root, which turns the reviewer's zero-red mutation red; one sentence in theaggregate.jsheader on the subagent-copy limit). 634/634,test-curl.sh45/45,boot-from-copy.shPASS.Suggestion 3 needs no action. #54 stays open until the post-merge checklist is done.