From f10ae73a74ba8840cb5e3fc1ecf81ef0ab77aa3b Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 18 Sep 2026 18:57:34 +0200 Subject: [PATCH] fix(hierarchy,#16633): route scan_md_hierarchy walk through canonical notebook_walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local p.rglob('*.ipynb') counted untracked run artifacts (*_executed.ipynb) present in lane worktrees: the +1 H1-DEEP drift of #16633 targets an untracked Lean-14c Golly artifact — an EMITTER defect, not a .gitignore defect (c.16633-1). The canonical walker (#8650) is tracked_only via git ls-files + SKIP_DIRS + papermill artifact filter; out-of-repo invocations (tmp fixtures, tarballs) degrade to the previous disk scan with the documented stderr warning, so fixture-based tests are unchanged. Committed baseline audited: 4 keys, zero artifact keys — no drift-noise possible from the walk change. Two integration tests: tmp git repo (tracked source + untracked _executed twin -> only source yielded) and out-of-repo degradation (both yielded, old behavior pinned). Co-Authored-By: Claude Sonnet 5 --- scripts/notebook_tools/scan_md_hierarchy.py | 10 +++- .../tests/test_scan_md_hierarchy.py | 49 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/scripts/notebook_tools/scan_md_hierarchy.py b/scripts/notebook_tools/scan_md_hierarchy.py index 133b0287ed..0878993af1 100644 --- a/scripts/notebook_tools/scan_md_hierarchy.py +++ b/scripts/notebook_tools/scan_md_hierarchy.py @@ -50,6 +50,14 @@ import argparse, json, re, sys, pathlib from datetime import datetime, timezone +# #16633 — marche canonique centralise (#8650) : tracked_only via `git ls-files` +# + SKIP_DIRS + filtre artefacts papermill. Le rglob local comptait les +# artefacts de run non trackes (`*_executed.ipynb`, `*_output.ipynb`) presentes +# dans les worktrees de lane : le finding `+1 H1-DEEP` de #16633 porte sur un +# fichier untracked — defaut de l'EMETTEUR, pas du .gitignore (cf c.16633-1). +# Degrade a l'ancien scan quand git est indisponible (fixtures tmp, tarball). +from notebook_walk import iter_notebooks as _walk_notebooks # noqa: E402 + BASELINE_DEFAULT = pathlib.Path(__file__).with_name('md_hierarchy_baseline.json') HEADING_RE = re.compile(r'^(#{1,6})\s+(.*\S)\s*$') @@ -250,7 +258,7 @@ def iter_notebooks(args): for a in args: p = pathlib.Path(a) if p.is_dir(): - yield from sorted(p.rglob('*.ipynb')) + yield from _walk_notebooks(p) elif p.suffix == '.ipynb' and p.is_file(): yield p else: diff --git a/scripts/notebook_tools/tests/test_scan_md_hierarchy.py b/scripts/notebook_tools/tests/test_scan_md_hierarchy.py index 2ff711332d..6f1c4d3e44 100644 --- a/scripts/notebook_tools/tests/test_scan_md_hierarchy.py +++ b/scripts/notebook_tools/tests/test_scan_md_hierarchy.py @@ -12,8 +12,11 @@ import tempfile from pathlib import Path +import pytest + sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) -from scan_md_hierarchy import scan_notebook, _has_collapsed_markdown, main # noqa: E402 +from scan_md_hierarchy import ( # noqa: E402 + iter_notebooks, scan_notebook, _has_collapsed_markdown, main) # --------------------------------------------------------------------------- @@ -353,3 +356,47 @@ def test_summary_is_the_last_stdout_line(capsys): _run([nb]) assert capsys.readouterr().out.rstrip().splitlines()[-1] == ( "=== 1/1 notebooks flagged ===") + + +# --------------------------------------------------------------------------- +# #16633 — gitignore-aware walk (canonical notebook_walk, #8650) +# --------------------------------------------------------------------------- + +def test_walk_skips_untracked_executed_artifacts(tmp_path): + """Fondateur #16633 : le `+1 H1-DEEP` portait sur un `*_executed.ipynb` + UNTRACKED dans un worktree de lane. Le rglob local le comptait ; le + marche canonique (git ls-files) ne doit rendre que le notebook tracke. + Defaut de l'EMETTEUR, pas du .gitignore (c.16633-1).""" + import subprocess + try: + subprocess.run(["git", "--version"], capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError): + pytest.skip("git indisponible") + clean = {"cells": [_md("# Titre\n"), _md("Texte.\n")], + "metadata": {}, "nbformat": 4, "nbformat_minor": 5} + (tmp_path / "Serie-1-Source.ipynb").write_text( + json.dumps(clean), encoding="utf-8") + # Copie artefact de run : meme contenu, suffixe _executed, NON commitee. + (tmp_path / "Serie-1-Source_executed.ipynb").write_text( + json.dumps(clean), encoding="utf-8") + for cmd in ( + ["git", "init", "-q"], + ["git", "add", "Serie-1-Source.ipynb"], + ["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "x"], + ): + subprocess.run(cmd, cwd=tmp_path, capture_output=True, check=True) + found = [p.name for p in iter_notebooks([str(tmp_path)])] + assert found == ["Serie-1-Source.ipynb"] + + +def test_walk_degrades_to_disk_scan_without_git(tmp_path): + """Hors repo git (fixtures tmp, tarball) : degrace a l'ancien rglob -- + les deux fichiers sont vus, avec le warning stderr documente (#8650). + Le comportement pre-#16633 est preserve hors repo, pas silencieusement + change.""" + clean = {"cells": [_md("# Titre\n")], "metadata": {}, + "nbformat": 4, "nbformat_minor": 5} + (tmp_path / "A.ipynb").write_text(json.dumps(clean), encoding="utf-8") + (tmp_path / "B_executed.ipynb").write_text(json.dumps(clean), encoding="utf-8") + found = sorted(p.name for p in iter_notebooks([str(tmp_path)])) + assert found == ["A.ipynb", "B_executed.ipynb"]