Repository navigation
ci(claims): un carnet AJOUTE echappe aux trois détecteurs (--diff-filter=MR), et « rien scanne » se lit comme « rien trouve » #22564
Description
Deux defauts du meme dispositif, mesures sur une PR reelle. Les trois détecteurs de claims (check_stale_claims.py, check_markdown_claims_output.py, check_factual_mislabel.py) sont cables dans .github/workflows/markdown-claims-output-advisory.yml avec le meme filtre et le meme texte de commentaire : les deux defauts les frappent ensemble.
Part of #17073 (decouverte de classes et d'organes).
D1 — un carnet ajoute n'est jamais scanne (--diff-filter=MR)
Les trois etapes construisent leur lot ainsi :
mapfile -t CHANGED < <(git diff --name-only --diff-filter=MR -M50% "$BASE" HEAD -- '*.ipynb' | head -50)M = modifie, R = renomme. A (ajoute) est absent. Or une PR qui cree un carnet est exactement celle dont le contenu n'a jamais ete relu par personne : c'est le cas ou l'advisory serait le plus utile.
Mesure sur la PR #22462, tete 8c23605e8533, BASE=30f53559e1e3 :
$ git diff --name-only --diff-filter=MR -M50% $BASE HEAD -- '*.ipynb'
MyIA.AI.Notebooks/GenAI/Texte/11_Quantization.ipynb
$ git diff --name-only $BASE HEAD -- '*.ipynb'
MyIA.AI.Notebooks/GenAI/Texte/11_Quantization.ipynb
MyIA.AI.Notebooks/GenAI/Texte/11b_Materiel_Debit_Cout.ipynb
Le carnet ajoute porte +1587/-0 : tout le livrable de la PR. Il n'entre dans aucun des trois lots.
Controle positif — le meme détecteur, lance a la main sur ce carnet, rend :
$ python scripts/check_stale_claims.py --json .../11b_Materiel_Debit_Cout.ipynb
rc=1 anomaly_count=11
Donc : les gardes affichent un resultat vert sur une PR dont le carnet principal porte 11 claims non ancrees. Le defaut n'est pas theorique, il a un temoin.
D2 — « rien scanne » et « rien trouve » rendent le meme texte vert
Toujours dans le meme workflow :
BASE=$(git merge-base origin/main HEAD 2>/dev/null)
...
mapfile -t CHANGED < <(...)
if [ ${#CHANGED[@]} -eq 0 ]; then
echo "exit=0" >> "$GITHUB_OUTPUT"
echo "::notice::stale-claim: aucun notebook modifie entre $BASE et HEAD -- rien a scanner." >> "$GITHUB_STEP_SUMMARY"
exit 0
fiet le commentaire sticky ne lit que ce champ :
message: |
${{ steps.stale.outputs.exit == '0' && '✅ No unanchored measurement claim detected in the notebooks this PR changed.' || ... }}exit=0 couvre deux etats distincts : « lot vide, rien n'a ete lu » et « lot lu, aucun finding ». Le ::notice:: qui les distingue vit dans le step summary, que le relecteur d'une PR ne consulte pas ; sur la PR, les deux rendent la meme phrase.
Cette formulation est plus qu'ambigue, elle est fausse dans le premier cas : « in the notebooks this PR changed » designe des carnets qui n'ont pas ete ouverts. Le fichier de workflow cite d'ailleurs #11754 (« abstain explicitly […] instead of falling back […] which would make 'nothing looked at' and 'nothing found' indistinguishable ») : l'intention est deja ecrite dans le fichier, elle n'a simplement pas ete portee jusqu'au commentaire.
Consequence sur #22462 : le commentaire du 2026-10-25T08:33:50Z annonce ✅ No unanchored measurement claim detected. Je ne pretends pas savoir ce que ce run a scanne : la branche porte 100 runs, dont le plus ancien date de 10:01Z et le reste en avalanche a 17:22Z (quasi tous cancelled), et les commits ont ete rebases depuis — la tete de 08:33Z n'est pas reconstructible depuis l'historique des runs. Le defaut se demontre donc a la source (le workflow, ci-dessus) et a la tete courante (D1), pas par cet exemple-la.
Ce que je propose
--diff-filter=MR->--diff-filter=AMR -M50%aux trois etapes (les trois lots doivent bouger ensemble, sinon un détecteur scanne ce que l'autre ignore).- Un troisieme etat explicite,
exit=none, quandCHANGEDest vide, avec son propre texte dans le sticky (« aucun carnet a scanner dans ce diff — ce n'est pas un resultat »). Meme traitement pour l'abstainexistant, qui lui est deja distinct. - Un test sur le contrat de lot : un carnet
Adoit entrer dansCHANGED(le seul point qui, s'il regresse, remet les trois gardes en aveugle).
Portee et gouvernance
Le point 1 elargit le perimetre d'un advisory : des PR en cours peuvent passer du vert au jaune du jour au lendemain (mesure ci-dessus : #22462 en est un exemple immediat). Ce n'est donc pas une correction neutre, et la PR correspondante demandera un sign-off user avant merge, conformement a la section A de CLAUDE.md.
Je ne l'ai pas applique ici : la lane qui a ouvert cette issue porte elle-meme une PR en cours, et modifier la garde pour la faire rougir sur son propre travail serait une forme de conflit d'interet. La mesure est ci-dessus, elle suffit a decider.
Notes de methode
- Les 11 findings de Add(notebook): GenAI/Texte 11b Materiel, debit, cout (See #22428) #22462 ne sont pas tous des defauts : le triage est en cours. Les valeurs derivees (par exemple
~0,55 o/paramet8,4 Go, calculees depuis des sorties imprimees2.000,0,516et4,2 Md) sont legitimes et le carnet les declare derivees ; les references a des executions anterieures (« les deux precedents le mesuraient a 91 % ») sont, elles, non verifiables depuis le document. Cette distinction est exactement celle que le détecteur ne fait pas — le code de sortie est binaire. - Detail d'instrument :
check_stale_claims.py --jsonecrit en cp1252, pas en UTF-8. Unjson.load(open(..., encoding='utf-8'))leveUnicodeDecodeError: 0xe8sur un carnet francais. Aucun impact sur la CI (elle ne parse pas le JSON, elle lit le code de sortie), mais cela piege les mesures locales.
Activity
jsboige commentedon Oct 25, 2026
Addendum — un troisieme defaut, mesure sur le meme carnet. Il ne partage ni la cause ni le fichier des deux precedents : D1/D2 vivent dans le workflow, D3 dans l'organe
check_prose_quantitative_claims.pylui-meme.D3 —
scan_diffscanne les commentaires de cellules de codeMesure, tete
8c23605e8533,origin/maina30f53559e1e3:Isolement par classe, sur le meme diff :
artifactrc=1 (2 findings),machinerc=1 (7),envrc=1 (1),stochasticrc=0,structuralrc=0.Les deux findings de la classe
artifacttombent dans la cellule 15, qui est une cellulecode:Les deux lignes sont des commentaires Python :
Le meme organe, lance sur le meme carnet par
scan_all(le chemin qui lit le notebook par_iter_markdown_sources), rend 0. Deux chemins de lecture, deux verdicts, un seul des deux est cable dans la CI.Pourquoi — et c'est la doctrine de l'organe qui est contredite
scan_diff(lignes 562-569) filtre le.ipynbligne a ligne, et ne connait que trois choses :output_type/execution_count/outputs, les cles JSON autres quesource, et"source". Sa carte de lignes (_ipynb_output_lines, lignes 447-474) cartographie les blocsoutputsuniquement. Aucun des deux ne litcell_type. Une ligne desourceest donc jugee identique qu'elle appartienne a une cellule markdown ou a une cellule de code.Or l'en-tete de l'organe enonce l'inverse, noir sur blanc : « Une cellule code qui compte et affiche … On ne la regarde pas. » Le filtre ligne-a-ligne ne peut pas le faire — son propre commentaire (lignes 550-557) le reconnait : « ne peut pas l'attribuer a un type de cellule ; la carte par fichier, si. » La carte par fichier existe, elle est appliquee aux
outputs, elle ne l'est pas aux cellules de code. C'est un oubli, pas un desaccord doctrinal.Pourquoi ca compte pour cette PR precisement
La CI de #22462 rend
prose-countsrouge sur ce diff, et ce rouge est entierement D3 : la correction que la PR vient de faire (rendre sa provenance a une valeur non ancree) n'y change rien, et aucun des deux commentaires fautifs n'est une claim a ancrer — « forward 1 ligne » decrit un appel de methode, « negligeables sur 1 ligne » decrit une empreinte memoire. Un gate bloquant qui rougit sur de la prose technique legitime apprend a la lane a le contourner, ce qui est exactement l'inverse du but.Ce que je propose, si D3 est retenu
Elargir
_ipynb_output_linesen une carte de regions non-prose :outputsetsourcedes cellulescode. Le mecanisme existe deja (parcours par indentation,OUTPUTS_OPEN_RE) ; il faut un second releve ancre sur"cell_type": "code". Deux bornes : un test qui charge un notebook a cellule de code commentee et exige 0 finding, et le meme notebook avec la phrase en markdown exigeant le finding — sinon on ne sait pas distinguer « corrige » de « devenu aveugle ».Gouvernance : ce changement restreint un gate bloquant (moins de findings). Contrairement au point 1 de l'issue, il ne demande pas de sign-off user au titre de l'elargissement — mais il demande la meme prudence, et je ne l'applique pas moi-meme : la lane qui l'ecrit porte la PR que le gate rougit. La mesure ci-dessus suffit a decider.
Ce que cet addendum ne dit pas
Il ne dit pas que les 11 findings de
check_stale_claims.pysont faux — c'est un autre organe, et son triage est celui des notes de methode ci-dessus. Il ne dit pas non plus que le rougeprose-countsde #22462 est le seul : la correction de fond de la PR reste due, elle est faite, et le body sera aligne sur le contrat exact de la CI dans le meme mouvement.myia-ai-01 commentedon Oct 25, 2026
[CLAIMED] lane myia-ai-01:CoursIA-2 — corriger D3 (prose-counts applique le regime prose aux commentaires de cellule code, bloque #22462) puis D1/D2 (--diff-filter sans A, texte vert identique pour rien scanne) -- paths: scripts/notebook_tools/check_prose_quantitative_claims.py, .github/workflows/markdown-claims-output-advisory.yml, .github/workflows/prose-counts-guard.yml, scripts//tests/ (pose au dispatch par myia-ai-01:CoursIA)
jsboige commentedon Oct 25, 2026
[INFO] lane myia-ai-01:CoursIA-2 -- D3 livré par PR #22580 (carte non-prose étendue au source des cellules code, contrôles positif+négatif, témoin in vivo sur la branche de #22462 : organe main [REFUS] 2 findings -> organe fixé [OK]). D1/D2 (workflow, 3 détecteurs) suivent dans une seconde PR conformément au dispatch — le claim reste actif.
jsboige commentedon Oct 25, 2026
[DELIVERED] lane myia-ai-01:CoursIA-2 -- PR #22580 (D3) + PR #22586 (D1/D2)
Les trois volets de l'addendum sont livrés :
_ipynb_non_prose_linescouvre lesourcedes cellules code (docstrings/commentaires) en plus desoutputs; contrôles positif (les deux témoins, 0 finding) et négatif (le même compteur en markdown reste un finding) exigés par l'addendum ; témoin in vivo sur la branche Add(notebook): GenAI/Texte 11b Materiel, debit, cout (See #22428) #22462 ([REFUS]2 findings →[OK]).--diff-filter=MR→AMR(6 occurrences) : un carnet ajouté entre dans le lot des trois détecteurs.exit=none, notice « lot vide, ce n'est pas un résultat », branche ⚪ distincte du ✅ dans les trois stickies.Contract test de garde (
test_markdown_claims_advisory_contract.py) : la commandegit diffest extraite du workflow parsé et rejouée sur un dépôt temporaire avec carnet ajouté — un retour àMRou àexit=0rougit nommément.Fix(guard,#22564): D1/D2 -- le lot advisory voit les carnets AJOUTES …
Fix(claims,#22564): la carte non-prose couvre le source des cellules …
jsboige commentedon Oct 29, 2026
[CLOSURE PREFLIGHT]
schema: 1
lane: myia-po-2028:CoursIA-3
issue: 22564
verdict: CLOSE
acceptance:
--diff-filter=AMRdans .github/workflows/markdown-claims-output-advisory.yml (6 occurrences comptees sur main, l.121 et l.187 pour deux des trois detecteurs) ; garde-fouscripts/tests/test_markdown_claims_advisory_contract.py::test_added_notebook_reaches_the_scan_lot(l.113) rejoue la commande git du workflow parse sur un depot temporaire avec carnet ajoute -- un retour a MR fait rougir nommementecho "exit=none"(workflow l.132) + notice "aucun notebook ouvert ni modifie ... lot vide, ce n'est pas un resultat (exit=none)" (l.133) ; controlestest_empty_lot_is_none_not_a_clean_zero(l.131-136) ettest_sticky_comments_distinguish_none_from_clean(l.142)_ipynb_non_prose_lines(organe l.568) litcell_type(CELL_TYPE_RE l.561) et inclut lesourcedes cellules code (l.607) en plus desoutputs; controletest_ipynb_non_prose_lines_maps_payloads_and_code_sources(l.597-627) : source markdown hors non-prose (l.622), source code dedans (l.623), payload de sortie dedans (l.624)residue: waiver: verification documentaire sur main du 2026-10-29 (workflow, tests de contrat, organe) ; le temoin in vivo de Fix(claims,#22564): la carte non-prose couvre le source des cellules code (D3) #22580 sur la branche de Add(notebook): GenAI/Texte 11b Materiel, debit, cout (See #22428) #22462 et les 11 claims de check_stale_claims.py (autre organe, triage distinct) ne sont pas recomptees par cette lane
open-prs: 0
comments-reviewed: 4
[/CLOSURE PREFLIGHT]
Lecture G.9 (lane tierce myia-po-2028:CoursIA-3)
Les trois volets sont sur
main, verifies par lecture directe des artefacts.--diff-filtersansA) : le workflowmarkdown-claims-output-advisory.ymlporte--diff-filter=AMR-- 6 occurrences comptees surmain, ce qui correspond au compte annonce dans le [DELIVERED] du 25/10. Le carnet ajoute entre donc dans le lot des trois detecteurs, ce qui etait le point demande. Le reste du depot conserveMRdans d'autres workflows (ascii-flowchart-advisory,degraded-mode-advisory,render-volume-delta-advisory) : hors perimetre de cette issue, qui ne nomme que les trois detecteurs de claims.exit=noneet une notice explicite « lot vide, ce n'est pas un resultat », distincte duexit=0« scanne, rien trouve » ; les stickies distinguent la branche. La garde de contrat ne se contente pas d'une assertion de chaine : elle rejoue la commandegit diffextraite du workflow parse sur un depot temporaire contenant un carnet ajoute -- un retour aMRou aexit=0rougit nommement.check_prose_quantitative_claims.pydispose d'une carte des regions non-prose qui lit desormaiscell_typeet inclut lesourcedes cellules de code a cote desoutputs. Le controle demande par l'addendum est present et dans les deux sens : la meme phrase en markdown reste un finding, la meme phrase en commentaire de cellule code n'en est pas un. C'etait la condition posee par l'addendum (« un test qui charge un notebook a cellule de code commentee et exige 0 finding, et le meme notebook avec la phrase en markdown exigeant le finding ») : elle est satisfaite, pas seulement affirmee.Perimetre de cette attestation. Verification documentaire sur
main(workflow, les deux fichiers de test, l'organe) et lecture integrale des 4 commentaires (l'addendum D3 du 25/10, le CLAIMED d'ai-01, l'[INFO] de livraison partielle, le [DELIVERED]). Je n'ai pas re-execute la suite de tests ni rejoue les detecteurs sur le diff de #22462 : les temoins chiffres sont ceux des PRs mergees, confrontes a l'etat de la source sur main. La correction de fond du carnet porte par #22462 releve d'un autre dossier (celui de la PR), pas de cette issue.Issues liees : parent EPIC #17073 (« Part of ») et #22462 (la PR ou le defaut a ete mesure) restent ouverts ; fermer #22564 ne ferme ni l'un ni l'autre.