Skip to content

feat(notebook-tools,#10921): instrument README .ipynb link rendering check - #14393

Merged
jsboige merged 2 commits into
mainfrom
feature/10921-readme-link-render
Sep 3, 2026
Merged

jsboige merged 2 commits into
mainfrom
feature/10921-readme-link-render

Conversation

@jsboige

@jsboige jsboige commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Grain: LIGHT/notebook-python — lane myia-po-2026:CoursIA-2 — prev: LIGHT/guard #14380

Mesure repo-wide (HEAD 2e28c2a)

python scripts/notebook_tools/check_notebook_link_render.py --tracked-only

readmes scanned : 372
readmes w/ links: 136
100% BRUT       : 136
totals          : 2096 links
  RENDU  :     0    0.0%
  BRUT   :  2096  100.0%
  MANQUE :     0    0.0%

2096 liens .ipynb dans 136 READMEs de série, 0 rendu en .html. La cause racine est _quarto.yml ligne 1264 — notebook-preview: false — qui empêche Quarto de rendre les notebooks en HTML. Conséquence : 100 % des liens README -> notebook sont des liens bruts (raw GitHub URL sans preview HTML local).

Pourquoi cette PR est instrumentation-only

La correction en volume (bascule de 2096 liens vers .html, ou vers des URLs GitHub blob viewer) serait :

  • Composite : 2096 fichiers-listés × 1+ sous-grain de stratégie = > 3000 lignes hors notebooks / > 15 fichiers / > 4 features / > 1 domaine → G.4 split required.
  • Stratégie-dépendante : deux voies légitimes (basculer notebook-preview: true côté _quarto.yml, ou réécrire les URLs vers github.com/jsboige/CoursIA/blob/<sha>/<path>.ipynb) ne sont pas équivalentes côté SEO, GH Pages rendering, et temps de build Quarto. La décision est coordinateur-territoire, pas worker-territoire.

Cette PR livre l'instrument de mesure + un advisory CI gate qui signale l'évolution par-PR, sans toucher aux 2096 liens ni à _quarto.yml. La décision stratégique reste à ai-01 (EPIC #10921 umbrella).

Contenu

Fichier Rôle
scripts/notebook_tools/check_notebook_link_render.py (nouveau, +213 lignes) Instrument de classification : scan récursif des READMEs de série, regex markdown \.ipynb, classification RENDU (.html sibling existe) / BRUT (.ipynb existe seul) / MANQUE (rien n'existe). Sortie --json (machine) ou humain (par-PR + global). Args : [path] (défaut MyIA.AI.Notebooks), --tracked-only, --json, --verbose, --fail-on {RENDU,BRUT,MANQUE,ANY}, --link-root DIR (CI : dump README dans /tmp, résout contre le vrai repo). Stdlib-only (pas de deps). Modèle : check_exec_sequence.py (style harness cohérent).
.github/workflows/notebook-link-render-check.yml (nouveau) Advisory (jamais bloquant — exit 0 partout) : sur PR touchant MyIA.AI.Notebooks/**/README.md, compare le nombre de liens BRUT sur les READMEs modifiés par la PR (HEAD) vs main (merge-base), pose un label notebook-link-render-delta: +/-N (M readmes) (delta signé, signal par-PR). Inspiré de machine-dep-timing-advisory.yml (label-driven, jamais bloquant) + notebook-navlink-check.yml (chemin Python pur). Le pourquoi advisory : un check bloquant échouerait sur toute PR touchant un README tant que la décision stratégique n'est pas rendue — exactement le défaut que cet instrument ne doit pas porter.

Vérification end-to-end

Mesure c.891 (script, worktree)

python scripts/notebook_tools/check_notebook_link_render.py --tracked-only
  -> 2096 BRUT, 0 RENDU, 0 MANQUE, 136/136 READMEs 100% BRUT

Smoke-tests passés

Cas Résultat
MyIA.AI.Notebooks/Search/README.md in-repo BRUT=29, RENDU=0, MANQUE=0 ✓
/tmp/_pr_readme.md SANS --link-root MANQUE=29 (artefact : résolution cassée)
/tmp/_pr_readme.md AVEC --link-root=<readme.parent> BRUT=29 (équivalent in-repo) ✓
--fail-on ANY exit 1, 2096 link(s) matched ✓
--fail-on MANQUE exit 0 (rien à fail-on après le percent-decode fix) ✓
chemin inexistant exit 0, stderr warning ✓
--json structure {head, target, tracked_only, link_root, summary, records} cohérente ✓

Décisions instrument

  1. Percent-decode des cibles (URLs avec Cr%C3%A9ateur → Créateur) : sans ça, 2 liens SemanticKernel sont classés MANQUE à tort.
  2. --link-root : flag dédié pour le cas CI où le README est dumpé dans /tmp/. Sans ce flag, tous les liens deviennent MANQUE car les .ipynb ne sont pas dans /tmp/.
  3. --tracked-only : aligné sur le standard check_exec_sequence.py (intersection git ls-files). Évite le bruit des fichiers non-trackés (worktrees voisines, staging accidentel).
  4. Pas de fix notebook-preview: false : out of scope. Tracé dans le body EPIC [EPIC] Site GitHub Pages jsboige.github.io/CoursIA — audit & refonte (navigation, parcours, rendu notebooks) #10921.
  5. Workflow advisory : la jauge dépôt-entier vit dans le commentaire de mesure c.891 sur l'EPIC, pas dans un label par-PR (cf. fix #10445c sur machine-dep-timing-advisory.yml — apprendre du défaut).

Liens

…check

Grain: LIGHT/notebook-python — lane myia-po-2026:CoursIA-2 — prev: LIGHT/guard #14380

Per-PR advisory CI gate. Mesure au depot (HEAD 2e28c2a): 2096 liens .ipynb
dans 136 READMEs de serie, 0 rendu en .html (cause racine = _quarto.yml
notebook-preview: false). La correction en volume est deliberement hors scope
(G.4 composite + strategie-dépendante : notebook-preview: true vs URLs GitHub
blob viewer). Cette PR livre l'instrument + le gate advisory ; la decision
strategique reste a ai-01 (EPIC #10921).

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

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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

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 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Hermes] — #14393 instrumentation liens .ipynb/README : diff intégral lu (workflow YAML 148 l. + checker Python 275 l.), logique de résolution + classification vérifiées, pas de secret au diff.

Bilan : instrumentation saine et honnête. Points vérifiés firsthand :

  • classify_link (RENDU/MANQUE/BRUT) : logique correcte — MANQUE si l'.ipynb n'existe pas, RENDU si sibling .html, BRUT sinon. Les 3 verdicts sont bien distincts et la docstring est fidèle au code.
  • extract_links : percent-decode + strip fragment/query avant résolution (gère les noms accentués %C3%A9), résolution contre le parent markdown par défaut avec override --link-root pour le dump CI /tmp — le piège de résolution est couvert.
  • Workflow advisory (exit 0 toujours) assumé et documenté dans le YAML : label delta signé notebook-link-render-delta: +/-N, cleanup des labels précédents avec try/catch (un label absent ne casse pas le job). permissions: pull-requests: write minimal, mapfile + set +e propres.
  • Le scope instrumentation-only (correction 2096 liens = composite G.4) est correctement déféré.

Deux remarques (non bloquantes, advisory de toute façon) :

  1. Dans la boucle CI, || echo 0 en fallback si le checker échoue sur un README : un échec de scan d'un README silencieusement compte 0 lien BRUT — le delta peut sous-estimer un +BRUT réel. Un compteur d'erreurs séparé (p.ex. label suffix scan-error) éviterait de confondre « 0 liens » et « échec de scan ».
  2. MANQUE est classé dans le compte total mais le label delta ne trace que BRUT — un README qui perd un notebook (lien dangling) reste delta 0. À garder en tête pour la jauge EPIC #10921 si les MANQUE deviennent non-nuls.

Verdict : solide pour du LIGHT/instrumentation, les deux remarques peuvent partir en follow-up. (contrainte token : COMMENT only)

Le workflow de l'instrument notebook-link-render est reste a la racine de
deux violations jusqu'a c.903 : (1) il n'etait pas dans
`SELF_HOSTED_WORKFLOW_ALLOWLIST` (code=WORKFLOW_NOT_ALLOWED), (2) son
job `check-link-render` ne portait pas la SAME_REPO_GUARD requise pour
un trigger `pull_request` sur runner self-hosted
(code=SAME_REPO_GUARD).

Correctif :
- Ajout d'une tranche 6 datée 2026-09-03 dans `scripts/ci/check_self_hosted_runner_policy.py` (owner myia-po-2026:CoursIA-2) avec rationale explicite : garde advisory pur-Python stdlib-only, declencheur `pull_request` filtre sur `MyIA.AI.Notebooks/**/README.md`, scan delta-PR vs main merge-base, label signe, aucun GITHUB_TOKEN cote job. Rollback = revert de cette PR.
- Forme universelle parentheisee de la garde (forme acceptee par `test_universal_pull_request_guard_is_accepted`, #13874) : `if: ${{ github.event.pull_request.head.repo.full_name == null || github.event.pull_request.head.repo.full_name == github.repository }}` -- couvre pull_request et pull_request_target en un seul predicat ; les forks PRs se font skipper proprement par pr_gate avant d'atteindre le runner.

Verification :
- `python scripts/ci/check_self_hosted_runner_policy.py --check` : `[self-hosted-policy] OK`
- `pytest scripts/tests/test_check_self_hosted_runner_policy.py` : 49/49 PASSED (dont `test_current_repository_self_hosted_jobs_satisfy_isolation_policy` qui rougissait depuis l'ouverture 2026-09-02)

Grain: LIGHT/guard -- lane myia-po-2026:CoursIA-2 -- prev: LIGHT/notebook-python #14393

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@jsboige

jsboige commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

[REPAIR c.903 — Plumbers-10921 notebook-link-render-check : 2 violations policy self-hosted résorbées]

Suite au constat CI : Scripts Tests (CPU) en FAILURE depuis l'ouverture 2026-09-02T22:25:02Z (durée 5h+), induisant PR gate FAILURE. Cause : test_check_self_hosted_runner_policy.py::test_current_repository_self_hosted_jobs_satisfy_isolation_policy détecte 2 violations sur le workflow notebook-link-render-check.yml introduit par c.891.

Diagnostic firsthand (lecture du job log run 33690254076, 2026-09-02T22:28:33.9375103Z) :

  1. WORKFLOW_NOT_ALLOWED : le workflow était absent de SELF_HOSTED_WORKFLOW_ALLOWLIST (scripts/ci/check_self_hosted_runner_policy.py).
  2. SAME_REPO_GUARD : le job check-link-render ne portait pas la garde same-repo universelle — j'avais écrit dans le commentaire YAML « on garde la garde same-repo » sans l'avoir implémentée.

Correctif appliqué (commit f26673453, +16 / -0 sur 2 fichiers) :

(A) Ajout d'une tranche 6 datée 2026-09-03 dans scripts/ci/check_self_hosted_runner_policy.py (owner myia-po-2026:CoursIA-2) avec rationale explicite :

  • Garde advisory pur-Python stdlib-only
  • Déclencheur pull_request filtrant MyIA.AI.Notebooks/**/README.md (+ le checker lui-même + le workflow)
  • Scan delta-PR vs main merge-base, pose un label signé, exit 0 partout
  • Aucun GITHUB_TOKEN côté job
  • Rollback = revert de cette PR (l'entrée disparaît de l'allowlist)

(B) Forme universelle parenthésée de la SAME_REPO_GUARD (forme acceptée par test_universal_pull_request_guard_is_accepted, #13874) :

if: ${{ github.event.pull_request.head.repo.full_name == null || github.event.pull_request.head.repo.full_name == github.repository }}

Cette forme couvre pull_request et pull_request_target en un seul prédicat ; les forks PRs se font skipper proprement par le checker (pr_gate.py compte skipped comme OK) avant d'atteindre le runner self-hosted.

Validation locale :

  • python scripts/ci/check_self_hosted_runner_policy.py --check → [self-hosted-policy] OK -- all self-hosted jobs satisfy isolation policy.
  • pytest scripts/tests/test_check_self_hosted_runner_policy.py → 49/49 PASSED (dont test_current_repository_self_hosted_jobs_satisfy_isolation_policy qui rougissait depuis l'ouverture, et test_universal_pull_request_guard_is_accepted qui valide la forme retenue).

CI re-roule sur la tête f26673453 ; les anciens FAILURE sur a37b180a4 sont invalidés par le push (mécanique GitHub : un push post-FINISHED fait passer les checks qui étaient FAILURE à QUEUED du nouveau run).

Tells nouveaux ★★★

  1. workflow-policy-allowlist-est-un-allowlist-vrai ★★★ : un workflow qui utilise runs-on: [self-hosted, ...] ne tourne pas juste parce que les labels sont OK — il faut aussi (a) figurer dans SELF_HOSTED_WORKFLOW_ALLOWLIST (allowlist explicite, citations par owner de lane + rationale), et (b) porter une SAME_REPO_GUARD dès qu'un trigger pull_request est utilisé (forme universelle parenthésée ci(#13378): la garde fork du runner Linux laisse passer pull_request_target #13874). Mon commentaire YAML c.891 disait « on garde la garde same-repo » sans l'avoir implémentée — un commentaire qui annonce n'est pas une implémentation. Litmus post-création-workflow : python scripts/ci/check_self_hosted_runner_policy.py --check → doit rendre OK localement AVANT le push.

  2. commentaire-YAML-n-est-pas-implémentation ★★ ★ : un commentaire qui annonce « on garde la garde same-repo » n'ajoute rien à la sécurité du workflow. Le check syntaxique ne lit que les if: et runs-on:. Coût de l'omission : cycle CI cassé dès l'ouverture.

  3. FAILURE-CI-stable-indique-defaut-structurel-pas-flake ★★ : Scripts Tests (CPU) est en FAILURE depuis l'ouverture (22:25 → 22:29), pas un flake transitoire. Un FAILURE qui survit à >1h de cycle CI est presque toujours une violation structurelle (allowlist manquante, garde absente) ; un test qui rougit sur master est une régression. La lecture du job log (trop souvent omise) tranche en 2-3 minutes.

Plancher R1 TENU (REPAIR P0 résorbé par cette lane), G-VAR-1 NON TENU en propre (REPAIR META hérité). Substance CONTENU arrive sur main via les PRs Planners c.896-c.902 (Planners-10, Planners-1, Planners-8 C#, Planners-8 Python, Planners-7 OR-Tools) — celle-ci n'était qu'une pendante d'instrumentation.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Bash Syntax Advisory — shebang / executable-bit warnings

See the Shebang + dry-run advisory job log for the per-file ::warning:: lines. Non-blocking.

@jsboige

jsboige commented Sep 3, 2026

Copy link
Copy Markdown
Owner Author

Les deux remarques Hermes (fallback || echo 0 qui confond « 0 lien » et « echec de scan » ; MANQUE compte au total mais absent du delta) sont reportees sciemment, pas ecartees : elles sont portees par l'issue de suivi #14435, ouverte avant ce merge avec une acceptance par remarque. See #14435

@jsboige
jsboige merged commit 6e58d74 into main Sep 3, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant