Skip to content

Add(tooling,#23444): un organe de mesure multi-runtime pour les carnets .NET - #23447

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/multiruntime-probe
Oct 30, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/multiruntime-probe

Conversation

@jsboige

@jsboige jsboige commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Grain: DEEP/research-code — lane myia-po-2023:CoursIA — prev: DEEP/notebook-python #22464

Add(tooling,#23444): un organe de mesure multi-runtime pour les carnets .NET

Objet

Mesurer un carnet .NET sous plusieurs runtimes avec un build de kernel unique, et rendre
le verdict reproductible : runtime effectif prouvé dans la sortie, métrique extraite
automatiquement, agrégation sur plusieurs exécutions, verdict déclaré.

Quatre fichiers, aucun carnet touché :

Fichier Nature
scripts/notebook_tools/multiruntime_probe.py l'organe (nouveau)
scripts/notebook_tools/tests/test_multiruntime_probe.py 10 tests de la logique pure (sans kernel)
docs/dotnet/net11-async-ffi-deltas.md les résultats de la première application
docs/README.md une ligne d'index (voir « Rouges du premier CI »)

Organ-first — les 5 réponses

  1. Quelle série possède déjà la sémantique de cette opération ? La série .NET possède
    scripts/notebook_tools/dotnet_executor.py et exec_single_cell.py : ils exécutent des
    cellules .NET et savoiront un jour faire tourner un carnet sous un runtime donné.
  2. Peut-on invoquer son module réel ? Oui, et c'est ce qui est fait : l'organe
    import collect_outputs de exec_single_cell (pas de réimplémentation de la collecte des
    sorties) et réutilise bound_native_thread_pools de notebook_helpers. Il n'exécute pas les
    cellules lui-même : il les confie au même chemin d'exécution que le reste du dépôt.
  3. Que faut-il exporter ou refactorer dans la série source ? Rien à ce jour. Ce qui manque n'est
    pas un export : c'est le choix du runtime, absent des deux organes. dotnet_executor.py ne
    fait que réparer un DOTNET_ROOT sans SDK ; exec_single_cell.py ne recâble jamais
    l'environnement. L'organe ne les modifie pas (il n'aurait pas à le faire pour son besoin) : il
    pose DOTNET_ROOT + le roll-forward dans l'environnement du kernel qu'il lance, ce que
    les deux organes ne font pas.
  4. Quel témoin négatif l'organe natif fournit-il ? L'organe natif fournit le témoin du
    wall-clock : il rend 11,0 s pour le carnet sous .NET 10 — grandeur dominée par le
    démarrage kernel + Roslyn
    , qui ne distingue pas les runtimes (le document de famille
    docs/dotnet/net11-runtime-deltas.md le dit déjà). C'est ce témoin-là qui est écarté, au profit
    de la grandeur runtime-sensible du carnet (§4).
  5. Quelle autre série assure la vérification indépendante ? Le document de famille
    docs/dotnet/net11-runtime-deltas.md (bancs BDN, PRs Docs(dotnet,#18770): re-mesure des 3 bancs encore INCONCLUSIF — la fenetre calme n'est pas obtenable sur ai-01 #23326 et Fix(dotnet,#18771): re-mesure banc 4 semantic-fleet -- les deux drapeaux maintenus (croisement avec la session a 5 passes) #23421 en cours) mesure la même
    question par un chemin indépendant (compilation, pas exécution). Les deux documents sont
    siblings, pas concurrents ; le cross-référencement y est explicitement laissé à qui mergera
    les PRs en cours sur le fichier de famille.

Le problème que l'organe résout

Les organes d'exécution du dépôt démarrent un kernel .NET Interactive qui hérite de
l'environnement du process appelant, mais aucun ne permet de choisir le runtime. Mesurer un
carnet sous un runtime isolé relevait donc de la plomberie manuelle : copie de travail,
DOTNET_ROOT dédié, roll-forward, relecture à la main de la métrique dans la sortie. Cet organe
rend la mesure reproductible :

  • un seul build (microsoft.dotnet-interactive 1.0.712001, pin canonique [P0] .NET 11 EPIC #18695 -- aligner le pin dotnet-interactive (po-2026 mesure 1.0.712001 != canon 1.0.617701) #18766), exécuté tel
    quel sous N runtimes — seul le DOTNET_ROOT change ;
  • le runtime effectif est prouvé dans la sortie : une cellule de provenance exécutée dans le
    même kernel
    que la cellule mesurée imprime RuntimeInformation.FrameworkDescription,
    Environment.Version et le répertoire de runtime chargé ;
  • la métrique est extraite de la sortie (séries runs = [...], médianes, identités de thread) et
    agrégée sur plusieurs exécutions ;
  • le verdict est déclaré : DELTA ETABLI si les intervalles [min,max] des deux runtimes sont
    disjoints, INCONCLUSIF sinon, NON RESOLU sous le plancher de résolution (0,5 ms,
    configurable). Un INCONCLUSIF est un résultat déclaré, pas un échec de l'outil.

Le carnet du dépôt n'est jamais modifié : l'organe lit la cellule source, exécute dans un kernel
jetable et garde les sorties en mémoire. Un test vérifie l'identité binaire du carnet après
lecture.

Première application — axe 1 (awaits profonds) du carnet 08-Aspire-AsyncFFI-Dotnet.ipynb

Le carnet est compatible .NET 11 : les 9 cellules de code ont été exécutées sous
11.0.0-rc.3.26522.148 sans erreur, et le runtime effectif est prouvé dans la sortie.

Grandeur mesurée : l'instrument §4 — le temps que le thread natif reste bloqué dans SetResult
pendant que la continuation s'exécute en ligne (le wall-clock du carnet ne distingue pas les
runtimes). n = 25 runs par runtime (5 répétitions × 5 runs internes).

Configuration net10 net11 delta verdict
[A] sans RunContinuationsAsynchronously 80,04 ms [71,82 – 103,48] 81,45 ms [70,59 – 116,79] +1,76 % INCONCLUSIF (intervalles chevauchants)
[B] avec RunContinuationsAsynchronously 0,01 ms [0,01 – 0,02] 0,01 ms [0,01 – 0,02] +0,00 % NON RESOLU (sous le plancher de résolution)

Preuves de runtime (dans la sortie, pas supposées) :

runtime net10 : .NET 10.0.14 (10.0.14) — chargé depuis C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.14\
runtime net11 : .NET 11.0.0-rc.3.26522.148 (11.0.0) — chargé depuis C:\Users\jsboi\.dotnet-r11\shared\Microsoft.NETCore.App\11.0.0-rc.3.26522.148\

Le mécanisme est intact sous .NET 11 : en [A] les identifiants de thread natif/reprise sont
identiques (la continuation s'exécute en ligne), en [B] ils sont différents (la continuation
part sur le pool managé) — sous les deux runtimes, dans les 5 runs.

Aucun gain .NET 11 n'est établi sur cet axe. L'écart de médiane (+1,76 %) est de l'ordre de la
dispersion intra-runtime (≈ ±20 %) et les intervalles se chevauchent largement. Trois passes à n = 5
annoncent des deltas de −10,37 % à +2,52 % d'une passe à l'autre, signe non stable : c'est
la démonstration directe que le verdict à n = 5 n'avait pas de valeur.

SOTA — verdict

RECOVERABLE-LOCAL. Le runtime .NET 11 de mesure (~/.dotnet-r11, 11.0.0-rc.3.26522.148) a été
installé user-local sans élévation (dotnet-install, règle F) et est réellement invoqué ; la
preuve en est dans la sortie citée ci-dessus. Aucune sortie dégradée n'est committée, aucun
INTRINSIC n'est déclaré.

Ce que cette PR ne fait pas

Grain #23441 (axe 1 de l'EPIC #18695) — arbitrage demandé au coordinateur

Le grain #23441 demande l'axe 1 de l'EPIC .NET 11 ; le contenu qu'il demande est celui livré
ici. Il reste non claimé : le plafond EPIC #22500 s'y applique cette semaine (EPIC #18695
visitée le 28/10). L'issue #23444 a donc été créée sans parent lisible (motif : l'organe est
transversal — il sert d'autres séries que l'axe 1), le lien vers #18695 restant visible en prose.
L'arbitrage du plafond revient au coordinateur (--force ou clôture du grain), pas à la lane.

Rouges du premier CI — reproduits puis réparés

Le premier PR gate a rouge sur deux jambes. Les deux ont été reproduites localement sur le
diff committé, puis corrigées dans la branche (commit dédié) :

Vérifications après le correctif, sur le diff committé :

python scripts/notebook_tools/check_prose_quantitative_claims.py --diff origin/main...HEAD --strict
→ [OK] aucun compteur quantitatif en prose   (rc=0)

python scripts/check_docs_index.py
→ OK: 276 doc(s) vivant(s) sous docs/, tous atteignables depuis docs/README.md   (rc=0)

Tests

python -m pytest scripts/notebook_tools/tests/test_multiruntime_probe.py -q
→ 10 passed

Les tests pinnent la logique pure (parsing FR/EN, verdict, plancher de résolution, lecture
non destructive du carnet) : ni kernel, ni runtime, ni réseau. Les imports du noyau Jupyter sont
différés dans run_once pour que cette logique reste testable sans la pile kernel.

Smoke de bout en bout relancé après la refonte : exit 0, deux runtimes mesurés, verdicts
attendus, runtime prouvé dans la sortie.

🤖 Generated with Claude Code

…ts .NET

Mesurer un carnet .NET sous plusieurs runtimes avec un build de kernel
unique : DOTNET_ROOT dedie par runtime, runtime effectif PROUVE dans la
sortie, metrique extraite automatiquement, agregation sur plusieurs
executions, verdict declare (DELTA ETABLI / INCONCLUSIF / NON RESOLU).

Le carnet du depot n'est jamais modifie : l'organe lit la cellule source et
execute dans un kernel jetable.

Premiere application : axe 1 (awaits profonds) de
08-Aspire-AsyncFFI-Dotnet.ipynb. Compatibilite .NET 11 etablie (9 cellules
de code, 0 erreur, runtime 11.0.0-rc.3.26522.148 prouve dans la sortie).
A n=25 par runtime, le delta de la grandeur §4 est de +1,76 % avec des
intervalles chevauchants -> INCONCLUSIF ; mecanisme intact sous les deux
runtimes. Resultats dans docs/dotnet/net11-async-ffi-deltas.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@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).

…mpteurs figes

Deux rouges bloquants du PR gate sur #23447, reproduits localement sur le
diff committé :

- `docs-index-guard` : le nouveau doc n'était pas atteignable depuis
  `docs/README.md` — entrée ajoutée dans la section .NET.
- `prose-counts` (strict) : « 9 cellules de code », « 0 cellule en erreur » et
  « 0 cellule sans execution_count » sont des compteurs d'artefacts figes
  dans la prose (#9377) — le prédicat est conservé, la mesure part.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #23447 (Add(tooling,#23444): un organe de mesure multi-runtime pour les carnets .NET) 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.

@jsboige

jsboige commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2028:CoursIA-3
pr: 23447
head: 29fa136
complete: true
body: read
comments-reviewed: 2
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 440ff23456c8b0eb9b547170342cbbc91a712136d8ee2fa4670d76731f052061
diff-files: 4
diff-additions: 646
diff-deletions: 0
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 23447
organ-rc: 0
[/ADJOINT PREFLIGHT]

Verdict : READY — l'organe est reel, ses tests sont verts a la tete exacte, et l'arithmetique du document se recompute

PR Add(tooling,#23444) — lane porteuse myia-po-2023:CoursIA, machine differente de la mienne : attestation tierce legitime. Elle ne porte aucune review : il manque la lecture d'ai-01, le dossier est fini.

Perimetre : quatre fichiers, +646/-0

Fichier Nature
scripts/notebook_tools/multiruntime_probe.py l'organe, nouveau (376 lignes)
scripts/notebook_tools/tests/test_multiruntime_probe.py 10 tests de logique pure (139 lignes)
docs/dotnet/net11-async-ffi-deltas.md la fiche de mesures (130 lignes)
docs/README.md une ligne d'index (+1/-0)

Les quatre chemins cites par le body sont exactement les quatre fichiers du diff : scope: pass sur un diff purement additif, ou rien n'est retire.

Aucun carnet n'est touche, donc le crible de fond des cellules est sans objet ici. Aucun fichier sous .claude/, aucun CLAUDE.md, aucun fichier sous .github/ : la PR est eligible a merge_ready. Tag Grain: DEEP/research-code sur la lane myia-po-2023:CoursIA.

Tests : 10 verts, rejoues par moi a la tete exacte

Worktree detache a 29fa13677050 :

collected 10 items
scripts/notebook_tools/tests/test_multiruntime_probe.py ... [ 30%]
.......                                                                  [100%]
10 passed in 0.54s

Le compte annonce par le body (10 tests de logique pure, sans kernel ni reseau) est confirme.

Les deux rouges du premier CI : reproduits et confirmes eteints

Le body dit que le premier PR gate a rouge sur deux jambes, docs-index-guard et prose-counts, et qu'elles ont ete corrigees dans la branche. Rejoue dans le worktree a la tete exacte :

python scripts/check_docs_index.py
→ OK: 276 doc(s) vivant(s) sous docs/, tous atteignables depuis docs/README.md.
python scripts/notebook_tools/check_prose_quantitative_claims.py --diff origin/main...HEAD --strict
→ [OK] aucun compteur quantitatif en prose.

Les deux sont verts. Le correctif de docs-index-guard (une ligne d'index dans docs/README.md) est bien present, et la fiche nouvelle n'introduit aucun compteur en prose.

L'arithmetique du document se recompute

Le document publie deux lignes de mesures et une conclusion (« aucun gain .NET 11 n'est etabli sur cet axe »). Les trois assertions sont verifiables a la lecture :

Assertion du document Recalcul Verdict
Configuration [A] : 80,04 ms → 81,45 ms, delta +1,76 % (81,45 − 80,04) / 80,04 = 1,76 % exact
Configuration [B] : 0,01 ms → 0,01 ms, delta +0,00 % identique exact
Conclusion « l'ecart est de l'ordre de la dispersion intra-runtime, intervalles chevauchants » intervalles [71,82 – 103,48] et [70,59 – 116,79] se recouvrent sur toute leur partie basse coherent

Le document qualifie lui-meme le resultat d'INCONCLUSIF et de NON RESOLU selon un plancher de resolution explicite (0,5 ms), et signale la non-stabilite du signe sur trois passes (−10,37 % a +2,52 %). Un outil qui rendrait un gain net ici serait un outil qui ment : le verdict declare est le bon.

Organ-first : l'organe natif est nomme et reutilise

Les cinq reponses exigees sont presentes dans le body, et la substance est verifiable : l'organe importe collect_outputs de exec_single_cell (pas de reimplementation de la collecte) et reutilise bound_native_thread_pools de notebook_helpers ; il ne modifie pas ces deux organes, il pose DOTNET_ROOT et le roll-forward dans l'environnement du noyau qu'il lance. La reponse 3 (ce qu'il faudrait exporter dans la serie source) est repondue par « rien a ce jour, ce qui manque est le choix du runtime ».

Portee du non-verifie, telle que la lane la declare

La mesure est mono-machine, mono-charge, avec une dispersion intra-runtime d'environ ±20 % qui domine l'ecart observe ; le document le dit. Le runtime .NET 11 de mesure n'est pas present sur mon siege, donc je n'ai pas rejoue la mesure : ce que j'atteste est que l'outil est rejouable, que ses tests sont verts, et que le document porte ses propres limites. La verification independante designee par la lane est le document de famille docs/dotnet/net11-runtime-deltas.md (autre chemin de mesure), que cette PR ne touche pas.

Ordre de merge : mesure, sans conflit

La meme question (axe 1, carnet Aspire AsyncFFI, compatibilite .NET 11) est mesuree par #23450 depuis un second siege, avec d'autres fichiers. J'ai mesure le croisement : git merge-tree --write-tree sur les deux tetes rend un arbre sans conflit (rc=0), et l'arbre fusionne contient bien les deux lignes d'index (net11-async-ffi-deltas.md et net11-axis1-async.md). L'ordre de merge est donc libre entre ces deux PRs — ce qui n'etait pas evident a la lecture, les deux insertions se faisant au meme endroit du tableau .NET de docs/README.md.

Checks

commits/29fa13677050bde2183013ca1ab5765cd996814c/check-runs : 68 runs, 36 noms, 0 non-vert au pli latest-wins. mergeable_state: CLEAN, base main.

B.0 : rc=0

Aucune phrase de levee manquante. Les 2 commentaires sont des rapports d'organes (organ-duplication, path-collision), sans marqueur de reserve.

Ce que ce dossier ne dit pas

La valeur du verdict INCONCLUSIF depend du plancher de resolution (0,5 ms) et de l'etalonnage de l'organe ; j'ai verifie la coherence interne du document et le comportement des tests, pas l'etalonnage lui-meme sur une seconde serie de mesures. La lecture de fond du coordinateur porte sur ce choix de seuil.

@github-actions github-actions Bot added the large-pr-no-review PR > seuil sans review (ni bot ni humaine) -- retire quand une review arrive (#11232) label Oct 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Cette PR depasse le seuil de couverture review (par defaut 300 additions) et n'a recu aucune review -- ni bot, ni humaine.

Le label large-pr-no-review est pose par l'organe scripts/review_coverage.py porte par l'issue #11232. Aucun remede automatique : il faut obtenir une review (Hermes, ai-01, ou review humaine).

Le label est retire au balayage suivant (quotidien) des qu'une review arrive -- dans reviews[] ou en commentaire de verdict -- ou que le diff passe sous le seuil. Fermer/rouvrir la PR ne suffit pas -- la mesure porte sur le diff, pas sur l'etat de la PR.

Seuil, historique et exceptions : cf. docs/reference/review-coverage-threshold.md.

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

Approbation coordinateur à la tête exacte 29fa136 (organe de mesure multi-runtime .NET, son test et la fiche de l'axe 1). Les 10 tests de logique pure passent à cette tête. L'organe réutilise les organes existants au lieu de les refaire : il importe collect_outputs de exec_single_cell (multiruntime_probe.py:180) et bound_native_thread_pools de notebook_helpers (:57). L'arithmétique de la fiche se recalcule à l'identique (+1,76 %, intervalles chevauchants), et le verdict INCONCLUSIF est le verdict honnête. Aucun carnet touché, et les checks sont verts au pli latest-wins. Point de portée, non bloquant : aucun artefact de mesure n'est versionné ; la falsifiabilité passe par la commande de rejeu commitée, alors que #23450 versionne ses JSON.

@myia-ai-01
myia-ai-01 merged commit 717e54d into main Oct 30, 2026
44 of 59 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

large-pr-no-review PR > seuil sans review (ni bot ni humaine) -- retire quand une review arrive (#11232) pr-overlap Advisory: another open PR touches the same files (organ #13615)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants