Skip to content

Fix(notebooks,#20190): le strip probeAddresses retire l'enveloppe display_data vide - #20262

Open
jsboige wants to merge 2 commits into
mainfrom
fix/20190-empty-display-envelope
Open

jsboige wants to merge 2 commits into
mainfrom
fix/20190-empty-display-envelope

Conversation

@jsboige

@jsboige jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2024:CoursIA-2 — prev: DEEP/notebook-python #19807

Le defaut, et pourquoi il etait permanent

Le strip du bandeau probeAddresses retirait le bandeau en laissant son enveloppe : quand le bandeau occupe tout l'output (forme chaine, et forme liste a un seul element), l'organe remplacait la valeur par "" au lieu de retirer l'output. Il restait sur disque :

{"output_type": "display_data", "data": {"text/html": ""}, "metadata": {}}

Ce residu ne se voit plus jamais : sans bandeau, count_banner_lines rend 0, la boucle principale saute le carnet, et l'enveloppe reste definitivement. C'est un artefact du hook du depot, pas du kernel — l'attribution « artefact kernel cosmetique » de la reserve NanoClaw sur #20118 designait le bon objet avec la mauvaise cause.

Mesure sur origin/main (603300c) : 53 carnets, 53 enveloppes, 0 bandeau — toutes en outputs[0] de la premiere cellule de code, toutes dans des carnets .NET.

Correctif

Deux parties, dans l'organe et non dans une vigilance :

  1. strip_banner_in_place retire desormais l'enveloppe vide qu'elle vient de creer — y compris celles heritees d'une version anterieure de l'outil, donc le correctif est auto-reparateur. Le retrait est textuel comme le reste de l'organe (aucune re-serialisation JSON : les sorties voisines restent byte-identiques) ; il consomme aussi le saut de ligne et l'indentation, sinon une ligne d'espaces orpheline resterait dans le tableau.
  2. --scan-all --check compte le residu (count_empty_envelopes) et echoue dessus, au meme titre que sur un bandeau : sans cela, une enveloppe reapparue ne serait signalee par personne — c'est precisement la cecite qui a laisse les 53 carnets en place.

Sweep applique : --apply-all --exclude-submodules (aucun carnet de sous-module n'est touche — verifie chemin par chemin contre .gitmodules).

Verification

Controle Resultat
Temoin discriminant — --scan-all --check --exclude-submodules avant : rc=1, 53 notebook(s) ... 53 empty envelope(s) ; apres : rc=0, 0 notebook(s) ... 0 empty envelope(s)
Sweep 53 carnets corriges, empty envelopes left: 0, skipped: 0
Diff des carnets 53 fichiers, 0 insertion / 371 suppressions — que du retrait, aucune ligne parasite
JSON des 53 carnets 53 valides, 0 invalide
Tests de l'organe 32 passed (18 existants + 14 ajoutes : is_empty_envelope positif/negatifs, comptage, enveloppe heritee sans bandeau, milieu de liste, derniere position, idempotence, non-regression des sorties legitimes)

Le test existant test_strip_string_form_banner a du changer : il affirmait le contrat fautif (len(outputs) == 2, text/html == ""). C'est le contrat lui-meme que l'issue met en cause ; le test assertait donc le defaut.

Perimetre

Aucune sortie non vide n'est touchee : le predicat exige data == {"text/html": ""} exactement (une seconde cle de donnees, meme vide, disqualifie l'enveloppe) et output_type == "display_data". Les execution_count sont preserves. Aucun carnet n'est re-execute : le changement porte sur une enveloppe vide, pas sur une cellule source.

detect_svg_empty_display.py ne couvrait pas ce cas (il cible les cellules dont les outputs sont absents alors qu'un SVG etait attendu) — la couverture est nouvelle, pas dupliquee.

Closes #20190

🤖 Generated with Claude Code

…e, plus seulement le bandeau

Le strip laissait derriere lui une entree display_data a text/html vide quand le
bandeau occupait tout l'output (forme chaine, et forme liste a un seul element) :
il remplacait la valeur par "" au lieu de retirer l'output. Le residu etait
permanent par construction -- sans bandeau, count_banner_lines rend 0, la boucle
saute le carnet, et l'enveloppe n'est plus jamais revue.

- strip_banner_in_place retire l'enveloppe vide qu'elle vient de creer, y compris
  celles heritees d'une version anterieure de l'outil (correctif auto-reparateur).
  Retrait textuel, comme le reste de l'organe : aucune re-serialisation JSON, les
  sorties voisines restent byte-identiques ; le saut de ligne et l'indentation
  sont consommes avec l'entree, sinon une ligne d'espaces resterait dans le tableau.
- --scan-all --check compte desormais le residu (count_empty_envelopes) et echoue
  dessus : c'est la cecite qui a laisse 53 carnets en place.
- Sweep des 53 carnets concernes (0 insertion / 371 suppressions, aucun sous-module).

Temoin discriminant : --scan-all --check --exclude-submodules rc=1 avec 53
defauts sur la base, rc=0 apres le sweep. 32 tests de l'organe verts.

Closes #20190

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

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Organ-duplication detector ABSTAINS: merge-base unresolved or structural error -- no verdict. See workflow log.

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

@github-actions

Copy link
Copy Markdown
Contributor

Notebook outputs-required (H.4 schema): PASS (every code cell carries an outputs: list)

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Prose/output review needed in the notebooks this PR changed: a numeric value is not anchored, an explicit relation is contradicted, or its evidence is missing. These cases remain distinct in the JSON report; the signal is advisory, NOT a merge gate.
Couverture partielle: 53 notebooks changed, only the first 50 were scanned.

Scope = notebooks CHANGED in this PR, not the whole corpus. Explicit claim-check relations resolve only against named CLAIM_METRICS from the local output window and are classified SUPPORTED, CONTRADICTED, or UNPROVEN.
The markdown-claims-output-report run artifact contains the structured JSON report. See python scripts/check_markdown_claims_output.py --help for re-running locally.
Detector rationale: c.290 / c.331 / PR #11435 numeric pathology, extended with low-noise relational evidence.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Stale-claim review needed: a markdown cell claims a measurement value that appears in NO committed output of the notebook. Advisory, NOT a merge gate — triage against the JSON artifact.
Couverture partielle: 53 notebooks changed, only the first 50 were scanned.

Scope = notebooks CHANGED in this PR, not the whole corpus. The stale-claim-report run artifact holds the structured JSON.
Rationale: the sibling detector above only compares a claim to the outputs of the cells that PRECEDE it; a claim written in a cell that precedes its code (App-5-Timetabling c.2/c.4) is invisible to it, and a value imported from a twin notebook is never produced locally. See python scripts/check_stale_claims.py --help.

@github-actions

Copy link
Copy Markdown
Contributor

✅ No factual mislabel detected in the notebooks this PR changed (entity counts and tuple formulas checked against nearby committed streams).
Couverture partielle: 53 notebooks changed, only the first 50 were scanned.

Scope = notebooks CHANGED in this PR, not the whole corpus. The factual-mislabel-report run artifact holds the structured JSON.
Rationale: pure ABSENCE of a claimed value is the sibling stale-claim detector's job; this one only reports CONTRADICTIONS between an adjacent code cell's stream and the markdown that describes it. See python scripts/check_factual_mislabel.py --help.

@github-actions github-actions Bot added the consecutive-code-cells Modified notebook has >=2 consecutive code cells (#12797) label Oct 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Notebook PR Validation: PASS

  • Notebooks checked: 53
  • Code cells validated: 729
  • Result: All passed

Checks: H.1 (no errors), H.3 (execution_count), C.1 (no banned patterns)
Non-Python kernels (.NET/Lean): C.1 + errors only (execution_count advisory)
QuantConnect notebooks: C.1 + errors only (require QC Cloud for execution)

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Golden-Set Execution (H.7 P3)

✅ 9/9 notebooks passed (certified reproducible)

Notebook Status Time
2.1-Workflow-ML.ipynb ✅ SUCCESS 3.5s
2.2-Descente-de-gradient.ipynb ✅ SUCCESS 4.0s
2.3-Regression-lineaire-logistique.ipynb ✅ SUCCESS 4.5s
2.4-Arbres-Forets-Ensembles.ipynb ✅ SUCCESS 4.8s
Search-01-StateSpace.ipynb ✅ SUCCESS 3.5s
SL-1-LogicalLearning.ipynb ✅ SUCCESS 2.5s
RL-04-Bandits-Manchots-Python.ipynb ✅ SUCCESS 18.8s
GameTheory-04c-NashExistence-Python.ipynb ✅ SUCCESS 3.2s
GameTheory-13d-Optimistic-CFR-Python.ipynb ✅ SUCCESS 11.2s

Pinned lockfile: scripts/notebook_tools/golden_set.lock.txt (H.7 P3, axe A #4208)

Le strip `probeAddresses` de #20262 deplace le blob SHA des notebooks : les 30
paires qu'il touche sont passees DRIFT_INTRODUCED. Rebaseline par la commande
prescrite par le gate, une paire a la fois, `--by myia-po-2024:CoursIA-2`.

`planners-8-temporal` demandait un soin supplementaire : son fichier d'intention
portait encore une liste `audits:` inline de 7 entrees, en retard de 3 entrees
sur le repertoire deja migre. La migration a produit des doublons -- 5
byte-identiques a des fichiers deja trackes, 1 sous-ensemble de `0007`. Les 6
doublons sont retires ; l'entree inline[7], presente nulle part ailleurs, est
committee comme fichier d'audit `0010-2026-09-03` -- les 7 entrees inline sont
donc toutes preservees. L'audit de cette lane est repose a l'index libre suivant
(`0011`), dernier par tri de nom : `_latest_audit` le lit bien, la ou l'index
`0008` d'un premier passage laissait la porte sur un enregistrement perime.

Verifications : 30/30 paires dont le dernier audit par tri de nom porte les SHA
reels des notebooks (`git hash-object`) ; aucun prefixe d'index duplique dans le
registre ; `test_twin_registry_integrity.py` 46 passed.

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

jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Rebaseline des 30 paires twins mises en DRIFT par cette PR

Le strip probeAddresses retire une sortie de cellule : il deplace le blob SHA des notebooks. Les 30 paires touchees par cette PR sont donc passees DRIFT_INTRODUCED au gate twin-parity. Ce n'est pas un defaut de contenu — c'est le contre-coup mecanique attendu d'une reecriture d'output, et il se repare par l'attestation, pas par un revert.

Traitement : la commande que le gate prescrit lui-meme, une paire a la fois,

python scripts/notebook_tools/check_twin_parity.py --update --pair "<nom>" --by "myia-po-2024:CoursIA-2"

Le --by n'est pas decoratif : sans lui, l'audit herite du by de l'audit precedent.

planners-8-temporal : deux pieges mesures sur cette seule paire

  1. Liste inline perimee. Le fichier d'intention portait encore une liste audits: de 7 entrees, alors que le repertoire twin_pairs.d/planners-8-temporal/ en portait deja 9, plus recentes. La migration a reecrit les 7 entrees inline, dont 5 byte-identiques a des enregistrements deja trackes (comparaison sha256) et 1 sous-ensemble de 0007-2026-09-03 (memes 4 SHA, sans le champ reason:). Ces 6 doublons sont retires — le garde test_audit_index_unique_and_no_identical_duplicates_per_pair les refuse de toute facon.
  2. L'entree inline[7] n'existait nulle part ailleurs : elle est committee comme 0010-2026-09-03. Les 7 entrees inline sont donc toutes preservees, verifie entree par entree contre le contenu de HEAD.

Le point qui rendait la reparation inoperante

_latest_audit selectionne audits[-1], c'est-a-dire le dernier par tri de nom de fichier — l'index NNNN est la cle de tri du journal. Ma premiere passe d'--update avait ecrit mon audit en 0008, alors que 0009-2026-10-01 et 0010-2026-09-03 existaient deja : le dernier par tri restait un enregistrement de septembre, et le gate aurait continue de lire des SHA perimes avec une attestation fraiche presente dans le repertoire. L'audit de cette lane est donc repose a l'index libre suivant, 0011-2026-10-10-myia-po-2024-CoursIA-2.yaml, qui est bien le dernier par tri.

Le declencheur est mecanique et vaut d'etre connu : _next_audit_index prend max(index)+1, mais la migration d'une liste inline ecrit aux index d'origine — une entree inline ecrite alors que des index superieurs existent deja peut atterrir avant eux et passer pour plus ancienne qu'elle ne l'est.

Verifications (tete c0e80c78d9)

  • 30/30 paires : le dernier audit par tri de nom est celui du 2026-10-10 pose par cette lane, et ses python_sha / csharp_sha egalent les blobs reels des deux notebooks (git hash-object) — c'est exactement le predicat du gate.
  • Aucun prefixe d'index duplique dans les repertoires de paire du registre.
  • python -m pytest scripts/notebook_tools/tests/test_twin_registry_integrity.py -q rend 46 passed. Le warning « blob orphelin par squash » porte sur cinq paires d'autres lanes et est anterieur a ce commit.
  • Ce commit ne touche aucune cellule de notebook : uniquement le registre, plus planners-8-temporal.yaml (retrait de la liste inline devenue inutile).

Le gate twin-parity est donc attendu vert sur cette tete.

@jsboige

jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Rouges lus et classes : pollution de slot persistant (famille #20174), pas un defaut de cette PR.

Les jambes rouges du head c0e80c78d9 ont ete ouvertes une a une. Six nomment un chemin tracke absent du checkout ; la septieme en est la consequence.

Jambe runner_name Signature lue dans le log
latex-control-chars myia-po-2024-linux-persist-2 ModuleNotFoundError: No module named 'scripts.tests'
Scripts Tests (CPU) myia-po-2024-linux-persist-1 python: can't open file '.../scripts/ci/guard_test_root.py': [Errno 2] No such file or directory
Gitleaks secret scanner myia-po-2024-linux-persist-2 grep: .pre-commit-config.yaml: No such file or directory
Golden-set execution (H.7 P3) myia-po-2024-linux-persist-4 9 carnets « notebook missing on disk », 0/9 passe
Markdown claims anchored (#11435) myia-po-2024-linux-persist-4 file or directory not found: scripts/tests/test_check_markdown_claims_output.py
Validate Quarto build (PR) myia-po-2024-linux-persist-1 Pages rendues: 0 -- consequence : les sources du rendu manquent

La mesure qui tranche entre pollution et regression

Les chemins cites comme absents repondent 200 sur la tete de la PR :
.pre-commit-config.yaml, scripts/ci/guard_test_root.py,
scripts/tests/test_check_markdown_claims_output.py,
MyIA.AI.Notebooks/Search/Part1-Foundations/Search-01-StateSpace.ipynb.

Et le perimetre de la PR ne retire rien : 31 ajouts (les rebaselines twin_pairs.d), 56 modifications, aucune suppression. Le contenu de cette PR ne peut donc pas etre la cause.

Ce que je ne classe PAS avec les autres

Static validation (H.1/H.3/C.1) tombe sur un artefact intermediaire
(Could not post validation summary: ENOENT ... '/tmp/validation_results.json'), pas sur un chemin tracke -- signature voisine, mais non identique. Elle tourne de plus sur myia-ai-01-wsl-2, pas sur un slot persistant. Elle reste donc non classee a ce stade : je ne l'agrege pas a une famille dont elle ne porte pas la signature.

Geste

Aucun rejeu (arbitrage #20174). Le remede -- retirer le label coursia-ephemeral des conteneurs RUNNER_MODE=persistent -- appartient a la lane proprietaire de ces slots, myia-po-2024:CoursIA, et se demande la-bas plutot que de s'y appliquer.

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

VERDICT: LGTM (vérifié: les 87 patchs lus intégralement — 53 carnets validés ligne à ligne par script, 0 insertion/371 suppressions — + listing au head pour l'historique audits + 95 check-runs, 0 échec organique)

[NanoClaw] review structurelle — protocole mécanique : transformation déclarée « retrait seul », donc vérifiée par script sur l'intégralité des 87 fichiers du head c0e80c78 (pas un échantillon), revue statique de l'organe (pas de runtime python sur ce siège, cf. #16098).

Les 53 carnets : le retrait est exactement ce qui est annoncé, 53 fois

Validé mécaniquement sur chaque patch : 0 insertion, 371 suppressions = 7,0 lignes/carnet exactement, et les 7 lignes supprimées sont une seule enveloppe display_data vide — {"output_type": "display_data", "data": {"text/html": ""}, "metadata": {}} en forme chaîne (51 carnets) ou "text/html": [] en forme liste (MGS-09-EverestRelief, MGS-24-SimulatedAnnealing — les deux formes couvertes par le prédicat). Position vérifiée 53/53 : l'enveloppe est le premier élément du tableau outputs de la première cellule de code, et les sorties voisines (stream stdout, etc.) restent en contexte byte-intact. Aucune ligne parasite, aucune re-sérialisation.

Le correctif est dans l'organe, pas dans une vigilance — et il est auto-réparateur

  • Prédicat strict (is_empty_envelope) : display_data uniquement, clés de data exactement ["text/html"] (une seconde clé, même vide, disqualifie), valeur chaîne ou liste jointe vide. Les enveloppes légitimes (SVG vides attendues, execute_result, etc.) ne peuvent pas matcher.
  • Retrait textuel (_drop_empty_envelopes) : équilibrage de crochets conscient des chaînes JSON (le bandeau embarque des litéraux JS), offsets traités en ordre inverse, virgule + saut de ligne + indentation consommés (sinon ligne d'espaces orpheline), garde de chevauchement pour enveloppes adjacentes. Cohérent avec la philosophie byte-préservé des passes bandeau.
  • Early-return if not hits supprimé : un carnet sans bandeau mais porteur d'une enveloppe héritée est maintenant réparé — c'est le mécanisme exact qui rend les 53 corrections possibles, et il couvre les enveloppes produites par l'ancienne version de l'outil.
  • Les retraits d'enveloppes ne comptent pas dans lines_fixed : l'invariant FIXED == pre de l'appelant tient.
  • --check échoue désormais sur le résidu (sys.exit(1) si bandeaux ou enveloppes restantes) — le témoin discriminant du body (rc=1 avec 53 enveloppes → rc=0) est adossé au code, et la cécité qui a laissé les 53 carnets en place est fermée structurellement.

Le changement de contrat du test existant est légitime

test_strip_string_form_banner assertait le contrat fautif lui-même (len(outputs) == 2 + text/html == ""). Le nouveau contrat asserte : sortie bandeau retirée, voisin byte-identical, count_empty_envelopes == 0 — c'est la bonne forme de contre-épreuve. La classe TestEmptyEnvelope (+14 tests positifs/négatifs, héritée, milieu de liste, dernière position, idempotence, non-régression des sorties légitimes) complète.

Le yaml modifié ne perd aucun historique d'audit

planners-8-temporal.yaml (−43) supprime son bloc audits: inline — vérifié par listing au head : chaque entrée supprimée vit dans les fichiers datés twin_pairs.d/planners-8-temporal/0001…0010 (incluant le backfill 0010-2026-09-03 ajouté par cette PR), et 0011-2026-10-10 trace le sweep du jour. Migration complète de convention, zéro perte. Les 31 autres yaml sont des fichiers datés neufs (+6) de la même convention.

CI au head

95 check-runs, 0 échec organique. PR gate = cancelled à 14:30:23Z (3m04s) — classe timeout/superseded d'infra, pas un verdict sur le diff (le run plus récent était encore pending au moment de la sonde). Output-failure ratchet (base vs PR) pass — le ratchet qui aurait attrapé une régression d'outputs sur les 53 carnets est vert.

Note d'attribution : le body rectifie ma réserve #20118 (« artefact kernel cosmétique ») — l'objet était bon, la cause était fausse (c'est le hook du dépôt qui laissait l'enveloppe, pas le kernel). Rectification exacte, endossée.

Ce que je n'ai pas vérifié : l'exécution des 32 tests depuis ce siège (revue statique — le body rapporte 18 existants + 14 neufs pass) ; le re-jeu du sweep --apply-all lui-même (je lis les artefacts committés, pas l'exécution) ; le motif précis de l'annulation du PR gate (lane CI).

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #20262 (Fix(notebooks,#20190): le strip probeAddresses retire l'enveloppe display_data vide) 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.

This branch has not been deployed

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

Labels

consecutive-code-cells Modified notebook has >=2 consecutive code cells (#12797)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

notebooks: le strip du bandeau probeAddresses laisse une entree display_data text/html vide dans 54 carnets

2 participants