Skip to content

fix(ci): back off installation quota reads in PR gate - #17987

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/17681-pr-gate-installation-quota-backoff
Sep 26, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/17681-pr-gate-installation-quota-backoff

Conversation

@jsboige

@jsboige jsboige commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2025:CoursIA — prev: DEEP/notebook-python #17960

Summary

  • Recognize the measured GitHub App installation-quota 403 separately from generic transient API failures.
  • Space its five retries at 30/120/300/600/900 seconds, clamped to the existing absolute gate deadline; a persistent unreadable state remains a named failure, never a fabricated pass.
  • Exercise recovery after a minutes-long outage and a clock that advances during sleep; retain the generic 503 retry cadence and permanent-error behavior.

See #17681 (partial: adaptive backoff and diagnostic). This does not implement quota instrumentation, concurrent-call throttling, or an automatic rerun after a quota outage lasting beyond the gate's 45-minute deadline. A persistent hourly exhaustion can still produce a red gate and requires a later rerun without a new push.

Validation

  • python -m pytest scripts/tests/test_pr_gate_transient_retry.py scripts/tests/test_pr_gate.py -q: 169 passed after the final test change.
  • python -m py_compile scripts/pr_gate.py: passed.
  • git diff --check: passed.
  • No notebook, workflow, or production API credentials changed.

🤖 Generated with Claude Code

Distinguish GitHub App installation quota failures from generic transient errors, space retries across the existing deadline, and keep unreadable check state fail-closed. Add clock-driven deadline and recovery coverage. See #17681.

Co-Authored-By: Claude-Code <noreply@anthropic.com>

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: LGTM (vérifié: lecture ligne à ligne de la boucle retry pr_gate.py et du fichier de tests au head c39fac5f5f — escalade, clamp deadline, fail-closed nommé, recovery par vrai état de check)

[NanoClaw] structural review — grain MED/guard, ref #17681 (partiel, périmètre honnête : pas d'instrumentation quota, pas de throttling concurrent, pas de rerun auto). 2 fichiers scripts/, aucun notebook/workflow/credential.

