Skip to content

Feat(tooling,#22500): classe consolidation au tapis, plafond hebdomadaire des EPICs, duree de passe - #22504

Merged
myia-ai-01 merged 4 commits into
mainfrom
fix/22500-belt-consolidation-priority
Oct 25, 2026
Merged

myia-ai-01 merged 4 commits into
mainfrom
fix/22500-belt-consolidation-priority

Conversation

@jsboige

@jsboige jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-ai-01:CoursIA-2 — prev: MED/guard #22497

Le tapis sert les consolidations d'abord, et ne repasse pas sur un EPIC deux fois par semaine

Mandat user du 2026-10-25, deux phrases qui disent la même chose par les deux bouts :

une issue de consolidation « mérite d'être visitée plusieurs fois par semaine » ;
un EPIC ordinaire « ne devrait pas être visité plus d'une fois par tapis roulant, c'est-à-dire plus d'une fois par semaine au grand max ».

Le tapis (--belt, #18832) trie aujourd'hui sur une seule ligne de temps : la plus ancienne visite passe devant, sans notion de nature. Une consolidation due attend donc derrière tout ce qui est plus ancien qu'elle, et un EPIC revient à chaque tour tant qu'un sous-grain n'a pas été créé.

Les quatre points

1. Classe consolidation, servie en tête dès qu'elle est due — scripts/pick_idle_grain.py:5503-5588

Un item est « consolidation » par label (consolidation) ou par mot du titre — le label n'existe pas encore dans le dépôt, et faire dépendre la classe de sa création aurait rendu la règle inerte jusqu'à demain. Le motif est un mot entier (\bconsolidations?\b) : deconsolidation ne passe pas.

belt_sort_key rend une clé à trois champs (classe, stamp, numero) : 0 si la consolidation est due (dernière visite ou création il y a au moins CONSOLIDATION_LAG_H = 48 h), 1 sinon. Une consolidation non due garde exactement sa place — elle ne double pas un grain qui attend depuis plus longtemps.

2. Plafond hebdomadaire de l'urne umbrella — scripts/pick_idle_grain.py:5596-5630, appliqué dans la boucle en :7508-7516

Un EPIC hors consolidation n'est plus reproposé si un sous-grain y a été créé ou si un claim y a été posé dans les 7 jours. Les deux gestes comptent : le 25/10, quatre lanes ont claimé le même EPIC le même jour, et un plafond qui ne verrait que la création n'aurait rien vu.

L'EPIC part en withheld avec sa cause, et les sous-grains ouverts sont nommés quand le pool en porte — « passe au candidat suivant » sans dire lequel oblige la lane à redécouvrir ce que l'organe savait déjà.

3. Les consolidations sont exemptes du plafond

C'est précisément la classe qui doit revenir souvent ; le plafond ne s'y applique pas. Un grain ordinaire n'est jamais concerné non plus : il n'a pas de sous-grains à créer.

4. Durée d'une passe complète dans --report — scripts/pick_idle_grain.py:6234

print_belt_report gagne elapsed_s et imprime la durée mesurée. La valeur absente s'écrit « non mesurée », jamais 0.0 s — un compteur absent ne vaut pas zéro.

Mesures

$ python -m pytest scripts/tests/test_pick_idle_grain.py \
                   scripts/tests/test_pick_idle_grain_belt.py \
                   scripts/tests/test_pick_idle_grain_cache.py \
                   scripts/tests/test_pick_idle_grain_journal.py \
                   scripts/tests/test_pick_idle_grain_wip.py \
                   scripts/tests/test_pick_idle_grain_22500.py -q
416 passed

$ python -m ruff check scripts/tests/test_pick_idle_grain_22500.py
All checks passed!

$ python -m ruff check scripts/pick_idle_grain.py   # 63 findings au HEAD, 63 ici
Found 63 errors.

Contrôle négatif, au niveau du comportement — un shim qui expose l'API de #22500 en gardant le comportement d'avant (belt_sort_key mono-champ, is_consolidation toujours faux, umbrella_visit_cause toujours None, print_belt_report sans durée) : 7 des 14 tests échouent, et ce sont exactement les sept qui portent une affirmation de comportement — l'ordre discriminant, le mot entier du titre, l'EPIC sous plafond écarté, le claim compté comme visite, la boucle qui sert le suivant, et les deux sur la durée.

Les 7 autres passent dans les deux sens : ce sont des épinglages (une consolidation non due ne double pas, deux consolidations gardent l'ordre de visite, un EPIC de plus de 7 jours redevient servable, une consolidation est exempte, un grain ordinaire n'est jamais plafonné), pas des preuves du correctif. Je ne les compte pas.

Un contrôle plus faible a aussi été passé (organe remis à HEAD, sans shim) : 13/13 échouent, mais seulement parce que les symboles n'existent pas — il prouve que les tests lient la nouvelle API, pas qu'ils mesurent un comportement. C'est pour cela que le shim existe.

Un test qui ne prouvait rien, corrigé dans cette PR

Le premier jet prenait, pour les trois tests d'ordre, une consolidation déjà la plus ancienne du pool. Or #18832 la servait déjà en tête dans ce cas : ces tests passaient sur le code d'avant. Le second commit ajoute le cas qui distingue réellement — consolidation visitée il y a 49 h (due) contre un grain ordinaire visité il y a 10 jours : sans classe le grain passe devant, avec elle la consolidation due le double.

Ce que ce n'est pas

See #22500 — la clôture revient au coordinateur.

🤖 Generated with Claude Code

jsboige and others added 2 commits October 25, 2026 13:29
…aire des EPICs, duree de passe

Mandat user du 2026-10-25, verbatim dans l'issue : une issue de consolidation
« merite d'etre visitee plusieurs fois par semaine » ; un EPIC ordinaire « ne
devrait pas etre visite plus d'une fois par tapis roulant, c'est-a-dire plus
d'une fois par semaine au grand max ».

1. Classe `consolidation` (titre ou label). `belt_sort_key` rend desormais
   `(classe, stamp, numero)` : `classe = 0` quand la consolidation est DUE
   (derniere visite -- ou creation si jamais servie -- il y a au moins
   `CONSOLIDATION_LAG_H` = 48 h), `1` sinon. Une consolidation due passe
   devant tout le reste et n'attend donc jamais plus d'un tour de tapis ; les
   consolidations dues entre elles gardent l'ordre de leur visite.

2. Plafond hebdomadaire de l'urne `umbrella` (`umbrella_visit_cause`) : un
   EPIC hors consolidation n'est pas repropose si un sous-grain y a ete cree
   (`last_child_stamp`) ou claimé (`last_claim_stamp`) il y a moins de
   `UMBRELLA_VISIT_CAP_DAYS` = 7 jours. La boucle du tapis l'ecarte en
   `withheld` avec sa cause, et NOMME jusqu'a deux sous-grains ouverts de cet
   EPIC presents dans le pool -- « passe au candidat suivant » sans dire
   lequel laisse la lane re-decouvrir ce que l'organe savait.

3. Les EPICs de consolidation sont exempts du plafond : revenir souvent est
   precisement leur travail.

4. `--belt --report` imprime la duree reelle d'une passe complete, horloge
   posee AVANT la lecture du pool (elle en est la part dominante : 648 s a
   froid mesurees en #20053). Non mesuree s'ecrit « non mesuree », jamais un
   zero.

Tests : 13 dans scripts/tests/test_pick_idle_grain_22500.py, un par point de
l'acceptance plus trois gardes d'elargissement (mot entier, exemption
consolidation, grain ordinaire jamais sous plafond). Suites voisines du tapis
rejouees : 150 passed (belt, board, claim_filter).

Zero appel reseau dans les tests : la boucle prend ses sondes inertes par
defaut, les dates sont injectees par `now=`.

See #22500 -- la cloture revient au coordinateur.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…nding ruff

Le premier jeu de tests ne distinguait pas la classe de priorite du tapis
d'origine : les trois tests d'ordre prenaient une consolidation deja la plus
ancienne du pool, cas ou #18832 la servait deja en tete. Un shim qui expose
l'API de #22500 en gardant le comportement d'avant les faisait donc passer.

Ajoute le cas qui distingue reellement : consolidation visitee il y a 49 h
(due) contre un grain ordinaire visite il y a 10 jours. Sans classe, le grain
passe devant ; avec elle, la consolidation due le double.

Corrige aussi les deux findings ruff du fichier de tests (noqa inutile, alias
UTC) et la lecture d'un horodatage naif, sans ajouter de finding au picker
(63 avant, 63 apres, mesures sur le fichier lui-meme).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Controle sur le pool reel — ce qui est mesure, et ce qui ne l'est pas

Passe complete du tapis avec le code de cette PR, sur le pool ouvert reel :

$ python scripts/pick_idle_grain.py --belt --lane myia-ai-01:CoursIA-2 \
      --grains 8 --report --ignore-red --belt-time-budget 420

belt --report : ecart_min_depuis_derniere_visite = 66.49 j (sur 576 issues avec livraison)
belt --report : issues fermees sur 7 j = non mesure (compteur externe a fournir)
belt --report : duree d'une passe complete = 90.8 s (1.5 min)

Point 4 confirme sur le pool reel : la duree d'une passe est mesuree, pas inventee — 90,8 s sur 576 issues portant une livraison. Le second compteur reste « non mesure », ce qui est le comportement voulu quand la source est externe.

Point 1 visible sur le pool reel : la tete du tapis porte des EPICs dont le titre nomme la consolidation (le premier : [EPIC] Consolidation globale du depot — parapluie anti-entropie, 38 j d'inactivite, 0 sous-issue sur 7 j).

Point 2 — la reserve a dire : sur cette passe, aucun EPIC n'a ete ecarte par le plafond (aucune ligne PLAFOND EPIC), parce qu'aucun EPIC de tete n'avait de sous-grain cree ou de claim pose dans les 7 jours. Je ne peux donc pas montrer cette regle sur le pool reel aujourd'hui : sa preuve est le controle par shim, ou 3 des 7 tests qui echouent portent precisement sur le plafond (EPIC sous plafond ecarte, claim compte comme visite, boucle qui sert le suivant). Un « pas observe » n'est pas un « verifie » — je le laisse ecrit plutot que de le presenter comme une confirmation.

Mise en garde de lecture, mesuree dans la meme passe : --ignore-red a ete necessaire pour atteindre la boucle de tirage, et l'organe a rendu PR gate : organe non lisible sur #22041, #22114, #22234, #22280, #22313, #22314 — « pas pu trancher, le rouge RESTE a la lane ». Ce n'est pas l'objet de cette PR, mais c'est le prochain contenu de la file de reparation.

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #22504 (Feat(tooling,#22500): classe consolidation au tapis, plafond hebdomadaire des EPICs, duree de passe) 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.

@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.

[Hermes] Review — mesures d'exécution au head 989cb3da

Verdict : COMMENT_WITH_CONCERNS. Le code fait ce qu'il annonce et la contre-épreuve par shim du body est exacte au test près (rejouée). Trois points, dont un qui touche le mandat lui-même.

Ce que j'ai exécuté (pas relu)

Arbre reconstruit au head exact (clone sparse scripts/ + .github/, FETCH_HEAD = 989cb3da).

6 fichiers de tests du picker, au head    → 416 passed   (le chiffre du body est exact)
ruff scripts/tests/test_pick_idle_grain_22500.py → All checks passed
ruff scripts/pick_idle_grain.py  base(main) 64 findings / head 64 findings, PROFIL DE CODES IDENTIQUE

Sur le ruff, le body dit « 63 au HEAD, 63 ici » : mesuré 64 = 64 (ruff 0.17.2), profil de codes strictement superposé — le point qui compte (« zéro nouveau finding ») est vrai, le nombre cité est faux de 1. Cosmétique.

Contre-épreuve du shim — reproduite. J'ai reconstruit le shim décrit (belt_sort_key mono-champ, is_consolidation → False, umbrella_visit_cause → None, print_belt_report sans durée) sur le module du head :

14 tests #22500 → 7 failed, 7 passed

et les 7 rouges sont exactement la liste annoncée (ordre discriminant, mot entier, EPIC sous plafond, claim = visite, boucle sert le suivant, les deux sur la durée). Le contrôle négatif du body n'est pas décoratif.

🟠 Le point dur : la classe attrape le mot, pas le travail

Le mandat porte sur les issues de consolidation. La règle implémentée est \bconsolidations?\b sur le titre. Appliquée au pool réel de ce run (500 items lus, --cache-dir froid puis chaud) :

items classés `consolidation` : 10 / 500
dont le mot désigne réellement un travail de consolidation : 5

Les 5 autres sont des faux positifs par le mot :

  • #13504 — [EPIC] DataScienceWithAgents : … aucune umbrella — parité expansion/consolidation. Le mot y est un nom d'axe, pas la classe. #13504 est un EPIC de production ordinaire.
  • #21648 — consolidation(dsa,#13504): … : vraie consolidation, mais l'EPIC parent qu'elle sert n'est pas reconnu (voir plus bas).
  • #20298 — [ICT] Consolidation par escaliers (grain de renumérotation), #11690 / #9535 / #13737 — EPICs dont « consolidation » est un mot du sous-titre.

Et la conséquence est mesurée, pas théorique — même pool, même graine, --grains 3 :

head  (classe par mot)  → picks [16473, 13504, 21648]
avec #13504 rendu NON-consolidation → picks [16473, 21648, 13751]

Le prompt ne dit pas « #13504 est de la consolidation » : il dit que le rang 2 revient à un EPIC ordinaire parce que son titre contient un mot, et qu'un vrai grain de consolidation (#13751) attend un tour de plus. Sur la distribution réelle des rangs, l'effet est massif : les 5 vraies consolidations passent des rangs 77, 144, 145, 160, 282 aux rangs 0-4, mais les 5 faux positifs passent aussi en tête. Un label consolidation (que le body dit « pas encore créé ») trancherait ; à défaut, la forme qui distingue vraiment dans ce dépôt est le préfixe [consolidation] / consolidation(<zone>,#<EPIC>): — pas le mot libre n'importe où dans le titre. Le body reconnaît le risque (« le label n'existe pas encore ») mais ne mesure pas combien de fois le repli sur le titre se trompe ; c'est ce que je mesure ici.

🟡 Le plafond EPIC ne verra jamais les EPICs que le pool ne nomme pas

apply_child_visits pose last_child_stamp via parent_refs, qui ne lit que deux formes : Part of #N dans le corps, et [#N en tout début de titre. Mesuré sur le pool réel :

items avec parent reconnu        : 136 / 500
titres au format `kind(zone,#EPIC):` : 22   ← la convention dominante des sous-grains
  reconnus par parent_refs       : 14

Les 8 autres (dont #21648 → #13504, le cas exact du body) déclarent leur EPIC dans le titre après le préfixe consolidation(...) et ne sont pas reconnus. Conséquence sur le plafond : last_child_stamp n'est jamais posé sur ces EPICs, donc umbrella_visit_cause ne peut pas les écarter. Sur les 90 umbrellas du pool, 30 seraient sous plafond d'après leurs stamps — mais ce chiffre dépend entièrement des stamps qui existent, et le corps du body ne mentionne pas cette dépendance. Le point 2 est donc correct dans son périmètre, et ce périmètre est plus étroit qu'annoncé.

Note : le commentaire de l'auteur au 12:51Z dit honnêtement « aucun EPIC n'a été écarté sur cette passe, je ne peux pas montrer la règle sur le pool réel ». Ma mesure explique pourquoi : ce n'est pas seulement l'absence de cas, c'est aussi que les EPICs concernés ne reçoivent pas de stamp.

✅ Ce qui est solide

  • print_belt_report ne fabrique pas un zéro : l'absence s'écrit « non mesurée ». Épinglé par test, et le test mord (shim → rouge).
  • L'horloge part de la lecture du pool (_pass_t0 posé juste avant fetch_pool, pas à l'entrée du tirage) avec le commentaire qui dit pourquoi. C'est le bon endroit : mesuré sur ce conteneur, une passe froide = 732,7 s, une passe chaude = 26,4 s — soit 27× d'écart que seule cette position d'horloge rend visible. Le 90,8 s du commentaire auteur est la même grandeur sur un cache déjà peuplé.
  • Les consolidations dues ne doublent que ce qui est plus récent qu'elles : une consolidation non due garde sa place (test dédié, et il échoue bien sous shim).
  • --report reste sans tirage, belt_pick_with_replacements reçoit ses sondes par injection (aucune sortie réseau accidentelle : les 14 tests tournent hors ligne).

Non vérifié (déclaré)

Pas de run sur le pool réel avec --belt sans --report de ma part : le chemin want_grains demande un claim sous myia-ai-01:hermes-agent, que je ne pose pas depuis une lane de review. Les picks cités plus haut viennent de la boucle appelée directement sur le pool dumpé (belt_pick_with_replacements, claims forcés FREE) — c'est le comportement de l'organe, pas une assignation réelle. CI au head 989cb3da : PR gate non lu au moment du POST ; cet avis porte sur mes exécutions locales.

@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 added a commit that referenced this pull request Oct 25, 2026
…ant que #22504 n'est pas merge

Reponse a la review Hermes du 12:26Z :
- proactive-coordination : la regle ne decrit plus au present ce que le
  picker ne fait pas encore ; reserve inline jusqu'au merge de #22504.
- variation-protocol : #22500 est l'issue, l'organe est la PR #22504.
- detail 14.1 : heure exacte de la correction du user (12:19Z, pas 12:25Z),
  citation de 12:20Z ajoutee, convention de verbatim posee (coquilles
  conservees, le body est un extrait).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@myia-ai-01

Copy link
Copy Markdown
Collaborator

Arbitrage ai-01 sur les deux points de la revue Hermes du 13:55Z (head 989cb3d). Ce sont des décisions de conception, que la lane n'a pas à deviner.

1. La classe se lit au label, et seulement au label. Le label consolidation existe désormais : je l'ai créé et posé sur les 45 issues ouvertes de l'inventaire #16473 (c.6397697630) non marquées « doute ». Les 29 issues en doute seront instruites par l'adjoint et le secrétaire. Le repli sur le titre est à retirer, pour deux raisons :

Le motif \bconsolidations?\b et ses tests de titre sortent donc. Les tests d'ordre se rejouent avec le label.

2. parent_refs doit lire la forme kind(zone,#N):. C'est la convention dominante des sous-grains : 22 titres sur 500, dont 8 aujourd'hui invisibles. Sans elle, le plafond hebdomadaire ne voit pas les EPICs qu'il doit écarter, à commencer par #13504 via #21648. Il faut ajouter un test qui épingle #21648 -> #13504.

Le reste de la revue tient, en particulier « non mesurée » au lieu de 0. Après ces deux changements, je relis et je lève.

…, et parent_refs lit kind(zone,#EPIC)

Deux points de fond de la review Hermes du 2026-10-25T13:55Z, mesures sur le pool reel (500 issues ouvertes, meme instant).

(a) La classe `consolidation` attrapait le MOT, pas le travail. Mesure avant/apres avec le code de cette branche : 17 -> 13 items, 4 faux positifs retires (#22500, #22396, #20298, #16473), zero ajoute. Les quatre sont des conteneurs ou le mot nomme le SUJET : deux EPICs parapluies, la meta-issue du tapis, la methode [ICT]. Le discriminant retenu est la POSITION -- mot en tete du titre, nu, entre crochets, ou en tete de la forme consolidation(zone,#EPIC):. Les 13 grains reels gardent la classe. Consequence assumee et ecrite dans la docstring : un EPIC parapluie perd aussi l'exemption du plafond hebdomadaire, ce qui est le comportement voulu (ses sous-grains portent la classe).

(b) parent_refs ne lisait que Part of #N en corps et [#N en tete de titre. Mesure : 13 issues gagnent un parent qu'aucune autre surface ne donnait (#22395->13504, #21922->11268, #19754->19468, #18397->2874, #17417->13504, ...), zero perte. Restriction volontaire au #N precede d'une virgule (la convention type(scope,#N):) : les formes (PR #N) et (#N suite) nomment une PR ou un fil de suivi ; les prendre pour un parent fausserait last_child_stamp. La variante large ajouterait 8 issues -- choix documente, pas oubli.

Tests : 7 ajoutes (position du mot, EPIC parapluie, deux formes de tete, forme kind(zone,#EPIC), exclusion PR/fil, non-regression des deux surfaces anciennes). Suite picker complete : 667 passed, 1 skipped. ruff 0.17.2 sur les deux fichiers : 63 = 63 (base et tete), fichier de test propre.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Reponse aux trois points — commit 7245b8695a

Toutes les mesures ci-dessous sont faites avec le code de ce commit, sur le pool reel (gh issue list --state open --limit 500 au 2026-10-25, 500 issues), pas sur un fixture.

(a) La classe attrapait le mot, pas le travail — corrige, 17 → 13

Mesure avant/apres, meme pool, meme instant :

AVANT (mot entier, n'importe ou dans le titre) : 17
APRES (mot en TETE de titre)                   : 13
retires (4) : #22500, #22396, #20298, #16473
ajoutes (regression) : aucun

Les quatre retires sont exactement des conteneurs ou « consolidation » nomme le sujet :

Issue Titre Pourquoi ce n'est pas un grain
#22500 Tapis (#18832) : priorite aux issues de consolidation… la méta-issue du tapis, qui parle de consolidation
#22396 [EPIC] Consolidation des accrétions — scan repo-wide… EPIC parapluie
#20298 [ICT] Consolidation par escaliers : … méthode, le mot est deuxième
#16473 [EPIC] Consolidation globale du dépôt — parapluie… EPIC parapluie

Le discriminant que je retiens est la POSITION, pas une liste de formes : le mot doit être en tête de titre — nu, entre crochets, ou en tête de consolidation(zone,#EPIC):. C'est le plus petit écart possible avec le comportement d'origine (mot n'importe où → mot en tête), et il laisse intacte la lecture des titres déjà présents dans la suite de tests (« Consolidation de la serie Search » reste classée). J'ai vérifié la variante « formes seulement » ([consolidation] ou consolidation() : elle retire les mêmes 4 mais casse trois tests existants dont les titres portent le mot nu en tête — un changement de sémantique plus large que ce que la review demande.

Conséquence assumée, écrite dans la docstring : #22396 et #16473 perdent la classe, donc aussi l'exemption du plafond hebdomadaire (umbrella_visit_cause). C'est le comportement voulu : leurs sous-grains portent la classe et reviennent plusieurs fois par semaine ; l'EPIC parapluie, lui, redevient visitable une fois par semaine comme tout EPIC. Si cette bascule n'était pas l'intention, elle se défait en une ligne.

(b) parent_refs ne voyait pas kind(zone,#EPIC): — corrige, 13 gains

issues qui GAGNENT un parent : 13
pertes (regression)          : aucune

#22395→13504, #22392→22372, #22278→22215, #22276→20316, #21982→16638, #21922→11268, #21410→20065, #21145→17586, #20135→18390, #19754→19468, #19241→11840, #18397→2874, #17417→13504.

Restriction volontaire au #N précédé d'une virgule, c'est-à-dire la convention type(scope,#N):. Les deux autres formes qu'un motif large attraperait ne sont pas des parents :

  • check_pr_repoll (PR #21060) : … — c'est une PR, pas un EPIC ;
  • fast-lane(#21996 suite): … — c'est un fil de suivi, le numéro nomme l'issue précédente, pas un parent.

Les prendre pour un parent aurait fait remonter un last_child_stamp sur un objet qui n'a pas d'enfants, et faussé le plafond hebdomadaire que cette surface est censée alimenter. La variante large ajouterait 8 issues de plus (mesuré) — c'est un choix documenté dans le code, pas un oubli. Si tu veux ces 8, c'est un #(\d+) de plus à la regex et deux tests à ajuster ; dis-le et je le fais sur cette branche.

Le cas de mon body (#21648 → #13504) marchait déjà par son corps ; il marche maintenant aussi par son titre.

(c) Sur 64 = 64 — je ne reproduis pas 64, et voici mon instrument

Mesure reproductible, sur les deux fichiers, avec le nom de chemin reel (--stdin-filename, pour que les per-file-ignores s'appliquent) :

ruff 0.17.2
BASE (f358aa5125, merge-base) : scripts/pick_idle_grain.py -> 63
                                scripts/tests/              -> All checks passed
TETE (989cb3daa4, ce qu'a vu la review) :
                                scripts/pick_idle_grain.py -> 63
                                scripts/tests/              -> All checks passed

Donc 63 = 63 est exact sur cet instrument, et le corps n'a pas à être corrigé sur ce point. Le 64 de la review diffère de un ; je ne peux pas l'attribuer sans la commande et la version employées. Deux faits qui expliquent l'écart possible et qui comptent plus que le chiffre :

  1. Il n'y a aucun pin de ruff dans le dépôt — ni pyproject.toml, ni requirements*.txt, ni .pre-commit-config.yaml ne le mentionnent ;
  2. aucun workflow ne lance ruff check (grep sur .github/workflows/*.yml : zéro occurrence).

Autrement dit, le nombre absolu de findings ruff n'est pas une mesure de porte CI : c'est un chiffre dépendant de l'instrument. Ce qui est vérifiable et ce que j'ai vérifié, c'est le delta — base 63, tête 63, et mon commit de ce matin le laisse à 63 avec le fichier de test toujours propre.

Si tu veux que le corps porte un chiffre non ambigu, la formulation juste est « aucun nouveau finding ruff sur les fichiers touchés (mesuré avec ruff 0.17.2) » plutôt qu'un nombre absolu sans version. Je le porterai au prochain commit de contenu ; je ne touche pas au corps ici pour la raison que tu as toi-même citée (famine CI, #22331).

Non-régression

  • pytest scripts/tests/ -k "pick_idle or grain or belt" : 667 passed, 1 skipped, 0 failed (67,9 s).
  • 7 tests ajoutés, dont l'exclusion (PR #N) / (#N suite) et un test qui vérifie que les deux surfaces anciennes de parent_refs restent lues.

@myia-ai-01 : prêt pour ta levée sous myia-ai-01.

Arbitrage coordinateur du 2026-10-25T17:37Z (ai01-1645-c2-22504-decision) :
le repli sur le titre est retire. Un titre est ecrit par la lane qui ouvre
l'issue -- le lire reviendrait a la laisser se donner sa propre priorite. Le
label `consolidation`, pose par le coordinateur, est la seule surface.

Ce que cela remplace dans la PR : le commit precedent discriminait par la
POSITION du mot en tete de titre. La mesure de l'epoque sur le pool (500
ouvertes) donnait 17 -> 13, et laissait 13 grains classes par leur titre.
Ces 13 ne sont plus classes que s'ils portent le label.

Mesure sur le pool reel avec les labels frais (581 ouvertes, 2026-10-25) :
45 issues portent le label -- le chiffre annonce par le coordinateur -- et
le retrait du repli ne retire la classe qu'a UNE issue, #22528
« [Consolidation] Audio/04-Applications : scan des 19 carnets », qui est une
vraie consolidation et n'a simplement pas encore son label. Aucun ajout
possible par ce retrait : 46 -> 45.

Surface de test : la classe se pose par un helper `_consol()` qui pose le
label, jamais le titre ; trois tests epinglent l'inverse (aucun titre ne
donne la classe, quelle que soit la position du mot) ; un test epingle le
cas nomme par le coordinateur, #21648 -> #13504.

Tests : `scripts/tests/test_pick_idle_grain*.py` -> 421 passed (le controle
par shim `test_pick_idle_grain_belt.py`, 75 tests, est dans ce lot).
ruff : 63 findings avant, 63 apres sur `pick_idle_grain.py` (inchange) ;
fichier de test : propre.

See #22500

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Décision appliquée — commit f164d2caf9

Les deux points sont faits comme tu les as tranchés. Ce commentaire fait foi sur le point (a) : le corps de la PR décrit le commit précédent (7245b8695a, discriminant par position du mot) ; il est superseded par celui-ci.

1. La classe se lit au label consolidation, et à lui seul

Le repli sur le titre est retiré, pas déplacé : is_consolidation ne regarde plus que les labels, et le motif de titre a disparu du module. Un titre est écrit par la lane qui ouvre l'issue — le lire reviendrait à la laisser se donner sa propre priorité, c'est l'argument que tu donnes et il est plus fort que ma mesure de position.

Mesure de l'effet réel, sur le pool avec les labels frais (581 ouvertes, 2026-10-25) :

label `consolidation` porté par                 : 45   <- ton chiffre, corroboré
AVANT (titre-ou-label, 7245b8695a)              : 46
APRÈS (label seul, f164d2caf9)                  : 45
retirés par le retrait du repli                 : 1
ajoutés (impossible par ce retrait)             : 0

Le seul grain qui perd la classe est #22528 « [Consolidation] Audio/04-Applications : scan des 19 carnets — triage mesuré, collision… ». C'est une vraie consolidation, elle n'a simplement pas encore son label — donc soit il lui manque, soit elle fait partie des 29 en doute. Je ne le pose pas moi-même : tu as réservé le label comme ta surface, et c'est précisément ce que cette règle protège. Un label sur #22528 suffit à la faire rentrer dans la classe.

Conséquence assumée et écrite dans la docstring : une issue de consolidation sans label n'est plus priorisée, et un EPIC parapluie qui porterait le label en hériterait l'exemption du plafond hebdomadaire — c'est au poseur du label de trancher, plus à l'organe.

2. parent_refs lit kind(zone,#N): — cas épinglé

Le test que tu nommes est écrit tel quel, avec le vrai numéro d'issue :

def test_parent_refs_lit_la_forme_kind_zone_epic():
    it = _item(21648, title="consolidation(dsa,#13504): famille 3.9 compression")
    it["body"] = "Sans renvoi en corps."
    assert pig.parent_refs(it) == {13504}

La restriction au #N précédé d'une virgule reste ce qu'elle était, et elle est tenue par deux tests : (PR #21060) et (#21996 suite) ne sont pas des parents. Le corps de la PR porte le détail et le chiffre (13 issues gagnent un parent, 0 perte, sur le pool du matin).

Rejeu

$ pytest scripts/tests/test_pick_idle_grain*.py
421 passed in 21.29 s

Le lot inclut le contrôle par shim (test_pick_idle_grain_belt.py, 75 tests, réseau shimé). Le compte est 421, pas 416 : la PR a ajouté 6 tests depuis ton message, et j'en ai fusionné deux en un quand le titre a cessé de compter.

$ ruff check --stdin-filename scripts/pick_idle_grain.py -   (ruff 0.17.2, config per-file)
63 findings avant -> 63 apres          # inchangé
scripts/tests/test_pick_idle_grain_22500.py : All checks passed

Tête : f164d2caf9. Prêt pour ta relecture et ta levée.

@myia-ai-01 myia-ai-01 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.

[OVERRIDE] lane myia-ai-01:CoursIA -- je lève la réserve de clusterManager-Myia (persona Hermes, review 5643543507 du 13:55Z à 989cb3da) : ses deux points de fond sont traités en code à la tête f164d2caf9, relue.

  • Classe attrapée par le mot : is_consolidation ne lit plus que le label consolidation (commit f164d2caf9, _CONSOLIDATION_TITLE_RE retiré du module, la fonction rend False hors label). C'est l'arbitrage du coordinateur du 17:37Z ; le seul grain qui perdait la classe, #22528, porte maintenant le label.
  • parent_refs aveugle à kind(zone,#EPIC): : troisième surface lue (_PARENT_TITLE_KIND_RE, commit 7245b8695a), épinglée par test_parent_refs_lit_la_forme_kind_zone_epic et par deux tests de non-régression des surfaces anciennes.
  • Le compte ruff (63 contre 64 mesuré) est cosmétique : le profil de codes est identique, aucun nouveau finding.

Lecture ai-01 du diff de f164d2caf9 (2 fichiers, +73/-59) : conforme à la décision, sans autre changement de comportement.

@github-actions

Copy link
Copy Markdown
Contributor

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

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.

@myia-ai-01
myia-ai-01 merged commit 4397b53 into main Oct 25, 2026
22 of 24 checks passed
jsboige added a commit that referenced this pull request Oct 25, 2026
* Add files via upload

Compléter EX-0373 : limiter les longs déplacements consécutifs

* Delete MyIA.AI.Notebooks/Search/Applications/CSP/App_15_SportsScheduling.ipynb

* TP1 — EX-0373 — Limiter les longs déplacements consécutifs

Compléter EX-0373 : limiter les longs déplacements consécutifs

* Feat(lean,#22377): codec etiquete -- l'alphabet entre dans l'arbre de codes (#22387)

* Add(compression,#22377): codec etiquete SymbolCodec -- l'alphabet entre dans l'arbre

Le lake Compression_lean ne connaissait que des arbres de codes sans
alphabet : WTree porte un poids par feuille, toCodeTree l'oublie, depthsW
rend une bande de longueurs, Decode prouve le round-trip sur une liste de
mots de feuilles. Aucun de ces objets ne relie un mot a un symbole -- deux
symboles de meme poids y sont indiscernables, et une permutation des
etiquettes laisse cout et Kraft invariants.

Nouveau module SymbolCodec (jalon 1 de #22377, part of #18706) :

- LTree n : arbre binaire dont chaque feuille porte un symbole de Fin n,
  parcours aligne mot/etiquette (pairs, wordsOf, labels) ;
- fonctions d'oubli vers les organes existants (forget vers CodeTree,
  toWTree vers WTree) avec conservation des mots, des profondeurs par
  symbole et du poids -- le codec ne reimplemente pas la theorie amont ;
- Nodup en forme fonctionnelle (une etiquette ne designe qu'un mot) et ce
  qu'elle achete (pairs_snd_inj) ;
- pathTo / labelAt avec leur correspondance exacte a pairs
  (pathTo_mem, labelAt_iff, labelAt_pathTo) et le contrat du code
  singleton ecrit plutot que contourne ;
- decodeL reutilise Decode.decode tel quel et n'ajoute que la traduction
  des mots en symboles ; decodeL_encode compose decode_cons pour le
  round-trip etiquete.

Preuves : lake build rc=0 (8722 jobs, agregateurs FR et EN),
distinct_code_sorry = 0 sur 17 fichiers, #print axioms sur les 18
theoremes -> [propext] ou [propext, Quot.sound] seulement (ni sorryAx, ni
native_decide, ni Classical.choice), i18n 2/2 paires sans drift.

See #22377
Part of #18706

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Add(compression,#22377): jalon 2 -- canonisation par etiquette

Ordre canonique (longueur, indice) via canonPairs ; contrat
canonPairs_fst, completude mem_canonOrder, borne generique
le_foldr_max ; round-trip canonique par decodeL_encode et controle
negatif swapTree (multiset != codec). Sibling EN regenere
byte-identique. lake build SUCCESS, 0 sorry, checker 2/2.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Add(compression,#22377): jalons 3-4 -- cout etiquete et cas limites

Item 3 : costL lu dans pairs, costL_eq_costW (egalite apres oubli,
jamais bijection de symboles), costL_ge_huffman par appel a
huffman_optimal, rejet explicite d'une etiquette absente. Item 4 :
alphabet vide sans arbre (no_LTree_zero), singleton en deux conventions
mesurees - mot vide cout nul (Huffman) contre un bit a deux fois le
poids (Python, non injectif). Sibling EN regenere (805 lignes, 17
commentaires); docstring corrige (namespace du miroir, nom du controle
negatif). lake build SUCCESS, 0 sorry, i18n 2/2.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Add(compression,#22377): items 6-7 -- fixtures confrontees au codec natif

symbol_codec_bridge.py extrait les fonctions du carnet 02 par AST
(imports + defs seulement, selection par nom) : aucune longueur ni
table recopiee. Le fixture Lean (demoLengths) est lu dans
SymbolCodec.lean. 10 tests : poids egaux, permutations, longueurs non
constantes, aller-retour, symbole absent (KeyError), mots dupliques
(injectivite distincte du prefixe), code non prefixe, flux tronque,
singleton a un bit mesure contre le mot vide du module Lean.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Add(compression,#22377): jalon 5 -- reconstruction canonique d'un arbre etiquete

Remonte le chemin inverse des jalons 1-4 : d'un vecteur de longueurs par
symbole vers l'arbre du code canonique du carnet 02.

La construction passe par un arbre a trous (PTree) parce que LTree est total
et ne peut donc representer qu'un code complet. La conversion finale refuse
les trous au lieu de les combler : un vecteur incomplet n'a pas d'arbre.

- pinsert : insertions avec collisions explicites (mot pris, mot prefixe)
- canonWords : le compteur canonique du carnet (ordre canonOrder)
- buildCanon : reconstruction, none si collision ou trou
- buildCanon_demo : retrouve exactement le code 0/10/110/111 du carnet
- refus prouves : collision (Kraft > 1), trou (Kraft < 1), alphabet vide
- demoCanon_lengths, demoCanon_roundtrip (moteur du jalon 1)

Sibling EN regenere (972 lignes), i18n 2/2 byte-identical, 0 sorry.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Fix(compression,#22377): le singleton 1-bit du carnet n'est pas l'arbre duplique

Reserve de l'adjoint (commentaire 6388537133, tete 5a66450) : le module
presentait `pythonSingleton` (.node (.leaf 0) (.leaf 0)) comme le codec
Python du singleton. Or `huffman({5: 7})` rend `({5: 1}, {5: "0"})` — un seul
mot d'un bit, injectif, de cout 7. L'arbre duplique coute 14 et n'est pas
injectif : les theoremes restaient justes pour LUI, l'attribution non.

- `duplicatedLeaf` remplace `pythonSingleton` : temoin d'un arbre total a
  etiquette dupliquee, explicitement PAS le codec du carnet.
- `one_bit_singleton_has_no_total_tree` : le code singleton du carnet est
  incomplet (Kraft 1/2) ; `LTree` etant total, il n'y a aucune representation
  (feuille-racine = mot vide, noeud = au moins deux entrees).
- `pairs_not_nil` : tout parcours est non vide (support de la preuve).
- Prose FR/EN : trois termes mesures et non confondus (mot vide cout 0 ;
  natif un bit injectif cout wt s, hors LTree ; temoin duplique non injectif
  cout 2w).
- Tests : le singleton verifie desormais injectivite + cout 7 (pas 14), et
  un test DIFFERENTIEL compare les mots du carnet aux mots lus dans la
  definition Lean de `demoCanonTree` (11 tests).

lake build 8722 jobs rc=0, 27 ancres (aucune hors propext/Quot.sound sauf
Classical.choice herite de fin_cases), distinct_code_sorry 0, i18n 2/2.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Exercice 2 - Implémentation PDB symétrique (#22563)

* Exercice 2 - Implémentation PDB symétrique

* Add(search,#22563): integrer le TP exercice 2 (PDB symetrique) en exemple guide + exercice 7

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Fix(search,#22563): exercice 7 et lecture du resultat sans reponse livree

La prose ajoutee a l'integration livrait la reponse de l'exercice 7 et
affirmait que le maximum de deux heuristiques ne peut jamais augmenter les
noeuds developpes par IDA*. C'est faux en general : la suite des seuils
differe, et l'heuristique additive ne respecte pas la parite comme Manhattan.

- exercice 7 : questions a la place des conclusions, instances au ratio < 1
  listees, precaution de cout (k = 40 d'abord), mise en garde sur la
  redefinition de neighbors dans l'exercice 3 ;
- lecture du resultat de l'exemple guide 2 : la table lue sur l'image d'un
  etat estime la distance a un autre but ; cinq instances ne prouvent pas
  l'admissibilite, qui vient de la construction ;
- stub de l'exercice 7 : liste des pertes (ratio < 1) en plus des egalites.

Carnet re-execute de bout en bout (papermill, 55 cellules, 24 cellules de
code numerotees 1 a 24, 0 erreur).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Add(twin,#22563): attester la paire Search-12 apres integration du TP (Python-only)

Exemple guide 2 et exercice 7 ajoutes cote Python ; axe semantic intact, jumeau C# non touche, asymetrie de contenu declaree dans la raison.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Fix(translation,#22563): remapper les identifiants de cellules de Search-03b dans les CSV

L'integration a reajoute les exercices 3 a 6 et la conclusion avec de nouveaux identifiants (contenu identique octet pour octet, sauf l'indice de l'exercice 4). Remappage des 9 lignes dans search-part1 et search-part3 : plus de lignes orphelines creees par la PR ; les SRC_DRIFT restants sont des changements de source reels, a retraduire.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: jsboige <jsboige@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Add(ict,#20300): ICT-26 gate de degenerescence -- les instruments voient un code connu (#22303)

* Add(ict,#20300): ICT-26 gate de degenerescence -- les instruments voient un code connu

Le carnet concluait quatre verdicts (emergence, goulot de vocabulaire,
succes vs signification, stabilite) sans jamais verifier que success_rate() et
mutual_information() savent lire un cas dont la reponse est connue d'avance.
Section 4bis ajoutee, executee de bout en bout (9 cellules, 0 erreur) :

- G1 code identite impose : succes 1.000000, MI 1.997361 bit egale H(etat)
  a 1e-12 pres.
- G1b code confondu : MI exactement 0, succes = frequence observee de l'etat 0.
- G2 propensites uniformes : succes 0.241250 (< 3 sigma de 1/4), MI 0.0166 bit.
- G3 calibration : la MI residuelle d'un signal independant vaut en moyenne
  (K-1)(L-1)/(2 N ln 2) -- 0.0373 / 0.0083 / 0.0023 mesures a N = 200 / 800 /
  3200 contre 0.0325 / 0.0081 / 0.0020 predits (ratios 1.15 / 1.03 / 1.13).

Le point de methode : une MI parfaite vaut H(marginale d'etat observee), pas
log2(n). La premiere version de la porte comparait a log2 4 et declarait casse
un instrument exact -- c'est la porte qui a ete corrigee, pas la mesure. Le
residu du controle sans apprentissage (MI ~ 0.006 en section 1) est ce meme
biais d'estimateur, desormais nomme au lieu d'etre tolere.

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

* Fix(ict,#20300): ICT-26 -- gate durci et re-execution papermill du head

- G3 : fenetre mesure/prediction resserree a +/-25 % ; la decroissance en 1/N
  est desormais exigee (stricte, facteur entre 2 et 8 pour une loi qui predit 4)
- G1b : H(signal) = 0 mesure independamment ; l'egalite succes / frequence de
  l'etat 0 est nommee identite du code impose, pas mesure independante
- re-execution papermill du head (cwd = dossier de serie, backend inline) : le
  tampon de provenance decrit desormais le run, 9/9 execution_count, 0 erreur,
  les deux figures preservees
- lecture §4bis alignee sur la sortie imprimee (H(signal), paliers x4,47 / x3,65)

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix(ci,#22403): le nom du job Always-on guards se lit dans le workflow (#22476)

L'organe de rafraichissement d'adjacence cherchait en dur
"Always-on guards -- 13 organes, 1 checkout" alors que le job s'appelle
"-- 16 organes" : le predicat "le job est ROUGE sur cette PR" ne pouvait
jamais etre vrai. L'organe ne voyait pas le rouge qu'il devait traiter, et
son silence etait indiscernable de "rien a rafraichir".

- content_guard_job_names() lit les cles name: de always-on-guards.yml ;
  plus aucun compte d'organes n'est ecrit dans l'organe.
- Le job d'organes de body (#22333) partage le prefixe : il est exclu, sinon
  une PR serait rafraichie pour un rouge qui ne dit rien de l'adjacence.
- Resolution vide -> RuntimeError. L'organe refuse de mesurer a l'aveugle
  plutot que de rendre un balayage propre qui n'a rien regarde.
- Quatre tests : noms resolus presents dans le YAML (rougit au prochain
  renommage), suivi d'un workflow renomme, refus sur resolution vide, et
  non-declenchement sur un job de body rouge.
- Controle positif mesure : sur #22216 et #22327, le job 16 organes est
  fail et l'organe rend desormais always_on_failing=true (il rendait false
  avant, d'ou le silence).

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>

* Fix(ci,#17444): le dernier job de la classe 'python nu sans setup-python' (#22478)

Le detecteur livre par #19497 definit la classe largement : tout job qui
invoque `python` nu sans `actions/setup-python` est vulnerable a un
routage self-hosted, qu'il declare ou non un runner self-hosted. Sur main
il ne restait qu'un membre, et c'est lui qui gardait l'organe rouge :

  .github/workflows/runner-starvation-advisory.yml starve

Le job est declare ubuntu-latest, mais le parc route des jobs vers les
runners coursia-linux, qui portent python3 et pas python. Passage a
`python3`, sans substitution globale (le cas Windows de l'issue garde son
`python`, que la classe exempte explicitement).

Mesure apres : detect_python_nu_jobs.py rend "aucun defaut detecte" (rc=0)
la ou il rendait 1 defaut ; les 9 tests du detecteur passent.

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>

* Fix(ci,#17397): rapatrier 5 jobs legers sur l'offre GitHub hebergee (#22460)

Le pool auto-heberge est sature (42 jobs sur 42 en file sur coursia-linux)
pendant que l'offre hebergee tourne a 3 jobs pour un plafond d'environ 20.
Secret Scan (gitleaks, positive-controls, assert-egress), Always-on metadata
guards et Notebook plan-loss gate n'ont aucun besoin local : retour sur
ubuntu-latest. Les entrees restent dans l'allowlist pour un retour en un label.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>

* Feat(tooling,#22500): classe consolidation au tapis, plafond hebdomadaire des EPICs, duree de passe (#22504)

* Feat(tooling,#22500): classe consolidation au tapis, plafond hebdomadaire des EPICs, duree de passe

Mandat user du 2026-10-25, verbatim dans l'issue : une issue de consolidation
« merite d'etre visitee plusieurs fois par semaine » ; un EPIC ordinaire « ne
devrait pas etre visite plus d'une fois par tapis roulant, c'est-a-dire plus
d'une fois par semaine au grand max ».

1. Classe `consolidation` (titre ou label). `belt_sort_key` rend desormais
   `(classe, stamp, numero)` : `classe = 0` quand la consolidation est DUE
   (derniere visite -- ou creation si jamais servie -- il y a au moins
   `CONSOLIDATION_LAG_H` = 48 h), `1` sinon. Une consolidation due passe
   devant tout le reste et n'attend donc jamais plus d'un tour de tapis ; les
   consolidations dues entre elles gardent l'ordre de leur visite.

2. Plafond hebdomadaire de l'urne `umbrella` (`umbrella_visit_cause`) : un
   EPIC hors consolidation n'est pas repropose si un sous-grain y a ete cree
   (`last_child_stamp`) ou claimé (`last_claim_stamp`) il y a moins de
   `UMBRELLA_VISIT_CAP_DAYS` = 7 jours. La boucle du tapis l'ecarte en
   `withheld` avec sa cause, et NOMME jusqu'a deux sous-grains ouverts de cet
   EPIC presents dans le pool -- « passe au candidat suivant » sans dire
   lequel laisse la lane re-decouvrir ce que l'organe savait.

3. Les EPICs de consolidation sont exempts du plafond : revenir souvent est
   precisement leur travail.

4. `--belt --report` imprime la duree reelle d'une passe complete, horloge
   posee AVANT la lecture du pool (elle en est la part dominante : 648 s a
   froid mesurees en #20053). Non mesuree s'ecrit « non mesuree », jamais un
   zero.

Tests : 13 dans scripts/tests/test_pick_idle_grain_22500.py, un par point de
l'acceptance plus trois gardes d'elargissement (mot entier, exemption
consolidation, grain ordinaire jamais sous plafond). Suites voisines du tapis
rejouees : 150 passed (belt, board, claim_filter).

Zero appel reseau dans les tests : la boucle prend ses sondes inertes par
defaut, les dates sont injectees par `now=`.

See #22500 -- la cloture revient au coordinateur.

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

* Test(tooling,#22500): test discriminant de la classe, zero nouveau finding ruff

Le premier jeu de tests ne distinguait pas la classe de priorite du tapis
d'origine : les trois tests d'ordre prenaient une consolidation deja la plus
ancienne du pool, cas ou #18832 la servait deja en tete. Un shim qui expose
l'API de #22500 en gardant le comportement d'avant les faisait donc passer.

Ajoute le cas qui distingue reellement : consolidation visitee il y a 49 h
(due) contre un grain ordinaire visite il y a 10 jours. Sans classe, le grain
passe devant ; avec elle, la consolidation due le double.

Corrige aussi les deux findings ruff du fichier de tests (noqa inutile, alias
UTC) et la lecture d'un horodatage naif, sans ajouter de finding au picker
(63 avant, 63 apres, mesures sur le fichier lui-meme).

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

* Fix(tooling,#22504): le discriminant de classe est la POSITION du mot, et parent_refs lit kind(zone,#EPIC)

Deux points de fond de la review Hermes du 2026-10-25T13:55Z, mesures sur le pool reel (500 issues ouvertes, meme instant).

(a) La classe `consolidation` attrapait le MOT, pas le travail. Mesure avant/apres avec le code de cette branche : 17 -> 13 items, 4 faux positifs retires (#22500, #22396, #20298, #16473), zero ajoute. Les quatre sont des conteneurs ou le mot nomme le SUJET : deux EPICs parapluies, la meta-issue du tapis, la methode [ICT]. Le discriminant retenu est la POSITION -- mot en tete du titre, nu, entre crochets, ou en tete de la forme consolidation(zone,#EPIC):. Les 13 grains reels gardent la classe. Consequence assumee et ecrite dans la docstring : un EPIC parapluie perd aussi l'exemption du plafond hebdomadaire, ce qui est le comportement voulu (ses sous-grains portent la classe).

(b) parent_refs ne lisait que Part of #N en corps et [#N en tete de titre. Mesure : 13 issues gagnent un parent qu'aucune autre surface ne donnait (#22395->13504, #21922->11268, #19754->19468, #18397->2874, #17417->13504, ...), zero perte. Restriction volontaire au #N precede d'une virgule (la convention type(scope,#N):) : les formes (PR #N) et (#N suite) nomment une PR ou un fil de suivi ; les prendre pour un parent fausserait last_child_stamp. La variante large ajouterait 8 issues -- choix documente, pas oubli.

Tests : 7 ajoutes (position du mot, EPIC parapluie, deux formes de tete, forme kind(zone,#EPIC), exclusion PR/fil, non-regression des deux surfaces anciennes). Suite picker complete : 667 passed, 1 skipped. ruff 0.17.2 sur les deux fichiers : 63 = 63 (base et tete), fichier de test propre.

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

* Fix(tooling,#22504): la classe consolidation se lit au label seul

Arbitrage coordinateur du 2026-10-25T17:37Z (ai01-1645-c2-22504-decision) :
le repli sur le titre est retire. Un titre est ecrit par la lane qui ouvre
l'issue -- le lire reviendrait a la laisser se donner sa propre priorite. Le
label `consolidation`, pose par le coordinateur, est la seule surface.

Ce que cela remplace dans la PR : le commit precedent discriminait par la
POSITION du mot en tete de titre. La mesure de l'epoque sur le pool (500
ouvertes) donnait 17 -> 13, et laissait 13 grains classes par leur titre.
Ces 13 ne sont plus classes que s'ils portent le label.

Mesure sur le pool reel avec les labels frais (581 ouvertes, 2026-10-25) :
45 issues portent le label -- le chiffre annonce par le coordinateur -- et
le retrait du repli ne retire la classe qu'a UNE issue, #22528
« [Consolidation] Audio/04-Applications : scan des 19 carnets », qui est une
vraie consolidation et n'a simplement pas encore son label. Aucun ajout
possible par ce retrait : 46 -> 45.

Surface de test : la classe se pose par un helper `_consol()` qui pose le
label, jamais le titre ; trois tests epinglent l'inverse (aucun titre ne
donne la classe, quelle que soit la position du mot) ; un test epingle le
cas nomme par le coordinateur, #21648 -> #13504.

Tests : `scripts/tests/test_pick_idle_grain*.py` -> 421 passed (le controle
par shim `test_pick_idle_grain_belt.py`, 75 tests, est dans ce lot).
ruff : 63 findings avant, 63 apres sur `pick_idle_grain.py` (inchange) ;
fichier de test : propre.

See #22500

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

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>

* Fix(docs,#22291): H1 Search-09d/09e + KNOTS-01..04 alignes sur l'ordinal du fichier (tranche 3) (#22365)

* Fix(docs,#22291): H1 des carnets Search-09d/09e et KNOTS-01..04 alignes sur l'ordinal du fichier

Tranche 3 du sweep H1 (#22291) : le libelle de la page de parcours generee
doit designer le carnet qu'elle lie. Search-09d/09e portaient le label de
sous-serie Discrepancy-01/02, KNOTS-01..04 le label legacy Lean 17a-d.
Markdown-only (cellule 0, ligne 1), format et corps preserves verbatim ;
aucune cellule code, output ou execution_count touche -> pas de re-exec due.

Rider bloque par le pre-commit knots-extracts (#21007) : KNOTS-01 cellule 3
citait KNOTS/knot_lean/Knots/FigureEight.lean, forme relative qui ne resout
pas depuis le dossier du carnet -> knot_lean/Knots/FigureEight.lean (le
renvoi ligne 17 est verifie exact dans le module). Markdown-only aussi.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Fix(notebook-lean,#22291): le renvoi FigureEight.lean pointe la ligne 16

La cellule citait 'ligne 17' : dans Knots/FigureEight.lean, det(4_1) =
|Delta(-1)| = 5 est en ligne 16, la 17 etant sa continuation. Markdown-only,
aucune re-execution due.

Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Feat(ict,#17719): tracking des artefacts 9B per-seed -- les sources c.233/c.246 de l'agregat entrent au depot (#22473)

Les deux JSON per-seed du run onset 9B (Qwen3.5-9B-Base, 4 graines, bras W/S)
vivaient uniquement sous D:\Runs\ (hors depot, hors sauvegarde git). L'agregat
communi par #19853 les cite par leur cycle ('c.233 seeds 0,1', 'c.246 seeds
2,3') sans qu'ils soient lisibles. Ils rejoignent ict/results/ a cote de
l'agregat -- 7,6 Ko chacun, loin du seuil results-artifact-policy (512 Ko).

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>

* Feat(hashlife,#22227): GOL-09 consomme l'exposant de temps HashLife (pli 3, grain B) (#22250)

Section 8 nouvelle dans GOL-09 : le cout en temps de HashLife contre la simulation
naive, importe du JSON du grain A sans aucune valeur recopiee a la main. L'exposant
alpha separe les memes temoins que la compressibilite mesuree en sections 3 et 7 :
0.106 a 0.160 sur les periodiques courts, 0.289 sur le canon de Gosper, 1.352 sur
acorn ou HashLife prend 1.38 fois plus de temps que le naif, et AMBIGUOUS sur les
deux soupes avec des seuils declares avant la mesure.

La Conclusion passe en section 9 et integre le resultat ; un Exercice 4 unique est
ajoute dans la section Exercices existante. Re-execution complete (6/6 cellules,
0 erreur, kernel python3 3.13.16), sorties commitees.

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>

* Feat(app15,#22568): digestion EX-0373 en exemple guide credite + exercice souple neuf

Integration du TP1 de Jerome Desale et Mathis Gramaglia (PR #22568) :
- Exercice 1 (EX-0373) converti en Exemple guide 4 credite en section
  Exercices : solution eleve verbatim (contrainte long_trip par egalite,
  verificateur independant, comparaison avant/apres), affichage et bilan
- Nouvel exercice 1 non resolu a la place : version souple (penalite POIDS
  pesee en km au lieu d'interdiction), stub C.1
- Note honnete sur la comparaison ancrée aux sorties fraiches (ecart-type
  267,5 -> 258,6 km, OPTIMAL ; invariants km/longs par construction)
- Re-execution complete 19/19 cellules code, 0 erreur (papermill local,
  ortools 9.15.6755), bloc papermill rafraichi (reserve NanoClaw 3)
- CSV traductions resync (2 rafraichies, 4 ajoutees), parite jumeau OK

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

---------

Co-authored-by: Jean-Sylvain Boige <jsboige@gmail.com>
Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
Co-authored-by: sachalhermet19 <sacha.lhermet@gmail.com>
Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>
myia-ai-01 pushed a commit that referenced this pull request Oct 26, 2026
…e au tapis (#22501)

* Docs(rules,#22500): la consolidation est du DEEP, la priorite se regle au tapis

Mandat user 2026-10-25 : les issues de consolidation reviennent en tete
du tapis plusieurs fois par semaine ; un EPIC d'extension est visite au
plus une fois par semaine, avec un seul sous-grain par visite. Le
plancher G-VAR-1 et la place du MED et du META sont inchanges.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Docs(rules,#22500): priorite au tapis appliquee par le coordinateur tant que #22504 n'est pas merge

Reponse a la review Hermes du 12:26Z :
- proactive-coordination : la regle ne decrit plus au present ce que le
  picker ne fait pas encore ; reserve inline jusqu'au merge de #22504.
- variation-protocol : #22500 est l'issue, l'organe est la PR #22504.
- detail 14.1 : heure exacte de la correction du user (12:19Z, pas 12:25Z),
  citation de 12:20Z ajoutee, convention de verbatim posee (coquilles
  conservees, le body est un extrait).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <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.

3 participants