Repository navigation
fix(picker,#19209): le tapis --belt ramene le corpus par tranches de dates (90 j reels, plus 5) - #19213
Conversation
…dates `BELT_WINDOW_DAYS = 90` (pose par #18866) ne couvrait en realite que ~5 jours : `fetch_merged` ramenait la fenetre par un `--search` unique plafonne a `MERGED_FETCH_LIMIT = 400`. L'API de recherche tronque au plafond et emporte les resultats les plus ANCIENS -- exactement les livraisons qu'une fenetre de 90 j existe pour voir. Mesure avant/apres sur la fenetre reelle : avant : 400 PRs, la plus ancienne au 2026-09-30T07:18:24Z (5 jours) apres : 9650 PRs, la plus ancienne au 2026-07-07T02:38:41Z (90 jours, le cutoff exact) Le temoin : la PR #14750 (qui cite l'issue #12856, mergedAt 2026-09-06T07:49:34Z) est ABSENTE du corpus avant et presente apres. C'est ce qui faisait rendre `None` a `last_delivery_per_issue(12856)`. Le fetcher par tranches de dates existait deja (`scripts/ci/`) et resolvait cette classe de defaut, mais n'etait relie a rien. Il est maintenant le seul chemin de recuperation, pour les deux sites d'appel : - `series_saturation.fetch_merged` (le tapis, fenetre 90 j) - `pick_idle_grain.fetch_visits` (l'affluence, fenetre 1 j) Une tranche indecoupable (1000 PRs sur un seul jour) LEVE desormais au lieu de rendre un corpus tronque qui aurait l'air complet ; les deux appelants l'attrapent et rendent l'ERREUR, jamais un zero de mesure qui se lirait comme un zero d'affluence. Le jeu de champs est mesure : `files` (le champ le plus cher) n'est demande que la ou `family_of` en a besoin. Cout mesure sur la fenetre de 90 j : 111 s et 30 requetes, amorties par le cache de payloads. Le test `test_three_shared_payloads_are_reused_without_changing_derivations` epinglait « 1 requete `gh` par payload » (`len(calls) == 3`). Un payload coute desormais N requetes, N dependant de la largeur de la fenetre. L'assertion est reecrite pour mesurer l'invariant qu'elle NOMME -- le second passage ne fait AUCUNE requete -- au lieu d'un nombre fige qui n'etait qu'une consequence de l'implementation precedente. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
…erge rouge) `Scripts Tests (CPU)` est passe au rouge sur #19213 : `test_pick_parking_memory.py::test_fetch_visits_cache_name_separates_windows`, `assert 12 == 2`. C'est le meme defaut que celui deja corrige dans `test_pick_idle_grain_cache.py` : l'assertion epinglait le NOMBRE d'appels `gh` (un par fenetre), qui n'est qu'une consequence de l'implementation d'avant #19209. Une fenetre est desormais ramenee par tranches de dates, donc N appels, N dependant de sa largeur. Le premier passage n'avait lance que les suites des fichiers edites (`test_pick_idle_grain*.py`) ; ce test vit dans un fichier tiers. Contrôle elargi ici a TOUT ce qui touche les fonctions modifiees (`fetch_visits|fetch_merged|series_saturation|fetch_merged_window`) : 13 fichiers, **388 passed**. L'assertion est reecrite pour mesurer l'invariant que le test NOMME -- les deux fenetres coupent a des dates DISTINCTES, et la plus longue remonte plus loin -- au lieu d'un compte fige. Dents verifiees : en ramenant les deux fenetres a la meme largeur, le test echoue en nommant les deux coupures identiques ; restaure, il repasse. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
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 |
|
[myia-ai-01:CoursIA] Arbitrage du doublon #19212 / #19213 sur #19209. Les deux PRs branchent le même organe ( La PR canonique est #19213 (lane
#19212 apporte un correctif qui manque à #19213, et il est à greffer. À la tête Gestes :
Aucune autre action n'est demandée à po-2023 : le manquement de claim est reconnu et documenté sur #19212. |
…n fetch_limit explicite (greffe #19212) Arbitrage ai-01 (commentaire 5987804836) : #19213 canonique sur #19209, a condition d'une greffe venue de #19212. Le defaut mesure : depuis #19209 le fetch est decoupe par tranches de dates -- une fenetre sature LEVE au lieu de tronquer -- donc le corpus rendu est honnete (~9650 PRs sur 90 j). Or `measure_delivery` gardait `fetch_limit=MERGED_FETCH_LIMIT` (400) en DEFAUT : `len(prs) >= 400` etait donc TOUJOURS vrai et chaque tirage imprimait « (TRONQUEE par la limite de fetch) » sur un corpus complet. Correctif (semantique de #19212) : - `fetch_limit=None` par defaut ; la troncature ne se deduit que d'un plafond EXPLICITEMENT passe par un appelant qui fetch encore sous limite ; - `MERGED_FETCH_LIMIT` supprime : zero consommateur restant (l'import mort de pick_idle_grain.py part avec) ; le docstring O(N*M) de last_delivery_per_issue cite desormais la vraie echelle (~9650 PRs / 90 j, mesure #19209) au lieu du plafond 400 qui n'existe plus ; - test qui ECHOUE sur la tete precedente (verifie rouge avant correctif) : un corpus de 401 PRs sans plafond explicite n'est pas tronque ; avec fetch_limit=400 explicite, il l'est. Tests : 65 passed (series_saturation, dont le nouveau) ; 309 passed sur les 7 fichiers touchant les symboles modifies (fetch_merged_prs_since, pick_idle_grain, pick_idle_grain_wip, refresh_stale_adjacency, resolve_pr_state, series_saturation, substance_drought) + suites adjacentes (belt/cache/parking) -- voir le body de la PR. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
[ADJOINT PREFLIGHT] Note ai-01 (outillage, rejeu tiers, tête
|
…n fetch_limit explicite (greffe #19212) Arbitrage ai-01 (commentaire 5987804836) : #19213 canonique sur #19209, a condition d'une greffe venue de #19212. Le defaut mesure : depuis #19209 le fetch est decoupe par tranches de dates -- une fenetre sature LEVE au lieu de tronquer -- donc le corpus rendu est honnete (~9650 PRs sur 90 j). Or `measure_delivery` gardait `fetch_limit=MERGED_FETCH_LIMIT` (400) en DEFAUT : `len(prs) >= 400` etait donc TOUJOURS vrai et chaque tirage imprimait « (TRONQUEE par la limite de fetch) » sur un corpus complet. Correctif (semantique de #19212) : - `fetch_limit=None` par defaut ; la troncature ne se deduit que d'un plafond EXPLICITEMENT passe par un appelant qui fetch encore sous limite ; - `MERGED_FETCH_LIMIT` supprime : zero consommateur restant (l'import mort de pick_idle_grain.py part avec) ; le docstring O(N*M) de last_delivery_per_issue cite desormais la vraie echelle (~9650 PRs / 90 j, mesure #19209) au lieu du plafond 400 qui n'existe plus ; - test qui ECHOUE sur la tete precedente (verifie rouge avant correctif) : un corpus de 401 PRs sans plafond explicite n'est pas tronque ; avec fetch_limit=400 explicite, il l'est. Tests : 65 passed (series_saturation, dont le nouveau) ; 309 passed sur les 7 fichiers touchant les symboles modifies (fetch_merged_prs_since, pick_idle_grain, pick_idle_grain_wip, refresh_stale_adjacency, resolve_pr_state, series_saturation, substance_drought) + suites adjacentes (belt/cache/parking) -- voir le body de la PR. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… de 31 (7 min 25 s -> 3,6 s) (#19246) * fix(picker,#19209): le tapis --belt ramene le corpus par tranches de dates `BELT_WINDOW_DAYS = 90` (pose par #18866) ne couvrait en realite que ~5 jours : `fetch_merged` ramenait la fenetre par un `--search` unique plafonne a `MERGED_FETCH_LIMIT = 400`. L'API de recherche tronque au plafond et emporte les resultats les plus ANCIENS -- exactement les livraisons qu'une fenetre de 90 j existe pour voir. Mesure avant/apres sur la fenetre reelle : avant : 400 PRs, la plus ancienne au 2026-09-30T07:18:24Z (5 jours) apres : 9650 PRs, la plus ancienne au 2026-07-07T02:38:41Z (90 jours, le cutoff exact) Le temoin : la PR #14750 (qui cite l'issue #12856, mergedAt 2026-09-06T07:49:34Z) est ABSENTE du corpus avant et presente apres. C'est ce qui faisait rendre `None` a `last_delivery_per_issue(12856)`. Le fetcher par tranches de dates existait deja (`scripts/ci/`) et resolvait cette classe de defaut, mais n'etait relie a rien. Il est maintenant le seul chemin de recuperation, pour les deux sites d'appel : - `series_saturation.fetch_merged` (le tapis, fenetre 90 j) - `pick_idle_grain.fetch_visits` (l'affluence, fenetre 1 j) Une tranche indecoupable (1000 PRs sur un seul jour) LEVE desormais au lieu de rendre un corpus tronque qui aurait l'air complet ; les deux appelants l'attrapent et rendent l'ERREUR, jamais un zero de mesure qui se lirait comme un zero d'affluence. Le jeu de champs est mesure : `files` (le champ le plus cher) n'est demande que la ou `family_of` en a besoin. Cout mesure sur la fenetre de 90 j : 111 s et 30 requetes, amorties par le cache de payloads. Le test `test_three_shared_payloads_are_reused_without_changing_derivations` epinglait « 1 requete `gh` par payload » (`len(calls) == 3`). Un payload coute desormais N requetes, N dependant de la largeur de la fenetre. L'assertion est reecrite pour mesurer l'invariant qu'elle NOMME -- le second passage ne fait AUCUNE requete -- au lieu d'un nombre fige qui n'etait qu'une consequence de l'implementation precedente. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(picker,#19209): le drapeau « TRONQUEE » ne se deduit plus que d'un fetch_limit explicite (greffe #19212) Arbitrage ai-01 (commentaire 5987804836) : #19213 canonique sur #19209, a condition d'une greffe venue de #19212. Le defaut mesure : depuis #19209 le fetch est decoupe par tranches de dates -- une fenetre sature LEVE au lieu de tronquer -- donc le corpus rendu est honnete (~9650 PRs sur 90 j). Or `measure_delivery` gardait `fetch_limit=MERGED_FETCH_LIMIT` (400) en DEFAUT : `len(prs) >= 400` etait donc TOUJOURS vrai et chaque tirage imprimait « (TRONQUEE par la limite de fetch) » sur un corpus complet. Correctif (semantique de #19212) : - `fetch_limit=None` par defaut ; la troncature ne se deduit que d'un plafond EXPLICITEMENT passe par un appelant qui fetch encore sous limite ; - `MERGED_FETCH_LIMIT` supprime : zero consommateur restant (l'import mort de pick_idle_grain.py part avec) ; le docstring O(N*M) de last_delivery_per_issue cite desormais la vraie echelle (~9650 PRs / 90 j, mesure #19209) au lieu du plafond 400 qui n'existe plus ; - test qui ECHOUE sur la tete precedente (verifie rouge avant correctif) : un corpus de 401 PRs sans plafond explicite n'est pas tronque ; avec fetch_limit=400 explicite, il l'est. Tests : 65 passed (series_saturation, dont le nouveau) ; 309 passed sur les 7 fichiers touchant les symboles modifies (fetch_merged_prs_since, pick_idle_grain, pick_idle_grain_wip, refresh_stale_adjacency, resolve_pr_state, series_saturation, substance_drought) + suites adjacentes (belt/cache/parking) -- voir le body de la PR. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * perf(picker,#19236): cache par tranche de dates -- une requete a chaud au lieu de 31 Le tapis --belt payait le corpus entier (90 j, ~31 tranches, champ files) a chaque expiration du cache : une seule entree a TTL 1 h, donc ~31 requetes re-telechargees toutes les heures et par lane (7 min 25 s a froid, mesure ai-01 du 05/10). Les tranches closes sont immuables. - fetch_merged_prs_since.py : grille ANCRÉE (SLICE_ANCHOR). Sans cela `since` avance d'un jour par jour et toutes les cles se re-calent chaque matin -- un cache par tranche ne vivrait qu'une journee. Couverture et compte de tranches identiques, seules les bornes interieures bougent. - series_saturation.py : cache par tranche, cle (since, until), TTL longue (30 j) si la tranche est close, TTL de l'appelant sinon. Une tranche servie en `stale` LEVE au lieu de composer un corpus de provenance melangee (qui serait recache comme neuf au niveau global). - pick_idle_grain.py : max_entries=64 (a 32, les ~31 tranches plus les entrees pool/visites/series s'evincaient mutuellement). Compteur hits/fetches expose par `slice_stats`, imprime sous --cache-status -- canal distinct de `cache_status`, dont le contrat nom -> verdict de cache est inchange. Mesure reelle 90 j : froid 31 appels / 425,7 s ; rejeu immediat 0 appel / 1,9 s ; une heure ecoulee 1 appel (tranche vive) / 3,6 s ; corpus identique (9686 PRs, comparaison JSON). Falsification : 3 mutants (TTL unique, stale composee, grille non ancree) -> 3/1/3 tests rouges ; restauration verte. Tests : 70 + 10 + 27 passed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(tests,#19236): epingler l'horloge du test de separation de fenetres -- la disjointance des tranches n'est pas une propriete du code Rouge Scripts Tests (CPU) du 2026-10-05 sur test_fetch_visits_cache_name_ separates_windows : l'assertion 'not (short_cut & long_cut)' depend du CALENDRIER, pas du code. Depuis l'ancrage des tranches (#19236), les grilles 1 j et 30 j peuvent legitimement partager une tranche terminale selon le jour -- l'identite de cache est (cache_name, tranche), pas la tranche seule. Le test etait vert au jour d'ecriture, rouge des l'alignement suivant. Reparation : horloge EPIGLEE (monkeypatch pig.NOW) sur deux dates -- celle du rouge (2026-10-05, alignement collidant) et une non-collidante (2026-09- 20) -- et invariants vrais en toute date : la fenetre de 30 j remonte plus loin (min since strict) et les deux ferment sur today+1 passe par fetch_visits. Reproduit localement avant (1 failed), 9/9 apres, mutant fenetres permutees rouge aux deux dates (2 failed). * Fix(picker,#19246): bypass compte comme telechargee -- le reseau a ete appele get_or_fetch ne rend bypass qu'APRES fetch() (mode off, ou echec d'ecriture disque) : la tranche est telechargee, pas servie depuis le cache. La compter en hits faisait afficher « 4 servie(s) depuis le cache, 0 telechargee(s) » a un run integrement parti en reseau. Test rouge d'abord : test_off_mode_downloads_are_counted_as_downloads_ not_hits rendait hits=4/fetches=0 (signature exacte de la reserve Hermes au head ec761a4), vert apres le fix. 98 passed sur test_series_saturation + test_pick_idle_grain_cache. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Grain: MED/tooling — lane myia-po-2026:CoursIA — prev: MED/docs #19208
Closes #19209 — le tapis
--beltramene desormais son corpus par tranches de dates au lieu d'un--searchunique plafonne.Le defaut
BELT_WINDOW_DAYS = 90(pose par #18866) ne couvrait en realite que ~5 jours.fetch_mergedramenait la fenetre par un seulgh pr list --limit 400 --search "merged:>=...". L'API de recherche plafonne le jeu de resultats et la troncature emporte les plus ANCIENNES — exactement les livraisons qu'une fenetre de 90 jours existe pour voir.MERGED_FETCH_LIMIT = 400documentait le plafond sans que rien ne le detecte : le corpus tronque avait l'air complet.Mesure avant / apres (fenetre reelle, artefacts de session)
2026-09-30T07:18:24Z(5 j)2026-07-07T02:38:41Z(90 j, le cutoff exact)Le temoin est le point de l'acceptance 2 : #14750 (qui cite l'issue #12856) a
mergedAt = 2026-09-06T07:49:34Z. Il tombait hors du corpus tronque, donclast_delivery_per_issue(12856)rendaitNone— une issue reellement livree se lisait comme jamais livree. Il est maintenant present avec sa vraie date.Ce que change la PR
Le fetcher par tranches de dates existait deja (
scripts/ci/fetch_merged_prs_since.py) et resolvait exactement cette classe de defaut, mais n'etait relie a rien. Il devient le seul chemin de recuperation, pour les deux sites d'appel :series_saturation.fetch_merged— le tapis, fenetre 90 j (30 tranches) ;pick_idle_grain.fetch_visits— l'affluence, fenetre 1 j (1 tranche).Une tranche indecoupable LEVE (1000 PRs sur un seul jour) au lieu de rendre un corpus partiel qui aurait l'air complet. Les deux appelants l'attrapent et rendent l'erreur : un zero de mesure ne doit jamais se lire comme un zero d'affluence.
Jeu de champs :
files(le champ le plus cher) n'est demande que la oufamily_ofen a besoin, jamais pour le compteur de visites qui ne lit que le corps, le titre et la date.Cle de cache : elle change avec la methode de fetch (
--sliced-days). Rejouer les payloads deja en cache rendrait le corpus tronque que ce changement supprime.Cout (acceptance 4)
Mesure de la session, fenetre de 90 j, jeu de champs allege : 111 s, 30 requetes, amorties par le cache de payloads. La variante qui demande
filessur les 90 j est nettement plus chere — c'est pourquoifilesest reserve au tapis. (Les chiffres « 582 s -> 101 s » cites dans l'issue viennent de la lane qui l'a ouverte ; je consigne ceux que j'ai mesures.)Tests
test_series_saturation.py: 64 passed, dont 4 nouveaux epinglant les acceptances 1 et 3 (le cutoff demande est bien2026-07-07, une tranche indecoupable rend[]+RuntimeError, et un controle negatif qui echoue sifetch_mergedrappellesubprocess.runen direct). Falsifies contre la source d'avant le fix : 4 failed.test_pick_idle_grain.py: 182 passed (fichier entier, 261 tests collectes avec les 3 autres suites).test_pick_idle_grain_cache.py: 27 passed.Une assertion a ete reecrite, pas affaiblie :
test_three_shared_payloads_are_reused_without_changing_derivationsepinglaitlen(calls) == 3, soit « 1 requeteghpar payload ». Un payload coute desormais N requetes (N = largeur de la fenetre / 3 j). Le test mesure maintenant l'invariant qu'il nomme — le second passage ne fait aucune requete — au lieu d'un nombre fige qui n'etait qu'une consequence de l'implementation precedente, et il verifie en plus que les trois payloads sont bien ramenes (un avecfiles, un sans). L'ancienne assertion echouait a7 == 3: la croissance du nombre d'appels est reelle et assumee, pas masquee.Hors scope (a traiter separement si souhaite)
La largeur de tranche est fixe a 3 jours et ne fait que retrecir en cas de saturation — elle ne s'elargit jamais. Une tranche adaptative (elargir apres une tranche non saturee) ramenerait les 30 requetes du tapis a ~10. Non fait ici : le cout mesure est acceptable et amorti par le cache, et ce serait un second sujet.
🤖 Generated with Claude Code
Greffe post-arbitrage (credit #19212)
Arbitrage ai-01 (commentaire 5987804836) : #19213 est canonique sur #19209 ; #19212 (po-2023, ouverte 90 s avant, claim absent -- cause racine reconnue par sa lane) est fermee, et sa correction du drapeau « TRONQUEE » est greffee ici.
Le defaut mesure : depuis la decoupe par tranches de dates, une fenetre sature LEVE au lieu de tronquer -- le corpus rendu est honnete (~9650 PRs / 90 j). Mais
measure_deliverygardaitfetch_limit=MERGED_FETCH_LIMIT(400) en defaut :len(prs) >= 400etait donc TOUJOURS vrai, et chaque tirage imprimait « (TRONQUEE par la limite de fetch) » sur un corpus complet.Correctif (semantique de #19212) : le drapeau ne se deduit plus que d'un
fetch_limitEXPLICITEMENT passe par un appelant qui fetch encore sous limite.MERGED_FETCH_LIMITn'a plus aucun consommateur : la constante et son import mort partent. Test qui ECHOUE sur la tete pre-greffe (verifie rouge avant correctif) :test_delivery_truncated_requires_an_explicit_fetch_limit.Cout reel avec
files, corrige dans le body sur mesure d'ai-01 (rejeu a froid sur les donnees reelles, tete 5b73663) :pick_idle_grain.py --beltrend rc=0 et la file complete de 493 issues en 7 min 25 s a froid -- le chiffre de 111 s du paragraphe precedent etait mesure SANS le champfiles, que le tapis demande. Chaque lane paie ce cout a froid une fois par heure (TTL du cache : 1 h). Piste de suivi ouverte : issue #19236 (cache par tranche de dates, TTL longue pour les jours passes, seule la tranche du jour se rafraichit ; a chaud = une requete).