Vérifié à la source (pr_gate.py au head)

  • Prédicat strict : un seul marker verbatim (rate limit exceeded for installation, signature mesurée #17262/#17681), docstring « strict subset of the transient family » — testé dans les deux sens (quota ⇒ transitoire, jamais l'inverse, test_pr_gate_transient_retry.py:329-333). Un 403 inconnu reste sur la cadence courte : il n'hérite pas du long backoff.
  • Escalade : INSTALLATION_QUOTA_BACKOFF_SEC = (30, 120, 300, 600, 900) — somme 1950 s = 32,5 min, tient dans le deadline 45 min avec ~12 min de marge de lecture ; testé sleeps == [30.0, 120.0, 300.0, 600.0, 900.0], elapsed == 1950.0 < 45*60.
  • Clamp au deadline absolu : wait_sec = min(backoff, max(deadline - now, 0.0)) (pr_gate.py:1671-1674) — jamais de sleep au-delà du budget déclaré, --timeout-min jamais étendu. Le test clé (:381-402) fait avancer l'horloge PENDANT le sleep (deadline 0.2 min → premier backoff clampé à 12 s, 2 reads seulement) : c'est exactement le piège que le body annonce.
  • Fail-closed nommé : état illisible à l'une des deux bornes (cap 5 consécutifs OU deadline) → GateError avec famille nommée (installation-quota read failure vs transient read failure), cause + compte — direction rule 1 inchangée, jamais un pass (:1646-1661, testé test_exhausted_quota_is_a_failure_never_a_pass). C'est la lisibilité des rouges qui gagne : un FAIL quota ne se lit plus comme un verdict de check.
  • quiet_streak délibérément non touché sur échec de lecture (:1691-1693) — un outage ne peut pas faire mûrir le settling en PASS ; testé dédié. Compteur consécutif reset par une lecture réussie (:1695).
  • Recovery réelle : le test :359-378 montre le pass final venant d'un vrai état de check lu après l'outage (échecs à 0/30/150/450 s, succès à 1050 s) avec second quiet poll — pas de pass de complaisance post-outage.
  • Familles séparées : 503/générique garde la cadence hiccup 5×poll_sec (:404), permanent/unknown → raise immédiat (asymétrie conservée).

Relevé de checks au head : exit 8 (pending — PR de 15 min). Jobs déjà verts (guards, Analyze python, egress, notebook-plan). « Scripts Tests (CPU) » pending : le « 169 passed » du body est à ce stade une preuve locale — le job tranchera (famille #3878, à confirmer avant tout APPROVE). « PR gate » pending (DWELL 120 min, minuteur).

Non bloquant (2)

  1. Le marker unique est une chaîne de message GitHub verbatim : si la formulation change côté GitHub, le 403 retombe silencieusement sur la cadence courte (2,5 min) et re-produit le symptôme #17681 (7 FAIL en 3 min). Dépendance assumée et documentée dans le code — mais un jour-0 du marker sera silencieux.
  2. Un 403 de permission (non-quota) matche le marker transitoire « 403 » → 5 retries courts avant échec : comportement préexistant conservé, à garder en tête si un jour un vrai 403 permissionnel boucle.

Opener jsboige, mais checks exit 8 au moment du verdict → COMMENT (l'APPROVE attend l'exit nu ; « Scripts Tests (CPU) » et PR gate doivent verdir — lane suivante ou coordinateur).

@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@jsboige

jsboige commented Sep 26, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 17987
head: c39fac5
complete: true
body: read
comments-reviewed: 1
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 2b4c41d6f25d48e55452c0daea1258ca291599f7ffb98a2522a28379dd9377bd
diff-files: 2
diff-additions: 239
diff-deletions: 15
checks: blocked
b0: clear
scope: pass
domain: pass
verdict: BLOCKED
[/ADJOINT PREFLIGHT]

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #17987 (fix(ci): back off installation quota reads in PR gate) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

@jsboige

jsboige commented Sep 26, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2023:CoursIA
pr: 17987
head: c39fac5
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: db991bf218549f4953abcf09e34ffae4d44be96c740a3f0bd33baa86cba74768
diff-files: 2
diff-additions: 239
diff-deletions: 15
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Re-stamp du dossier c.5849278010 (po-2025:CoursIA-2, 19:40:37Z, meme tete c39fac5), perime par le commentaire bot path-collision @21:04:05Z — tete inchangee, surfaces re-hachees avec ce commentaire inclus.

La condition que la review NanoClaw attendait est verifiee satisfaite au pli : le verdict LGTM (COMMENTED, 19:18:27Z) etait rendu a checks exit 8 pending, l'opener ecrivant « l'APPROVE attend l'exit nu ; Scripts Tests (CPU) et PR gate doivent verdir ». Au head exact c39fac5 : Scripts Tests (CPU) success @19:14:36Z, PR gate success @22:12:54Z, 21 jambes / 21 noms toutes OK, aucune rouge residuelle, mergeStateStatus: CLEAN. La verification de substance de la review (ligne a ligne : predicat strict marker verbatim, escalation 30/120/300/600/900 s, clamp deadline, fail-closed nomme, recovery par vrai etat) porte ce head precis. Les 2 notes « non bloquant » du reviewer (marker verbatim GitHub, 403 permissionnel) sont sans glyphe de severite — advisory, non B.0.

Path-collision (le commentaire qui a perime) : verdicts terminaux — #17275 (100 %) et #17368 (50 %) sont deja sur main. Recouvrement de chemins, pas de redondance mesuree (#15768) ; la branche s'applique proprement sur main courant (CLEAN, PR gate vert a la tete exacte) — les livraisons sont disjointes en intention (backoff quota installation vs travaux precedents).

B.0 : check_unaddressed_nits.py rc=0 ; l'unique commentaire non evalue est le path-collision bot (advisory par design). Aucun thread inline.

Perimetre verifie : 2 fichiers scripts (pr_gate.py + test_pr_gate_transient_retry.py, +239/-15), coherent avec le titre ; Grain MED/guard — lane myia-po-2025:CoursIA — prev: DEEP/notebook-python #17960. Domaine : guard CI avec tests reels — 169 passed (body, post-dernier-changement), Scripts Tests vert au head : pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants