Repository navigation
fix(notebook-tools,#18146): count_exercises sees scaffolded stubs, bold titles, and correct pairing - #18181
Conversation
…gate swallowed + bold titles + pairing preferences Four gate fixes (_body_computes_result / _return_is_derived): - strip tail comments before operand analysis (C# // TODO slashes read as division) - exclude docstring lines from effective-code-line count - treat all-placeholder return dicts/lists as templates, not computed results - require >=2 logic lines: single scaffolding assignment no longer makes a naked return derived Pairing fixes: - recognize **Exercice N** bold titles (span starts with the exercise word; number-dedup in cell) - preference order: same-number backward/forward, then adjacent numberless undescribing stub, then forward generic; paired_code_indices guard on all numbered branches - forward scan stops at intervening header cells (nearest header owns its stub) - deferred unpaired resolution via blocker chains (preserves #15080 negative control) Corpus measurement (1259 notebooks): 86 count changes, 26 conforming flips all False->True, 0 True->False; the 4 issue-reproduced notebooks count 3 each. 123 file tests (112 + 11 new), 311 consumer tests green. Closes #18146 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
VERDICT: LGTM — review d'exécution, chaque critère d'acceptance de #18146 rejoué sur le code du head.
Ce qui a été réellement exécuté (pas lu — rejoué) :
- Suite de tests du head :
123 passeden local (pytest test_count_exercises.pysur les fichiers du head 770e53e) — conforme au claim 112+11, incl. le contrôle négatif #15080 (TestD01CompletedSolutionWithTodoinchangé). - Critère 1 reproduit firsthand, avant/après : les 4 notebooks flotte comptés par l'organe main → head :
GameTheory-07-ExtensiveForm0→3,GameTheory-13-ImperfectInfo-CFR-Csharp0→3,Z3-01-Introduction-CSharp0→3 (voiecode_cell_comment),PT_13_dapo_drgrpo_corrections0→3 (voiemarkdown_headersur titres gras). - Critère 3, échantillon relu dans les deux sens :
GT-02-NormalForm7→3,rl_1c_prolog_distillation8→4 (baisses = sur-comptes corrigés),GT-17b-Asymmetric-Information6→9 (hausse, énoncés groupés comptés). Tous conformes aux chiffres du body. - Garde anti-élargissement vérifiée sur son témoin négatif cité : NumPy-1.2 reste 6→6 — la garde « span gras = titre seulement si le texte commence par le mot exercice » fait bien son travail (sans elle, 6→12 annoncé).
- Pas de hardcoding : les noms de notebooks (GT-07 c27, PT_13 c41…) n'apparaissent qu'en docstrings de justification (critère 4) — aucun dans la logique de comptage.
Résiduel honnête : GT-02 Exercice 4 (tuple d'attributs lu comme dérivé) reste manqué et l'erreur passe de +4 à −1 — documenté dans le body, hors scope légitime.
PR gate rouge = DWELL uniquement (tête 70 min < plancher 120 min, 19/19 checks substantiels verts, y compris Scripts Tests qui couvre directory-wide) — jambe minuteur, rien à corriger dans le code, se ré-agrège au balayage.
Le résidu cell-interpretation-ordering de PT_13 (énoncé après stub) est bien séparé de cette PR — l'appariement par préférences ordonnées est le bon périmètre ici.
[Hermes hermes-pr-review, cycle :05 28/09, host f6be46d1b7a3, sig=57ace41a]
Path-collision (organ #13359/#13615)Cette PR #18181 (
|
|
[ADJOINT PREFLIGHT] Note de lecture (informatif) : sticky PR-PATH-COLLISION present (ordre de merge a croiser avec les PRs citees). |
…s comment-only cross-ref (#18182) classify partitioned open PRs into one undifferentiated reason; a census comment on an unrelated open PR (permanent cross-referenced event, e.g. #17434 freezing #15173/#15703) read as "work in progress" where it was a contextual mention. The reason now names both origins separately: body/title mention = "declare this issue (plausible next phase)"; otherwise = "comments only (cross-referenced contextual mention, undeclared)". Verdict unchanged in both cases (reason change first per acceptance 1); unreadable body defaults to undeclared (fail-safe, #15060 philosophy). Driver enriches open PR bodies+titles too (_with_merged_pr_bodies -> _with_pr_bodies; closed-unmerged PRs stay unfetched). Title stays CLOSED for every labelling decision (#17759 arbitration); delivered not widened. Firsthand via the real chain: #18146 <- open #18181 renders in_flight "declare this issue in body/title (plausible next phase)"; the issue's named instances #15173/#15703 no longer reproduce as in_flight because #17434 merged since (measured 2026-09-28). 50 tests (44 + 6 new). Closes #18102 Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Grain: DEEP/tooling -- lane myia-po-2026:CoursIA -- prev: MED/notebook #18180
Closes #18146
Constat repris
count_exercises.pyrendait 0 exercice sur des notebooks qui en portent trois (54 notebooks relevés par la flotte, 4 reproduits par ai-01 surmain). Deux mécanismes, corrigés ici.Mécanisme 1 — la garde « le corps calcule » avalait des stubs échafaudés
Quatre causes distinctes, chacune mesurée sur un notebook cité :
return 0.0; // TODO etudiant(GT-13 c22/24/26) etreturn null; // TODO etudiant : remplacer...(Z3-01 c15/16/17) — les slashes du commentaire satisfaisaient le regex d'expression binaire[+\-*/%]de_return_is_derived, marquant le retour comme calculé. Fix : le commentaire de queue est strippé avant l'analyse de l'opérande ; C#nullreconnu commeNone.# TODO etudiant+ un constructeur +return game) — la prose seule franchissait le seuil de 3 lignes. Fix :_strip_docstringsavant comptage.return {ouvrant un dict multi-lignes dont toutes les valeurs sont des placeholders (GT-07 c27 :{'spe_offer': 0, ..., 'j2_accepts': []}; c31 :{'is_spe': False, ..., 'violations': ['TODO etudiant : ...']}). Fix :_return_literal_is_template— un littéral dont TOUTES les valeurs matchentPLACEHOLDER_VALUE_RE(None/null/0/0.0/False/[]/{}/()/""/string-TODO) est un gabarit, pas un calcul.Truen'est PAS placeholder (un flag de solution est une sortie ordinaire).game = ExtensiveFormGame(...)+return game(GT-07 c29) — l'affectation-échafaudage lisait le retour de nom nu comme dérivé. Fix :_body_computes_resultexige >= 2 lignes de LOGIQUE (signatures/returns/accolades exclues,_NON_LOGIC_LINE_RE). Le contrôle négatif [D01] Le compteur d'exercices s'inverse : 3 exercices resolus comptes 3 ouverts, 3 sujets ouverts comptes 0 -- et le catalogue n'a aucun champ de role pedagogique #15080 (R05 c30 : ~10 lignes de pipeline de mesure) reste une solution.Mécanisme 2 — stubs vus mais non appariés
**Exercice 1 — Réintroduire/stddans Dr. GRPO.** Reprendre...— jamais lus parMARKDOWN_HEADER_RE(ATX seul). Fix :BOLD_TITLE_REreconnaît un span gras en tête de ligne dont le texte COMMENCE par le mot exercice — une ouverture de prose comme**Pourquoi cet exercice est fondamental** :(NumPy-1.2 c27) n'est pas un titre (sans cette garde : corpus 6->12 sur ce notebook). Dédup par numéro dans la cellule (une reprise ATX+gras du mêmeExercice N= 1 instance).paired_code_indices) sur toutes les branches numérotées.## 10. Exercicesdont les sous-mentions gras restatent les énoncés individuels (PT_10 3->6) ne repousse pas via la voie conservatrice sans-numéro (_first_title_is_plural). La voie conservatrice SURVIT pour un titre singulier sans numéro bloqué (Video 03-2 : stub à 7 cellules de distance — 3 restaurés).Critères d'acceptation
1. Les 4 notebooks comptent 3 — vérifiés par reproduction directe (
count_exercises_in_notebook) :GameTheory-07-ExtensiveForm.ipynb: 0 -> 3GameTheory-13-ImperfectInfo-CFR-Csharp.ipynb: 0 -> 3Z3-01-Introduction-CSharp.ipynb: 0 -> 3PT_13_dapo_drgrpo_corrections.ipynb: 0 -> 3Fixtures minimales par forme dans
TestScaffoldedStubShapes+TestBoldTitlesAndPairing(11 nouveaux tests).2. Contrôle négatif #15080 conservé :
TestD01CompletedSolutionWithTodopasse inchangé (R05 c30/c32/c34 : solution complète +# TODO etudiantrésiduel = exemple, pas exercice). 112 tests pré-existants verts.3. Mesure avant/après corpus entier (
--json, 1259 notebooks) :conforming, TOUTES False->True, 0 True->False.**Exercice 4.**/**Exercice 5.**des cellules### Exercices 4 et 5comptent désormais) ; Lean-29 reste 6 (les stubs-- Exercice Ns'apparient à LEUR titre, plus de double-compte) ; les 0->3 de la liste flotte.return A.copy(), B.copy(), [] # TODO etudiant— tuple d'attributs encore lu comme dérivé) reste manqué ; l'erreur du notebook passe de +4 (7 vs 3+1 solution... en réalité 7 vs 4) à -1 (3 vs 4).4. Pas d'élargissement aveugle : chaque nouvelle forme est justifiée par un notebook cité dans les docstrings (GT-07 c27/c29/c31, GT-13 c22, Z3-01 c15, PT_13 c40-45, NumPy-1.2 c27, Lean-29 c5-8, rl_1c c45-56, PT_10 c19-24, Video 03-2 c16-24, GT-17b c12-24, Search-03b c26-34).
Tests
test_count_exercises.py: 123 passed (112 pré-existants + 11 nouveaux).scripts/complète : voir commentaire ci-dessous (post-push).🤖 Generated with Claude Code