Skip to content

fix(picker,#17474): fetch_open_prs — plafond aligné sur le pool, troncature dite - #17568

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/17474-pr-fetch-truncation
Sep 24, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/17474-pr-fetch-truncation

Conversation

@jsboige

@jsboige jsboige commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Grain: LIGHT/tooling -- lane myia-po-2026:CoursIA -- prev: LIGHT/tooling #17457

Probleme

fetch_open_prs() demandait --limit 300 a gh pr list. Or gh rend du plus
RECENT au plus ancien -- mesure firsthand du 2026-09-23 sur jsboige/CoursIA :
158 PRs ouvertes, tete #17565 (2026-09-23T13:29:52Z), queue #15942
(2026-09-13T08:33:00Z). Un plafond franchi fait donc disparaitre les PRs les
plus ANCIENNES, en silence et sans erreur -- exactement celles que les deux
appelants existent pour voir : la file de reparation (unattributed_blocked_prs,
une PR bloquee depuis plus de 24 h est d'abord une candidate de reparation) et le
compte WIP de lane (Q41, #17457). Le fichier definissait deja
POOL_FETCH_LIMIT = 2000 cote issues, avec un commentaire decrivant ce risque mot
pour mot ; cote PRs, la garde manquait.

Ce que fait la PR

  • OPEN_PRS_FETCH_LIMIT = POOL_FETCH_LIMIT, demande a gh a la place du 300
    litteral.
  • [PRS TRONQUEES] sur stderr quand le nombre rendu atteint le plafond (meme
    convention que [POOL TRONQUE]), nommant l'inversion recent/ancien.
  • La bascule REST de tooling(picker,#16765): pick_idle_grain — fallback REST et distinction « pool vide » / « non mesurable » #17038 portait le meme defaut, et il est couvert ici : la
    voie gh api repos/.../pulls a son propre plafond (POOL_REST_PAGE x
    POOL_REST_MAX_PAGES = 400) et s'arrete sans rien lever. Le meme message y est
    emis, avec le remede qui nomme POOL_REST_MAX_PAGES. Le texte est extrait en
    _warn_open_prs_truncated(rendered, ceiling, remedy) : deux appelants, un seul
    message.
  • Cout nul sous le plafond, mesure : GH_DEBUG=api gh pr list --limit 300 et
    --limit 2000 sur 158 ouvertes = 2 requetes dans les deux cas (gh pagine
    par 100 et s'arrete a l'epuisement de la population comme au plafond). Aucun
    avertissement, payload identique.

Merge origin/main (union avec #17158, commit b749e72116)

#17158 (abf155556) a restructure fetch_open_prs -- gh pr list en try,
gh api repos/.../pulls en bascule -- et entrait en conflit textuel sur la meme
fonction. Resolution en union, ni --ours ni --theirs :

Voie Avant la fusion Apres
GraphQL --limit 300 (main) / plafond nomme (branche) plafond nomme + troncature dite (branche)
REST absente (branche) / presente (main) conservee telle quelle + troncature dite (ajout de cette fusion)

La seconde ligne est un ajout de la fusion, pas un elargissement de sujet : le
defaut corrige ici est « un plafond franchi se tait », et la bascule de #17038 en
ouvrait une seconde porte avec le meme silence. Nommer le plafond d'une voie en
laissant l'autre muette aurait rendu la correction dependante du transport.

Preuve

  • python -m pytest scripts/tests/test_pick_idle_grain.py -q : 161 passed ;
    suite picker complete (test_pick_*) : 204 passed.
  • Test de faux negatif (test_the_oldest_blocked_orphan_survives_the_cap) : un
    faux gh reproduit la troncature REELLE (il rend population[:limit]), la
    population portant 320 PRs taggees devant une orpheline bloquee. Controle de
    falsification dans le test
    : au plafond historique de 300, la meme fixture ne
    voit RIEN (plafond vs 300 -> unattributed_blocked_prs() == []) ; au plafond
    aligne, elle voit la traine. Un test qui serait vert avec le bug ne mord pas.
  • test_open_prs_truncation_is_said_not_silent : silence sous le plafond,
    [PRS TRONQUEES] au plafond.
  • test_truncation_rest_est_dite_comme_celle_de_graphql : pages pleines jusqu'a
    epuisement du quota REST -> message ; puis controle NEGATIF (max_pages
    releve d'une unite, pages inchangees) -> silence sous le plafond. Sans ce
    controle, un avertissement inconditionnel passerait le test.
  • Live : fetch_open_prs() sur l'ouvert reel rend 158 PRs, sans avertissement.
  • Resolution de fusion verifiee : 0 marqueur de conflit, ast.parse OK, 161
    passed. Pre-commit au commit de fusion (gitleaks, text=True sans
    encoding, H.3) : passed.
  • ruff : distribution de regles identique a origin/main (31 findings
    pre-existants, aucun nouveau) -- verifie par diff des listes --output-format=concise.

Perimetre

Périmètre : 2 fichiers -- scripts/pick_idle_grain.py (+49/-2) et
scripts/tests/test_pick_idle_grain.py (+129/-5), mesures sur
git diff --numstat origin/main...HEAD a la tete b749e72116. Aucun workflow CI
touche ; catalogue byte-identique a main (R1).

Hors scope (signale, non touche)

Le meme litteral 300 sur l'ouvert existe dans scripts/review_coverage.py:234
(autre organe, autre appelant) : meme classe de defaut, PR dediee si confirme.
Le cap WIP lui-meme (#17457) et le reste du picker restent hors scope, comme
l'issue le pose.

Closes #17474

…ncature dite

`fetch_open_prs()` demandait `--limit 300` a `gh pr list`, qui rend du plus
RECENT au plus ancien : un plafond franchi faisait disparaitre les PRs les plus
ANCIENNES, en silence -- precisement la traine que la file de reparation
(`unattributed_blocked_prs`) et le compte WIP de lane (Q41, #17457) existent
pour voir.

- `OPEN_PRS_FETCH_LIMIT = POOL_FETCH_LIMIT` (meme garde que le pool d'issues,
  qui la portait deja avec le commentaire decrivant ce risque).
- `[PRS TRONQUEES]` sur stderr au plafond, nommant l'inversion recent/ancien.
- Cout nul sous le plafond : `GH_DEBUG=api` mesure 2 requetes pour 158 ouvertes
  aux deux plafonds (`gh` pagine par 100 et s'arrete a l'epuisement).
- Tests : faux `gh` qui reproduit la troncature reelle (`population[:limit]`),
  avec controle de falsification -- au plafond de 300 la meme fixture ne voit
  rien, au plafond aligne elle voit l'orpheline la plus ancienne.

156 passed sur le fichier, 204 sur la suite picker ; ruff identique a
`origin/main`.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-po-2026:CoursIA` voit ces signaux actifs sur les mergees du jour (UTC 2026-09-23) :

G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au 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).

…e dite sur les DEUX transports

Conflit unique dans `scripts/pick_idle_grain.py` : #17158 (abf1555) a
restructure `fetch_open_prs` (try GraphQL / bascule REST). Resolution en
UNION, ni --ours ni --theirs :

- voie GraphQL : `--limit 300` code en dur remplace par
  `OPEN_PRS_FETCH_LIMIT = POOL_FETCH_LIMIT` (2000) + signature de troncature ;
- voie REST (nouvelle sur main) : conservee telle quelle, et son propre
  plafond (POOL_REST_PAGE x POOL_REST_MAX_PAGES = 400) se dit desormais lui
  aussi -- sinon la bascule de #17038 rouvrait la troncature muette par
  l'autre porte, qui est exactement le defaut corrige ici ;
- message extrait en `_warn_open_prs_truncated(rendered, ceiling, remedy)` :
  deux appelants, un seul texte.

Verifie apres resolution : 0 marqueur de conflit, AST OK, 161 passed (dont le
controle NEGATIF : sous le plafond, rien n'est dit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #17568 (fix(picker,#17474): fetch_open_prs — plafond aligné sur le pool, troncature dite) 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 23, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 17568
head: b749e72
complete: true
body: read
comments-reviewed: 3
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: d16b36ce675eaf3cd0043d7c90b3caade8d733bb16f522aad462e9981b1ea924
diff-files: 2
diff-additions: 178
diff-deletions: 7
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

@myia-ai-01
myia-ai-01 merged commit dca1eb9 into main Sep 24, 2026
18 of 24 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 26, 2026
…pus d'issues ouvertes (#17860)

L'organe livre par #13937 ne mesure plus rien. `list_open_epics` demandait
`--limit 500` puis levait des que la reponse atteignait ce plafond, et le depot
est passe a 506 issues ouvertes (mesure firsthand : `search/issues` ->
`total_count: 506`). `python scripts/epic_body_staleness.py` sortait donc rc=1
avec `open-issue corpus reached its 500-issue fetch limit`, sans aucun resultat :
le mandat de curation de #13906 (les bodies d'Epic qui ignorent leurs propres
livraisons) n'etait plus mesurable au niveau programme.

Un `--limit` fixe ne peut pas distinguer « le depot a N issues ouvertes » de
« la lecture s'est arretee a N ». La sonde elargit la requete jusqu'a ce qu'une
reponse revienne plus courte que demandee -- seul observable qui prouve
l'epuisement -- et refuse au plafond plutot que de presenter un corpus tronque
comme complet. Le refus reste fail-CLOSED : la borne de 500 etait sous la
donnee, donc elle n'etait plus un garde mais un interrupteur.

Un payload qui n'est pas une liste est desormais refuse lui aussi : `null` est
un corpus non lu, jamais un corpus vide (l'ancien `or []` confondait les deux).

Classe deja rencontree sur ce depot : #17474 (`fetch_open_prs` tronque a 300
PRs, corrige par #17568 pour le picker, sans traiter la classe).

See #13906

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

pick_idle_grain : fetch_open_prs() tronque à 300 PRs en perdant les plus anciennes (file de réparation et compte WIP aveugles au-delà)

2 participants