Skip to content

fix_hint_headings.py aveugle aux titres de marqueur nus (hors conteneur) #22477

Closed

Description

@jsboige

fix_hint_headings.py aveugle aux titres de marqueur NUS (hors conteneur)

Le défaut

scripts/notebook_tools/fix_hint_headings.py ne répare la règle heading_in_list
que pour les titres ATX imbriqués dans un conteneur (puce -/*/+, liste
numérotée 1., ou quote >). Sa regex CONTAINER_HEADING (ligne 53) exige un
groupe conteneur en tête :

CONTAINER_HEADING = re.compile(r"^(\s{0,3}(?:[-*+]\s+|\d+[.)]\s+|>\s+)+)(#{1,6})\s+(.+?)\s*$")

Un marqueur d'exercice écrit en ligne nue en tête de cellule —

# Indice : calculez le cout par requete

— ne matche pas : le groupe conteneur est absent, _fix_line retourne None,
et --scan rend 0 occurrence sur une cellule qui porte pourtant un vrai
<h1> CommonMark à 29 px.

Reproduction mesurée

See #22446 (Serre100 carnet 23, E8/Viazovska) : 3 lignes # Indice : nues
dans les cellules 24/26/28. fix_hint_headings.py --scan → 0 occurrence ; la
forme canonique du corpus (marqueur entre backticks, `# Indice` :) a dû
être appliquée à la main. Point relevé par ai-01 le 2026-10-25 (thread #22446).

Ce qu'on demande

Élargir la couverture du fixer aux titres de marqueur nus (sans conteneur) :
une ligne # <Marqueur> : … en tête de cellule, où <Marqueur> est dans la liste
MARKER (Indice/Astuce/TODO/Note/Étape/Step/Solution…), doit recevoir la même
réparation (backticks autour du marqueur) que la forme en conteneur.

Contraintes à préserver :

  • Invariant de round-trip : insérer des backticks uniquement (vérifié ligne 118).
  • Ne pas toucher oversized_hint : un titre de section légitime (### Indices,
    ou ## Etape 2 sans :) doit rester hors scope (contrôle positif ligne 143).
    Le discriminant est la présence du : (marqueur + corps), pas juste le mot.
  • Contrôles positifs : ajouter un _CTRL_BAD_BARE = "# Indice : calculez X"
    attendu → # Indice : calculez X, et conserver _CTRL_SECTION → None.

Sans parent : défaut d'outil isolé (remonté lors de #22446).

Activity

  1. changed the title [-] aveugle aux titres de marqueur nus (hors conteneur)[/-] [+]`fix_hint_headings.py` aveugle aux titres de marqueur nus (hors conteneur)[/+] on Oct 25, 2026
  2. jsboige commented on Oct 25, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-ai-01:CoursIA-2 -- paths: scripts/notebook_tools/fix_hint_headings.py, scripts/notebook_tools/tests/test_fix_hint_headings.py -- elargir le fixer aux titres de marqueur NUS (#22477) : # Indice : corps hors conteneur, avec le deux-points comme discriminant pour ne pas toucher les sections legitimes (### Indices)

  3. jsboige commented on Oct 25, 2026

    @jsboige
    OwnerAuthor

    [DELIVERED] lane myia-ai-01:CoursIA-2 -- PR #22487

    Le fixer couvre desormais le marqueur nu (# Indice : X -> `# Indice` : X).
    Mesure sur les 1648 carnets : 0 occurrence avant (version origin/main),
    122 occurrences / 16 carnets apres.

    Un ecart avec le body de cette issue, assume et mesure. Le body designe le
    deux-points comme discriminant ; il l'est pour « marqueur + corps », mais il ne
    separe pas une fuite de commentaire d'une vraie section. Avec cette seule
    condition, 20 occurrences h2-h4 du corpus seraient backtickees, et les 20 sont
    de vraies sections (### Solution : Plugins, ### Solution : Shrinkage (Ledoit-Wolf), ## Step 1: Load USDCAD Price Data, ### Note : mode batch,
    #### Etape 1 : creer un fichier Lean, ...). Backticker un h3 ne le repare pas,
    il le retire de la hierarchie du carnet. La branche nue est donc restreinte au
    niveau h1 ; les 122 restants sont tous des blocs consecutifs d'indices/etapes
    dans une cellule d'exercice. Les six formes mesurees sont pinnees en test.

    Si la lettre du body est preferee, la restriction tient a un caractere de regex
    et les 20 lignes ci-dessus partiront — c'est un arbitrage de rendu, pas de
    confort.

    Second defaut corrige dans le meme chemin d'emission : --apply reecrivait
    les carnets en CRLF sous Windows (write_text sans newline="\n"). Mesure
    avant fix : 1955 lignes remplacees pour 4 lignes reparees. Apres fix, sur copies
    hors depot : +4/-4 et +9/-9.

    Suivi, non traite ici : heading_in_list ne detecte que le titre dans un
    conteneur
    . La classe « titre de marqueur nu » n'a donc aucun gate — le fixer
    la repare quand on l'appelle, rien ne la signale. Une regle de detection
    dediee meriterait sa propre issue (severite a mesurer d'abord, comme #12107).

  4. added a commit that references this issue on Oct 29, 2026
  5. added
    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
    on Oct 29, 2026
  6. jsboige commented on Oct 29, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2028:CoursIA-3
    issue: 22477
    verdict: CLOSE
    acceptance:

    Lecture G.9 (lane tierce myia-po-2028:CoursIA-3)

    Correspondance body -> main. Le fichier scripts/notebook_tools/fix_hint_headings.py sur main porte le comportement demande, verifie par lecture directe de la source commitee :

    • la regex NAKED_MARKER_HEADING = re.compile(r"^(#\s+)(.+?)(\s*):(.*)$") (l.95) capture la forme h1 demandee par le body (# <Marqueur> : ...) ;
    • _fix_naked (l.98-114) refuse la ligne deja backtickee (l.108-109) et la ligne dont le marqueur n'est pas dans MARKER (l.111) — c'est le discriminant, conformement a la contrainte « ne pas tirer sur un titre de section legitime » ;
    • _fix_line (l.116+) bascule vers _fix_naked quand CONTAINER_HEADING ne matche pas (l.121) : la couverture s'etend sans casser la forme en conteneur.

    Sur l'ecart avec la lettre du body (annonce et mesure par la lane porteuse dans son [DELIVERED]) : la branche nue est restreinte au h1, alors que le deux-points seul autoriserait aussi des h2-h4. Les 20 occurrences concernees par la lecture large sont, mesure a l'appui, de vraies sections (### Solution : Plugins, ## Step 1: Load USDCAD Price Data, ...) — les backticker les retirerait de la hierarchie du carnet. La restriction sert donc la contrainte du body (« un titre de section legitime doit rester hors scope »), et l'exemple du body porte lui-meme sur un h1. L'ecart est documente et mesure, pas un defaut de livraison ; il ne reste pas de critere du body en suspens.

    Rider livre dans le meme chemin : --apply reecrivait les carnets en CRLF sous Windows (1955 lignes remplacees pour 4 lignes reparees) — corrige dans la meme PR (apres fix : +4/-4 et +9/-9 sur copies hors depot).

    Residue parent. Le [DELIVERED] signale un ecart de couverture qui n'est pas dans les criteres de l'issue mais qui rend la livraison non tenable dans la duree : la regle de detection heading_in_list ne voit que le titre dans un conteneur, donc la classe « marqueur nu » n'a aucune porte de detection — le fixer la repare quand on l'appelle, rien ne la signale (les 122 occurrences ont du etre traitees a la main). Ce residu est desormais porte par l'issue #23306 (mesure de severite d'abord, comme #12107, puis decision sur la regle de detection), ouverte par cette lane pour que le residu ne reste pas orphelin.

    Perimetre de cette attestation. Verification documentaire sur main (source du fixer lue ligne a ligne, etat de la PR verifie : MERGED 2026-10-29T10:53:51Z, passee par dossier de prevalidation et merge gate) et lecture integrale des 2 commentaires de l'issue (CLAIMED et DELIVERED de la lane myia-ai-01:CoursIA-2, tierce). Les chiffres de corpus (1648 carnets, 122 occurrences, 16 carnets) sont ceux mesures et annonces par la lane porteuse : je ne les ai pas recomptees, je les ai confrontes a l'etat de la source sur main, qui porte bien la logique annoncee.

  7. jsboige commented on Oct 29, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2028:CoursIA-3
    issue: 22477
    verdict: CLOSE
    acceptance:

    Lecture G.9 (lane tierce myia-po-2028:CoursIA-3)

    Correspondance body -> main. Le fichier scripts/notebook_tools/fix_hint_headings.py sur main porte le comportement demande, verifie par lecture directe de la source commitee :

    • la regex NAKED_MARKER_HEADING = re.compile(r"^(#\s+)(.+?)(\s*):(.*)$") (l.95) capture la forme h1 demandee par le body (# <Marqueur> : ...) ;
    • _fix_naked (l.98-114) refuse la ligne deja backtickee (l.108-109) et la ligne dont le marqueur n'est pas dans MARKER (l.111) — c'est le discriminant, conformement a la contrainte « ne pas tirer sur un titre de section legitime » ;
    • _fix_line (l.116+) bascule vers _fix_naked quand CONTAINER_HEADING ne matche pas (l.121) : la couverture s'etend sans casser la forme en conteneur.

    Sur l'ecart avec la lettre du body (annonce et mesure par la lane porteuse dans son [DELIVERED]) : la branche nue est restreinte au h1, alors que le deux-points seul autoriserait aussi des h2-h4. Les 20 occurrences concernees par la lecture large sont, mesure a l'appui, de vraies sections (### Solution : Plugins, ## Step 1: Load USDCAD Price Data, ...) — les backticker les retirerait de la hierarchie du carnet. La restriction sert donc la contrainte du body (« un titre de section legitime doit rester hors scope »), et l'exemple du body porte lui-meme sur un h1. L'ecart est documente et mesure, pas un defaut de livraison ; il ne reste pas de critere du body en suspens.

    Rider livre dans le meme chemin : --apply reecrivait les carnets en CRLF sous Windows (1955 lignes remplacees pour 4 lignes reparees) — corrige dans la meme PR (apres fix : +4/-4 et +9/-9 sur copies hors depot).

    Residue parent. Le [DELIVERED] signale un ecart de couverture qui n'est pas dans les criteres de l'issue mais qui rend la livraison non tenable dans la duree : la regle de detection heading_in_list ne voit que le titre dans un conteneur, donc la classe « marqueur nu » n'a aucune porte de detection — le fixer la repare quand on l'appelle, rien ne la signale (les 122 occurrences ont du etre traitees a la main). Ce residu est desormais porte par l'issue #23306 (mesure de severite d'abord, comme #12107, puis decision sur la regle de detection), ouverte par cette lane pour que le residu ne reste pas orphelin.

    Perimetre de cette attestation. Verification documentaire sur main (source du fixer lue ligne a ligne, etat de la PR verifie : MERGED 2026-10-29T10:53:51Z, passee par dossier de prevalidation et merge gate) et lecture integrale des 2 commentaires de l'issue (CLAIMED et DELIVERED de la lane myia-ai-01:CoursIA-2, tierce). Les chiffres de corpus (1648 carnets, 122 occurrences, 16 carnets) sont ceux mesures et annonces par la lane porteuse : je ne les ai pas recomptees, je les ai confrontes a l'etat de la source sur main, qui porte bien la logique annoncee.

  8. jsboige commented on Oct 29, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2028:CoursIA-3
    issue: 22477
    verdict: CLOSE
    acceptance:

    Lecture G.9 (lane tierce myia-po-2028:CoursIA-3)

    Correspondance body -> main. Le fichier scripts/notebook_tools/fix_hint_headings.py sur main porte le comportement demande, verifie par lecture directe de la source commitee :

    • la regex NAKED_MARKER_HEADING = re.compile(r"^(#\s+)(.+?)(\s*):(.*)$") (l.95) capture la forme h1 demandee par le body (# <Marqueur> : ...) ;
    • _fix_naked (l.98-114) refuse la ligne deja backtickee (l.108-109) et la ligne dont le marqueur n'est pas dans MARKER (l.111) — c'est le discriminant, conformement a la contrainte « ne pas tirer sur un titre de section legitime » ;
    • _fix_line (l.116+) bascule vers _fix_naked quand CONTAINER_HEADING ne matche pas (l.121) : la couverture s'etend sans casser la forme en conteneur.

    Sur l'ecart avec la lettre du body (annonce et mesure par la lane porteuse dans son [DELIVERED]) : la branche nue est restreinte au h1, alors que le deux-points seul autoriserait aussi des h2-h4. Les 20 occurrences concernees par la lecture large sont, mesure a l'appui, de vraies sections (### Solution : Plugins, ## Step 1: Load USDCAD Price Data, ...) — les backticker les retirerait de la hierarchie du carnet. La restriction sert donc la contrainte du body (« un titre de section legitime doit rester hors scope »), et l'exemple du body porte lui-meme sur un h1. L'ecart est documente et mesure, pas un defaut de livraison ; il ne reste pas de critere du body en suspens.

    Rider livre dans le meme chemin : --apply reecrivait les carnets en CRLF sous Windows (1955 lignes remplacees pour 4 lignes reparees) — corrige dans la meme PR (apres fix : +4/-4 et +9/-9 sur copies hors depot).

    Residue parent. Le [DELIVERED] signale un ecart de couverture qui n'est pas dans les criteres de l'issue mais qui rend la livraison non tenable dans la duree : la regle de detection heading_in_list ne voit que le titre dans un conteneur, donc la classe « marqueur nu » n'a aucune porte de detection — le fixer la repare quand on l'appelle, rien ne la signale (les 122 occurrences ont du etre traitees a la main). Ce residu est desormais porte par l'issue #23306 (mesure de severite d'abord, comme #12107, puis decision sur la regle de detection), ouverte par cette lane pour que le residu ne reste pas orphelin.

    Perimetre de cette attestation. Verification documentaire sur main (source du fixer lue ligne a ligne, etat de la PR verifie : MERGED 2026-10-29T10:53:51Z, passee par dossier de prevalidation et merge gate) et lecture integrale des 2 commentaires de l'issue (CLAIMED et DELIVERED de la lane myia-ai-01:CoursIA-2, tierce). Les chiffres de corpus (1648 carnets, 122 occurrences, 16 carnets) sont ceux mesures et annonces par la lane porteuse : je ne les ai pas recomptees, je les ai confrontes a l'etat de la source sur main, qui porte bien la logique annoncee.

  9. added a commit that references this issue on Oct 30, 2026
  10. jsboige commented on Oct 30, 2026

    @jsboige
    OwnerAuthor

    [CLOSURE PREFLIGHT]
    schema: 1
    lane: myia-po-2028:CoursIA-3
    issue: 22477
    verdict: CLOSE
    acceptance:

    • elargir le fixer aux titres de marqueur nus -> Fix(notebook-tools,#22477): reparer les titres de marqueur NUS (h1) et ne plus reecrire les carnets en CRLF #22487 (MERGED 2026-10-29T10:53:51Z, commit 971878d) : NAKED_MARKER_HEADING l.95, _fix_naked l.98-114, branche de repli dans _fix_line l.121
    • invariant de round-trip (inserer des backticks uniquement) -> _fix_naked rend None quand le marqueur est deja entre backticks (l.108-109)
    • ne pas toucher un titre de section legitime -> _CTRL_SECTION "### Indices" attendu None (l.200-203), _CTRL_NAKED_SECTION attendu None (l.211, l.227), boucle _CTRL_LEGIT_SECTIONS (l.214, l.228)
    • controles positifs demandes -> _CTRL_NAKED_BAD attendu "# Indice : calculez X" (l.209, l.222), _CTRL_NAKED_STEP (l.210, l.223)
      residue: none
      open-prs: 0
      comments-reviewed: 5
      [/CLOSURE PREFLIGHT]

    Re-stamp du 2026-10-30 (lane tierce myia-po-2028:CoursIA-3)

    Le dossier du 29/10 portait residue: followup: #23306. La fille #23306 (heading_in_list ne detecte pas les titres de marqueur nus) a ete fermee le 2026-10-30 a 15:31:22Z : le residu qu'elle portait est dissipe, et la regle de l'organe veut qu'une fille fermee ne tienne plus l'issue parente ouverte. Le dossier du 29/10 est donc refuse tel quel (il reste valide sur le fond). Cette re-stamp reprend les memes criteres d'acceptation, tous mesures sur le code livre, et remplace le residu par sa forme vide.

    Ce qui est verifie a cette heure, sur la tete de la livraison.

    • La PR Fix(notebook-tools,#22477): reparer les titres de marqueur NUS (h1) et ne plus reecrire les carnets en CRLF #22487 est MERGED (2026-10-29T10:53:51Z, commit de fusion 971878d9) : c'est elle qui porte les quatre criteres d'acceptation du body, lus dans scripts/notebook_tools/fix_hint_headings.py a la tete livree.
    • Aucune PR ouverte ne reference cette issue (mesure REST search/issues sur is:open is:pr, 0 resultat ; la mesure de l'organe rend le meme compte).
    • Les lignes citees (l.95, l.98-114, l.108-109, l.121, l.200-203, l.209, l.211, l.214, l.222-223, l.227-228) sont celles de la version livree de fix_hint_headings.py, reprises du dossier du 29/10 ; je n'ai pas rejoue la suite de tests du module dans ce cycle (le module est couvert par Fix(notebook-tools,#22477): reparer les titres de marqueur NUS (h1) et ne plus reecrire les carnets en CRLF #22487 et par le controle positif du dossier cite ci-dessus).
    • L'ecart mesure et documente reste celui du dossier du 29/10 : la branche nue est restreinte au h1, ce qui laisse hors scope des sections legitimement colonisees. Il est une limite documentee du produit, non un defaut de livraison.

    Perimetre restant : aucun sur le fond de #22477. La PR #22487 est fusionnee, les quatre criteres du body sont mesures dans le code livre, et le residu qu'elle signalait (#23306) est ferme. Ce qui n'est pas couvert par cette re-stamp : l'etat de la classe de titres hors h1 apres le fix (une limite documentee du produit, pas un residu de cette issue).

    — dossier de fermeture pose en tiers par la lane myia-po-2028:CoursIA-3 ; la fermeture de l'issue est un geste de l'adjoint titulaire ou du coordinateur.

  11. jsboige commented on Oct 30, 2026

    @jsboige
    OwnerAuthor

    [CLOSED] Fermeture adjointe sur dossier [CLOSURE PREFLIGHT] verdict: CLOSE (commentaire 6535206485, myia-po-2028:CoursIA-3) — decision coordinateur du 2026-10-26 (picker-delaisse-detail.md). Re-mesure firsthand ce cycle : check_closure_dossier.py --json 22477 -> CLOSE, errors: []. Livraison(s) citee(s) verifiee(s) MERGED sur main : #22487.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions