From 44d58970ad6244a7f6737cf1e01b6b92b8a84ce1 Mon Sep 17 00:00:00 2001 From: le king fu Date: Mon, 19 Oct 2026 20:21:29 -0400 Subject: [PATCH] docs: apply three review suggestions, record Max's choices (#63) - .claude/hooks/** joins the always-high row: a hook script runs on the workstation. - Bas narrows to **/*.md and LICENSE: a script or an image added under docs/ or reports/ is no longer low. - package.json: a package moved between devDependencies and dependencies, or any line of the dependencies block, is high; the script now tracks the block each package line belongs to. - Calibration records the choices Max confirmed on 2026-10-19. Replay unchanged: 20 haut out of 31. Co-Authored-By: Claude Opus 5.5 --- REVIEW.md | 8 +++++--- scripts/review-levels.py | 36 +++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/REVIEW.md b/REVIEW.md index f13e4f0..f6d64a7 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -29,11 +29,11 @@ Motifs : chemins depuis la racine. `*` ne traverse jamais un `/` ; `**/` vaut z | `.forgejo/workflows/**` | toujours : le runner `linux-builder` est un service de l'hôte de production, à portée instance, et le job `push` de `main` tourne pendant le déploiement | CI et gardes | | `vitest.config.js`, `scripts/boot-from-copy.sh` | toujours : ils décident quels tests tournent et si le `COPY` est vérifié | CI et gardes | | `__tests__/{auth,writes}.test.js` | le diff change ou retire une ligne existante hors commentaire ; un cas ajouté sans rien toucher d'autre reste normal | CI et gardes | -| `package.json` | le diff change autre chose que le champ `version` de l'app ou la version d'un paquet déjà listé : paquet ajouté ou retiré, bloc `dependencies` (le runtime est sans dépendance), `scripts`, `main`, `type`, `overrides` | Dépendances | +| `package.json` | le diff change autre chose que le champ `version` de l'app ou la version d'un paquet déjà listé dans le même bloc : paquet ajouté, retiré ou déplacé entre `devDependencies` et `dependencies`, toute ligne du bloc `dependencies` (le runtime est sans dépendance), `scripts`, `main`, `type`, `overrides` | Dépendances | | `package-lock.json` | le diff ajoute ou retire un paquet (une clé `"node_modules/"` qui apparaît ou disparaît), change l'origine d'un paquet (hôte ou schéma de `resolved`) ou un `integrity` sans nouvelle version | Dépendances | | `.gitignore` | le diff retire ou assouplit une ligne (`.env` et `.env.local` en dépendent) | Agents du poste, Dépendances | | fichier à la racine du dépôt, hors `*.md` | ajouté, renommé ou supprimé, comme tout fichier d'un dossier caché nouveau : une configuration nouvelle peut changer le build Coolify, la CI ou le chargement de vitest sans motif qui la nomme | Image et démarrage, CI et gardes | -| `.claude/settings.json`, `.mcp.json` | toujours : hooks, permissions et serveurs MCP décident de ce que les agents exécutent sur le poste, sans CI pour le voir | CI et gardes | +| `.claude/settings.json`, `.claude/hooks/**`, `.mcp.json` | toujours : hooks et leurs scripts, permissions et serveurs MCP décident de ce que les agents exécutent sur le poste, sans CI pour le voir | CI et gardes | | `REVIEW.md` | le diff touche la section « Niveaux » | Règles de revue | Restent au moins normaux, et leurs règles de zone s'appliquent quand même : @@ -47,7 +47,7 @@ Restent au moins normaux, et leurs règles de zone s'appliquent quand même : ### Bas -Une PR est de niveau bas quand tous ses fichiers sont désignés par `**/*.md`, `docs/**`, `LICENSE` ou `reports/**`, hors des exceptions de la liste précédente. Exemples : `README.md`, `STATE.md`, `spec-*.md`. +Une PR est de niveau bas quand tous ses fichiers sont désignés par `**/*.md` (ceux de `docs/` et de `reports/` compris) ou `LICENSE`, hors des exceptions de la liste précédente. Un fichier d'un autre type sous `docs/` ou `reports/` (un script, une image) reste normal. Exemples : `README.md`, `STATE.md`, `spec-*.md`. ### Normal @@ -66,6 +66,8 @@ Niveau haut sur les 31 : routage et authentification (#9, #20, #28, #30, #52, #5 L'écart à la cible de 20 % du modèle de simpl-resultat est voulu et justifié dans la PR qui ferme #63 : l'historique du dépôt est celui de la construction de ses surfaces sensibles (quatre tokens, trois routes d'écriture, deux agents qui tiennent un secret ou la copie hors site), chacune livrée par la PR qui l'a créée, et le merge part en production sans attendre la CI. Les PR qui ne font qu'étendre une surface existante, une route de lecture ou un test restent normales. Le script qui calcule ces niveaux est `scripts/review-levels.py` (API Forgejo, lecture seule, plus le clone local pour trois PR au diff vide ; `python3 scripts/review-levels.py 60 62` pour quelques PR) ; `python3 scripts/review-levels.py --check` vérifie, sans réseau, que ses motifs sont ceux du tableau Haut. Il n'approche qu'une partie des conditions et ne sert qu'à la calibration. +Choix confirmés par Max le 2026-10-19, sur la PR qui ferme #63 : le taux de 65 % est gardé ; une montée de version d'un paquet déjà présent reste normale ; les agents du poste listés au tableau restent toujours hauts, même si leur merge ne déploie rien ; la liste des fichiers jamais bas est celle ci-dessus ; les gardes `auth.test.js` et `writes.test.js` sont hautes dès qu'une ligne existante change ; les nettoyeurs restent normaux, élargir une liste fermée relevant de la règle de zone. Tout écart à ces choix se demande à Max. + Une PR qui modifie cette section dit, pour chaque motif retiré ou assoupli, ce qu'il protégeait, met `scripts/review-levels.py` à jour dans le même diff et le rejoue sur les mêmes PR. ## Ce qui compte par zone diff --git a/scripts/review-levels.py b/scripts/review-levels.py index b2f2cc4..5c129d1 100755 --- a/scripts/review-levels.py +++ b/scripts/review-levels.py @@ -55,11 +55,11 @@ HIGH_ALWAYS = [ "Dockerfile", ".forgejo/workflows/**", "vitest.config.js", "scripts/boot-from-copy.sh", - ".claude/settings.json", ".mcp.json", + ".claude/settings.json", ".claude/hooks/**", ".mcp.json", ] SHIPPED = ["{index,metrics,claude-usage,claude-usage-history,backups}.js"] GUARDS = ["__tests__/{auth,writes}.test.js"] -LOW = ["**/*.md", "docs/**", "LICENSE", "reports/**"] +LOW = ["**/*.md", "LICENSE"] NEVER_LOW = ["CLAUDE.md", "REVIEW.md", "agent/README.md", ".claude/**"] AUTH = re.compile(r"\b(ROUTES|TOKEN_KINDS|resolveRoute|checkAuth|tokenMatches|logRejection|" @@ -147,15 +147,33 @@ def touches(block, pattern): def package_json_changes(block): - # any key/value change except "version" and a bump of a package already listed + # anything but "version" and a bump of a package listed in the same block; + # any line of "dependencies" counts, since the runtime has none before, after = {}, {} - for l in changed(block): - m = re.match(r'^([+-])\s*"([^"]+)":\s*(.*?),?\s*$', l) - if not m: - return True - (before if m.group(1) == "-" else after)[m.group(2)] = m.group(3) + for hunk in re.split(r"(?m)^@@", block)[1:]: + section = None + for l in hunk.split("\n")[1:]: + if l.startswith(("+++", "---")) or l[:1] not in " +-": + continue + m = re.match(r'^[ +-]\s*"([^"]+)":\s*\{', l) + if m: + section = m.group(1) + if l[:1] == " " or not meaningful(l[1:]): + if re.match(r"^[ +-]\s*\}", l): + section = None + continue + if section == "dependencies": + return True + m = re.match(r'^([+-])\s*"([^"]+)":\s*(.*?),?\s*$', l) + if not m: + return True + if m.group(3) == "{": + continue + (before if m.group(1) == "-" else after)[(section, m.group(2))] = m.group(3) + if re.match(r"^[+-]\s*\}", l): + section = None for key in set(before) | set(after): - if key == "version": + if key[1] == "version" and key[0] is None: continue if key in before and key in after and all(re.match(r'^"[\^~]?\d', v) for v in (before[key], after[key])): continue