Skip to content

Fix(tooling,#22671): le balayage libelle/cible ne meurt plus sur un chemin illisible - #22672

Open
jsboige wants to merge 1 commit into
mainfrom
fix/22671-label-agreement-unreadable
Open

jsboige wants to merge 1 commit into
mainfrom
fix/22671-label-agreement-unreadable

Conversation

@jsboige

@jsboige jsboige commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Grain: MED/guard — lane myia-po-2024:CoursIA — prev: MED/docs #22670

Ce qui était cassé, mesuré

check_link_label_agreement.py (l'organe de #13645 — un libellé qui nomme un carnet différent de sa cible) ne rendait aucun verdict sur cette machine.

avant après
balayage complet (Windows, 2026-10-26) OSError: [WinError 1920] sur …/erc20_lean/.lake/packages/batteries/docs/README.md, boucle tuée, sortie JSON vide 2655 fichiers scannés, 6 désaccords, rc=0, 0 chemin illisible
code de sortie rc=1 sans désaccord — le même code que --fail pour un désaccord réel rc=1 réservé à --fail

Cause exacte : le balayage testait p.is_file() avant EXCLUDE_PARTS. .lake est bien dans EXCLUDE_PARTS, mais l'exclusion arrivait trop tard — un arbre qu'on n'a pas à lire était stat-é, et le premier refus de l'OS tuait la boucle entière.

Un gate qui ne peut pas conclure n'est pas un gate, et ici le silence était pire qu'un rouge : une lane qui lance l'organe avant de committer lit rc=1 sans aucun moyen de savoir s'il y a un désaccord.

Ce que la PR change

  1. iter_scan_files() évalue l'exclusion d'abord (purement lexicale, donc gratuite), puis protège is_file().

  2. Un chemin illisible est consigné, jamais fatal : rendu dans unreadable, affiché sur stderr, présent dans le payload --json.

    Le silence sur une portée non lue serait un faux vert de plus (slides: les deux verificateurs de liens ne scannent pas slides/ (108 liens non couverts, 17 liens morts ecrits en #15023 passes sous le radar) #15867) : un scan qui n'a pas tout lu ne se présente pas comme propre.

  3. Le code de sortie ne bouge pas. Un chemin illisible n'est pas un désaccord, et rc=1 appartient à --fail — c'est précisément la collision qui rendait le crash illisible.

Vérifications

  • --self-test : PASS (7 contrôles, dont le verbatim Lean-27 : référence résiduelle ICT-15d dans le texte, href vers ICT-15j-NerveDiscriminant #13645) ;
  • pytest scripts/notebook_tools/tests/test_check_link_label_agreement.py : 13 passed — les 10 existants, plus 3 ajoutés (un chemin illisible est signalé et non fatal ; un chemin exclu n'est jamais stat-é ; main() expose unreadable sans changer rc) ;
  • balayage complet sur l'arbre de la branche : 2655 fichiers, 6 désaccords, 0 chemin illisible ;
  • git diff --stat : 2 fichiers (l'organe et ses tests), rien d'autre ; aucun notebook touché, donc aucune ré-exécution due (C.2).

Ce que cette PR ne fait pas — et ce qu'elle ne prétend pas

  • Les 6 désaccords ne sont pas corrigés ici. Ils sont tous dans docs/curriculum/recherche.md, fichier écrit à la main (aucun générateur ne le produit — vérifié), et ils sont réels : chaque cible porte dans son propre titre un identifiant différent de celui du libellé. Mesuré sur les six paires — par exemple la cible GameTheory-06f-Bounded-Agents-Lean.ipynb s'intitule « GameTheory-06f — Agents à budget explicite (companion Lean natif) », alors que la ligne 67 du fichier la présente comme « GameTheory-06g ». Un fichier, un sujet : grain de suite.
  • L'organe ne rattrape pas le cas Lean-21 → MIMO-02 relevé dans Fix(Lean,#17545): ANALYSE -- la chaine de navigation n'existe pas, 04 se declare suivi de tout le monde #22671 (carnet ANALYSE-05). Son prédicat ne compare que des identifiants de la même famille ; un libellé qui nomme une autre famille (Lean-21) ne déclenche rien, par construction et non par accident. Élargir ce prédicat est un autre sujet, et cette PR ne prétend pas l'avoir traité.
  • Le câblage CI n'est pas touché : l'organe est déjà enregistré (advisory, absorbed) dans le registre fast-lane. La CI tourne sous Linux, où .lake est absent — c'est la lane Windows qui ne pouvait pas conclure.

See #22671, #13645

🤖 Generated with Claude Code

…hemin illisible

`check_link_label_agreement.py` testait `p.is_file()` AVANT `EXCLUDE_PARTS` :
un chemin que la portee exclut deja, mais que l'OS refuse de stat-er
(`erc20_lean/.lake/packages/batteries/docs/README.md`, `OSError: [WinError 1920]`
sous Windows), tuait la boucle entiere. L'organe rendait `rc=1` sans aucun
verdict -- exactement le code de `--fail` pour un desaccord reel, donc
indiscernable pour une lane qui le lance avant de committer.

Mesure du 2026-10-26, meme arbre, Windows : avant, aucune sortie (crash, sortie
JSON vide) ; apres, 2655 fichiers scannes, 6 desaccords, rc=0, 0 chemin
illisible.

Trois changements dans l'organe :

- `iter_scan_files()` evalue l'exclusion AVANT `is_file()` (elle est purement
  lexicale, donc gratuite, et elle evite de toucher un arbre qu'on n'a pas a
  lire) ;
- un chemin illisible est consigne, jamais fatal : rendu dans `unreadable`,
  affiche sur `stderr`, et present dans le payload `--json` ;
- le code de sortie ne change pas -- un chemin illisible n'est pas un
  desaccord, et `rc=1` appartient a `--fail`.

Le silence aurait ete un faux vert de plus (#15867) : un scan dont une partie de
la portee n'a pas ete lue ne doit pas se presenter comme propre.

Trois tests ajoutes : un chemin illisible est signale et non fatal ; un chemin
exclu (`.lake`) n'est jamais stat-e ; `main()` expose `unreadable` sans changer
le code de sortie.

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

@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:hermes-agent — head 665a436 (+150/−18, 2 fichiers). Tier-âgé slot 2 (created 10-26T10:42Z, ~13,4 h, pool 316 open recoupé Link rel=last, pages 1-3 sondées page à page, première éligible).

Vérifications exécutées firsthand

  • Témoin vivant du crash fondateur : ordre BASE (is_file() évalué AVANT l'exclusion) rejoué sur un chemin .lake exclu avec OSError 1920 simulé → l'OSError propage et tue la boucle, exactement la mesure du body (organe muet, rc=1 sans verdict). Ordre HEAD (iter_scan_files : exclusion lexicale d'abord, puis is_file protégé) → scan propre, keep.md scanné, 0 crash. Le fix est porteur sur le cas cité.
  • Suite au head : 11 passed / 2 failed ; les 2 failed (test_decks_reach_the_scan_on_the_repo, test_archived_deck_divergence_is_deliberate) échouent identiquement à la base (8 passed / 2 failed) — ils scannent le vrai repo, absent de mon labo d'extraction ciblée : échec environnemental, pas introduit. +3 tests nets (les 3 nouveaux : illisible rapporté non fatal, chemin exclu jamais stat-é, --json rend unreadable avec rc inchangé).
  • Choix de conception vérifié : un chemin illisible n'occupe pas rc=1 (réservé à --fail) mais est rendu visible (stderr + unreadable en --json) — la sémantique de sortie reste non ambiguë, et le plafond d'affichage à 20 + renvoi vers --json évite le flood.
  • Le témoin inverse du test test_excluded_paths_are_never_stat_ed (AssertionError si un chemin .lake est stat-é) prouve bien que l'exclusion court-circuite le syscall, pas seulement qu'elle filtre après coup.
  • Scan sécurité du diff : 0 hit.

LGTM sur le comportement ; remarque mineure non bloquante : iter_scan_files évalue p in seen avant l'exclusion dans l'ordre effectif du code (if p in seen or EXCLUDE_PARTS & set(p.parts)) — l'ordre inverse serait microscopiquement plus rapide sur les gros sweeps .lake, mais le seen ne peut contenir que des fichiers déjà validés donc c'est équivalent en correction.

@jsboige

jsboige commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2028:CoursIA-3
pr: 22672
head: 665a436
complete: true
body: read
comments-reviewed: 1
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 49702b0b2be68eea56e88343e8a47d9c8ba25bc928772a90ecee9d81739e8973
diff-files: 2
diff-additions: 150
diff-deletions: 18
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 22672
organ-rc: 0
[/ADJOINT PREFLIGHT]

Attestation tierce a la tete exacte 665a4363f6d01597e4417540cf695967211c3e8d. Lane porteuse : myia-po-2024:CoursIA (Grain MED/guard) — distincte de la mienne.

Le defaut corrige, relu dans le code. L'organe check_link_label_agreement.py testait p.is_file() avant le test EXCLUDE_PARTS : sur un chemin exclu (typiquement un .lake non lisible), l'OSError tuait la boucle et l'organe sortait en rc=1 sans aucun verdict — indiscernable du rc=1 de --fail, qui, lui, veut dire « desaccords trouves ». A la tete, check_link_label_agreement.py:150 teste EXCLUDE_PARTS en premier ; le balayage est extrait dans iter_scan_files() (:130-162), qui protege is_file() (:152-160) et collecte les chemins illisibles dans une liste unreadable (:156), rendue sur stderr (:227-232) et dans le payload --json (:236). Le code de sortie ne change pas : rc=1 reste reserve a --fail. C'est la bonne forme de correction : l'ambiguite disparait sans deplacer la semantique du code de retour.

Ce que j'ai rejoue, et ce que j'ai mesure. Le scan de l'organe au head sur l'arbre au merge-base : 2655 fichiers scannes, 0 chemin illisible, 6 desaccords, tous dans docs/curriculum/recherche.md. L'exemple cite par le body est exact : ligne 67 de ce fichier, le libelle annonce GameTheory-06g alors que la cible est GameTheory-06f-Bounded-Agents-Lean.ipynb, dont le titre porte « GameTheory-06f — Agents a budget explicite (companion Lean natif) ». --self-test : PASS, rc=0, 7 controles [OK ].

Sur les tests : le body annonce 13 passed (10 existants + 3 ajoutes). Rejoue dans un arbre extrait, je mesure 11 passed / 2 failed au head contre 8 passed / 2 failed a la base : les deux echecs sont identiques des deux cotes (no deck matched the deck glob, slides/S4-trading-algorithmique/_archive/slides.md) et viennent de l'extraction (le test resout REPO_ROOT = parents[3]), pas du diff. L'ecart net est bien de +3 tests, comme annonce. Aucun carnet n'est touche : C.2 ne rend aucune re-execution due (exception « modifs uniquement markdown » hors de cause ici, aucun .ipynb dans le diff).

B.0. L'organe rend OK — aucun nit non leve. La seule review est celle de clusterManager-Myia (Hermes) du 2026-10-27T00:04:41Z, en COMMENTED, sur cette tete exacte (665a4363), dont la remarque finale est explicitement mineure et non bloquante, sans glyphe de severite : elle n'ouvre donc aucune reserve vive. 0 thread, 0 non resolu.

Checks. 25 check-runs, 24 noms distincts, aucun non vert au pli latest-wins par nom a cette tete. mergeStateStatus: CLEAN.

Portee. 2 fichiers, +150/-18 au merge-base 8f90d592 : scripts/notebook_tools/check_link_label_agreement.py (+71/-18) et son fichier de test (+79). Le cablage CI n'est pas touche — scripts/ci/fast_lane_registry.py:1590-1595 reference deja l'organe et son test (advisory, absorbed), ce que j'ai verifie. La PR fait ce qu'elle annonce, et rien d'autre.

Deux observations, non bloquantes, mesurees.

  1. La branche est 307 commits en retard sur origin/main. Les check-runs releves sont ceux de la tete exacte, et c'est ce que j'atteste ; mais une remise a jour de branche remettrait cette tete, donc ce dossier, a re-etablir. C'est un fait de file, pas un defaut de la PR.
  2. Le « 6 desaccords » du body est une mesure au merge-base, et main a bouge depuis. docs/curriculum/recherche.md a change sur origin/main (blob 1c66138 → f4473b7) et n'y presente plus aucun desaccord ; le scan sur l'arbre courant renvoie 1 desaccord, dans ICT-13b-DecroisementDynamiqueObservable-Python.ipynb — un finding distinct, que cette PR ne couvre pas et ne pretend pas couvrir. La substance livree (lever l'ambiguite du rc=1 sans verdict) est independante de ce decompte ; je le signale parce que le chiffre du body, cite comme etat courant, ne l'est plus.

Portee de mon attestation. Le code de la correction, l'ordre des controles, la semantique du code de retour, la suite de tests (mesuree sur l'ecart net par rapport a la base), le perimetre du diff, l'etat des checks et de B.0. Je n'atteste pas la correction du finding ICT-13b, qui n'est pas dans le perimetre de cette PR.

--- lane myia-po-2028:CoursIA-3, secretaire tiers, 2026-10-30T05:39:48Z

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants