feat(agent): claude-usage agent — plan limits, local cache, push (#50) #53

Merged
maximus merged 3 commits from issue-50-claude-usage-agent into main 2026-10-04 19:29:53 +00:00
Owner

Resume

Agent claude-usage du ThinkPad (issue #50), lance toutes les 15 min par le crontab : lit les limites du plan Claude, maintient le cache des jours figes, assemble le snapshot schemaVersion: 1 et le pousse vers POST /claude-usage.

  • agent/claude-usage/limits.js : credentials lus en lecture seule (fs.readFileSync), jamais rafraichis ni ecrits ; URL Anthropic en constante (https://api.anthropic.com/api/oauth/usage, aucune variable d'env), redirect: "error", timeout 10 s, un appel, aucun retry. limits[] -> items (resets_at -> resetsAt, scope.model.display_name -> model), repli five_hour/seven_day, sinon unavailable/bad_response. detail sort uniquement de l'enum (no_credentials, parse_error, no_version, timeout, network, bad_response, http_<code>).
  • agent/claude-usage/state.js : ~/.local/state/maximus-claude-usage/state.json, mode 600, ecriture atomique (tmp suffixe pid + rename). Fige tout jour strictement anterieur a hier (Toronto), vide compris ; elague au-dela de 35 jours ; lastGoodLimits mis a jour sur ok seulement et servi avec son fetchedAt sinon ; fichier illisible -> etat vide (scan complet).
  • agent/claude-usage/push-claude-usage.js : agrege, lit les limites, ecrit le cache avant le push, mesure le corps (> 65 536 octets -> payload_too_large, code 2, rien d'envoye), pousse une fois (https: exige, sinon code 1). --dry-run imprime le snapshot sans envoyer.
  • agent/claude-usage/run-claude-usage.sh (executable) : flock -n sur ~/.local/state/maximus-claude-usage/run.lock (sortie 0 si un passage tourne deja), source ~/.config/maximus-claude-usage.env, meme style que agent/run-push.sh, sans set -x.
  • agent/README.md : section dediee (copie figee, fichier env 600, ligne crontab, raison de la presence dans ce depot). CLAUDE.md : compte de tests et une ligne de Gotchas.
  • Fixture __tests__/fixtures/claude-usage/oauth-usage.json : reponse de l'endpoint anonymisee.

Aucun fichier serveur modifie (index.js, claude-usage.js, Dockerfile intacts).

Decisions (autopilot)

  • percent arrondi puis borne a 0-100 (MEDIUM : un utilization au-dela de 100 reste affiche « sature » plutot que d'etre jete).
  • --dry-run n'ecrit pas state.json (MEDIUM : apercu sans effet de bord ; il appelle encore Anthropic sauf CLAUDE_USAGE_LIMITS=off, documente).
  • Credentials sans accessToken -> no_credentials ; erreur de lecture autre qu'ENOENT -> parse_error ; expiresAt absent -> appel tente (le serveur tranche par 401).
  • lastGoodLimits servi pour tout statut non ok, disabled compris (texte de l'issue).
  • claudeCodeVersion omis (pas null) quand aucun transcript n'en porte : le sanitizer jetterait null.
  • Jeton d'ingestion valide en ASCII imprimable avant usage (code 1) : l'erreur d'en-tete invalide d'undici cite la valeur. Codes d'erreur imprimes limites a E.../UND_ERR_... en majuscules ou a un nom de classe *Error, sinon UNKNOWN.
  • Echec d'ecriture du cache : avertissement (code seul), push maintenu.

Tests

  • __tests__/agent-claude-usage.test.js : 106 cas (38 de #49 + 68 nouveaux). Couvre tous les criteres d'acceptation, dont : fetch (double) jamais appele sur jeton expire / off / credentials tronques / absents ; 429 -> http_error/http_429 avec les dernieres limites ; corps hostiles -> detail dans l'enum ; bearer OAuth vers la seule URL epinglee ; espions sur toutes les ecritures de node:fs ; aucun fragment de 5 caracteres des deux jetons (snapshot, etat, sortie, vrai processus) ; deux passages autour d'un jour vide ; state.json corrompu ; snapshot inchange par sanitizeClaudeUsage ; http: -> 1 ; > 64 Kio -> 2 ; wrapper lance deux fois en parallele (faux node, HOME temporaire).
  • Verifie par mutation : sans gel des jours vides (3 rouges), avec err.message dans la sortie (3 rouges), sans flock (2 rouges).
  • npm test : 606 cas verts. bash test-curl.sh : 45/45. sh scripts/boot-from-copy.sh : PASS.
  • node agent/claude-usage/push-claude-usage.js --dry-run sur fixtures (HOME temporaire, limites off) : 30 jours, snapshot identique apres sanitizeClaudeUsage. Aucun appel reel a api.anthropic.com ni a health.lacompagniemaximus.com.

Actions requises de Max

Apres merge de la pile et deploiement du serveur (#48 : CLAUDE_USAGE_INGEST_TOKEN cote Coolify, montage /data/claude-usage) :

  1. Sur le ThinkPad, copie figee (depuis le depot a jour sur main) :
    mkdir -p ~/.local/share/maximus-claude-usage
    cp agent/claude-usage/aggregate.js agent/claude-usage/limits.js agent/claude-usage/state.js agent/claude-usage/push-claude-usage.js agent/claude-usage/run-claude-usage.sh ~/.local/share/maximus-claude-usage/
    chmod +x ~/.local/share/maximus-claude-usage/run-claude-usage.sh
    
  2. Fichier ~/.config/maximus-claude-usage.env (ecrit avec printf), chmod 600 : CLAUDE_USAGE_API_URL=https://health.lacompagniemaximus.com, CLAUDE_USAGE_INGEST_TOKEN=<token>, NODE_BIN si besoin.
  3. Un passage a la main (~/.local/share/maximus-claude-usage/run-claude-usage.sh), puis la ligne crontab :
    */15 * * * * $HOME/.local/share/maximus-claude-usage/run-claude-usage.sh 2>&1 | logger -t claude-usage
    

Pile

#52 (#48, routes serveur) -> #51 (#49, agregation) -> cette PR (#50). Base : issue-49-claude-usage-aggregate. A merger dans cet ordre.

Resolves #50

Generated autonomously by /autopilot run of 2026-10-04

🤖 Generated with Claude Code

## Resume Agent `claude-usage` du ThinkPad (issue #50), lance toutes les 15 min par le crontab : lit les limites du plan Claude, maintient le cache des jours figes, assemble le snapshot `schemaVersion: 1` et le pousse vers `POST /claude-usage`. - `agent/claude-usage/limits.js` : credentials lus en lecture seule (`fs.readFileSync`), jamais rafraichis ni ecrits ; URL Anthropic en constante (`https://api.anthropic.com/api/oauth/usage`, aucune variable d'env), `redirect: "error"`, timeout 10 s, un appel, aucun retry. `limits[]` -> items (`resets_at` -> `resetsAt`, `scope.model.display_name` -> `model`), repli `five_hour`/`seven_day`, sinon `unavailable`/`bad_response`. `detail` sort uniquement de l'enum (`no_credentials`, `parse_error`, `no_version`, `timeout`, `network`, `bad_response`, `http_<code>`). - `agent/claude-usage/state.js` : `~/.local/state/maximus-claude-usage/state.json`, mode 600, ecriture atomique (tmp suffixe pid + rename). Fige tout jour strictement anterieur a hier (Toronto), vide compris ; elague au-dela de 35 jours ; `lastGoodLimits` mis a jour sur `ok` seulement et servi avec son `fetchedAt` sinon ; fichier illisible -> etat vide (scan complet). - `agent/claude-usage/push-claude-usage.js` : agrege, lit les limites, ecrit le cache **avant** le push, mesure le corps (> 65 536 octets -> `payload_too_large`, code 2, rien d'envoye), pousse une fois (`https:` exige, sinon code 1). `--dry-run` imprime le snapshot sans envoyer. - `agent/claude-usage/run-claude-usage.sh` (executable) : `flock -n` sur `~/.local/state/maximus-claude-usage/run.lock` (sortie 0 si un passage tourne deja), source `~/.config/maximus-claude-usage.env`, meme style que `agent/run-push.sh`, sans `set -x`. - `agent/README.md` : section dediee (copie figee, fichier env 600, ligne crontab, raison de la presence dans ce depot). `CLAUDE.md` : compte de tests et une ligne de Gotchas. - Fixture `__tests__/fixtures/claude-usage/oauth-usage.json` : reponse de l'endpoint anonymisee. Aucun fichier serveur modifie (`index.js`, `claude-usage.js`, Dockerfile intacts). ## Decisions (autopilot) - `percent` arrondi puis borne a 0-100 (MEDIUM : un `utilization` au-dela de 100 reste affiche « sature » plutot que d'etre jete). - `--dry-run` n'ecrit pas `state.json` (MEDIUM : apercu sans effet de bord ; il appelle encore Anthropic sauf `CLAUDE_USAGE_LIMITS=off`, documente). - Credentials sans `accessToken` -> `no_credentials` ; erreur de lecture autre qu'ENOENT -> `parse_error` ; `expiresAt` absent -> appel tente (le serveur tranche par 401). - `lastGoodLimits` servi pour tout statut non `ok`, `disabled` compris (texte de l'issue). - `claudeCodeVersion` omis (pas `null`) quand aucun transcript n'en porte : le sanitizer jetterait `null`. - Jeton d'ingestion valide en ASCII imprimable avant usage (code 1) : l'erreur d'en-tete invalide d'undici cite la valeur. Codes d'erreur imprimes limites a `E...`/`UND_ERR_...` en majuscules ou a un nom de classe `*Error`, sinon `UNKNOWN`. - Echec d'ecriture du cache : avertissement (code seul), push maintenu. ## Tests - `__tests__/agent-claude-usage.test.js` : 106 cas (38 de #49 + 68 nouveaux). Couvre tous les criteres d'acceptation, dont : `fetch` (double) jamais appele sur jeton expire / `off` / credentials tronques / absents ; 429 -> `http_error`/`http_429` avec les dernieres limites ; corps hostiles -> `detail` dans l'enum ; bearer OAuth vers la seule URL epinglee ; espions sur toutes les ecritures de `node:fs` ; aucun fragment de 5 caracteres des deux jetons (snapshot, etat, sortie, vrai processus) ; deux passages autour d'un jour vide ; `state.json` corrompu ; snapshot inchange par `sanitizeClaudeUsage` ; `http:` -> 1 ; > 64 Kio -> 2 ; wrapper lance deux fois en parallele (faux `node`, HOME temporaire). - Verifie par mutation : sans gel des jours vides (3 rouges), avec `err.message` dans la sortie (3 rouges), sans `flock` (2 rouges). - `npm test` : 606 cas verts. `bash test-curl.sh` : 45/45. `sh scripts/boot-from-copy.sh` : PASS. - `node agent/claude-usage/push-claude-usage.js --dry-run` sur fixtures (HOME temporaire, limites `off`) : 30 jours, snapshot identique apres `sanitizeClaudeUsage`. Aucun appel reel a api.anthropic.com ni a health.lacompagniemaximus.com. ## Actions requises de Max Apres merge de la pile et deploiement du serveur (#48 : `CLAUDE_USAGE_INGEST_TOKEN` cote Coolify, montage `/data/claude-usage`) : 1. Sur le ThinkPad, copie figee (depuis le depot a jour sur `main`) : ``` mkdir -p ~/.local/share/maximus-claude-usage cp agent/claude-usage/aggregate.js agent/claude-usage/limits.js agent/claude-usage/state.js agent/claude-usage/push-claude-usage.js agent/claude-usage/run-claude-usage.sh ~/.local/share/maximus-claude-usage/ chmod +x ~/.local/share/maximus-claude-usage/run-claude-usage.sh ``` 2. Fichier `~/.config/maximus-claude-usage.env` (ecrit avec `printf`), `chmod 600` : `CLAUDE_USAGE_API_URL=https://health.lacompagniemaximus.com`, `CLAUDE_USAGE_INGEST_TOKEN=<token>`, `NODE_BIN` si besoin. 3. Un passage a la main (`~/.local/share/maximus-claude-usage/run-claude-usage.sh`), puis la ligne crontab : ``` */15 * * * * $HOME/.local/share/maximus-claude-usage/run-claude-usage.sh 2>&1 | logger -t claude-usage ``` ## Pile #52 (#48, routes serveur) -> #51 (#49, agregation) -> **cette PR** (#50). Base : `issue-49-claude-usage-aggregate`. A merger dans cet ordre. Resolves #50 Generated autonomously by /autopilot run of 2026-10-04 🤖 Generated with [Claude Code](https://claude.com/claude-code)
maximus added 2 commits 2026-10-04 13:29:16 +00:00
limits.js reads the Claude Code OAuth credentials read-only and calls the
pinned Anthropic usage endpoint once (redirect refused, 10 s timeout, no
retry, never a refresh); detail codes come from a fixed enum only.
state.js freezes every Toronto day before yesterday, empty ones included,
prunes past 35 days, keeps the last good limits, and writes atomically in
mode 600. push-claude-usage.js writes the cache before pushing, refuses a
non-https URL (exit 1) and a body over 64 KiB (exit 2, nothing sent), and
prints no token. run-claude-usage.sh takes a non-blocking flock so a beat
that overlaps the previous one exits 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs(agent): document the claude-usage agent install and its guarantees
All checks were successful
CI / verification (pull_request) Successful in 27s
9279b15b7f
Resolves #50

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
maximus added the
autopilot:pending-human
label 2026-10-04 13:29:20 +00:00
Author
Owner

Auto-revue (autopilot) — git diff origin/issue-49-claude-usage-aggregate...HEAD

Verdict : pret pour revue humaine, aucun point bloquant trouve. Critere par critere :

Critere Etat Preuve
Reponse anonymisee -> items (dont weekly_scoped / Fable) ; repli five_hour/seven_day ; tout null -> unavailable OK limits — the endpoint's answer
Jeton expire, off, credentials tronques, absents : fetch jamais appele OK limits — cases that never call the endpoint (8 cas, plus « off ne lit meme pas les credentials »)
429 -> http_error/http_429, items = dernieres limites valides avec leur fetchedAt OK bout en bout sur deux passages de main()
Corps hostile -> detail dans l'enum OK 8 reponses hostiles (texte, en-tetes renvoyes, 302, erreur citant le jeton)
Bearer OAuth vers https://api.anthropic.com seulement ; aucune env ne change l'URL OK double fetch + epingle sur le source de limits.js
Credentials jamais ouverts en ecriture OK espions sur toutes les ecritures de node:fs (Sync, callback, promises), contenu et mtime inchanges
Aucun jeton dans le snapshot ni stdout/stderr OK fragments de 5 caracteres, dont un vrai processus
Deux passages autour d'un jour vide OK jour vide fige ; le second passage n'ouvre que le fichier recent
state.json corrompu ; elagage 35 j OK
Snapshot sur fixtures inchange par sanitizeClaudeUsage OK toEqual strict, sur 4 etats de limites
http: -> 1 ; > 64 Kio -> 2 sans envoi OK snapshot reel de ~70 Ko construit depuis des transcripts
run-claude-usage.sh en parallele : second sort 0 sans travailler OK vrai script, faux node, HOME temporaire ; verrou relache ensuite

Mutations verifiees (le test rougit) : jours vides non figes, err.message imprime, flock retire.

Points d'attention pour Max (non bloquants) :

  • percent est arrondi et borne a 100 : un depassement s'affiche 100 %.
  • --dry-run appelle encore Anthropic si CLAUDE_USAGE_LIMITS n'est pas a off (documente dans le README).
  • CLAUDE.md compte aussi les 38 cas de #49, que sa PR n'avait pas documentes : conflit trivial possible si #51 touche cette ligne avant le merge.

Generated autonomously by /autopilot run of 2026-10-04

## Auto-revue (autopilot) — `git diff origin/issue-49-claude-usage-aggregate...HEAD` Verdict : **pret pour revue humaine**, aucun point bloquant trouve. Critere par critere : | Critere | Etat | Preuve | |---|---|---| | Reponse anonymisee -> items (dont `weekly_scoped` / `Fable`) ; repli `five_hour`/`seven_day` ; tout `null` -> `unavailable` | OK | `limits — the endpoint's answer` | | Jeton expire, `off`, credentials tronques, absents : `fetch` jamais appele | OK | `limits — cases that never call the endpoint` (8 cas, plus « `off` ne lit meme pas les credentials ») | | 429 -> `http_error`/`http_429`, items = dernieres limites valides avec leur `fetchedAt` | OK | bout en bout sur deux passages de `main()` | | Corps hostile -> `detail` dans l'enum | OK | 8 reponses hostiles (texte, en-tetes renvoyes, 302, erreur citant le jeton) | | Bearer OAuth vers `https://api.anthropic.com` seulement ; aucune env ne change l'URL | OK | double `fetch` + epingle sur le source de `limits.js` | | Credentials jamais ouverts en ecriture | OK | espions sur toutes les ecritures de `node:fs` (Sync, callback, promises), contenu et mtime inchanges | | Aucun jeton dans le snapshot ni stdout/stderr | OK | fragments de 5 caracteres, dont un vrai processus | | Deux passages autour d'un jour vide | OK | jour vide fige ; le second passage n'ouvre que le fichier recent | | `state.json` corrompu ; elagage 35 j | OK | | | Snapshot sur fixtures inchange par `sanitizeClaudeUsage` | OK | `toEqual` strict, sur 4 etats de limites | | `http:` -> 1 ; > 64 Kio -> 2 sans envoi | OK | snapshot reel de ~70 Ko construit depuis des transcripts | | `run-claude-usage.sh` en parallele : second sort 0 sans travailler | OK | vrai script, faux `node`, HOME temporaire ; verrou relache ensuite | Mutations verifiees (le test rougit) : jours vides non figes, `err.message` imprime, `flock` retire. Points d'attention pour Max (non bloquants) : - `percent` est arrondi **et borne** a 100 : un depassement s'affiche 100 %. - `--dry-run` appelle encore Anthropic si `CLAUDE_USAGE_LIMITS` n'est pas a `off` (documente dans le README). - `CLAUDE.md` compte aussi les 38 cas de #49, que sa PR n'avait pas documentes : conflit trivial possible si #51 touche cette ligne avant le merge. Generated autonomously by /autopilot run of 2026-10-04
Author
Owner

Revue adversariale — PR #53 (issue #50)

Verdict : APPROVE

Resume : l'agent tient ses invariants (credentials en lecture seule, bearer OAuth vers la seule URL constante avec redirect: "error", detail en enum, cache ecrit avant le push, aucune donnee de token dans la sortie) et chaque propriete testee resiste a une mutation du relecteur. Contrat aligne avec sanitizeClaudeUsage (meme regex ISO, percent entier borne, 12 items max).

Verifications executees

  • Tete 9279b15 extraite hors depot (git archive) : npx vitest run -> 606/606 verts. CI de la tete : success.
  • Mutations du relecteur, sur la copie, __tests__/agent-claude-usage.test.js seul :
Mutation Rouges
redirect: "error" retire de l'appel Anthropic 1
redirect: "error" retire du push 1
controle expiresAt <= now neutralise 2
gel de hier (day >= today) 3
pas de repli sur lastGoodLimits 2
lastGoodLimits mis a jour quel que soit le statut 2
saveState retire 6
controle https: neutralise 1
TOKEN_RE neutralise 1
export ... CLAUDE_USAGE_LIMITS retire du wrapper 1
percent non borne 1
plafond MAX_ITEMS retire 1
fs.chmodSync(tmp, 0o600) retire de saveState 0
  • Commits conventionnels (feat(agent), docs(agent)). Timeline de #50 : pull_ref closes depuis #53, conforme au Resolves #50.

Suggestions (non bloquantes)

  1. agent/claude-usage/aggregate.js:242 + state.js:104 — un jour se fige sur ce que le passage a pu lire : readLines et readdir avalent une erreur de lecture (EACCES, fichier en cours de rotation), et le jour fige avec un total incomplet n'est plus jamais recalcule (35 jours de retention). Le choix « une erreur ne coule pas le snapshot » est juste pour les jours ouverts ; pour les jours qu'on fige, il vaudrait mieux ne pas geler lors d'un passage qui a rencontre une erreur de lecture, ou au minimum documenter dans agent/README.md que rm state.json force un recalcul complet.
  2. agent/claude-usage/limits.js:177-180 — MAX_RESPONSE_BYTES est verifie apres res.text(), donc apres la mise en memoire du corps entier, et sur text.length (unites UTF-16, pas octets). Le commentaire « past it the body is not parsed » est exact, mais ce n'est pas une borne memoire. Risque faible (URL constante, timeout 10 s) : soit lire le flux avec un plafond, soit reformuler le commentaire.
  3. agent/claude-usage/state.js:141 — le chmodSync du fichier temporaire (cas du .tmp laisse par un passage tue) n'est epingle par aucun test : sa suppression laisse 106/106 verts. Le fichier ne contient pas de secret, donc epingle a ajouter en suivi plutot qu'un tour de plus.

Actions faites

  • Copie hors depot supprimee en fin de revue.
  • Label de #50 : status:review -> status:approved.
## Revue adversariale — PR #53 (issue #50) **Verdict : APPROVE** **Resume** : l'agent tient ses invariants (credentials en lecture seule, bearer OAuth vers la seule URL constante avec `redirect: "error"`, `detail` en enum, cache ecrit avant le push, aucune donnee de token dans la sortie) et chaque propriete testee resiste a une mutation du relecteur. Contrat aligne avec `sanitizeClaudeUsage` (meme regex ISO, `percent` entier borne, 12 items max). ### Verifications executees - Tete `9279b15` extraite hors depot (`git archive`) : `npx vitest run` -> **606/606 verts**. CI de la tete : `success`. - Mutations du relecteur, sur la copie, `__tests__/agent-claude-usage.test.js` seul : | Mutation | Rouges | |---|---| | `redirect: "error"` retire de l'appel Anthropic | 1 | | `redirect: "error"` retire du push | 1 | | controle `expiresAt <= now` neutralise | 2 | | gel de *hier* (`day >= today`) | 3 | | pas de repli sur `lastGoodLimits` | 2 | | `lastGoodLimits` mis a jour quel que soit le statut | 2 | | `saveState` retire | 6 | | controle `https:` neutralise | 1 | | `TOKEN_RE` neutralise | 1 | | `export ... CLAUDE_USAGE_LIMITS` retire du wrapper | 1 | | `percent` non borne | 1 | | plafond `MAX_ITEMS` retire | 1 | | `fs.chmodSync(tmp, 0o600)` retire de `saveState` | **0** | - Commits conventionnels (`feat(agent)`, `docs(agent)`). Timeline de #50 : `pull_ref` `closes` depuis #53, conforme au `Resolves #50`. ### Suggestions (non bloquantes) 1. `agent/claude-usage/aggregate.js:242` + `state.js:104` — un jour se fige sur ce que le passage a pu lire : `readLines` et `readdir` avalent une erreur de lecture (EACCES, fichier en cours de rotation), et le jour fige avec un total incomplet n'est plus jamais recalcule (35 jours de retention). Le choix « une erreur ne coule pas le snapshot » est juste pour les jours ouverts ; pour les jours qu'on fige, il vaudrait mieux ne pas geler lors d'un passage qui a rencontre une erreur de lecture, ou au minimum documenter dans `agent/README.md` que `rm state.json` force un recalcul complet. 2. `agent/claude-usage/limits.js:177-180` — `MAX_RESPONSE_BYTES` est verifie apres `res.text()`, donc apres la mise en memoire du corps entier, et sur `text.length` (unites UTF-16, pas octets). Le commentaire « past it the body is not parsed » est exact, mais ce n'est pas une borne memoire. Risque faible (URL constante, timeout 10 s) : soit lire le flux avec un plafond, soit reformuler le commentaire. 3. `agent/claude-usage/state.js:141` — le `chmodSync` du fichier temporaire (cas du `.tmp` laisse par un passage tue) n'est epingle par aucun test : sa suppression laisse 106/106 verts. Le fichier ne contient pas de secret, donc epingle a ajouter en suivi plutot qu'un tour de plus. ### Actions faites - Copie hors depot supprimee en fin de revue. - Label de #50 : `status:review` -> `status:approved`.
Author
Owner

Rebasee sur le correctif de #51 (c888f7c). Conflit d ajout en fin de __tests__/agent-claude-usage.test.js resolu par union. Commit ajoute docs(claude-md) : compteurs 106 -> 111 et 606 -> 611. Tip de pile : vitest 611/611, test-curl 45/45, boot-from-copy PASS. Le verdict APPROVE precedent porte sur un code inchange (seuls les tests et CLAUDE.md ont bouge dans ce maillon).

Rebasee sur le correctif de #51 (`c888f7c`). Conflit d ajout en fin de `__tests__/agent-claude-usage.test.js` resolu par union. Commit ajoute `docs(claude-md)` : compteurs 106 -> 111 et 606 -> 611. Tip de pile : vitest 611/611, test-curl 45/45, boot-from-copy PASS. Le verdict APPROVE precedent porte sur un code inchange (seuls les tests et CLAUDE.md ont bouge dans ce maillon).
maximus force-pushed issue-50-claude-usage-agent from 9279b15b7f to 719d65129a 2026-10-04 19:26:36 +00:00 Compare
maximus changed target branch from issue-49-claude-usage-aggregate to main 2026-10-04 19:29:44 +00:00
maximus merged commit 719d65129a into main 2026-10-04 19:29:53 +00:00
Author
Owner

Mergee en fast-forward par l API (tete 719d651). Le commit 719d651 (docs : compteurs de tests dans CLAUDE.md apres le rebase sur le correctif de #51) a ete ajoute apres l APPROVE, non relu ; il ne touche que CLAUDE.md. Tip valide avant merge : npm ci propre, vitest 611/611 (sonde rouge -> rc=1), test-curl 45/45, boot-from-copy PASS, CI verte.

Mergee en fast-forward par l API (tete `719d651`). Le commit `719d651` (docs : compteurs de tests dans CLAUDE.md apres le rebase sur le correctif de #51) a ete ajoute apres l APPROVE, non relu ; il ne touche que CLAUDE.md. Tip valide avant merge : npm ci propre, vitest 611/611 (sonde rouge -> rc=1), test-curl 45/45, boot-from-copy PASS, CI verte.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/vps-health-api#53
No description provided.