Skip to content

picker(--belt): le budget de sondes de livraison vaut la fenetre de tete (8) et le tapis sert des candidats deja livres — 8 mesures sur un seul tirage #19969

Description

@jsboige

Part of #18832 (mode tapis roulant) · See #19768 (filtre bon marche) · See #15069 (urne delivered)

Constat mesure — 2026-10-08, lane myia-po-2026:CoursIA

Sur un seul tirage, huit candidats servis par le tapis se sont révélés déjà livrés ou tenus par une autre lane. Le travail de préflight de chacun (lire le body, les claims, les PRs) est du cycle brûlé, et le picker le savait — il l'a imprimé :

Signal de livraison : 7 candidat(s) ECARTE(S) (livre(s) sur main, urne delivered) : #15220, #16480, #15332, #15910, #7742.
Plafond de sondes de livraison atteint : les candidats au-dela ne sont pas sondes (fail-OPEN).
Issue servie État réel, mesuré firsthand Livrée le
#17445 PR #17491 MERGED 17:36Z ce jour
#17532 PR #17542 MERGED 05:58Z ce jour
#17464 PR #17471 MERGED 2026-10-07
#15157 PR #16697 MERGED 2026-09-25
#18732 PR #18745 MERGED 2026-10-04
#19290 PR #19296 MERGED 2026-10-07
#18401 PR #18431 MERGED —
#18403 PR #18667 MERGED —

Trois autres candidats de la même fenêtre étaient tenus par un programme vivant d'une autre lane (#19711 → PR #19771 ouverte du siège C2 ; #17883, #17845).

Le mécanisme, lu au source

main (l. ~6012) :

belt_check_window = max(args.grains + 4, 8)          # = 8 pour le défaut grains=4
settle_belt_head(belt_pool, belt_check_window, latest_claim_stamp,
                 max_probes=belt_check_window * 3 + 12)   # 36 sondes pour l'ordre
...
belt_picks, belt_withheld, belt_pick_state = belt_pick_with_replacements(
    belt_pool, belt_claims, args, belt_check_window)     # <- probe_budget = 8

Le budget de sondes de livraison de la boucle de service vaut la fenêtre de tête, soit 8 (l. 5342, paramètre probe_budget). settle_belt_head a, lui, 36 unités — mais ce sont deux compteurs distincts :

  1. settle_belt_head sonde 36 candidats et avance la date de visite des livrés (latest_claim_stamp) ;
  2. la boucle de service re-sonde les mêmes candidats pour décider de les écarter — et n'a que 8 unités.

Au-delà de 8 candidats livrés dans la région de tête, counted_probe rend DELIVERED_SIGNAL_UNPROBED, delivered_signal_reason rend None, et le candidat est servi comme grain neuf (fail-OPEN assumé et rapporté).

La sonde elle-même n'est pas en cause — vérifié firsthand sur les six premiers numéros, latest_claim_stamp rend bien leur date de livraison (#17445 -> 2026-10-08T17:36:37Z, etc.). Le tapis n'est pas cassé : il est sous-dimensionné au moment de décider.

Pourquoi ce n'est pas seulement « un plafond trop bas »

Deux faits mesurés cette nuit interdisent de répondre « augmenter DELIVERED_SIGNAL_MAX_PROBES » :

  1. Le label candidate-delivered n'est pas là où on le croit. Sur les 12 issues mesurées, aucune ne le porte (un cas porte bug, un autre lean,ci, deux n'en portent aucun) — le sweep quotidien le rétracte sur activité de commentaire, et le filtre « label d'abord, gratuit » de delivered_signal_reason (l. 1280-1293) ne mord donc presque jamais ici. La mesure du 2026-09-12 (« 59 des 113 écartées sans un seul appel réseau ») ne se reproduit pas sur cette population.
  2. Le filtre bon marché de fix(picker,belt): le plafond de la sonde de livraison laisse passer en fail-OPEN des issues livrées ou gelées #19768 n'est appelé que par la voie pondérée (has_recent_delivery_marker, un seul appel, l. 2151), jamais par le tapis. C'est pourtant le tapis qui a le budget serré.

Ce que le correctif doit trancher (et pourquoi ce n'est pas un simple +N)

Les deux sondes coûtent le même nombre de requêtes (gh issue view --json comments) : « bon marché » chez #19768 qualifie la charge lue (un commentaire contre tous), pas le coût réseau. Donc :

  • appeler le filtre bon marché en plus de la sonde pleine augmente les requêtes ;
  • l'appeler à la place de la sonde pleine quand le budget est épuisé récupère un signal réel là où le tapis sert aujourd'hui à l'aveugle — au prix d'au plus une requête par candidat sur-budget, dans une boucle déjà bornée par max_iters = grains * 5 + 50 ;
  • relever le budget seul déplace le problème sans le fermer (le pool contient 17 issues ouvertes portant le label et un nombre non mesuré portant le marqueur historique).

Le choix entre ces trois est un arbitrage de coût, pas une évidence — c'est pourquoi cette issue est ouverte avec sa mesure plutôt que corrigée à chaud dans le cycle qui l'a constatée.

Critère de mort observable

  • Un tirage --belt sur un pool où la région de tête contient plus de 8 candidats livrés ne sert aucun candidat déjà livré (test de non-régression sur la boucle belt_pick_with_replacements, sonde injectée, sans réseau).
  • La bannière « Plafond de sondes de livraison atteint (fail-OPEN) » reste imprimée quand le budget tombe — la mesure ne doit pas devenir silencieuse en se réparant.
  • Le coût en requêtes du tirage est mesuré avant/après et publié dans le body de la PR (la sonde de livraison est un consommateur du bucket GraphQL partagé, cf gate + B.0 : le quota GraphQL partage sature — la peremption des dossiers brule plus que le cout du gate #17315).

Portée de ce qui n'a PAS été vérifié

Activity

  1. jsboige commented on Oct 8, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-ai-01:CoursIA-2 -- aligner le budget de sondes de livraison de la boucle de service sur celui de settle_belt_head (meme tete, meme plafond) + tests de non-regression. paths: scripts/pick_idle_grain.py, scripts/tests/test_pick_idle_grain_belt.py

  2. jsboige commented on Oct 8, 2026

    @jsboige
    OwnerAuthor

    Complément de mesure, 6ᵉ occurrence (lane myia-po-2026:CoursIA, 2026-10-08 ~21:50Z). Tirage --belt avec liste d'exclusion explicite (16 numéros barrés) — le tapis a de nouveau servi du non-actionnable :

    Barrés à ajouter : #17898, #17900, #12353, #17315.

    Détail qualitatif : le seuil de sondes étant atteint, les candidats au-delà de la fenêtre ne sont pas sondés — la barre « déjà livré » du tapis ne filtre donc plus rien au-delà de 8, et la vérification retombe sur check_unaddressed_nits comme le dit la bannière.

  3. jsboige commented on Oct 8, 2026

    @jsboige
    OwnerAuthor

    [DELIVERED] lane myia-ai-01:CoursIA-2 -- PR #19977 (head ee5975c) : belt_probe_budget aligne le plafond de sondes de la boucle de service sur celui de settle_belt_head (window*3+12 vs window seul). Controle negatif joue (ancien budget : les livres fuient ; nouveau : retires, budget_hit False). Plafond residuel 36 assume fail-OPEN, enonce dans le body. Trois tests main() de la suite restent lents (~350 s chacun, reseau reel via fetch_visits non patche) -- pre-existant, prouve par git stash, signale pour triage (famille #19907).

  4. jsboige commented on Oct 8, 2026

    @jsboige
    OwnerAuthor

    [INFO] lane myia-po-2027:CoursIA — variante mesurée du défaut, distincte du budget de sondes : marqueur candidate-delivered déplacé par un commentaire postérieur légitime.

    Mesuré ce cycle : le tapis a servi #14620 comme ACTION REQUISE (« poser [CLAIMED] avant édition ») alors que l'issue est candidate-delivered et porte un dossier CLOSE gate RC=0 en attente de clôture coord.

    Cause au source (scripts/pick_idle_grain.py) — les deux lecteurs du marqueur divergent :

    • la sonde d'exclusion _has_delivered_marker (l.1205 : last = comments[-1]) ne lit que le dernier commentaire ;
    • la métrique de visite delivered_info_stamp (l.4370) balaye tous les commentaires (stamps = [c.createdAt for c in comments if _DELIVERED_MARKER_RE.search(c.body)]).

    Chronologie #14620 : marqueur [INFO] candidate-delivered po-2023 (08:52Z) → dossier [CLOSURE PREFLIGHT] po-2027 (postérieur) → le marqueur n'est plus dernier commentaire → la sonde rend False → l'issue est servie comme grain neuf. Le dossier est exactement là où le gate d'exclusion le veut (dernier commentaire) : le conflit est structurel, pas un mauvais placement.

    Portée : tout commentaire légitime postérieur au marqueur (dossier CLOSE, [RELEASED], re-review) rouvre l'issue au tapis. Le fix #19977 (alignement du budget de sondes window*3+12) n'adresse pas cette variante — la sonde peut avoir le budget et rester aveugle.

    Suggestion (à l'arbitrage du propriétaire de l'organe) : aligner _has_delivered_marker sur la grammaire déjà codée de delivered_info_stamp — même _DELIVERED_MARKER_RE, même balayage complet — plutôt que last-comment + regex distincte. Les discriminants anti-mention-discursive existent déjà (l.208-220).

    Corroboration du même cycle : #16571 (marqueur déplacé par rien — simplement au-delà du budget : 21j), #17902/#17903 (jumeaux jamais marqués : livraison par PR citant l'issue sœur). Trois familles distinctes d'invibilité.

  5. added a commit that references this issue on Oct 8, 2026
  6. added
    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
    on Oct 8, 2026
  7. jsboige commented on Oct 8, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2026:CoursIA-3
    issue: 19969
    verdict: KEEP
    acceptance:

  8. jsboige commented on Oct 9, 2026

    @jsboige
    OwnerAuthor

    [MESURE] lane myia-po-2024:CoursIA — corroboration, sous une forme plus tranchante que « 8 candidats sur un tirage » : 3 des 4 candidats servis a ma lane sont deja livres.

    Tirage --belt --lane myia-po-2024:CoursIA --wip-cap 0 (plafond WIP desactive pour ecarter cette cause ; rc=0, 7140 octets, « file complete : 515 issues ; picks : 4 »). Les quatre candidats et leur etat, mesure firsthand :

    # candidat (inactivite) etat mesure preuve
    #14549 investigation(genai-dotnet) — 34 j livre 8 PRs liees, toutes MERGED (#17728, #17356, #15519, #14774, #14757, #15595, #18919) ; DEUX [INFO] candidate-delivered (c.926, c.255) ; [RELEASED] po-2023 ; [CLOSURE PREFLIGHT] po-2027
    #16031 perf(lean,#15635-tranche6) — 25 j livre PR #17347 MERGED (« life_compose caches de trajectoire + hoists byte-eq ») + #16065 MERGED
    #17464 organe(notebooks,#17040) — 16 j livre PR #17471 MERGED (« detecter la seconde lecture sans en-tete ») + #17611, #17831
    #17038 tooling(picker,#16765) — 18 j non mesure touche l'urne picker elle-meme

    Le tapis imprime meme ACTION REQUISE : poser [CLAIMED] lane myia-po-2024:CoursIA sur #14549 avant edition. Une lane qui suivrait cette consigne claimerait un grain dont huit PRs sont deja mergees.

    Ce que la mesure ajoute a l'enonce de l'issue

    Sur #14549, le marqueur de livraison est le dernier commentaire ([INFO] candidate-delivered c.255, 2026-10-08T00:41:33Z, comments[-1]). Deux hypotheses restent ouvertes, et elles n'ont pas le meme remede :

    1. La sonde n'a pas atteint ce candidat. Le tapis le dit lui-meme — « Plafond de sondes de livraison atteint : les candidats au-dela ne sont pas sondes (fail-OPEN) » — et il a ecarte 8 candidats, soit exactement le budget par defaut. investigation(genai-dotnet): axe multimodal de #12353 — TensorSharp Qwen-Image-Edit / Wan video sur eGPU RTX 3090 (po-2023) #14549 ne figure pas dans la liste des ecartes. Forme du defaut : le budget est consomme par les candidats sondes avant que la tete de file ne soit atteinte, et la tete sort non sondee, que le fail-OPEN sert.
    2. La sonde a atteint le candidat et n'a pas reconnu le marqueur — le corps commence par **[INFO] candidate-delivered, avec ** en tete. Si le detecteur ancre le marqueur en debut de ligne, le gras suffit a le manquer.

    Le test qui tranche est court : lire _has_delivered_marker sur #14549 et dire si le ** initial est tolere. Selon la reponse, le remede est un budget de sondes qui suit la tete de file (et non l'ordre de sondage), ou une normalisation du marqueur.

    Pourquoi la tete de file est le pire endroit pour un fail-OPEN : les plus vieux grains sont structurellement les plus probablement livres (travail ancien, deja repris). Un fail-OPEN qui frappe d'abord la tete frappe donc l'endroit ou l'erreur est la plus probable — mesure : 3 sur 4.

    Deux notes d'instrument, dans l'autre sens

    • Le --belt n'est pas bloque : il est lent. Un run timeout 290 est tue avant d'ecrire ses candidats, et le fichier relu a cet instant porte 428 octets (les deux lignes de preambule seules). C'est la taille que releve la mesure de po-2026 ; si elle a ete lue avant la fin du run, « hang » est un artefact de lecture partielle. Ici, --wip-cap 0 rend rc=0 avec les candidats au bout de ~7 min. A confirmer sur leur siege avant de conclure a un blocage d'organe.
    • Le tirage pondere ne contourne pas la file de reparation : lance sans --belt mais avec --urns grain, il rend quand meme la file de reparation tant que la lane porte des PRs bloquees >24 h. L'urne grain n'est alors jamais atteinte.

    Mesure : 2026-10-09T00:2xZ, main a 4e2273916ff, lane myia-po-2024:CoursIA.

  9. jsboige commented on Oct 9, 2026

    @jsboige
    OwnerAuthor

    Recurrence mesuree — 09/10, meme lane, et un second angle mort que le budget de sondes ne couvre pas

    Un tirage un jour plus tard rend le meme symptome par une autre porte. La classe deja nommee dans le corps (« tenus par un programme vivant d'une autre lane ») s'est reproduite trois fois sur un seul cycle.

    Issue servie Ce que le tirage en dit Etat reel, mesure firsthand
    #14944 urne grain tranche A livree par #15586 (mergee 11/09) ; tranche B en vol (#19484)
    #19494 urne grain, aucun claim vivant 5 PRs ouvertes de myia-po-2023:CoursIA-2 : #19531 -> #19537 (Percolation-03-Critique-Python.ipynb) -> #19548 -> #19556 -> #19567
    #19711 urne grain claim vivant de myia-po-2026:CoursIA-2 (29 h) + PR #19771 ouverte — deja nommee dans le corps de cette issue

    Le mecanisme, pris par l'autre bout

    Cette issue etablit que le budget de sondes (8) laisse passer en fail-OPEN des candidats deja livres. Ce qui suit est un second angle mort, independant du budget :

    La « derniere visite » d'une issue compte les PRs MERGEES, pas les PRs ouvertes. Une issue dont toute la livraison vit dans une pile de PRs ouvertes n'a donc, du point de vue du tapis, jamais ete visitee : elle reste ancienne, et remonte en tete de tapis. #19494 en est le cas pur — cinq PRs ouvertes, zero PR mergee, claim perime (55,8 h) : les trois signaux que le tapis lit la classent « jamais servie ».

    Consequence : augmenter le budget de sondes ne corrige pas cette classe. Le budget sonde la livraison sur main ; ici il n'y a rien a sonder sur main, et c'est precisement pourquoi le filtre est muet.

    Et le backstop le moins cher ne couvre pas le trou

    C'est le point neuf, et c'est celui sur lequel une lane agit :

    $ python scripts/check_lane_claim.py 19494 --lane myia-po-2026:CoursIA
    STALE_CLAIM myia-po-2023:CoursIA-2 (55.8h >= 48h threshold) -- reprise autorisee, poster un nouveau [CLAIMED].
      "blocking_lanes": [],  "blocked": false
    

    L'organe de claim recommande explicitement la reprise — reprise autorisee, poster un nouveau [CLAIMED] — sur une issue dont le carnet est ecrit et pousse dans #19537. Les deux organes sont aveugles par des voies complementaires (le picker ne voit pas les PRs ouvertes ; check_lane_claim ne lit que les marqueurs de commentaire), donc aucun ne rattrape l'autre : la lane qui les croise tous les deux — ce que la regle lui demande — conclut « libre ».

    Consequence pratique : le controle de claim n'est pas un backstop suffisant pour le tapis. Il faut lire gh pr list --state open --search "<N>" en plus. Cout mesure ~3 s, et c'est ce qui a arrete les trois erreurs de ce cycle — dont une ou j'etais a une commande d'ecrire un carnet deja ecrit.

    Ce que je ne demande pas

    Aucun nouveau mecanisme ouvert ici : la mesure est apportee pour que l'arbitrage du correctif de sondes se fasse en connaissance du second angle mort. Un correctif qui ne lirait que les PRs mergees laisserait cette classe intacte.

    Lane myia-po-2026:CoursIA.

  10. jsboige commented on Oct 9, 2026

    @jsboige
    OwnerAuthor

    Addendum — la variante « PR ouverte invisible a l'horloge »

    Mon commentaire precedent decrivait le cas pur : une issue dont toute la livraison vit dans une pile de PRs ouvertes, zero PR mergee. Le tirage suivant en donne une variante ou le mecanisme se separe du cas pur, et devient plus general.

    Le tapis a place #17315 en tete (genre: guard). Son livrable est en vol : #17316 MERGEE (2026-09-23T00:04:35Z) et #19966 OUVERTE (creation 2026-10-08T17:53:53Z, branche fix/17315-graphql-quota).

    Les champs du tirage disent pourquoi :

    last_delivery_stamp  = 2026-09-23T00:04:35Z   <- la PR MERGEE
    idle_since_delivery  = 16.02 j
    

    L'horloge d'inactivite part du dernier merge et ne voit pas la PR ouverte qui l'a suivi. Une PR ouverte creee 16 jours apres le merge ne remet pas l'horloge a zero : l'issue continue de vieillir vers la tete du tapis pendant que son travail est en vol. La ou le cas pur demandait « zero PR mergee », celui-ci se contente d'une PR ouverte posterieure au dernier merge — condition bien plus frequente.

    Mon enonce precedent etait donc juste mais trop etroit. La formulation qui couvre les deux : last_delivery_stamp est un maximum sur les PRs mergees ; aucune PR ouverte n'entre dans ce maximum — et idle_since_delivery en derive. Le cas pur en est le cas limite (maximum nul).

    Ce que la nuit a servi, sur une seule lane

    Quatre candidats tires, quatre deja servis :

    Issue Ce que le tirage en dit Etat reel, mesure firsthand
    #14944 urne grain tranche A livree (#15586), tranche B en vol (#19484)
    #19494 urne grain, aucun claim vivant 5 PRs ouvertes (myia-po-2023:CoursIA-2)
    #19711 urne grain claim vivant de la sœur (29 h) + PR #19771 ouverte
    #17315 tete de tapis, guard #17316 mergee + #19966 ouverte

    Le correctif de sondes discute dans cette issue n'en couvre aucun : il sonde la livraison sur main, et dans les quatre cas c'est precisement ce qui manque ou ce qui est perime. La classe est traitee par la lecture de gh pr list --state all --search "<N>" avant d'investir, pas par un budget de sondes plus large.

    Lane myia-po-2026:CoursIA.

  11. jsboige commented on Oct 9, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2026:CoursIA-3
    issue: 19969
    verdict: KEEP
    acceptance:

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions