claude-usage : constats non bloquants des revues de la pile #52/#51/#53 #54
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#54
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Contexte
Constats non bloquants des revues de la pile #52 -> #51 -> #53 (milestone
planned-2026-10-04-suivi-claude, mergee le 2026-10-04), repris pour ne pas mourir avec les PRs fermees. Analyse du 2026-10-07 surmain@719d651: les six premisses sont confirmees dans le code. Arbitrages de Max consignes plus bas.Une seule issue, une seule PR, deux commits (serveur, agent) : les deux cotes se livrent separement (redeploy Coolify pour le serveur, copie de fichiers sur le ThinkPad pour l'agent, voir
agent/README.md).Partie serveur (
index.js,claude-usage.js)S1. Nettoyage du temporaire jamais exerce
__tests__/claude-usage.test.js:739(« 500 when the snapshot cannot be written, and no temp file is left ») echoue des lemkdirSync, avant que le temporaire existe. LeunlinkSync(tmpPath)dewriteClaudeUsage()(index.js:529) n'est donc jamais execute : le retirer laisse tout vert.renameSyncechoue apres l'ecriture du temporaire :CLAUDE_USAGE_PATHpointe sur un dossier non vide. Exiger 500, et aucun.tmpdans le dossier.writeHostSnapshot()(index.js:431) :hosts.test.jsn'a aucun cas d'echec durename. Ajouter le cas symetrique (<HOSTS_DIR>/thinkpad.jsoncree comme dossier non vide).S2.
receivedAtrelu accepte parnew Date()readClaudeUsage()(index.js:557) etreadHostSnapshot()(index.js:503) acceptent tout ce quenew Date()sait lire,"1"compris.ISO_TIMESTAMP_RE/cleanTimestamp()declaude-usage.js(a exporter), sans dupliquer la regex.receivedAt: "1","2026-10-07"et"2026-10-07T12:00:00"(sans decalage) degradent enno_datacote/claude-usage, et enneverSeencote/hosts.Partie agent (
agent/claude-usage/)A1. Double comptage d'une requete a cheval sur minuit (decision : corriger)
Un jour ne se fige qu'a J+2 (
nextState(),state.js:96). Le passage qui le fige voit donc toutes les lignes de la requete, et la deduplication l'attribue au jour de sa meilleure ligne. En cas d'egalite d'output(aggregate.js:378,>strict), c'est la ligne la plus ancienne qui gagne. Si cette ligne tombe sur la veille, le jour est fige avec la requete. Aux passages suivants, le filtre « jour fige » (aggregate.js:360) ecarte cette ligne avant la deduplication. La ligne d'apres minuit reste seule et la requete est comptee une seconde fois sur le jour ouvert.state.json. Variante ecartee : memoriser les cles(message.id, requestId)dansstate.json, plus lourde pour le meme resultat.output(23:59 le jour D-1, 00:01 le jour D) et D-1 fige. La requete ne doit pas apparaitre sur D. Second cas : meilleure ligne sur D, et D-1 fige sans la requete, qui reste comptee une fois sur D.A2. Erreur de lecture : ne rien figer (decision)
readLines()(aggregate.js:254) avale une erreur de flux.listTranscripts()saute aussi en silence unreaddiren echec, donc un dossier de projet entier. La boucle principale saute unstaten echec. Le jour est ensuite fige incomplet pour 35 jours.aggregate()rend un drapeau (ex.readErrors: number), compte sur les trois chemins :readdir,statet flux. UnENOENTsur la racine elle-meme reste un scan vide, pas une erreur.push-claude-usage.js: sireadErrors > 0,nextState()ne fige aucun nouveau jour lors de ce passage (les jours deja figes etlastGoodLimitssuivent la regle actuelle). Le snapshot part quand meme.loggersans chemin ni message d'erreur, avec un compteur seulement (ex.read_errors=N), selon la regle de sortie de l'agent.openFilequi emeterrorsur un fichier,readdirqui echoue sur un sous-dossier. Aucun jour nouvellement fige, snapshot envoye. Le passage suivant, sans erreur, fige normalement.A3.
MAX_RESPONSE_BYTESverifie trop tard (decision : lecture bornee)readBody()(limits.js:177) lit tout parres.text(), puis comparetext.length(des caracteres) a 256 Kio.res.bodymorceau par morceau en comptant les octets, et annuler le flux (cancel) des que le cumul depasseMAX_RESPONSE_BYTES. Le resultat reste dans l'enumdetailexistant, sans nouveau texte libre.fetchdont le corps est un flux. Corps de 256 Kio + 1 octet en caracteres multi-octets, sous 256 Ki caracteres : refuse. Flux infini : refuse et annule sans tout lire. Corps de 256 Kio pile : accepte.A4.
chmodSyncdesaveStatenon couvertstate.js:141: le cas existant (agent-claude-usage.test.js:1281) cree un temporaire neuf, etmode: 0o600suffit alors.${file}.${process.pid}.tmpen mode 644 (reste d'un passage tue), puissaveState: le fichier final est en mode 600.Fichiers concernes
index.js:readClaudeUsage(),readHostSnapshot()claude-usage.js: export du validateur de date ISOagent/claude-usage/aggregate.js: ordre filtre/deduplication, compteur d'erreurs de lectureagent/claude-usage/push-claude-usage.js,agent/claude-usage/state.js(nextState, option pour ne pas figer)agent/claude-usage/limits.js:readBody()__tests__/claude-usage.test.js,__tests__/hosts.test.js,__tests__/agent-claude-usage.test.jsCLAUDE.md(compteurs de cas),agent/README.md(comportement en cas d'erreur de lecture)Criteres d'acceptation
npm test,bash test-curl.sh,sh scripts/boot-from-copy.shverts, CI vertewrites.test.jsreste vert (aucune nouvelle surface d'ecriture)CLAUDE.mdagent/claude-usage/sur le ThinkPad (a confirmer selonagent/README.md)Complexite
Medium : six petits volets independants, A1 et A2 touchent le coeur de l'agregation.
PR #56 merged on main (
f694677). Waiting on:agent/claude-usage/*to~/.local/share/maximus-claude-usage/on the ThinkPad, thenjournalctl -t claude-usageon the next beat.The issue is closed by hand once both are done.
Step 1 done (2026-10-07): Coolify deployment
finishedonf694677. Checked from inside the container (token never leaves it):/data/defenseursand/data/defenseurs/reportsRW=false,/data/hostsand/data/claude-usageRW=true;GET /claude-usage: content, 30 days, limitsok,stale: false(the storedreceivedAtpasses the new ISO check);GET /hosts: thinkpadonline: true, notneverSeen.Remaining: step 2, the copy of
agent/claude-usage/*on the ThinkPad.Step 2 done (2026-10-07): the five files of
agent/claude-usage/copied frommain(f694677) to~/.local/share/maximus-claude-usage/on the ThinkPad at 21:45:43, byte-identical to the repo,node --checkclean.First beat on the new code, 22:00:03:
claude-usage: ok status=204 limits=ok bytes=8741, noread_errorsline.state.jsonrewritten at 22:00:03, 31 frozen days (2026-09-05 to 2026-10-05; yesterday 2026-10-06 and today stay open, as expected).Both post-merge steps are done: closing. Reopen if a
read_errors=Nline persists over several beats injournalctl -t claude-usage, or if a day's count doubles around midnight.