Repository navigation
Feat(ict25a,#5105): records auditables par completion dans les artefacts d'echelle - #22441
Conversation
…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>
Path-collision (organ #13359/#13615)Cette PR #22441 (
|
…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
left a comment
There was a problem hiding this comment.
[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 dumainet le dict inlineholder["records"].append({y est présent — les tests distinguent bien l'avant de l'après. - Artéfact
runs/ict25a_N_traces_1.5B.jsoncommitté (commit 3, 05:02Z) : structure conforme — 3 graines × (records_sample64 +records_sample_tail64), champsstep/gt/pred/hack/mc/reward/lengthtous présents,gtjamais"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 avecMAX_COMPLETION_LENGTHdu 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.pyexécutée sur l'artefact réel avec le vrai moteur : les 6 fenêtres (3 graines × head/tail) rendentStatus.OK, 0 alerte.
Réserves 🟡 :
- 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.
- 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 » ; headOKvs « 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é derecord_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.
…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>
Reponse a la review Hermes (5641167499) — traitee au commit
|
myia-ai-01
left a comment
There was a problem hiding this comment.
[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.
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels |
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
|
Au head C'est la lecture « dernier
|
|
Correction — mon commentaire precedent se trompait de job. Le rouge est reel, et sa cause est le runner, pas la PR. J'avais lu «
Le vert que j'ai cite appartenait a Cause reelle de l'echec, lue dans le log du job 121102034753, step C'est le meme Jambe rejouee sans nouveau commit ( |
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 :
runs/ict25a_*.jsonn'ont, par graine, quearm, model, seed, steps, train_s, vram_*_gb, n_records, hack_early/late, mc_early/late, reward_early/late— pas derecords_sample{"step", "hack", "reward", "mc"}: nigt, nipred, ni longueurrewardspy.RolloutRecord(0.1.0, envcoursia-ml-training) :call_id, timestamp, step, scalar_reward, components, call_duration_ms, input_length, output_lengthCe que le texte des completions n'aurait pas apporte. J'ai d'abord ajoute le texte brut a chaque record, puis mesure le consommateur :
LengthDriftDetectorlitr.output_length,ComponentDominanceDetectorexige 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 dejarecord_windows,window_statsetprotocol_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 pourLengthDrift, etgtabsent resteNone(un"None"litteral serait indiscernable d'une reponse valant ce mot).records_sample,records_sample_tail,records_total— tete et queue, la fenetre head-only etant exactement le defaut corrige par [ICT-25] l'echantillonrecords_samplene 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 dansGRPOConfiga la constanteMAX_COMPLETION_LENGTH, utilisee aux deux endroits (deux valeurs divergentes rendraient la comparaison entre bras non re-derivable).--max-records(defaut 64) cable jusqu'arun_seed.Correctif 2 — le pont vers le vrai moteur
audit_rows(records, arm, seed)traduit les records vers la forme queMetricStore.appendaccepte. Trois choix dictes par le consommateur :componentsporte les deux composantes du protocole (math_correctlegitime,hackgameable) ;output_lengthvient delength;timestampest 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.pyfait 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 siTHRESHOLDSapparait dans le fichier.Validation
python -m pytest scripts/tests/test_ict25_records.py -q-> 16 passed, y compris sous l'interpretateur qui porterewardspy(le controle de correspondance a la dataclass reelle s'execute alors, au lieu de se sauter).hackable_recordvs formule de referencereward,mcethackidentiques"HACK"-> 4,""-> 0 : la colonne deLengthDriftexistegt=NoneresteNone,pred=None, reward 0.0record_windows(recs, max_records),protocol_block(,args.max_recordsholder["records"].append({absent du runneraudit_rowscall_iddeterministe,scalar_reward,componentsa deux cles,output_length,timestampmonotonerewardspy.RolloutRecordquand le paquet est installeDetectionEngine+MetricStore+RolloutRecord(**row)presents, aucun seuil en durControle negatif du cablage : les assertions de structure rejouees contre la source de
HEADrendent 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).collapsehack=1,mc=0, recompense=0, sortie 160)Status.ALERT, 8 alertes{component 4, ceiling 1, slope 3}ramphack0 -> 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. UnStatus.OKbrut 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 :
Status.ALERTsur un effondrement, donc lesOKobserves sur les traces reelles sont un resultat, pas une absence d'instrument ;StatusquitteOK.Consequence, dite maintenant pour ne pas la redecouvrir apres un run GPU : rewardspy seul ne tranche pas Gate 21. Un
ALERTbrut 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 comparablesterme a terme.
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, fenetresheadettail, 64 records chacune) : 12 fenetres (2 bras x 3 graines x2 fenetres),
Status.OKet 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