Repository navigation
feat(notebook-tools,#17093): garde d'atteignabilite de la chaine de navigation (+ tests + baseline) - #17285
Conversation
…avigation `check_notebook_navlinks.py` verifie que chaque cible de lien EXISTE (404). Il ne verifie pas que la chaine ATTEINT chaque notebook. Les deux proprietes divergent exactement sur un notebook insere dont les voisins pointent encore l'un vers l'autre : `12b` n'a alors aucun lien entrant, tous les liens resolvent, le garde 404 est vert -- et le notebook est inatteignable. C'est la classe qui a laisse passer QC-Py-12b et QC-Py-23c (#17093), corriges comme INSTANCES par #17277 mais jamais comme CLASSE. Le garde neuf la detecte sur le corpus reel : il designe ces deux notebooks la, sur main, tant que #17277 n'est pas merge. Mesure : 1353 notebooks au graphe, 70 series jugees, 450 findings (396 entrees orphelines, 54 notebooks inatteignables), 138 dossiers non juges, 18 series a chaine bouclee. Le vocabulaire d'arete a ete CALIBRE par mesure, pas devine : sur un echantillon de 30 orphelins, 12 portaient une reference entrante d'apres grep -- instruites une par une, toutes de la prose, une liste « voir aussi » ou un lien de jumeaux, donc 0 faux positif. Trois conventions manquantes ont ete trouvees ainsi et ajoutees (fleches, marqueur hors du lien, marqueur en cellule), chacune parce qu'elle fabriquait un faux orphelin. Livrable volontairement hors `.github/workflows/` : le cablage CI est signale, pas pose. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[Hermes] APPROVE — preuve-vive rejouée firsthand dans un worktree au head 75283c4.
Rejeu intégral, pas lecture du body :
- Tests :
uv run pytest scripts/notebook_tools/tests/test_check_notebook_nav_chain.py→ 35/35 PASS (5.6 s). - Garde sur le corpus réel (worktree sparse élargi à
MyIA.AI.Notebooks/, 1360 .ipynb locaux) :--json→ 450 findings = 396 orphan_entry + 54 unreachable, 1353 notebooks au graphe, 70 séries, 18 bouclées, 138 non jugées — chaque nombre du body reproduit. Le garde retrouve tout seul les deux fondateursQC-Py-12b-Backtest-ValidityetQC-Py-23c-TimesFM(état d'avant #17277 encore sur main) : détecteur indépendant = correction manuelle, la falsification est réelle. - Mode
--check:OK: 0 NEW findingrc=0 ; mutation de la baseline (retrait de QC-Py-12b) →FAIL: 1 NEW findingrc=1, baseline restaurée byte-identique (git status vide). - Contrats d'erreur :
--familyinconnue → rc=2 (l'épingle du bug « vert silencieux sur rapport vide » tient) ;--family QuantConnectfiltre et rapporte (408 résolus, 42 connus). - Sec-scan des 3 fichiers : 0 match. Import de
LINK_PATTERN/_iter_notebooks/_resolve_targetdepuis le garde 404 vérifié à la source — source unique, pas de duplication divergente.
Une observation de narration, non bloquante : le body dit « grep en trouvait 12 » quand le commentaire du garde (l.117) dit « 13 portaient une référence entrante » — la lecture cohérente est 12 mentions de prose + 1 rangée-flèches (vraie arête, qui a donné la convention). Les deux comptages sont justes mais portent des objets différents ; un lecteur pressé y verra une contradiction. À harmoniser au prochain passage sur le fichier, pas un motif de blocage.
Pas de CI posé dans cette PR (décision #17284) : rien à vérifier côté workflows, conforme au périmètre annoncé.
|
[ADJOINT PREFLIGHT] |
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
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 |
|
[ADJOINT PREFLIGHT] Note READY : tête 577fd73 = 75283c4 (tête approuvée par Hermes le 21/09) + une fusion de rafraîchissement de base prouvée sans contenu (second parent f0e5f2c ancêtre de main, arbre identique à l'auto-merge des parents, ea794f3a2e). Diff inchangé : 3 fichiers neufs, +3202/-0, dont 2328 lignes de baseline JSON générée (le code et les tests font 874 lignes). Tests rejoués ici à la tête : 35/35 passent. L'observation de narration de Hermes (12 contre 13 références, body contre commentaire l.117) est qualifiée non bloquante par son auteur, qui la renvoie au prochain passage sur le fichier. Organe B.0 rc=0. Checks relus à la source, aucun rouge ni vol en cours, MERGEABLE. Le dossier du secrétariat du 22/09 visait 75283c4 ; il est périmé depuis la fusion de base. |
…avigation (#17285) `check_notebook_navlinks.py` verifie que chaque cible de lien EXISTE (404). Il ne verifie pas que la chaine ATTEINT chaque notebook. Les deux proprietes divergent exactement sur un notebook insere dont les voisins pointent encore l'un vers l'autre : `12b` n'a alors aucun lien entrant, tous les liens resolvent, le garde 404 est vert -- et le notebook est inatteignable. C'est la classe qui a laisse passer QC-Py-12b et QC-Py-23c (#17093), corriges comme INSTANCES par #17277 mais jamais comme CLASSE. Le garde neuf la detecte sur le corpus reel : il designe ces deux notebooks la, sur main, tant que #17277 n'est pas merge. Mesure : 1353 notebooks au graphe, 70 series jugees, 450 findings (396 entrees orphelines, 54 notebooks inatteignables), 138 dossiers non juges, 18 series a chaine bouclee. Le vocabulaire d'arete a ete CALIBRE par mesure, pas devine : sur un echantillon de 30 orphelins, 12 portaient une reference entrante d'apres grep -- instruites une par une, toutes de la prose, une liste « voir aussi » ou un lien de jumeaux, donc 0 faux positif. Trois conventions manquantes ont ete trouvees ainsi et ajoutees (fleches, marqueur hors du lien, marqueur en cellule), chacune parce qu'elle fabriquait un faux orphelin. Livrable volontairement hors `.github/workflows/` : le cablage CI est signale, pas pose. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>
…EAU (#17985) * feat(ci,#17284): cable nav-chain guard en organe bloquant sur le NOUVEAU Workflow notebook-nav-chain-guard.yml sur le modele notebook-navlink-check (baseline + --check, exit 1 sur finding absent du baseline, jambe Linux self-hostee, garde same-repo) + entree bloquante dans fast_lane_registry.py + assertion de test. Baseline regenere au jour du cablage (446 -> 453 findings) : main avait pris 13 orphelins depuis #17285 sans qu'aucun gate ne les voie -- le snapshot reflete la dette assumée au cablage, elle se reduira en rollout par serie. Controle positif mesure : notebook piste sans lien entrant -> exit 1 ; sans lui -> exit 0 (baseline courant). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(ci,#17284): allowlist self-hosted du workflow nav-chain-guard La jambe Scripts Tests rougissait sur test_current_repository_self_hosted_jobs_satisfy_isolation_policy : WORKFLOW_NOT_ALLOWED -- le nouveau workflow self-hosted doit figurer dans SELF_HOSTED_WORKFLOW_ALLOWLIST, comme son modele notebook-navlink-check.yml. 58 passed sur la suite policy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(nav,#17284): wire 2.11f into the 2.11 series chain - first live catch of the nav-chain guard The gate fired on its first run: 2.11f (#17960) landed with a README row but no internal nav edge, adding a NEW orphan_entry vs baseline. Source fix, not a baseline bump: transition cell with a "Suivant" edge in 2.11e -> 2.11f (markdown-only, no code cell touched). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix(nav,#17284): rattacher les 2 orphelins neufs sur main (Complexity-03c, Lean-24b) Deux notebooks arrives sur main apres la tete du cablage etaient en NEW vs baseline (mesure coordinateur 27/09 ~07:40Z sur main 1c8d884) : Complexity-03c (via #17769) et Lean-24b (via #17530). Reperation a la source, pas consecration : arrete entrante ajoutee dans le notebook precedent de chaque serie, selon la convention existante. - Complexity-03 : lien [03c - Zoo navigable] ajoute a la ligne Navigation (mirroir des liens 02/03b existants). - Lean-34 : lien [Lean-24b (Confiance et preuves)] ajoute a la ligne Navigation (mirroir du "<< Precedent" que 24b declare vers 34). - Complexity-03 : chemins absolus papermill scrubs par le hook pre-commit (normalisation toleree, metadata hors sorties). check_notebook_nav_chain.py --check : 0 NEW (453 connus), rc=0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix(nav,#17284): rattacher FT-00d et ICT-45 — lignes Navigation predecesseur + miroir Les merges #17506 (FT-00d) et #18017 (ICT-45) ont pose deux orphelins nav-chain (orphan_entry mesure sur main 02fb2e1). Lignes **Navigation** ajoutees chez les predecesseurs (FT-00c, ICT-44) et en miroir chez les deux nouveautes — patron identique au rattachement 1f2d2db (03c/24b). Markdown-only, aucune cellule code touchee. Detecteur : 457 -> 452 findings (2 orphan_entry + 3 conexes resolus), check --check vs baseline rc=0 (aucun NEW). See #17284 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(ci,#17284): scope bloquant nav-chain au diff de la PR (decision c.5854935546) La jambe ne rougit plus sur tout NEW finding repo-wide : seuls les NEW imputables a la PR blocquent (notebook du diff, ou serie dont le README est touche) ; un NEW ne du merge d'une AUTRE PR entre-temps devient un avertsement. Fail-closed : diff absent/vide = tout NEW bloque. - check_notebook_nav_chain.py : --diff-files + _partition_new_by_diff - workflow : changed files par git diff base...HEAD (sans GITHUB_TOKEN, posture tranche 1 preservee), fetch-depth 0 - tests : controles positif (notebook du diff, README de serie) et negatif (hors diff) + fail-closed x2 — 22 passed See #17284 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix(guard,#17284): imputation independent_chain -- la serie est la cle + baseline des 2 ilots Re-mesure ai-01 (c.5856207624) : pour les findings independent_chain, la cle est le REPERTOIRE de la serie, pas un fichier -- `notebook in diff` etait toujours faux, et `rsplit("/", 1)[0]` designait le README du PARENT : un lien ajoute dans une serie sans chaine ne bloquait jamais. _partition_new_by_diff traite desormais kind == "independent_chain" en premier : imputable des qu'un chemin du diff vit sous <serie>/ ou que SON README y est. 4 tests nouveaux dont le cas exige (paire ajoutee dans une serie sans chaine, notebooks dans le diff -> bloque) et la regression parent-README ; controle negatif mesure : 3/4 echouent contre la logique pre-fix. Suite 26/26 pass. Baseline : inscription des 2 ilots nees du rattachement 1683cb7 (GenAI/FineTuning, IIT/ICT-Series -- entrees posees, chaine interne absente), 5 orphan_entry resolus sortent (453 -> 450). Dette declaree, suivi reel : #18057. See #17284 See #18057 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#17985): qualifier les 2 annotations seuil (~450 -> 450 exact, non normatif) Rouge n°2 de la review NanoClaw : les deux littéraux numeriques en commentaire sont alignes sur la mesure (450 entrees au head) et marques informatifs -- le JSON baseline_nb_nav_chain.json est nomme source de verite. 81 tests fast_lane verts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Grain: MED/guard -- lane myia-po-2026:CoursIA -- prev: MED/guard #17174
Quoi: un garde neuf,
check_notebook_nav_chain.py, qui verifie une propriete que le garde 404 existant ne peut pas voir — la chaine de navigation atteint-elle chaque notebook — plus 35 tests et une baseline. Le cablage CI n'est pas pose ici (un sujet par PR) : il est signale dans #17284.Preuve: 35/35 tests verts ; le garde designe les deux notebooks fondateurs sur le corpus reel (mesure ci-dessous) ;
--checkrendOK: 0 NEW findingrc=0 ; calibration du vocabulaire d'arete faite par mesure (30 orphelins echantillonnes, 12 instruits, 0 faux positif) ; pre-commit integralement vert (gitleaks inclus) ; CR=0 mesure sur les trois fichiers.Perimetre: 3 fichiers neufs (
scripts/notebook_tools/check_notebook_nav_chain.py,scripts/notebook_tools/tests/test_check_notebook_nav_chain.py,scripts/tests/baseline_nb_nav_chain.json). Zero fichier existant modifie, zero notebook touche, zero.github/workflows/touche.Le defaut de methode que ce garde ferme
check_notebook_navlinks.pyrepond a « cette cible de lien existe-t-elle ? » (404). Il ne repond pas a « cette chaine atteint-elle ce notebook ? ». Les deux questions divergent sur exactement un cas, et c'est le cas qui s'est produit :Dans cet etat, le notebook insere n'a aucun lien entrant, tous les liens du depot resolvent, et le garde 404 est vert. Le notebook est inatteignable et rien ne le dit. C'est la classe qui a produit #17093 (
QC-Py-12b,QC-Py-23c) — corrigee en instances par #17277, jamais en classe : rien n'empechait la meme insertion de se reproduire le lendemain ailleurs.La verification qui compte : le garde sur le corpus reel
Un test synthetique prouve que le code fait ce que j'ai ecrit. Il ne prouve pas que la propriete visee est la bonne. La preuve qui compte est que le garde retrouve, tout seul, les deux notebooks que #17277 a trouves a la main — et #17277 n'est pas encore merge, donc l'etat d'avant est encore
main:(ces deux lignes sont extraites du rapport JSON, filtrees sur les deux notebooks fondateurs — la commande seule rend les 450 findings)
Le garde neuf designe exactement les deux notebooks fondateurs, sans qu'on les lui ait donnes. C'est cette concordance — detecteur independant contre correction manuelle — qui valide le detecteur, pas la suite de tests seule.
Etat mesure du corpus (2026-09-21, sur
main)Les 138 non-juges sont un resultat, pas une omission : un dossier de recherche sans lien de nav interne n'est pas une chaine, et une serie d'un seul notebook n'a pas de chaine. Les 18 series bouclees (le « suivant » du dernier pointe le premier) sont une convention legitime : le garde choisit le point de depart qui couvre le plus et juge, il n'ecarte pas.
Les 450 findings sont absorbes par la baseline : le garde ne rougit donc que sur du nouveau. C'est le mode utile immediatement — il ferme la classe sans exiger de traiter 450 cas avant d'etre cable. Le backlog est un rollout (#17284), pas un prealable.
Le vocabulaire d'arete a ete calibre, pas devine
Le risque d'un tel garde est de confondre une arete de navigation avec une reference. Un faux positif ici ne coute pas un test rouge : il coute la credibilite du garde. J'ai donc mesure le taux de faux positifs au lieu de le supposer :
sur un echantillon de 30 orphelins declares,
grepen trouvait 12 portant une reference entrante. Instruits un par un, les 12 sont de la prose (nom de fichier entrebackticks), une liste « voir aussi » titree, ou un lien de parite de jumeaux C#/Python. 0 vrai faux positif. Le taux brutgrep(12/30) est une borne superieure —grepne distingue pas une arete d'une mention, c'est precisement pourquoi le garde devait exister.Trois conventions ont ete trouvees par cette mesure, chacune parce qu'elle fabriquait un faux orphelin, et chacune est punaisée par un test portant la mesure qui l'a fait adopter :
← → ↑ ↓comme marqueurs[← MGS-7b LandscapeMultidim](...) · [MGS-8 ... →](...)— nav sans mot**Serie MGS** | Precedent : [MGS-9 - Relief Everest](...)## Navigationen table : titre et liens sur deux lignesLa troisieme est un bon exemple du piege : un marqueur cherche dans la ligne produit un faux orphelin
Sudoku-05-PSOalors que le notebook est parfaitement navigue. Le garde cherche desormais dans la cellule, et la ligne reste un signal secondaire.Sont volontairement pas des aretes, et punaises par des tests dedies : une mention en prose, une liste « voir aussi » titree, un lien de parite de jumeaux. Le residu connu — une rangee d'en-tete « Ladder L1 · L2 · L3 » sans mot de navigation — est documente comme residu, pas maquille en couverture.
Un bug trouve par les tests, dans le garde lui-meme
--family <X>comparait desPatha des chaines repo-relatives : le filtre ne selectionnait rien, rendait « 0 serie jugee » avec rc=0. Un vert silencieux sur un rapport vide est le pire des verts — il ressemble a une serie saine. Le testTestSeriesFilterRegressionpunaise ce cas precis (le filtre doit selectionner la serie et le rapport doit rester non vide), et--familyinexistante rend desormais rc=2, pas 0.Trois autres defauts ont ete trouves par la suite de tests pendant l'ecriture (cle de rapport renommee non propagee dans
main(),NOTEBOOKS_ROOTnon redirige dans deux tests, un test d'ilot detache qui asseraitunreachablela ou le notebook etait en fait atteignable depuis l'entree — remplace par un test de cycle detache, qui est le vrai casunreachable).Ce que cette PR ne fait PAS, explicitement
.github/workflows/est touche par trois PRs ouvertes en ce moment ; un sujet par PR et zero collision. Le cablage est decrit, avec son acceptance, dans nav-chain reachability: wire the guard into CI + drive the 450-finding backlog #17284 — c'est une decision de coordinateur, pas un effet de bord d'une PR de tooling.check_notebook_navlinks.py. Le garde neuf l'importe comme source unique de verite pour l'extraction de liens (LINK_PATTERN,_iter_notebooks,_resolve_target) au lieu de redupliquer la logique : une divergence entre les deux gardes serait invisible, et c'est exactement le genre de duplication qui produit deux verdicts contradictoires sur le meme lien.Liens
See #17093— la classe, corrigee en instances par fix(qc,#17093): les notebooks inseres 12b et 23c ne sont plus sautes par la chaine de navigation #17277, fermee en classe par ce garde.See #17284— cablage CI + rollout du backlog 450.See #17154— meme famille d'angle mort : un garde qui ne mesure pas ce qu'il croit mesurer.Note de lane : cette PR est un grain de contrôle, pas de correction de notebook. Les deux notebooks que le garde retrouve (
QC-Py-12b,QC-Py-23c) sont corriges par #17277, ouvert, non merge — c'est cette non-integration qui rend la falsification valide : le garde tourne contre l'etat d'avant, et retrouve les deux cas.🤖 Generated with Claude Code