feat(claude-usage): archive closed days by month, history route, one-shot backfill #57
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#57
LoadingâŠ
Reference in a new issue
No description provided.
Delete branch "issue-55-claude-usage-history"
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 #55
What
Keep the closed days of the Claude usage snapshot past its 30-day window.
POST /claude-usage. Once the snapshot is written, every day it carries except the most recent date (today, still open, whatever the order sent) and any date past the UTC reception day is merged into<dir of CLAUDE_USAGE_PATH>/history/YYYY-MM.json.renameSync.GET /claude-usage/history?from=YYYY-MM&to=YYYY-MM, behind the read token, is a newROUTESentry with no change tocheckAuth().from > to, or on a span over 36 months.sanitizeDay(), now exported fromclaude-usage.js.node claude-usage-history.js --import <file|->, run inside the container. An already archived date always wins. An invalid import file writes nothing.agent/claude-usage/backfill-history.jsruns by hand from the repo. The cron agent is unchanged.--max-windows(default 24).--out(mode 600, never overwritten), opens no connection and never reads the credentials file.read_errors=0.COPYnow shipsclaude-usage-history.js.CLAUDE.mdandagent/README.md, which has the backfill and import procedure.Deviations from the issue body
--max-windows, then trims the leading empty days. A full scan takes about 5 s per window.Tests
npm testbash test-curl.shsh scripts/boot-from-copy.shNew and updated test files:
__tests__/claude-usage-history.test.js: 76 cases;agent-claude-usage.test.js: +10 backfill cases;auth.test.js: the route enters the GET matrix, plus one more404 (not 401)case;writes.test.js: the POST case requires the rename of the month file; the history route enters the read routes; a corrupt month or a refused range writes nothing.Mutations
Each mutation was applied from a
cpbackup, the suite was rerun, and the file was restored. All 30 turned the suite red (F1-F3 added after the review).preferExistingto = fromdefaulthistory/--outoverwritten--outwithout mode 600readErrorsignoredPost-merge checklist (hence
Refs, not a closing keyword)GET /claude-usage/history. It should have content after the first POST (one cron beat).backfill-history.json the ThinkPad, scp the output, import it in the container, then check through the route. The procedure is inagent/README.md./data/claude-usage/(and/data/hosts,/data/defenseurs) is covered. If it is not, set it up and document it incoolify-ops.md.đ€ Generated with Claude Code
agent/claude-usage/backfill-history.js runs by hand from the repo (never from the cron install, the agent is unchanged). It rebuilds the closed days with the agent's own aggregate() on past windows and writes { days } to the --out file, which the server merges with `claude-usage-history.js --import` (an already archived date wins). Each 30-day window keeps only its 28 inner days and the next steps back 28 days, so a request straddling a window boundary is deduplicated by the window that keeps it and counted once. The first window stops at D-2. The walk is bounded by --max-windows (default 24) rather than guessed from file mtimes, which say nothing of the oldest line a resumed session holds. It never reads the credentials file, opens no connection, writes only the --out file (mode 600, never overwritten) and prints counts only. Refs #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Verdict : APPROVE
Résumé. L'archive mensuelle, la route
GET /claude-usage/historyet le backfill tiennent ce que la PR annonce. L'ordre snapshot puis archive, le 204 inchangé, le mois illisible jamais écrasé, la route sur le token de lecture sans touchercheckAuth()et leCOPYdu Dockerfile sont en place. Les deux rÚgles de fusion (POST : le dernier snapshot gagne ; import : la date archivée gagne) sont épinglées.Ce qui a été exécuté
npm test: 727/727 ;bash test-curl.sh: 51/51 ;sh scripts/boot-from-copy.sh: PASS (ports 3097-3099 libres, vérifiés avant).b136749:success.git statuspropre) :parseRangesans le refusfrom > toslice(0, 28))lastArchivedMonthrend le premier mois au lieu du dernierwriteMonthécrit directement le fichier final (plus de temporaire ni derename)preferExistingslice(0, -2))Fermeture d'issue
La timeline de #55 porte
pull_refavecref_action: "none": aucun mot-clé de fermeture, conforme auRefset à la fermeture manuelle annoncée.Suggestions (non bloquantes)
index.js,archiveClosedDays;claude-usage-history.js,lastArchivedMonth).cleanDayDatevĂ©rifie qu'un jour existe, pas qu'il soit passĂ©. Un snapshot portant un jour2099-12-31(horloge du poste dĂ©rĂ©glĂ©e, bug de l'agent) crĂ©ehistory/2099-12.json. Ce fichier fixe ensuite pour de bon la plage par dĂ©faut deGET /claude-usage/historysur 2099-01..2099-12, donc la route rĂ©pond vide jusqu'Ă une suppression Ă la main. Deux parades possibles : n'archiver que les dates<=jour dereceivedAt+ 1, ou bornerlastArchivedMonthau mois courant.dayssupposĂ© (archiveClosedDays,days.slice(0, -1)). Le code suppose le dernier Ă©lĂ©ment Ă©gal Ă aujourd'hui. Le contrat ordonnedaysdu plus ancien au plus rĂ©cent, maissanitizeDaysne trie pas. Ăcarter la date maximale (au lieu du dernier Ă©lĂ©ment) rendrait la rĂšgle indĂ©pendante de l'ordre reçu.history/ne sont couverts par aucun balayage. C'est assumĂ© et documentĂ©, la remarque ne sert qu'Ă la trace.Post-merge
La checklist de la PR (redeploy, backfill, sauvegarde de
/data/claude-usage/, fermeture manuelle) reste Ă faire.Merged fast-forward on
c5ded9e, CI green on that head.c5ded9e(fix(server): archive by latest date, never a day past reception) was added after the APPROVE and was not reviewed. It applies review suggestions 1 and 2 at Max's request ("corrige et merge"):Mutations on the fix, from a
cpbackup, tree restored after each one:Suite: 727 tests before the fix, 731 after; test-curl 51/51; boot-from-copy PASS.
Suggestion 3 (no sweep of temp files in
history/) is left as is, as documented.#55 stays open until the post-merge checklist is done: redeploy, backfill, VPS backup check.