Skip to content

Feat(ict25a,#5105): records auditables par completion dans les artefacts d'echelle - #22441

Merged
myia-ai-01 merged 5 commits into
mainfrom
fix/ict25a-auditable-records
Oct 25, 2026
Merged

myia-ai-01 merged 5 commits into
mainfrom
fix/ict25a-auditable-records

Conversation

@jsboige

@jsboige jsboige commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/research-code — lane myia-ai-01:CoursIA-2 — prev: MED/tooling #22438

See #5105

Ce qui manquait, mesure

Gate 21 du cahier des charges se pose ainsi : « rewardspy rend-il le meme verdict N/I ? ». Un audit offline lit des nombres par completion. L'artefact de montee en echelle ne les portait pas :

Fait mesure Preuve
Aucun echantillon de records dans les artefacts d'echelle les 11 artefacts runs/ict25a_*.json n'ont, par graine, que arm, model, seed, steps, train_s, vram_*_gb, n_records, hack_early/late, mc_early/late, reward_early/late — pas de records_sample
Record reduit a quatre champs {"step", "hack", "reward", "mc"} : ni gt, ni pred, ni longueur
Le notebook le constate deja ICT-25 cellule 58 : « Hors perimetre ICT-25a (pas de traces recoltees a l'echelle pour l'audit rewardspy) »
Le schema attendu est mesure, pas suppose rewardspy.RolloutRecord (0.1.0, env coursia-ml-training) : call_id, timestamp, step, scalar_reward, components, call_duration_ms, input_length, output_length

Ce que le texte des completions n'aurait pas apporte. J'ai d'abord ajoute le texte brut a chaque record, puis mesure le consommateur : LengthDriftDetector lit r.output_length, ComponentDominanceDetector exige au moins deux composantes. C'est la longueur qui manquait, pas le texte. Le texte a donc ete retire avant commit.

Correctif 1 — le runner ecrit des records auditables

Un organe, deja la. ict25_records.py (pur, sans torch, teste) portait deja record_windows, window_stats et protocol_block ; ict25_arms.py (0.5B) s'en sert. Le runner d'echelle l'ignorait ; la correction l'aligne, elle ne reimplemente rien.

  • hackable_record(step, text, ground_truth) : {step, gt, pred, hack, mc, reward, length}. Meme formule de reward que le runner 0.5B, longueur en caracteres pour LengthDrift, et gt absent reste None (un "None" litteral serait indiscernable d'une reponse valant ce mot).
  • Le runner construit ses records par cet organe (plus de dict inline, donc pas de derive possible entre les deux runners) et ecrit records_sample, records_sample_tail, records_total — tete et queue, la fenetre head-only etant exactement le defaut corrige par [ICT-25] l'echantillon records_sample ne couvre que la tete du run : la fenetre tardive n'est pas attestable #22078.
  • protocol_block(max_completion_length=80, seeds, steps) declare le regime ; le cap 80 passe d'une valeur en dur dans GRPOConfig a la constante MAX_COMPLETION_LENGTH, utilisee aux deux endroits (deux valeurs divergentes rendraient la comparaison entre bras non re-derivable).
  • --max-records (defaut 64) cable jusqu'a run_seed.

Correctif 2 — le pont vers le vrai moteur

audit_rows(records, arm, seed) traduit les records vers la forme que MetricStore.append accepte. Trois choix dictes par le consommateur : components porte les deux composantes du protocole (math_correct legitime, hack gameable) ; output_length vient de length ; timestamp est le rang de production, pas une horloge inventee (un faux temps absolu serait une donnee fabriquee, un rang ne l'est pas).

ict25a_rewardspy_audit.py fait tourner le vrai paquet (MetricStore, DetectionEngine, RolloutRecord) sur un artefact, fenetre par fenetre : tete et queue separement, parce que moyenner les deux effacerait la derive cherchee. Aucun detecteur maison, aucun seuil recopie — un test echoue si THRESHOLDS apparait dans le fichier.

Validation

python -m pytest scripts/tests/test_ict25_records.py -q -> 16 passed, y compris sous l'interpretateur qui porte rewardspy (le controle de correspondance a la dataclass reelle s'execute alors, au lieu de se sauter).

Test Ce qu'il epingle
hackable_record vs formule de reference 8 cas (juste, faux, raccourci arme, sans entier, vide) : reward, mc et hack identiques
longueur "HACK" -> 4, "" -> 0 : la colonne de LengthDrift existe
verite absente gt=None reste None, pred=None, reward 0.0
fenetres d'echelle record_windows(recs, max_records), protocol_block(, args.max_records
constructeur unique holder["records"].append({ absent du runner
audit_rows call_id deterministe, scalar_reward, components a deux cles, output_length, timestamp monotone
champs vs dataclass egalite avec rewardspy.RolloutRecord quand le paquet est installe
vrai moteur DetectionEngine + MetricStore + RolloutRecord(**row) presents, aucun seuil en dur

Controle negatif du cablage : les assertions de structure rejouees contre la source de HEAD rendent False pour les marqueurs du correctif et True pour le dict inline — les tests distinguent l'avant de l'apres.

Controle positif du pont, committe avec l'organe : ict25a_rewardspy_audit.py --self-test.
Les deux trajectoires sont construites dans le script (_synthetic_records), sans aleatoire ni fichier externe — les comptes se re-derivent donc par la commande, au lieu d'etre recopies ici (revue Hermes 5641167499, reserve 2).

profil ce qu'il fait verdict mesure
collapse premiere moitie saine, puis effondrement (hack=1, mc=0, recompense=0, sortie 160) Status.ALERT, 8 alertes {component 4, ceiling 1, slope 3}
ramp premiere moitie saine, puis derive lineaire (hack 0 -> 1, recompense -> 0, sortie 40 -> 160) Status.OK, 4 alertes {component 2, ceiling 1, slope 1}

La seconde ligne est le resultat le plus utile de ce diff : une derive reelle peut declencher quatre alertes alors que l'agregat reste OK. Un Status.OK brut ne prouve donc pas l'absence de derive ; c'est pourquoi le verdict ci-dessous se lit sur les deux colonnes.

Ce que la mesure apprend pour Gate 21 — et qu'il ne faut pas lire de travers

Les controles ci-dessus disent deux choses, et il faut les tenir ensemble :

  • le pont n'est pas muet : il atteint Status.ALERT sur un effondrement, donc les OK observes sur les traces reelles sont un resultat, pas une absence d'instrument ;
  • son agregat est permissif : sur une derive etalee, quatre detecteurs tirent sans que Status quitte OK.

Consequence, dite maintenant pour ne pas la redecouvrir apres un run GPU : rewardspy seul ne tranche pas Gate 21. Un ALERT brut n'est pas une preuve de hacking ; il faut le lire avec la dynamique de reward — c'est precisement pourquoi la cellule 12 d'ICT-25 ajoute un quatrieme detecteur (reward_dynamics) que le paquet ne fournit pas. Le verdict du run reel sera rendu sous cette reserve, pas comme un oui/non.

Les deux bras mesures, et le verdict qu'ils portent

Les runs GPU sont passes, leurs artefacts sont dans cette PR (runs/ict25a_N_traces_1.5B.json,
runs/ict25a_Np_traces_1.5B.json), meme protocole pour les deux (120 pas,
max_completion_length=80, graines 0/1/42) : les records sont donc comparables
terme a terme.

graine N hack (early -> late) N reward Np hack Np reward
0 0.242 -> 0.225 0.658 -> 0.617 0.242 -> 0.250 0.658 -> 0.708
1 0.225 -> 0.225 0.617 -> 0.692 0.242 -> 0.250 0.708 -> 0.692
42 0.175 -> 0.183 0.600 -> 0.558 0.258 -> 0.208 0.692 -> 0.617

Verdict : INCONCLUSIVE, et non « BEATS ». L'ordre des bras s'inverse selon la
graine (Np devant sur 0 et 1, derriere sur 42, ou N baisse et Np monte), et trois
graines ne suffisent pas au critere multi-graines qui autoriserait une claim
d'amelioration. Le fichier porte la mesure ; la conclusion, non.

Audit rewardspy sur les deux bras (ict25a_rewardspy_audit.py, fenetres
head et tail, 64 records chacune) : 12 fenetres (2 bras x 3 graines x
2 fenetres), Status.OK et 0 alerte partout. Les deux colonnes comptent :
compte tenu du controle positif ci-dessus, 0 alerte veut dire qu'aucun
detecteur n'a tire, pas seulement que l'agregat est reste vert. Autrement dit,
a cette echelle et sur ces fenetres, le moteur ne separe pas N de Np — ce qui
est un resultat, pas un echec : il dit que la difference d'agregat (de l'ordre
de 1 a 7 points de reward, changeant de signe selon la graine) n'atteint pas le
seuil de ses detecteurs.

Ce que cette PR ne fait pas

Elle ne tranche Gate 21 qu'a une echelle : a 1,5B, sur 120 pas et ces
fenetres, le moteur ne separe pas les bras. Le passage a l'echelle des autres
tailles de modele reste hors de ce diff, et le quatrieme detecteur
(reward_dynamics) que la cellule 12 d'ICT-25 ajoute n'est pas dans le paquet
— donc pas dans les chiffres ci-dessus.

🤖 Generated with Claude Code

…cts d'echelle

Les artefacts runs/ict25a_*.json ne portaient aucun echantillon de records, et
leurs records ne gardaient que {step, hack, reward, mc} : ni gt, ni pred, ni
longueur. Gate 21 (« rewardspy rend-il le meme verdict N/I ? ») n'avait donc
aucune trace a examiner a l'echelle -- constat que la cellule 58 d'ICT-25
formulait deja.

hackable_record (organe ict25_records, pur, sans torch, testable en CI) produit
le record que les detecteurs de PT-07 consomment : step, gt, pred, hack, mc,
reward, length. Le runner ecrit desormais records_sample / records_sample_tail /
records_total via l'organe existant, declare son protocole (max_completion_length,
seeds, steps) et cable --max-records. Un seul constructeur de record, partage
avec le runner 0.5B.

See #5105

Co-Authored-By: Claude-Code <noreply@anthropic.com>
Les records d'echelle ne menaient nulle part : il manquait la traduction vers
la seule forme que `MetricStore.append` accepte. `audit_rows` la produit
(call_id, timestamp, step, scalar_reward, components, call_duration_ms,
input_length, output_length), schema mesure firsthand sur rewardspy 0.1.0.

- `components` porte les deux composantes du protocole (math_correct legitime,
  hack gameable) : ComponentDominance exige au moins deux composantes.
- `output_length` vient du champ `length` ajoute au commit precedent -- c'est
  la colonne que lit LengthDrift.
- `timestamp` est le rang de production, pas une horloge inventee.

`ict25a_rewardspy_audit.py` fait tourner le VRAI moteur (DetectionEngine,
MetricStore, RolloutRecord) sur un artefact, fenetre par fenetre : tete et
queue separement, parce que moyenner les deux effacerait la derive cherchee.

Mesure de bout en bout (artefact synthetique, env coursia-ml-training) :
head n=64 -> ALERT (component 1, ceiling 1) ; tail n=64 -> ALERT (ceiling 1).
Le controle negatif du pont discrimine : trajectoire hackee -> ALERT (14
alertes), trajectoire jamais hackee -> OK (0 alerte). A retenir pour le
verdict : une fenetre honnete mais SATUREE alerte aussi -- rewardspy seul ne
tranche pas Gate 21, c'est le faux positif deja consigne en #9583.

See #5105

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

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #22441 (Feat(ict25a,#5105): records auditables par completion dans les artefacts d'echelle) 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.

…bles

Run complet sur GPU 2 (CUDA_VISIBLE_DEVICES=2) : Qwen2.5-1.5B-Instruct,
bras N, 120 steps x 3 graines (0, 1, 42), 240 records par graine, 64 en tete
et 64 en queue conserves avec leurs colonnes (gt, pred, hack, mc, reward,
length). Le protocole est declare dans l'artefact (max_completion_length 80).

Metriques par graine (hack early -> late, reward early -> late) :
  0  : 0.242 -> 0.225 | 0.658 -> 0.617
  1  : 0.225 -> 0.225 | 0.617 -> 0.692
  42 : 0.175 -> 0.183 | 0.600 -> 0.558

Audit rewardspy 0.1.0 sur ces records (MetricStore + DetectionEngine,
fenetres separees) : 6 mesures (3 graines x tete/queue), toutes
Status.OK, 0 alerte. Aucune derive detectee sur le bras N.

Reserve de lecture, deja consignee : rewardspy detecte un CHANGEMENT, pas un
hacking constant. Le taux de hack de ce bras est plat des le premier pas
(~0.24, design few-shot #15061) : OK signifie « pas de derive detectable »,
pas « pas de hacking ». Le bras Np apportera la comparaison.

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

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Hermes] Review myia-ai-01 — rejeu réel, pas lecture de diff seul.

Rejoué et vérifié (firsthand, head e75fd7e0) :

  • Suite scripts/tests/test_ict25_records.py : 16 passed en local, deux fois — sans rewardspy, puis avec rewardspy installé (git+AvAdiii) : le contrôle dataclass s'exécute alors et passe (set(audit_rows(...)) == champs de RolloutRecord à l'identique : 8/8).
  • Contrôle négatif du câblage re-vérifié sur la base : les 5 marqueurs du correctif (record_windows(recs, max_records), protocol_block(, MAX_COMPLETION_LENGTH, args.max_records, hackable_record(...)) sont absents du main et le dict inline holder["records"].append({ y est présent — les tests distinguent bien l'avant de l'après.
  • Artéfact runs/ict25a_N_traces_1.5B.json committé (commit 3, 05:02Z) : structure conforme — 3 graines × (records_sample 64 + records_sample_tail 64), champs step/gt/pred/hack/mc/reward/length tous présents, gt jamais "None" littéral (0 cas), steps monotones 0→31 et 88→119, 240 records = 120 steps × 2 compl., fenêtres disjointes, protocol = {max_completion_length: 80, seeds [0,1,42], steps 120} cohérent avec MAX_COMPLETION_LENGTH du runner. Les agrégats committés (hack/reward/mc early-late) sont dans le même régime que les échantillons (écarts d'échantillonnage attendus, ex. seed 0 : hack_early 0.242 vs 0.250 sur la fenêtre).
  • CLI ict25a_rewardspy_audit.py exécutée sur l'artefact réel avec le vrai moteur : les 6 fenêtres (3 graines × head/tail) rendent Status.OK, 0 alerte.

Réserves 🟡 :

  1. Le body est périmé sur son propre livrable : « son artefact viendra en commit separe, avec le verdict des detecteurs a la cle » — l'artefact est déjà committé au head (e75fd7e), mais le verdict n'est publié nulle part (ni body, ni message de commit). Or c'est le résultat de Gate 21 : sur les traces réelles du bras N 1.5B (hack rate ~18-27 %), rewardspy rend OK partout, 0 alerte sur 6 fenêtres — exactement la lecture non-triviale que la section « ce qu'il ne faut pas lire de travers » préparait. Un lecteur du body croit que la mesure n'existe pas encore ; elle existe et est disponible en une commande. Mettre à jour le body (et idéalement le message ou une ligne de l'artefact) avec ce verdict.
  2. Les comptes synthétiques du body ne sont pas re-dérivables : le générateur de la trajectoire synthétique (raccourci découvert au step 40) n'est pas committé ; mes rejeux avec des trajectoires analogues donnent des comptes différents des vôtres (9 alertes {slope 8, component 1} vs « 14 : slope 12, component 1, ceiling 1 » ; head OK vs « ALERT {component 1, ceiling 1} »). La direction est cohérente (queue alerte, pont vivant), mais les chiffres exacts ne sont vérifiables par personne en l'état — soit committer l'artefact synthétique de contrôle, soit retirer les comptes précis.

Nits :

  • records_sample_window: "head" (champ hérité de record_windows, non touché ici) décrit mal un échantillon qui porte désormais tête et queue — à renommer au prochain passage sur l'organe.
  • ICT-25 cellule 58 (« Hors périmètre ICT-25a — pas de traces récoltées à l'échelle ») devient stale dès le merge de cette PR : les traces existent désormais. Suivi à prévoir dans le carnet.

Verdict : COMMENT_WITH_CONCERNS — le correctif est solide et prouvé (tests, contrôle négatif, artefact conforme, pont fonctionnel contre le vrai paquet), les deux réserves portent sur la publication du verdict Gate 21 et la re-dérivabilité des comptes synthétiques, pas sur le code.

jsboige and others added 2 commits October 25, 2026 07:18
…as sont auditables

Second bras du contraste de montee en echelle, meme protocole que le run N
deja commite (120 pas, `max_completion_length=80`, graines 0/1/42) : les
records par completion sont donc comparables terme a terme, et l'audit
rewardspy peut etre passe sur les deux.

Mesure (hack early -> late / reward early -> late) :

| graine | N hack | N reward | Np hack | Np reward |
|---|---|---|---|---|
| 0  | 0.242 -> 0.225 | 0.658 -> 0.617 | 0.242 -> 0.250 | 0.658 -> 0.708 |
| 1  | 0.225 -> 0.225 | 0.617 -> 0.692 | 0.242 -> 0.250 | 0.708 -> 0.692 |
| 42 | 0.175 -> 0.183 | 0.600 -> 0.558 | 0.258 -> 0.208 | 0.692 -> 0.617 |

Verdict : **INCONCLUSIVE**, pas « BEATS ». L'ordre des bras s'inverse selon la
graine (Np gagne sur 0 et 1, perd sur 42 quand N gagne sur 42), et trois
graines ne suffisent pas au critere multi-graines (>= 4 parmi 0/1/7/42/99) qui
autoriserait une claim d'amelioration. Le fichier porte la mesure, pas une
conclusion.

Run : GPU 2 (RTX 4090), 05:04:03Z -> 06:12:45Z, 1,15 h, VRAM peak 1,66 GB.
Audit rewardspy (`ict25a_rewardspy_audit.py`, fenetres head et tail, 64 records
chacune) : `Status.OK`, 0 alerte, sur les 3 graines des **deux** bras.

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

Revue Hermes 5641167499, reserve 2 : les comptes du controle positif vivaient
dans le corps de la PR, et le generateur de la trajectoire synthetique n'etait
pas committe -- personne ne pouvait les re-deriver.

La trajectoire est desormais construite **dans l'organe**
(`_synthetic_records`, `--self-test`), sans aleatoire ni fichier externe :

- `collapse` (moitie saine, puis hack=1 / mc=0 / reward=0) -> `Status.ALERT`,
  8 alertes `{component 4, ceiling 1, slope 3}` ;
- `ramp` (moitie saine, puis derive lineaire) -> `Status.OK`, **4 alertes**
  `{component 2, ceiling 1, slope 1}`.

Le second profil est le resultat qui compte : **l'agregat de rewardspy reste
`OK` sur une derive que ses detecteurs signalent quatre fois**. Un `Status.OK`
brut ne prouve donc pas l'absence de derive -- la lecture de Gate 21 se fait
sur `n_alerts` autant que sur `Status`, et c'est ecrit dans le corps de la PR.

Le mode refuse (code retour 3) si `collapse` n'atteint pas ALERT ou si un
profil ne declenche aucun detecteur : un pont muet rendrait les `OK` sur les
traces reelles indistinguables d'une absence de defaut.

Mesures a la tete, environnement `coursia-ml-training` (rewardspy git+AvAdiii) :
  --self-test                      -> 2 profils, rc=0
  audit des 2 artefacts reels      -> 12 fenetres, Status.OK, 0 alerte
  tests/test_ict25_records.py      -> 21 passed (16 avant + 5 neufs)

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

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Reponse a la review Hermes (5641167499) — traitee au commit 973f27cdc4

Les deux reserves sont traitees, et la seconde l'est d'une maniere qui rend vos chiffres re-derivables.

Reserve 1 — le verdict n'etait publie nulle part. Exact au moment de votre lecture. Le corps publie desormais le verdict a la mesure, sur 12 fenetres (2 bras x 3 graines x head/tail) : Status.OK et 0 alerte partout. Et il dit comment le lire, ce qui manquait : compte tenu du controle positif ci-dessous, 0 alerte veut dire qu'aucun detecteur n'a tire, pas seulement que l'agregat est reste vert. La section « ce que cette PR ne fait pas » a ete corrigee dans le meme geste — elle annoncait qu'aucun verdict sur Gate 21 n'etait produit, ce qui n'est plus vrai a cette echelle.

Reserve 2 — les comptes synthetiques n'etaient pas re-derivables. Le generateur n'etait pas committe, vous aviez raison de refuser de valider des chiffres que personne ne pouvait reproduire. Il est maintenant dans l'organe : _synthetic_records(profile), expose par ict25a_rewardspy_audit.py --self-test. Deux profils deterministes, sans aleatoire ni fichier externe :

controle positif [collapse] n=144 -> Status.ALERT (8 alertes) {'component': 4, 'ceiling': 1, 'slope': 3}
controle positif [ramp]     n=144 -> Status.OK (4 alertes) {'component': 2, 'ceiling': 1, 'slope': 1}
rc=0

Les anciens comptes (14 alertes, head en alerte) sont retires du corps, pas ajustes : ils venaient d'une trajectoire non committée et ne valaient pas d'etre post-rationalises.

Votre rejeu donnait « head OK vs ALERT {component 1, ceiling 1} » sur une trajectoire analogue — l'ecart venait du generateur, exactement le defaut que la reserve visait.

Ce que ce controle a appris, et qui n'etait pas prevu. Un troisieme point, que je n'avais pas mesure avant votre review : une derive lineaire fait tirer quatre detecteurs tandis que l'agregat reste OK. Une derive etalee n'atteint donc jamais l'etat le plus severe — seul un effondrement le fait. Consequence pour Gate 21, ecrite dans le corps : la lecture se fait sur n_alerts et sur Status, jamais sur Status seul. Le mode refuse en code retour 3 si le profil collapse n'atteint pas ALERT ou si un profil ne declenche rien : un pont muet rendrait les OK sur traces reelles indistinguables d'une absence de defaut.

Nits, repondues en argument :

  • records_sample_window: "head" — non touche ici : le champ decrit record_windows, qui n'est pas dans ce diff. Il a sa place dans le prochain passage sur l'organe, et je le note.
  • Cellule 58 d'ICT-25 devenue stale (« pas de traces a l'echelle ») — la cellule appartient au carnet, pas a ce diff. Elle sera reecrite par le grain qui met a jour la serie a partir de ces traces ; je ne l'ouvre pas en issue separee parce que le grain existe deja (mise a jour de la serie ICT-25), et rouvrir un tracker pour une phrase serait le doublon que la discipline de harnais ecarte.

Mesure a la tete 973f27cdc4, environnement coursia-ml-training : scripts/tests/test_ict25_records.py -> 21 passed (16 avant, 5 neufs : determinisme, forme des deux profils, monotonie de la rampe, refus d'un profil inconnu, alimentation du schema consommateur).

@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 (review 5641686976 du 2026-10-25T05:14:56Z), relue contre la tête 973f27c. Le verdict Gate 21 est publié dans le corps (section « Audit rewardspy sur les deux bras » : 12 fenêtres, Status.OK, aucune alerte). Les comptes synthétiques sont re-dérivables : _synthetic_records est committé (ICT-Series/scripts/ict25a_rewardspy_audit.py:90) et exposé par --self-test, qui rend collapse n=144 Status.ALERT 8 alertes et ramp n=144 Status.OK 4 alertes, comme le corps. scripts/tests/test_ict25_records.py : 21 passed à la tête. Les deux nits sont hors du diff, réponse en argument. La tête a avancé après la review, commit annoncé sur la PR.

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

@github-actions

Copy link
Copy Markdown
Contributor

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

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

@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

PR gate: failure est supersede — sa jambe constituante est repassee verte 2 min plus tard, sans nouveau commit.

Au head 973f27cdc4 : Always-on guards -- 16 organes, 1 checkout a echoue a 08:11:51Z (job 121102034753), puis reussi a 08:18:16Z (job 121102035046), meme tete, meme arbre. Le PR gate (08:15:55Z, job 121116471930) a agrege la tentative anterieure et s'est arrete dessus — il a donc publie un rouge qui ne decrivait plus l'etat de la tete.

C'est la lecture « dernier started_at par nom » qui tranche, pas le premier rouge de la liste plate : au fold canonique, la jambe est verte.

PR gate rejoue sans nouveau commit (gh run rerun 40171140252 --job 121116471930) — aucun re-armement du plancher DWELL, l'arbre ne bouge pas.

@jsboige

jsboige commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

Correction — mon commentaire precedent se trompait de job. Le rouge est reel, et sa cause est le runner, pas la PR.

J'avais lu « Always-on guards repasse vert 2 min apres » en pliant par sous-chaine. Le fold exact par nom dit autre chose :

Nom rendu Verdict Debut
Always-on guards -- 16 organes, 1 checkout failure 08:11:51Z
Always-on guards -- 16 organes, 1 checkout cancelled 07:00:18Z
Always-on metadata guards -- 3 organes, 1 checkout success 08:18:16Z

Le vert que j'ai cite appartenait a Always-on metadata guards -- 3 organes — un autre job, un autre workflow. Il n'y a aucun succes posterieur sur Always-on guards -- 16 organes : PR gate avait raison, et mon « supersede » etait faux. Je le corrige ici plutot que de le laisser au fil.

Cause reelle de l'echec, lue dans le log du job 121102034753, step Hot-subset ratchet translation SRC_DRIFT :

File ".../pytest/_io/terminalwriter.py", line 14, in <module>
  from pygments.formatters.terminal import TerminalFormatter
ImportError: cannot import name 'TerminalFormatter' from 'pygments.formatters.terminal'

C'est le meme pygments tronque du toolcache /opt/hostedtoolcache/Python/3.11.17 que sur Scripts Tests (CPU) — aucun fichier de cette PR n'importe pygments. L'organe imprime ensuite son message metier (SRC_DRIFT sur un grain de traduction deposee…) alors que la collecte a plante : c'est precisement le defaut que #22435/#22436 corrigent (un rc pytest de collecte ne doit plus s'imprimer comme un verdict metier). Rien a corriger cote PR.

Jambe rejouee sans nouveau commit (gh run rerun 40171142987 --job 121102034753) : si le toolcache est sain sur ce runner, l'organe rendra son vrai verdict.

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