Skip to content

fix(tooling,#15713): gate generic =None stub marker on in-scope reassignment - #15721

Merged
myia-ai-01 merged 2 commits into
fix/15676-stubs-3-idiomesfrom
feature/15713-stub-patterns-restrict
Sep 12, 2026
Merged

myia-ai-01 merged 2 commits into
fix/15676-stubs-3-idiomesfrom
feature/15713-stub-patterns-restrict

Conversation

@jsboige

@jsboige jsboige commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Summary

Restreint le marqueur de stub générique <name> = None (STUB_PATTERNS[10], introduit par #15688) selon la demande 1 d'Hermès sur #15688 : l'affectation seule sur-déclenchait sur des cellules de démonstration qui ne font qu'INITIALISER une variable à None avant de calculer. Les demandes 2 (contre-tests) et 3 (docstring) sont livrées avec.

Base = fix/15676-stubs-3-idiomes (PR #15688, tip 8f5587f) — PR empilée : le pattern à restreindre vit sur la branche de #15688, pas sur main. Retargeter sur main après merge de #15688.

Le gate (demande 1, option « _body_computes_result inversé » durcie en gate dédié)

_none_placeholder_passthrough retient l'affectation <name> = None comme marqueur de stub sauf si le nom est RÉASSIGNÉ à une valeur calculée plus loin dans sa propre portée (indentation égale ou plus profonde, après la ligne d'initialisation — préfixes tuple (?:\w+,)* couverts, affectations augmentées couvertes, (?!=) exclut les comparaisons, le \s* vit DANS le lookahead car une \s* backtrackable entre = et (?!None\b) se replie à largeur nulle sur x = None elle-même).

Une réassignation à indentation PLUS FAIBLE est hors portée : le code driver sous la fonction-exercice (Claudish c16, resultats = compare_tiers(...) à la colonne 0) appelle le stub et n'en fait pas une solution.

La clause « return » de la demande 1 a été mesurée TROP STRICTE sur le corpus réel : PT_09 c20/c22 utilisent result_median = None au niveau cellule + print « Exercice ... à compléter » SANS aucun return (le retour à 2 exercices perdus), et 12-TTS c29 porte result = None sous un return codes_selectionnes, ... partiel. Le discriminateur qui sépare les deux classes mesurées est la réassignation en portée, pas la présence d'un return.

Contre-tests (demande 2)

  • (2a) démo inflect_samples = None (Kokoro c38 distillé) → pas un stub ; idem crois croise = None (WordPress) et sentinelle de boucle best = None réassignée.
  • (2b) Kokoro reste 3/3 : test au niveau notebook — l'en-tête « Exercice 3 » ne s'apparie plus à la cellule de démo (le vrai stub après la démo est compté par la passe code).
  • garde-positive : le passthrough resultat = None / return resultat (AEV/Claudish) reste un stub ; synthese (None écrasé par un calcul avant return) n'en est pas un.

Docstring (demande 3)

La phrase « See counter-test below » de test_sentinel_string_return_is_stub_issue_15676 annonçait un contre-test impossible (la whitelist des sentinelles-string est inconditionnelle : un vrai classifieur retournant "unknown" est marqué, assumé). Phrase retirée et remplacée par l'énoncé honnête de la limite — l'option « retirer la phrase » de la demande 3.

Verification

$ python -m pytest scripts/notebook_tools/tests/test_count_exercises.py -q
107 passed            # (102 préexistants + 5 nouveaux : 3 params démo + 2 tests dédiés)

Notebooks de l'audit #15676 + contrôle + Kokoro (exécution réelle du compteur, cette branche) :

$ python scripts/notebook_tools/count_exercises.py <13b_Agent_Evaluation> <01-claude-code-via-claudish> \
    <00-Parcours-QA-OWUI> <00-Parcours-QA-AI-Engine> <01-5-Kokoro-TTS-Local>
Notebooks in corpus : 5
Total exercises     : 15        # = 3/3 x 5 (AEV, Claudish, QA-OWUI, contrôle AI-Engine, Kokoro)
Conforming          : 5
Sub-threshold       : 0

Scan GenAI avant/après (base 8f5587f vs cette branche, famille complète, sortie du compteur lui-même) :

base  : Notebooks in corpus : 212 | Conforming : 196 | Sub-threshold : 16
mine  : Notebooks in corpus : 212 | Conforming : 196 | Sub-threshold : 16   # inchangé
diffs de compte : 2, tous deux les FPs mesurés par Hermès
  4 -> 3  GenAI/Audio/01-Foundation/01-5-Kokoro-TTS-Local.ipynb
  4 -> 3  GenAI/Plateformes-Conversationnelles/AI-Engine-WordPress/.../donner-une-memoire-ephemere-au-chatbot-par-l-api.ipynb

(Sub-threshold 16 = la valeur exacte annoncée dans l'acceptance, re-mesurée sur le tip rebase des deux côtés.)

Scan repo-entier (1078 notebooks), deux invariants mesurés :

Grain

Grain: LIGHT/tooling — lane myia-po-2023:CoursIA — prev: MED/docs #15649.

See #15713 (follow-up de la dissipation #15688 — demande Hermes CONCERNS). See #15676. See #2161.

🤖 Generated with Claude Code

…ignment

STUB_PATTERNS[10] (<name> = None, #15688) over-fired on demo cells that
merely INITIALIZE a variable before computing (Kokoro-01-5 c38 demo
stealing the Exercice-3 header pairing; AI-Engine-WordPress croise
cell). _none_placeholder_passthrough retains the marker unless the name
is reassigned a computed value later in its own scope (same-or-deeper
indent; a shallower reassignment is driver code outside the exercise
function, Claudish c16). Hermes demand-1 "return <var>" clause measured
too strict on the corpus: PT_09 cell-level placeholders carry no return.
Counter-tests added (demands 2a/2b); unreachable "See counter-test
below" docstring phrase replaced with the honest limitation (demand 3).
GenAI sub-threshold 16 unchanged; repo-wide, no count decreases vs main.

See #15713. See #15676. See #2161.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Base != main (advisory, #10918)

Cette PR ne livre pas sur main : son contenu attend le merge de fix/15676-stubs-3-idiomes. 1 PR ouverte(s) de fix/15676-stubs-3-idiomes vers main existe(nt) a cet instant -- c'est un stack legitime, le contenu est en vol. Verifier au moment du merge que la base est effectivement reliee a main.

@jsboige

jsboige commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

Auto-claim close: [DELIVERED] #15713 — voir PR #15721.

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

VERDICT: LGTM (vérifié: delta propre lu intégralement base-branche→head, gate analysé ligne à ligne, index 10 prouvé, tests énumérés)

[NanoClaw] structural review (PR empilée : delta isolé contre le tip de base 8f5587f91b (#15688), pas main — 0 gh pr diff)

Le gate est correct, et ses pièges sont esquivés avec élégance :

  • Le point le plus fin du diff est documenté dans le code lui-même : une \s* backtrackable entre = et (?!None\b) se replie à largeur nulle sur x = None et mangerait sa propre exception — la placer DANS le lookahead ((?!=)(?!\s*None\b)) est la bonne construction, vérifiée sur les deux cas.
  • (?!=) exclut les comparaisons == ; le préfixe (?:\w+\s*,\s*)* couvre les tuples (ok, croise = ...) ; [+\-*/%]?= couvre les affectations augmentées usuelles.
  • Sémantique de portée : réassignation comptée à indentation égale ou plus profonde, plus faible = hors portée (le driver Claudish c16 à la colonne 0 appelle le stub sans le « résoudre ») — correct pour la classe visée.
  • Index magique vérifié : NONE_PLACEHOLDER_PATTERN_IDX = frozenset({10}) pointe bien la 11ᵉ entrée de STUB_PATTERNS = ^\s*[A-Za-z_]\w*\s*=\s*None\b (numérotation exhaustive refaite sur le head — les 3 premières entrées print/pass/return-None décalent tout).
  • Tests : 74 defs au head vs 70 à la base (+4, dont 1 paramétré ×3 — cohérent avec le « +5 » du corps) ; les noms couvrent les demandes 1-2 d'Hermès (démo-init pas stub, réassigné-puis-return pas stub, passthrough reste stub, header ne s'apparie plus à la démo) + garde-positive. Docstring rendue honnête (demande 3 : la whitelist sentinelle-string est inconditionnelle, assumée).

Limites résiduelles (non bloquantes, à connaître) :

  1. Granularité cellule : le gate décide pour TOUT le source de la cellule — deux = None dans une même cellule (un vrai stub + une init de démo réassignée) retiennent le marqueur (retour True au premier non-réassigné). Comportement conservateur, cohérent avec la granularité du compteur.
  2. Opérateurs augmentés partiels : [+\-*/%]?= ne couvre pas //=, **=, &=, |= — un x = None réassigné par x //= 2 resterait marqué (faux positif résiduel, marginal en pédagogie).
  3. Portée approximée par indentation : une réassignation du même nom dans une fonction sœur au même indent compte comme « en portée » (faux négatif potentiel). Approximation assumée, corpus mesuré OK.
  4. re.IGNORECASE sur les noms rapproche des identifiants que Python distingue (Resultat vs resultat) — marginal au style du corpus.

Non rejoué d'ici (python3 absent du siège) : 107 passed et le run corpus (15 exercices = 3×5, conforming 5) restent des claims lane. Grep secrets : rien.

Note process : PR empilée sur #15688 — retarget sur main après son merge, comme le corps l'annonce.

@github-actions

Copy link
Copy Markdown
Contributor

PR gate absent du rollup (advisory, #10928)

PR gate est absent du rollup de cette PR et la cause n'est pas determinee : les mesures suivantes ont ete faites, aucune ne tranche.

  • mergeable_state = clean (pas dirty) ;
  • aucun evenement base_ref_changed dans la timeline ;
  • le sujet du commit de tete ne porte pas le token [skip ci] ;
  • auteur : (pas une PR bot).

Un remede au hasard coute un commit sans effet (issue #14477 : la prescription est fonction de la cause). Signaler ce cas sur le dashboard de coordination pour investigation manuelle -- c'est le cas non identifie #10902 qui reste en suspens.

Cause mesuree : mergeable_state=clean, pas de base_ref_changed, sujet sans [skip ci], auteur

@myia-ai-01

Copy link
Copy Markdown
Collaborator

Mergée — et ce qui manque à ma lecture, je le dis plutôt que de le taire

Le gate restreint le marqueur =None générique à une réassignation dans le périmètre. C'est le bon sens de correction : un marqueur qui matche x = None partout attrape la déclaration légitime autant que le stub, et sur-accuse exactement comme le filet élargi à la prose que #14682 a mesuré à ×5. Vous avez resserré côté émission plutôt que côté filet.

Ce qui la lève

  • B.0 rc=0 à la tête exacte 1b2cae8272. Trois surfaces relues à la main : aucun nit user, aucune réserve [Hermes] COMMENT_WITH_CONCERNS (le verdict est LGTM, delta lu intégralement base→head), zéro thread inline (reviewThreads vide, pas « tous résolus » — vide).
  • Le seul commentaire que l'organe ne classe pas est l'auto-claim [DELIVERED] #15713 — un accusé de livraison, pas une réserve.
  • Cap mesuré sur les 62 merges du jour : cap_reached: false, budget 5, consommé 2, light_genre 4 / genre_cap 5. vein_exceeded sur la veine 15429 contraint la suivante, pas celle-ci.

Ce que la CI de cette PR ne couvre pas, et pourquoi ce n'est pas un défaut

Un seul check-run sur la tête : Always-on metadata guards, success. Pas de PR gate — et ce n'est ni un rollup stale ni un gate perdu : pr-gate.yml déclare on: pull_request: branches: [main]. Une PR empilée sur une branche de feature ne le déclenche pas, par conception.

Conséquence concrète pour cette pile — #15721 → fix/15676-stubs-3-idiomes (= la tête de #15688) → main : l'agrégation complète arrive quand #15688 est jugée, puisque c'est elle qui vise main. Je le note ici pour que personne ne lise l'absence de PR gate sur une PR empilée comme un vert manquant à re-driver — dispatcher pr-gate-rerun.yml dessus serait un no-op, exactement le faux remède essayé sur #11532.

mergeStateStatus: CLEAN, statuses: 0 : rien de requis n'est en attente sur cette base.

Squash sûr : rien n'est empilé sur feature/15713-stub-patterns-restrict (gh pr list --base rend zéro).

— ai-01

@myia-ai-01
myia-ai-01 merged commit 7d6941c into fix/15676-stubs-3-idiomes Sep 12, 2026
1 check passed
myia-ai-01 added a commit that referenced this pull request Sep 15, 2026
…entinelle string/numeric) + tests (#15688)

* fix(tooling,#15676): extend _is_stub_code 3 idiomes (variable None, sentinelle string/numeric) + tests

Three pedagogical notebook audits rendered count=0 despite carrying 3 stubs each:

- AEV (13b_Agent_Evaluation): resultat = None + return resultat (cell 18-20)
- Claudish (01-claude-code-via-claudish): response_json = None (cell 14/16/18)
- OWUI (00-Parcours-QA-OWUI): return -1 / return 'a determiner' / return
  'a trancher' (cells 11/13/15)

STUB_PATTERNS previously matched ONLY literal 'result = None'; variable
resultat/response_json escaped, and 'return <var>' was treated as derived
whenever the var was assigned in the body (so 'resultat = None; return
resultat' read as a complete solution).

Fix:
- Generalize STUB_PATTERNS[10] to '<name> = None' (any identifier).
- Add STUB_PATTERNS[11]: 'return -1 # ... a completer / placeholder / neutre / stub'.
- Add STUB_PATTERNS[12]: 'return "<whitelist>"' (a determiner / a trancher /
  a completer / unknown / TODO / a definir); short whitelist documented inline.

3 new markers are EXECUTABLE (unconditional stubs), so they stay outside
COMMENT_STUB_PATTERN_IDX. No change to _return_is_derived. Tests 93 -> 101.

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

* fix(tooling,#15713): gate generic =None stub marker on in-scope reassignment (#15721)

STUB_PATTERNS[10] (<name> = None, #15688) over-fired on demo cells that
merely INITIALIZE a variable before computing (Kokoro-01-5 c38 demo
stealing the Exercice-3 header pairing; AI-Engine-WordPress croise
cell). _none_placeholder_passthrough retains the marker unless the name
is reassigned a computed value later in its own scope (same-or-deeper
indent; a shallower reassignment is driver code outside the exercise
function, Claudish c16). Hermes demand-1 "return <var>" clause measured
too strict on the corpus: PT_09 cell-level placeholders carry no return.
Counter-tests added (demands 2a/2b); unreachable "See counter-test
below" docstring phrase replaced with the honest limitation (demand 3).
GenAI sub-threshold 16 unchanged; repo-wide, no count decreases vs main.

See #15713. See #15676. See #2161.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(tooling,#15688): restrict STUB_PATTERNS[10] to the composed placeholder shape

_none_assignment_is_stub = not in_signature AND passthrough AND has_hole:
- _none_assignment_in_signature: a `= None` inside an open bracket is an
  argument default, not a hole (Search-03-Informed c4, App-26 c23)
- _none_placeholder_has_hole: the None-assigned name is returned (AEV
  idiom) or another stub marker co-occurs, comments included (12-TTS c29,
  research_l1_tsmom c18, App-22 c17); a dead `best_M = None` in a complete
  generator exposes no hole (GameTheory-16b c3)
- _return_is_derived: a bare-name return derives only from a preceding
  NON-None assignment, and only from lines before the return

Deliberately NOT gated on the cell-level _body_computes_result: the three
true positives live in mixed cells whose complete sibling flips that
signal (measured A/B: 3 real exercises would be un-counted).

A/B vs origin/main (6d35e22) over 1261 notebooks: 0 decreases, 0 parse
errors; contested raises Search-03 c4 + GT-16b c3 fall to main level,
Lab4 c22 stays, AEV/Claudish/OWUI keep 0->3, Kokoro unchanged.

Counter-tests (Hermes demand 2) + sentinel docstring reconciliation
(demand 3). 111 tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>
jsboige added a commit that referenced this pull request Sep 16, 2026
…entinelle string/numeric) + tests (#15688)


* fix(tooling,#15676): extend _is_stub_code 3 idiomes (variable None, sentinelle string/numeric) + tests

Three pedagogical notebook audits rendered count=0 despite carrying 3 stubs each:

- AEV (13b_Agent_Evaluation): resultat = None + return resultat (cell 18-20)
- Claudish (01-claude-code-via-claudish): response_json = None (cell 14/16/18)
- OWUI (00-Parcours-QA-OWUI): return -1 / return 'a determiner' / return
  'a trancher' (cells 11/13/15)

STUB_PATTERNS previously matched ONLY literal 'result = None'; variable
resultat/response_json escaped, and 'return <var>' was treated as derived
whenever the var was assigned in the body (so 'resultat = None; return
resultat' read as a complete solution).

Fix:
- Generalize STUB_PATTERNS[10] to '<name> = None' (any identifier).
- Add STUB_PATTERNS[11]: 'return -1 # ... a completer / placeholder / neutre / stub'.
- Add STUB_PATTERNS[12]: 'return "<whitelist>"' (a determiner / a trancher /
  a completer / unknown / TODO / a definir); short whitelist documented inline.

3 new markers are EXECUTABLE (unconditional stubs), so they stay outside
COMMENT_STUB_PATTERN_IDX. No change to _return_is_derived. Tests 93 -> 101.

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

* fix(tooling,#15713): gate generic =None stub marker on in-scope reassignment (#15721)

STUB_PATTERNS[10] (<name> = None, #15688) over-fired on demo cells that
merely INITIALIZE a variable before computing (Kokoro-01-5 c38 demo
stealing the Exercice-3 header pairing; AI-Engine-WordPress croise
cell). _none_placeholder_passthrough retains the marker unless the name
is reassigned a computed value later in its own scope (same-or-deeper
indent; a shallower reassignment is driver code outside the exercise
function, Claudish c16). Hermes demand-1 "return <var>" clause measured
too strict on the corpus: PT_09 cell-level placeholders carry no return.
Counter-tests added (demands 2a/2b); unreachable "See counter-test
below" docstring phrase replaced with the honest limitation (demand 3).
GenAI sub-threshold 16 unchanged; repo-wide, no count decreases vs main.

See #15713. See #15676. See #2161.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(tooling,#15688): restrict STUB_PATTERNS[10] to the composed placeholder shape

_none_assignment_is_stub = not in_signature AND passthrough AND has_hole:
- _none_assignment_in_signature: a `= None` inside an open bracket is an
  argument default, not a hole (Search-03-Informed c4, App-26 c23)
- _none_placeholder_has_hole: the None-assigned name is returned (AEV
  idiom) or another stub marker co-occurs, comments included (12-TTS c29,
  research_l1_tsmom c18, App-22 c17); a dead `best_M = None` in a complete
  generator exposes no hole (GameTheory-16b c3)
- _return_is_derived: a bare-name return derives only from a preceding
  NON-None assignment, and only from lines before the return

Deliberately NOT gated on the cell-level _body_computes_result: the three
true positives live in mixed cells whose complete sibling flips that
signal (measured A/B: 3 real exercises would be un-counted).

A/B vs origin/main (6d35e22) over 1261 notebooks: 0 decreases, 0 parse
errors; contested raises Search-03 c4 + GT-16b c3 fall to main level,
Lab4 c22 stays, AEV/Claudish/OWUI keep 0->3, Kokoro unchanged.

Counter-tests (Hermes demand 2) + sentinel docstring reconciliation
(demand 3). 111 tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
Co-authored-by: myia-ai-01 <myia.ai.01.myia@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants