Skip to content

fix(guard,#17185): le verdict de drift nomme l'env canonique epingle de la serie - #17237

Merged
myia-ai-01 merged 2 commits into
mainfrom
fix/17185-drift-env-hint
Sep 24, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
fix/17185-drift-env-hint

Conversation

@jsboige

@jsboige jsboige commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

Grain: LIGHT/guard -- lane myia-po-2026:CoursIA -- prev: FIX/infra #17230

See #17185 — le constat de l'issue est corrige avant d'ecrire, et ce qui restait vraiment manquant est livre.

Ce que l'issue impute, et ce que le depot porte deja

L'issue conclut : « le depot n'epingle pas d'env canonique pour les re-executions de la serie ICT, donc chaque lane re-execute avec son env local ». Verification firsthand : c'est faux, l'env existe et il est soigneusement epingle.

Artefact Contenu (verbatim)
MyIA.AI.Notebooks/IIT/ICT-Series/pyproject.toml requires-python = ">=3.9,<3.10", pyphi==1.2.0, numpy>=1.21,<2.0 — avec le pourquoi de chaque pin (pyphi 1.2.0 exige ≤ 3.9, collections.Iterable retire en 3.10)
MyIA.AI.Notebooks/IIT/requirements.txt les memes, plus pyemd==0.5.1 et le piege documente (sdist compilee contre numpy 2.x → numpy.dtype size changed)

L'env canonique ICT est l'artefact executable que l'acceptance demande. Rien a creer de ce cote.

Ce qui manquait : le pointeur, au moment ou la lane lit le verdict

Le garde nommait le mecanisme du drift et jamais ou rejouer :

- kernel or language_version changed between base and HEAD; re-execution may have
  used a different Python interpreter (3.11 -> 3.13) which alters repr() ...
- 1 code cells show float-array repr drift consistent with a NumPy 1.x -> 2.x upgrade ...

Une lane qui lit ca n'a qu'une issue : re-executer avec son env local — ce qui reproduit exactement le drift signale. L'information existait, elle n'etait pas rejoignable depuis le verdict.

Le correctif

canonical_env_hint(nb_path, root) remonte depuis le dossier du notebook et rend le pyproject.toml (prioritaire) ou le requirements.txt le plus proche, avec requires-python et le pin numpy quand ils sont declares. La cause est ajoutee dans la branche --explain — celle que le workflow consomme (--explain --json).

Borne assumee : la remontee s'arrete a 3 niveaux. Au-dela, on nommerait un artefact qui ne couvre plus la serie (racine du depot) — un chemin qui a l'apparence d'une reponse. Aucun artefact trouve ⇒ None, et aucun chemin n'est invente : l'absence est une information.

Preuves

1. Suite complete — 43 passed (test_check_kernel_drift.py 14, test_check_kernel_drift_fixes.py 23, le nouveau fichier 6).

2. Execution reelle du vrai garde (A/B dans un seul run) — depot git synthetique, deux series subissant le meme drift, l'une avec un pyproject.toml, l'autre non (temoin) :

notebook : .../SerieDemo/nb.ipynb
  - kernel or language_version changed ... (3.11 -> 3.13) ...
  - 1 code cells show float-array repr drift ... NumPy 1.x -> 2.x ...
  - the series pins a canonical environment at
    `MyIA.AI.Notebooks/Denemo/SerieDemo/pyproject.toml` / requires-python >=3.9,<3.10
    / numpy>=1.21,<2.0; re-executing under that environment keeps the committed
    repr stable, whereas a local interpreter reproduces this drift

notebook : .../SerieSansEnv/nb.ipynb
  - kernel or language_version changed ... (3.11 -> 3.13) ...
  - 1 code cells show float-array repr drift ... NumPy 1.x -> 2.x ...

Le temoin recoit 2 causes, aucune nommant un artefact : la troisieme cause est bien produite par la presence de l'artefact, pas par le drift.

3. Falsification des deux pins — une garde qui ne rougit jamais quand le code est faux ne pinne rien :

Mutation Resultat
cablage retire (env_hint = None) test_explain_branch_calls_the_env_hint FAILED
borne retiree (parents[:_ENV_WALK_LEVELS] → parents) test_walk_stops_at_the_series_boundary FAILED

Restauration par cp depuis une copie, puis 6 passed et git diff --stat au perimetre attendu.

Perimetre

Un seul sujet. 3 fichiers — le garde (+70), un fichier de tests neuf (le fichier de tests existant est modifie par la PR #17236 ouverte sur le meme organe ; je n'y touche pas pour ne pas creer de conflit d'append), et le workflow du garde (paths + invocation pytest).

Correction d'une affirmation fausse de ma premiere redaction

J'y ecrivais que ce workflow etait le seul a lancer les tests du garde, et qu'ajouter le fichier neuf a son invocation etait necessaire pour qu'ils tournent en CI. Faux, et verifie apres coup : pytest.ini porte testpaths = scripts/notebook_tools/tests — un repertoire — et scripts-tests.yml passe explicitement ce repertoire (pytest ... scripts/notebook_tools/tests ...). Tous les fichiers de tests du dossier, le mien comme le _fixes preexistant, sont donc deja collectes et lances par Scripts Tests (CPU). Le grep sur .github/workflows/ qui m'avait fait conclure a un fichier orphelin ne voyait pas ce cablage : il ne passe par aucun chemin nomme.

L'edition du workflow reste dans cette PR, mais pour une raison plus etroite : donner au garde un signal local et rapide sur ses propres tests (quand ce fichier change, c'est le workflow du garde qui tourne en premier, pas seulement la suite large). Si le relecteur prefere le diff minimal, cette partie peut etre retiree sans rien casser — les tests tourneraient quand meme, par Scripts Tests (CPU).

🤖 Generated with Claude Code

…de la serie

Le garde nommait les CAUSES du drift (« un autre interpreteur, 3.11 -> 3.13 »,
« NumPy 1.x -> 2.x ») sans jamais dire OU rejouer. Une serie qui epingle son
environnement obtenait donc un verdict qui la renvoyait a sa propre
introspection, et la lane re-executait avec son env local -- reproduisant le
drift signale.

canonical_env_hint() remonte depuis le dossier du notebook (au plus 3 niveaux :
au-dela on nommerait un artefact qui ne couvre plus la serie) et rend le
pyproject.toml / requirements.txt le plus proche, avec requires-python et le pin
numpy quand ils sont declares. La cause est ajoutee dans la branche --explain,
celle que consomme le workflow (--explain --json).

Correction du constat de l'issue, verifiee avant d'ecrire : l'env canonique ICT
EXISTE deja, epingle et documente (IIT/ICT-Series/pyproject.toml : Python 3.9,
pyphi==1.2.0, numpy>=1.21,<2.0 ; IIT/requirements.txt porte en plus pyemd==0.5.1
avec le pourquoi). Ce qui manquait n'est pas l'artefact, c'est le pointeur vers
lui au moment ou la lane lit le verdict.

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

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #17237 (fix(guard,#17185): le verdict de drift nomme l'env canonique epingle de la serie) 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.

@github-actions github-actions Bot added the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Grain tag obligatoire (#10045, bloquant).

Grain tag absent (no Grain: / in body).

Pour passer ce gate, le body doit porter en tete une ligne de la forme :

Grain: <DEEP|MED|LIGHT>/<genre> -- lane <machine:workspace> -- prev: <TIER>/<GENRE> #<PR>

Le <genre> doit figurer dans l'enumeration §1 de variation-protocol.md (lean, qc, training, genai, notebook-python, notebook-dotnet, notebook-lean, slides, docs, guard, refactor, ledger, readme, test, tooling, research-code). Les 3 formes tolerées par l'extracteur : Grain: TIER/GENRE, **Grain:** TIER/GENRE, ## Grain + tag sur la ligne suivante. La lane doit suivre le format <machine>:<workspace> (cf. lane-claim-protocol.md).

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

@github-actions github-actions Bot added variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) and removed variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) labels Sep 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2 light cap reached (advisory, non bloquant).
La lane myia-po-2026:CoursIA a deja consomme son budget LIGHT du jour (axe genre G-VAR-2/3 (light-genre, quel que soit le tier declare) : #17275 (MED/guard, merge a 2026-09-23T00:03:57Z), #17272 (MED/guard, merge a 2026-09-23T00:11:26Z), #17432 (MED/guard, merge a 2026-09-23T00:23:00Z), #17384 (LIGHT/notebook, merge a 2026-09-23T00:59:13Z), #17414 (MED/guard, merge a 2026-09-23T01:15:48Z), #17247 (LIGHT/test, merge a 2026-09-23T01:24:30Z), #17382 (LIGHT/notebook, merge a 2026-09-23T04:12:48Z), #17285 (MED/guard, merge a 2026-09-23T06:30:29Z), #17368 (MED/guard, merge a 2026-09-23T10:06:27Z), #17447 (MED/guard, merge a 2026-09-23T10:07:49Z), #17502 (MED/guard, merge a 2026-09-23T10:09:47Z), #17393 (MED/guard, merge a 2026-09-23T10:15:23Z), #17291 (MED/guard, merge a 2026-09-23T13:17:10Z), #17243 (LIGHT/guard, merge a 2026-09-23T13:31:10Z), #17254 (LIGHT/test, merge a 2026-09-23T18:41:01Z), #17282 (MED/guard, merge a 2026-09-23T19:01:32Z), #17289 (MED/docs, merge a 2026-09-23T19:04:13Z)).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour,
toutes categories LIGHT confondues
(guard, doc, refs, ... partagent un seul budget) :
c'est un RATIO, pas un plafond plat. La decision de merge reste au coordinateur.

@github-actions github-actions Bot added variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory) variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) labels Sep 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

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

  • TIER-INFLATION : declared LIGHT << effective LIGHT-genre (tally : declared=7 genre=17 cap=16)
  • CAP-EXCEEDED-BY-GENRE : light_genre > cap partage G-VAR-2 (tally : declared=7 genre=17 cap=16)

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.

@jsboige

jsboige commented Sep 24, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-ai-01:CoursIA
pr: 17237
head: 709222f
complete: true
body: read
comments-reviewed: 5
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 64a5cdddb51a6f722cf44838eb2c1b2180a132b54e4d106b54b0cfda405a4e57
diff-files: 3
diff-additions: 192
diff-deletions: 1
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

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

Labels

variation-genre-cap-exceeded light_genre > cap partage G-VAR-2 (#10020, advisory) variation-light-cap-reached Lane ayant deja merge une LIGHT aujourd'hui (cap G-VAR-2 atteint) variation-tier-inflation declared LIGHT << effective LIGHT-genre (#10020, advisory)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants