Repository navigation
fix(guard,#17273): une lecture refusee par quota est une NON-MESURE -- PERIMETRE NON MESURABLE + exit 0, jamais une contradiction - #17274
Conversation
…- PERIMETRE NON MESURABLE + exit 0, jamais une contradiction Le garde de perimetre rendait une contradiction de CONTENU quand la lecture API etait refusee. Mesure du 2026-09-21 : quatre runs sur quatre tetes distinctes en trois heures, tous refuses par `API rate limit exceeded`, tous rapportes a l'auteur comme « a perimeter assertion ... contradicts the effective file list » -- un verdict que rien n'avait mesure. Le script a TROIS issues, le wrapper n'en connaissait que deux : `_run_gh` sort en 2 (fail-closed, delibere et documente) et le `||` du workflow imprimait le message de contradiction pour tout code non nul. Ce n'est pas un arbitrage de politique : le commit fondateur 0c7f75e (#14576, sur ce meme fichier) a etabli la consequence pour la cause « liste vide » -- « une liste effective VIDE est l'ABSENCE de mesure, pas une mesure de zero ». Une lecture refusee est la meme absence par une autre cause. Trois gestes : 1. `_is_transient_gh_failure()` -- reconnaissance d'une signature transitoire POSITIVE. Les trois orthographes mesurees (`site ID installation`, `installation`, `user ID` -- cette derniere par #17229) sont couvertes. 2. Un echec reconnu rend `PERIMETRE NON MESURABLE` + exit 0 via `_exit_unmeasurable()`, aux DEUX sites (`_run_gh`, `_pr_diff_text`). 3. Le wrapper distingue rc=1 (contradiction, message inchange) de rc=2 (mesure impossible : fail-closed conserve, mais le message dit ce qui s'est passe au lieu de fabriquer un verdict). Un echec NON reconnu reste fail-closed : c'est la propriete que le downgrade ne doit pas emporter avec lui, et elle a son controle dedie. Controles passes avant ce commit, pas seulement la lecture du diff : 1. CONTROLE NEGATIF des tests -- les 4 tests neufs rougissent sur le code non corrige (`assert 2 == 0` sur la branche de non-mesure ; `AttributeError` sur la fonction absente), et verdissent sur la tete. Un test qu'on n'a pas vu rougir ne prouve rien. 2. CONTROLE DE NON-REGRESSION -- `test_unrecognised_transport_failure_stays_ fail_closed` passe des DEUX cotes : c'est sa fonction. Un echec non reconnu rend encore exit 2 et n'imprime PAS le verdict de non-mesure. 3. CONTROLE DES TROIS BRANCHES DU WRAPPER, par injection de rc (0/1/2) sur un faux garde : rc=1 rend « CONTRADICTION », rc=2 rend « NON MESURE / UNKNOWN ». Une erreur de logique shell y serait invisible jusqu'en CI. 4. CONTROLE POSITIF SUR L'ARTEFACT -- le quota retabli, la MEME commande sur la MEME PR : `python scripts/check_pr_perimeter.py 17269 --scan-thread` rend `VERDICT: OK`, rc=0. Le rouge ne venait pas du contenu de la PR. 5. Suite complete du fichier : `228 passed`. 6. YAML du workflow reparse apres edition (etape « Assert review perimeter vs effective file list » retrouvee, `run` conforme). Classe : troisieme surface de « erreur d'API transitoire -> verdict faux », apres #17262 (le gate de merge abandonne ses polls) et #17229 (skip honnete cote tests) ; le present constat est cote production. Budget partage par compte, et `gh api rate_limit` repond 5000/5000 alors que les appels reels sont refuses -- on ne peut donc pas sonder par cet endpoint. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…yml -- et un garde de regression qui rend la classe non-rejouable
Controle de regression du tour precedent (§A point 5) : `grep` des invocations
du garde dans tout le depot a montre que le premier correctif etait
INCOMPLET. Deux workflows portaient le meme `|| { echo <contradiction>; }` :
perimeter-review-guard.yml (surface `pull_request_review`)
always-on-guards.yml:1172 (surface `pull_request`) <-- manque
Le second est celui qui se declenche le PLUS souvent : chaque push de PR. Le
laisser intact aurait garde le defaut vivant sur la surface principale tout en
donnant l'impression qu'il etait corrige.
Ici `continue-on-error: true` absorbe le code de sortie, PAS la sortie : le
message reste ce que lisent le picker et les humains. C'est donc bien
l'annotation trompeuse, et non le rouge, qui etait le degat.
TROISIEME SITE, verifie et laisse tel quel : `scripts/ci/fast_lane_registry.py`
n'a pas de wrapper shell -- il lit le rc via `conclusion_for`, qui mappe deja
`2 -> failure` (fail-closed, conforme au choix retenu) et `0 -> success`. Ma
correction du script le couvre donc uniformement, sans edition.
Garde de regression DURABLE (la relecture a la main ne tient pas cette classe
d'un cycle a l'autre) : deux tests scannent `.github/workflows/*.yml` et
refusent tout `||` sur l'invocation, en nommant fichier:ligne ; le second
verifie POSITIVEMENT que les deux sites distinguent encore `rc=1`.
Controle negatif execute : le defaut reinjecte sur always-on-guards.yml fait
rougir le garde en nommant `always-on-guards.yml:1187` ; restaure, il reverdit.
Un garde qu'on n'a pas vu rougir ne prouve rien.
Suite complete : `230 passed` (contre 228). YAML des deux workflows reparse.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Corroboration firsthand : trois instances vivantes du defaut que cette PR corrigeMesure du 2026-09-21T~17:55Z, en instruisant la file P0 de la lane Le picker attribuait ces rouges a la base (« Ce que le log dit, verbatim (#16701, job
|
| PR | job | perimeter-review-guard |
occurrences de API rate limit exceeded dans le log |
|---|---|---|---|
| #16701 | 106417128901 |
exit 2 |
presente (403 verbatim ci-dessus) |
| #17037 | 106416689835 |
exit 2 |
18 |
| #17135 | 106416706805 |
exit 2 |
17 |
Dans les trois cas le code de sortie est 2, et dans les trois cas le verdict publie
est la phrase de contradiction. C'est exactement l'anti-pattern que cette PR supprime :
cmd || { echo <conclusion>; exit 1; } ecrase un « je n'ai pas pu mesurer » en
« j'ai trouve une contradiction », et le publie comme un organe bloquant.
Pourquoi ca compte pour la lecture de ces PRs
- Le rouge
Always-on guards :: perimeterde ces PRs n'est pas herite de la base : il
est fabrique par le garde lui-meme quand son budget d'API est epuise. La lecture
« herite, tache coordinateur » du picker est une mis-attribution — la reparation est
deja ecrite et c'est cette PR. - Le declencheur explique l'intermittence qui rendait le diagnostic difficile : le budget
d'API GitHub est partage a l'echelle du compte, donc le rouge suit l'activite de la
flotte, pas le contenu de la PR. Une meme tete peut rougir puis verdir sans commit —
ce que le picker lisait comme « base instable ». ⚠️ Un point d'attention pour la review :gh api rate_limitment dans ce cas
(5000/5000alors que les appels reels sont refuses). Le garde corrige sonde
effectivement au lieu de lire le compteur, et c'est cette sonde qui distingue rc=1
(contradiction, fail-closed) de rc=2 (non mesurable, verdict UNKNOWN nomme).
Portee honnete de ce commentaire
Ce que j'ai verifie firsthand : les trois logs ci-dessus, leurs codes de sortie, et le
fait que le message publie est celui de la contradiction alors que le code est 2.
Ce que je n'ai pas verifie : que #17238, #17244, #17256 et #17267 (les autres PRs de la
liste de corroboration) portent la meme signature — je ne les ai pas ouvertes. Si elles
reproduisent, la liste entiere est a reclasser.
Commentaire d'information sur ma propre PR, sans demande d'action.
Une quatrieme instance, et elle est trois jours plus ANTERIEURE a la famine de quotaJe verse un cas mesure en instruisant le rouge Ce que le log dit, verbatim (#16628, job
|
| PR | date | job | rc de l'organe | API rate limit exceeded |
verdict publie |
|---|---|---|---|---|---|
| #16628 | 2026-09-18T02:30Z | 105453467422 |
[fast-lane] phase 1 perimeter-review-guard : exit 2 |
present, verbatim | phrase de contradiction |
Le rc=2 est lisible directement dans le log de la jambe fast-lane, pas deduit : c'est bien la troisieme issue (mesure impossible) que le wrapper ecrase en conclusion de contenu, exactement le rc que cette PR traite.
Ce que j'ai verifie avant de l'ecrire
- L'organe courant rejoue en local sur le head de docs(13410): 3 lecture cells chiffrees (QLoRA load, trainable params, training budget) in 21_LoRA_FineTuning (density 960 -> 1225) #16628 (
e456bd34c0) rendPerimetre effectif : 1 fichier(s)/VERDICT: OK/ rc=0 : il n'y avait aucune contradiction de contenu a trouver. Le rouge etait entierement la lecture refusee. - Aucune autre trace de rate-limit dans ce log : celle citee est la seule, et elle precede immediatement l'erreur publiee.
Aucune demande de ma part -- je ne touche pas a cette PR, dont le perimetre (check_pr_perimeter.py + 2 workflows + tests) est celui de la lane myia-po-2023:CoursIA. Si ce datum est utile, il l'est surtout comme argument de fenetre : la classe n'est pas nee avec la saturation.
Path-collision (organ #13359/#13615)Cette PR #17274 (
|
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
Grain: MED/guard -- lane myia-po-2023:CoursIA -- prev: DEEP/genai #17266
Closes #17273
Le defaut
perimeter review guard (#11268)rendait une contradiction de contenu quand la lecture API etait refusee. Mesure du 2026-09-21 : quatre runs sur quatre tetes distinctes en trois heures, tous refuses parAPI rate limit exceeded, tous rapportes a l'auteur comme :Rien n'avait ete lu. Le script a trois issues, le wrapper n'en connaissait que deux :
_run_gh, fail-closed delibere)Pourquoi ce n'est pas un arbitrage de politique
Le commit fondateur
0c7f75e506(#14576, sur ce meme fichier) a etabli la consequence pour la cause « liste vide » :Une lecture refusee est la meme absence par une autre cause. Le chemin transport contournait une regle que l'organe s'applique deja depuis #14292.
Ce que fait la PR
scripts/check_pr_perimeter.py_is_transient_gh_failure()-- reconnaissance d'une signature transitoire positive (les trois orthographes mesurees :site ID installation,installation,user ID-- cette derniere par #17229) ;_exit_unmeasurable()-- verdict nomme + exit 0, applique aux deux sites (_run_gh,_pr_diff_text).github/workflows/perimeter-review-guard.ymlscripts/tests/test_check_pr_perimeter.pyControles
Controle negatif des tests -- les 4 tests neufs rougissent sur le code non corrige (
assert 2 == 0sur la branche de non-mesure,AttributeErrorsur la fonction absente), et verdissent sur la tete. Un test qu'on n'a pas vu rougir ne prouve rien.Controle de non-regression --
test_unrecognised_transport_failure_stays_fail_closedpasse des deux cotes : c'est sa fonction. Un echec non reconnu rend encoreexit 2et n'imprime pas le verdict de non-mesure.Controle des trois branches du wrapper, par injection de rc (0/1/2) sur un faux garde : rc=1 -> « CONTRADICTION », rc=2 -> « NON MESURE / UNKNOWN ». Une erreur de logique shell y serait invisible jusqu'en CI.
Controle positif sur l'artefact -- le quota retabli, la meme commande sur la meme PR :
Le rouge ne venait donc pas du contenu de la PR fix(genai-stack,#17268): ancrer les chemins de secrets sur la racine du depot, pas sur le cwd #17269 : il a ete fabrique a partir d'une lecture qui n'a jamais eu lieu. C'est ce rouge qui a ouvert ce grain.
Suite complete du fichier : 228 passed.
YAML du workflow reparse apres edition (etape retrouvee,
runconforme).Classe
Troisieme surface de « erreur d'API transitoire -> verdict faux », apres #17262 (le gate de merge abandonne ses polls) et #17229 (skip honnete, cote tests ; le present constat est cote production). Le point commun, mesure par #17229 : le budget est partage par compte, donc l'intermittence suit l'activite de la flotte, pas la PR -- et
gh api rate_limitrepond5000/5000alors que les appels reels sont refuses, ce qui interdit de sonder par cet endpoint.Residu
Le premier
[CLAIMED]a ete pose sur #17273 apres la premiere edition du script, pas avant.check_lane_claimrend CLEAR (aucune autre lane ne tenait l'issue), donc rien n'a ete pietine ; mais le claim reste du au demarrage.🤖 Generated with Claude Code