Le défaut
Quatre PRs ouvertes livrent zéro fichier : #16966, #16975, #16976, #16978 — toutes de la campagne réaccent #16638, toutes créées le 2026-09-20. Chaque branche porte un commit de réaccent substantiel (172, 140, 124, 52 lignes) suivi de commits REPAIR-N qui l'annulent intégralement : le diff net contre le merge-base f57c35fb48 est vide, le notebook est byte-identique à son état d'avant campagne.
$ git diff --stat f57c35fb48 5eea88bb34 # PR #16975
<vide>
$ gh pr view 16975 --json changedFiles,additions,deletions
{"changedFiles":0,"additions":0,"deletions":0}
Les REPAIR-N étaient des réponses à des réserves citant des sur-corrections nommées (prouve→prouvé ×2, donne→donné ×3…). Ils ont reverté tout le réaccent au lieu des seules formes visées. Personne ne l'a vu pendant ~36 h, sur quatre PRs qui continuaient d'accumuler dossiers, réserves et re-audits à chaque cycle.
Cadrage mesuré — la classe est bornée
Balayage des 328 PRs ouvertes : 4 vides, 324 non-vides (contrôle positif). Ce n'est pas un défaut diffus du dépôt, c'est une classe circonscrite à une campagne. Commande :
gh pr list --repo jsboige/CoursIA --state open --limit 400 \
--json number,changedFiles,additions,deletions,createdAt,title \
--jq 'sort_by(.createdAt) | .[] | select(.changedFiles==0) | [.createdAt[0:10],.number,.title] | @tsv'
Pourquoi les trois organes en place ne pouvaient pas le dire
Aucun n'a échoué : chacun a mesuré sa classe, exactement. C'est le produit que personne ne mesurait.
| Organe |
Verdict rendu |
Ce qu'il affirme réellement |
check_adjoint_prevalidation.py |
rc=1 |
« pas de dossier digne de confiance » — le dossier était à un head antérieur, les REPAIR l'ont périmé. Jamais « la PR est vide » |
check_unaddressed_nits.py (B.0) |
rc=1 |
« une réserve morphologique non levée » — il débat de formes qui n'existent plus dans aucun diff |
check_trivial_diff.py |
ok |
conforme à sa conception : sa jambe genre_meta exige un genre light (docs/readme/guard/ledger/test). Un fix(lean,…) ne la franchit pas — c'est le mécanisme même qui laisse passer le fix de 2 lignes d'un bug critique, contre-exemple explicite du mandat #15740 |
Un rc=1 de gate et un rc=1 de B.0 se lisent, de loin, comme « cette PR a des soucis à régler ». Ils ne se distinguent pas de « cette PR n'a plus d'objet ».
L'organe demandé — extension, pas nouveau script
Organ-first : ne pas écrire un détecteur neuf. Étendre scripts/check_trivial_diff.py, qui possède déjà le chargement du payload PR, les états de verdict, le câblage CI (.github/workflows/always-on-guards.yml:631) et la discipline unknown sur données absentes (#14849).
Ajouter un verdict empty, indépendant du genre :
Contrôles exigés pour l'acceptance
| Contrôle |
Attendu |
Positif — #16975 (head 5eea88bb34) |
verdict empty |
| Positif — les 3 autres (#16966, #16976, #16978) |
verdict empty |
Négatif — #16956 (head de branche feature/16638-deaccent-lean4, net 38/38) |
verdict ≠ empty — même campagne, même genre, même auteur : c'est le contrôle qui prouve que le prédicat lit le diff et non la campagne |
| Négatif — fix de 2 lignes, genre non-light |
verdict ok (non-régression du contre-exemple #15740) |
| Données absentes |
unknown, zéro warning |
Les tests vivent à côté de l'existant : scripts/tests/test_check_trivial_diff.py.
Ce que ça ne doit pas devenir
Pas un garde de volume. Le mandat #15740 interdit explicitement la taille comme critère principal, et son contre-exemple — « une correction de 2 lignes d'un bug critique serait acceptable » — reste la borne. empty ne mesure pas une petitesse, il constate une absence.
Traçabilité
Les quatre PRs portent chacune la mesure complète en commentaire (diff net, table commit-par-commit, pourquoi chaque organe est resté muet) et attendent leur porteur myia-po-2027.
-- myia-ai-01:CoursIA
Le défaut
Quatre PRs ouvertes livrent zéro fichier : #16966, #16975, #16976, #16978 — toutes de la campagne réaccent #16638, toutes créées le 2026-09-20. Chaque branche porte un commit de réaccent substantiel (172, 140, 124, 52 lignes) suivi de commits
REPAIR-Nqui l'annulent intégralement : le diff net contre le merge-basef57c35fb48est vide, le notebook est byte-identique à son état d'avant campagne.Les
REPAIR-Nétaient des réponses à des réserves citant des sur-corrections nommées (prouve→prouvé×2,donne→donné×3…). Ils ont reverté tout le réaccent au lieu des seules formes visées. Personne ne l'a vu pendant ~36 h, sur quatre PRs qui continuaient d'accumuler dossiers, réserves et re-audits à chaque cycle.Cadrage mesuré — la classe est bornée
Balayage des 328 PRs ouvertes : 4 vides, 324 non-vides (contrôle positif). Ce n'est pas un défaut diffus du dépôt, c'est une classe circonscrite à une campagne. Commande :
gh pr list --repo jsboige/CoursIA --state open --limit 400 \ --json number,changedFiles,additions,deletions,createdAt,title \ --jq 'sort_by(.createdAt) | .[] | select(.changedFiles==0) | [.createdAt[0:10],.number,.title] | @tsv'Pourquoi les trois organes en place ne pouvaient pas le dire
Aucun n'a échoué : chacun a mesuré sa classe, exactement. C'est le produit que personne ne mesurait.
check_adjoint_prevalidation.pyrc=1check_unaddressed_nits.py(B.0)rc=1check_trivial_diff.pyokgenre_metaexige un genre light (docs/readme/guard/ledger/test). Unfix(lean,…)ne la franchit pas — c'est le mécanisme même qui laisse passer le fix de 2 lignes d'un bug critique, contre-exemple explicite du mandat #15740Un
rc=1de gate et unrc=1de B.0 se lisent, de loin, comme « cette PR a des soucis à régler ». Ils ne se distinguent pas de « cette PR n'a plus d'objet ».L'organe demandé — extension, pas nouveau script
Organ-first : ne pas écrire un détecteur neuf. Étendre
scripts/check_trivial_diff.py, qui possède déjà le chargement du payload PR, les états de verdict, le câblage CI (.github/workflows/always-on-guards.yml:631) et la disciplineunknownsur données absentes (#14849).Ajouter un verdict
empty, indépendant du genre :changed_files == 0. Une seule jambe — contrairement àtrivial, qui est une conjonction parce que la petitesse est ambiguë. Le vide ne l'est pas : une PR à zéro fichier n'a rien à squasher, quel que soit son genre, son domaine ou son auteur.unknownsi les stats sont absentes — la discipline always-on-guards: le cap G-VAR-2 retombe sur cap_reached=False quand son entree manque (garde qui verdit sur absence de mesure) #14849 déjà en place dans le script : jamais de décision sur donnée manquante. Ununknownn'avertit de rien.::warning+ label), alignée sur la posture actuelle du script et sur le verbatim du mandat ci: un organe advisory qui rougit sur la TRIVIALITE d'un diff, pas sur sa taille (2e moitie du concern user sur #15724) #15740 (« lever un warning »). Passer ce verdict en bloquant ajoute une interdiction au harnais et relève donc de CLAUDE.md §A (PR + sign-off user) : c'est posé comme question au registre, pas décidé ici.Contrôles exigés pour l'acceptance
5eea88bb34)emptyemptyfeature/16638-deaccent-lean4, net 38/38)empty— même campagne, même genre, même auteur : c'est le contrôle qui prouve que le prédicat lit le diff et non la campagneok(non-régression du contre-exemple #15740)unknown, zéro warningLes tests vivent à côté de l'existant :
scripts/tests/test_check_trivial_diff.py.Ce que ça ne doit pas devenir
Pas un garde de volume. Le mandat #15740 interdit explicitement la taille comme critère principal, et son contre-exemple — « une correction de 2 lignes d'un bug critique serait acceptable » — reste la borne.
emptyne mesure pas une petitesse, il constate une absence.Traçabilité
Les quatre PRs portent chacune la mesure complète en commentaire (diff net, table commit-par-commit, pourquoi chaque organe est resté muet) et attendent leur porteur
myia-po-2027.--
myia-ai-01:CoursIA