docs: REVIEW.md with review levels and a calibration script (#63) #65
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#65
Loading…
Reference in a new issue
No description provided.
Delete branch "infra/review-md-63"
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?
Fixes #63
Ce que la PR ajoute
REVIEW.md, sur le modèle de simpl-resultat : niveaux (tableau Haut avec conditions, Bas, Normal, Calibration), règles par zone avec leur source (routage et auth, surfaces d'écriture et montages, contrats servis au dashboard, image et démarrage, agents du poste, CI et gardes), commandes de vérification, pièges de revue.scripts/review-levels.py, adapté de simpl-resultat : rejoue les niveaux sur les PR intégrées et offre--check(sans réseau).CLAUDE.md: la section « Deploy » disait « Pas d'auto-deploy ». C'est faux depuis le 2026-10-12 (hook Forgejo id 9,coolify-ops.md) : le merge est le déploiement, en même temps que la CI dupushet sans l'attendre. Corrigé, plus un renvoi versREVIEW.md(nouvelle section « Documents de reference »).Sources récoltées
CLAUDE.md,agent/README.md,la-compagnie-maximus/docs/{coolify,forgejo}-ops.md, en-tête deci.yml, épingles des tests, et les blocages des revues passées : #39 (garantie d'écriture plus large que la couverture), #42 (exemple de clé échappée décodé), #45 (wait_foret un port voisin), #51 (seuil de scan qui vidait des jours ouverts), #59 (trois passes : la rotation pouvait vider la copie hors site).Calibration
Ensemble : les 31 PR dont la tête a atteint
main(#2 à #62), liste figée dans le script. Forgejo marque « fermées » les PR d'une pile intégrée en avance rapide (#18 à #23, #31, #34, #37)./pulls/{n}/filesrépond 500 quand leur base a disparu, donc le script lit la liste des fichiers dans le.diff. Trois PR ont un.diffvide parce que leurmerge_basevaut leur tête (#18, #31, #34) : elles sont lues dans le clone local.python3 scripts/review-levels.py:python3 scripts/review-levels.py --check:Il échoue (code 1) quand on retire un motif d'un côté. Vérifié en renommant
Dockerfiledans le tableau (REVIEW.md only: Dockerfil/script only: Dockerfile), puis en supprimant la ligne.gitignore(script only: .gitignore).65 % de PR hautes, pour une cible de 20 %. C'est voulu. L'historique est celui de la construction des surfaces sensibles (quatre tokens, trois routes d'écriture, deux agents qui tiennent un secret ou la copie hors site), et chaque surface a été livrée par la PR qui l'a créée. Le merge part en production. Les PR qui étendent une surface existante, une route de lecture ou des tests restent normales (11). Deux resserrements faits pendant la calibration :
__tests__/ci.test.jsest sorti des gardes (le workflow etboot-from-copy.shsont déjà hauts), et une lecture deprocess.envou une ligne de journal ne suffisent plus à rendre haut un module livré. On est passé de 71 % à 65 %.Vérification dans le worktree
npm ci && npm test: 13 fichiers, 879 cas vertsbash test-curl.sh: 57 passed, 0 failedsh scripts/boot-from-copy.sh: PASSChoix de Max (2026-10-19)
Consignés dans
### CalibrationdeREVIEW.md(commentaire 25657) :auth.test.jsetwrites.test.jssont hautes dès qu'une ligne existante change.Suggestions de la revue 25515 : 1, 2 et 3 sont appliquées (
.claude/hooks/**haut, niveau bas restreint à**/*.mdetLICENSE, déplacement versdependenciesclassé haut). La 4 (rangement dans--check) est écartée. Les trois passes de/pr-review: 25498 (REQUEST_CHANGES, corrigé), 25515 et 25665 (APPROVE).Prête à merger. Une fois mergé, les PR ouvertes seront jugées avec ces niveaux.
🤖 Generated with Claude Code
Niveau : normal (pas de REVIEW.md)
Verdict : REQUEST_CHANGES
Résumé. Le
REVIEW.mdest solide : chaque règle de zone cite une source qui existe (vérifié : hook id 9 et auto-deploy du 2026-10-12, absence dewatch_paths, montage/srv/vps-health-api/backups-pullet sonroot:rootdu 2026-10-16 danscoolify-ops.md, runnerlinux-builderà portée instance dansforgejo-ops.md,apk add git bash curlet l'absence deconcurrency:dansci.yml,.env/.env.localdans.gitignore). La correction de la section « Deploy » deCLAUDE.mdest juste. Un seul défaut bloquant : le piège n° 5 deREVIEW.mda lui-même subi le piège qu'il décrit.Ce qui a tourné (copie hors dépôt de la tête
1d3be3c)npm ci && npx vitest run: 13 fichiers, 879 cas verts ;agent-pg-backup-pull.test.jscompte bien 21 cas (la ligne ajoutée àCLAUDE.mdest exacte).python3 scripts/review-levels.py --check:ok: 17 patterns, code 0."agent/push-metrics.js"deHIGH_ALWAYS→REVIEW.md only: agent/push-metrics.js, code 1. La garde sait échouer.EMPTY_DIFFdu clone local).CI / verification (pull_request)= success.pull_refclosesdepuis #65.Problème bloquant
REVIEW.md:159: « Un-tapé dans un outil arrive décodé dans le fichier ». L'exemple d'origine était la séquence d'échappement\u002d(celle de PR #42, toujours présente telle quelle dansCLAUDE.md, sectionci.test.js:"continue\u002don-error"). Elle est arrivée décodée en-: la phrase ne dit plus rien, et le relecteur ne sait pas quoi chercher. Réécrire l'exemple en construisant la barre oblique hors de l'outil d'édition (chr(92)en Python, cf. la mémoire « Escapes decoded in tool calls »), puis vérifier pargrep -n 'u002d' REVIEW.md(l'occurrence doit être précédée d'une barre oblique inverse) ouod -c.Suggestions (non bloquantes)
scripts/review-levels.py:20-23(docstring) : la liste des effets approchés cite encore « environment » et « log lines ». Le corps de la PR dit que ces deux déclencheurs ont été retirés pendant la calibration, etEFFECTSne les contient plus. Mettre la docstring à jour.AUTHreconnaît aussitimingSafeEqualetcreateHash, absents de la condition du tableau Haut. Le script est plus strict que le document : sans effet sur la calibration (aucune PR ne change de niveau à cause d'eux), mais à aligner dans un sens ou dans l'autre.CLAUDE.md: les trois montages passent à « Pose (releve du 2026-10-19) », alors que le relevé consigné danscoolify-ops.mddate du 2026-10-16. Si un relevé a bien été fait aujourd'hui, l'ajouter àcoolify-ops.md; sinon citer celui du 2026-10-16. Point non vérifié par la revue (pas de SSH vers le VPS).Les six choix posés à Max dans le corps de la PR relèvent de son arbitrage et ne sont pas jugés ici. Une remarque factuelle sur le choix n° 1 : avec
touches(), un hunk dont la ligne de contexte@@nomme une fonction à effet de bord (readBody,writeHostSnapshot…) rendindex.jshaut même si la ligne changée n'y touche pas. Le taux de 65 % en est un peu gonflé, dans le sens prudent.Actions faites
scratchpad/pr65,rl.py) supprimée en fin de revue.status:review(labels actuels :source:human,type:infra), donc rien n'est changé.Niveau : normal (pas de REVIEW.md)
Verdict : APPROVE
Résumé.
REVIEW.mdest fidèle à ses sources,scripts/review-levels.pyreproduit exactement la calibration annoncée, et--checksait échouer. Les corrections deCLAUDE.md(montagebackups-pull, auto-deploy) concordent aveccoolify-ops.md. Il reste quatre suggestions, aucune bloquante.Ce qui a été vérifié (copie hors dépôt de la tête
9547965)npm ci && npx vitest run: 13 fichiers, 879 cas verts.agent-pg-backup-pull.test.jscompte bien 21 cas, comme le dit la ligne ajoutée àCLAUDE.md.python3 scripts/review-levels.py: sortie identique ligne pour ligne à celle du corps de la PR (31 PR : 0 / 11 / 20, haut 64,5 %). Les six SHA d'EMPTY_DIFFexistent dans le clone.--check:ok: 17 patterns. Mutation :"Dockerfile"renommé en"Dockerfil"dansHIGH_ALWAYSdonneREVIEW.md only: Dockerfile/script only: Dockerfilet rc=1. Annulée, le check revient à ok.review-levels.py 65classe cette PR haut (REVIEW.md, « Niveaux » à vérifier à la main). C'est cohérent avec la règle « jugée par la version demain», qui la laisse en normal ici.handler,resolveRoute,checkAuth,tokenMatches,logRejection,safeLogValueet GHSA-376v-xgjx-7mfr existent dansindex.js. L'en-tête deci.ymlporte bien « pas dedefaults:/working-directorysuruses:/ pas deconcurrency:».forgejo-ops.mdconfirmelinux-builderà portée instance, etcoolify-ops.mdle hook id 9 (auto-deploy depuis le 2026-10-12),/srv/vps-health-api/backups-pull -> /data/backups-pull rw=trueet leroot:rootdu 2026-10-16."continue-on-error"deCLAUDE.mdest resté échappé dans le fichier (od -c:\ u 0 0 2 d).CI / verification (pull_request)= success.pull_refclosesdepuis #65.Suggestions (non bloquantes)
.claude/hooks/**n'est pas haut. Le tableau justifie.claude/settings.jsonpar « hooks, permissions et serveurs MCP décident de ce que les agents exécutent ». Or le corps d'un script de hook est ce qui s'exécute, et le modifier ne touche passettings.json. Sonde : un nouveau.claude/hooks/x.shest classénormal. Ajouter.claude/hooks/**(ou « tout fichier quesettings.jsonréférence ») au tableau et àHIGH_ALWAYS. Rien n'est versionné sous.claude/aujourd'hui.reports/**etdocs/**acceptent n'importe quel type de fichier en Bas. Sonde :reports/run.shajouté est classébas.reports/ne reçoit que desDAILY-REPORT-*.md, doncreports/**/*.mdsuffirait.package_json_changes()ignore les changements de bloc. Une clé présente des deux côtés avec une valeur de version passe pour une montée de version. Sonde :vitestdéplacé dedevDependenciesvers un blocdependenciesdéjà présent donnenormal. Aujourd'hui, l'absence de blocdependenciesfait apparaître une ligne"dependencies": {qui rattrape le cas. Le défaut devient réel dès qu'un bloc existe. Comparer clé et bloc, ou le dire dans la docstring.--checkcompare l'union des motifs, pas leur rangement. DéplacerDockerfiledeHIGH_ALWAYSversCONDITIONALlaisse le check vert, alors queDockerfilene déclencherait plus rien. Ce risque est faible, mais une ligne de doc ou une comparaison par liste (sans condition, ou avec) le fermerait.Sur le choix 1 (65 %) : les 20 PR hautes correspondent toutes à une surface créée ou à un garde modifié, sans faux positif évident dans la liste. Le taux me paraît défendable tel quel.
Actions faites
scratchpad/pr65), supprimée en fin de revue.status:review(labels actuels :source:human,type:infra).Choix de Max (2026-10-19), consignes dans
### CalibrationdeREVIEW.md(44d5897) :auth.test.js/writes.test.jshautes des qu une ligne existante change.Suggestions de la revue 25515 appliquees : 1 (
.claude/hooks/**haut), 2 (Bas restreint a**/*.mdetLICENSE), 3 (package.json: deplacement versdependencies). La suggestion 4 (rangement dans--check) est ecartee. Calibration rejouee : inchangee, 20 hautes sur 31.Niveau : normal (pas de REVIEW.md)
Verdict : APPROVE
Résumé. Troisième passe, centrée sur
44d5897, le commit qui suit l'APPROVE 25515. Les trois suggestions retenues sont appliquées correctement et chacune sait échouer. Les choix de Max consignés dans### Calibrationreprennent fidèlement le commentaire 25657. La calibration ne bouge pas : 31 PR, 0 basse, 11 normales, 20 hautes.Ce qui a été vérifié (copie hors dépôt de la tête
44d5897)npm ci && npx vitest run: 13 fichiers, 879 cas verts. Aucun test ne litREVIEW.mdni le script.python3 scripts/review-levels.py: la sortie est identique ligne pour ligne à celle du corps de la PR (comparaison pardiff).--check:ok: 18 patterns, avec.claude/hooks/**en plus. Mutation : retirer.claude/hooks/**deHIGH_ALWAYSdonneREVIEW.md only: .claude/hooks/**, rc=1..claude/hooks/x.shest classé haut ;reports/run.shest classé normal,reports/x.mdbas ;vitestdéplacé dedevDependenciesvers un blocdependenciesexistant (déplacement pur, sans autre changement) est classé haut ;vitestet une montée duversionde l'app restent normales.package_json_changes(): sans la clé de bloc et sans le retour surdependencies, le déplacement pur retombe en normal. La sonde distingue donc l'ancien comportement du nouveau.CI / verification (pull_request)= success.pull_refclosesdepuis #65.Suggestions (non bloquantes)
44d5897). Le corps annonce aussi17 patterns, au lieu de 18 aujourd'hui. Le mettre à jour avant le merge, pour qu'un lecteur ne croie pas le merge bloqué.package_json_changes()ne connaît le bloc que si son en-tête figure dans le contexte du hunk. Exemple : une montée de version au milieu d'un blocdependenciesde plus de trois entrées, dont la ligne"dependencies": {sort des 3 lignes de contexte. Elle est classée normale, alors que le tableau dit « toute ligne du blocdependencies». Ce cas est impossible aujourd'hui, le runtime n'ayant aucune dépendance, et le script ne sert qu'à la calibration. Une ligne dans la docstring suffit.Actions faites
scratchpad/pr65, plus les fichierspr65-*), supprimée en fin de revue.status:review(labels :source:human,type:infra).Merge fast-forward par l API sur
1dff0c8(tete validee :npm ci, sonde rouge en code 1,npm test879 cas,test-curl.sh57 cas,boot-from-copy.shPASS,--check18 motifs).1dff0c8a ete ajoute apres l APPROVE 25665 et n a pas ete relu : il ne touche que la docstring descripts/review-levels.py(angle mort depackage.jsonnomme). Redeploiement automatique Coolify termine sur1dff0c8; production :/healthsans token 401, route inconnue 404.Remarques non bloquantes restantes : aucune a reprendre. La suggestion 4 (rangement dans
--check) est ecartee par Max, l angle mort depackage.jsonest documente, et le declenchement dindex.jssur la ligne de contexte@@entre dans le taux de 65 % que Max a garde.