Skip to content

ci(claims): un carnet AJOUTE echappe aux trois détecteurs (--diff-filter=MR), et « rien scanne » se lit comme « rien trouve » #22564

Open

Description

@jsboige

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
fi

et 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

  1. --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).
  2. Un troisieme etat explicite, exit=none, quand CHANGED est 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'abstain existant, qui lui est deja distinct.
  3. Un test sur le contrat de lot : un carnet A doit entrer dans CHANGED (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/param et 8,4 Go, calculees depuis des sorties imprimees 2.000, 0,516 et 4,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 --json ecrit en cp1252, pas en UTF-8. Un json.load(open(..., encoding='utf-8')) leve UnicodeDecodeError: 0xe8 sur 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

  1. jsboige commented on Oct 25, 2026

    @jsboige
    OwnerAuthor

    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.py lui-meme.

    D3 — scan_diff scanne les commentaires de cellules de code

    Mesure, tete 8c23605e8533, origin/main a 30f53559e1e3 :

    $ python scripts/notebook_tools/check_prose_quantitative_claims.py --diff "origin/main...HEAD" --strict
    [REFUS] 2 compteur(s) quantitatif(s) en prose, 1 fichier(s)
    rc=1
    

    Isolement par classe, sur le meme diff : artifact rc=1 (2 findings), machine rc=1 (7), env rc=1 (1), stochastic rc=0, structural rc=0.

    Les deux findings de la classe artifact tombent dans la cellule 15, qui est une cellule code :

    cell15 type: code
    findings in cell15 source: 2
    --- ce que scan_all verrait (markdown uniquement) ---
    markdown findings total: 0
    

    Les deux lignes sont des commentaires Python :

    """Octets VRAM par parametre : quantifie au .cuda() (bitsandbytes), forward 1 ligne."""
    couche(x1)  # alloue les éventuels buffers de travail (négligeables sur 1 ligne)

    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 .ipynb ligne a ligne, et ne connait que trois choses : output_type/execution_count/outputs, les cles JSON autres que source, et "source". Sa carte de lignes (_ipynb_output_lines, lignes 447-474) cartographie les blocs outputs uniquement. Aucun des deux ne lit cell_type. Une ligne de source est 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-counts rouge 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_lines en une carte de regions non-prose : outputs et source des cellules code. 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.py sont 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 rouge prose-counts de #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.

  2. myia-ai-01 commented on Oct 25, 2026

    @myia-ai-01
    Collaborator

    [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)

  3. jsboige commented on Oct 25, 2026

    @jsboige
    OwnerAuthor

    [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.

  4. jsboige commented on Oct 25, 2026

    @jsboige
    OwnerAuthor

    [DELIVERED] lane myia-ai-01:CoursIA-2 -- PR #22580 (D3) + PR #22586 (D1/D2)

    Les trois volets de l'addendum sont livrés :

    Contract test de garde (test_markdown_claims_advisory_contract.py) : la commande git diff est extraite du workflow parsé et rejouée sur un dépôt temporaire avec carnet ajouté — un retour à MR ou à exit=0 rougit nommément.

  5. added 2 commits that reference this issue on Oct 26, 2026
  6. added
    candidate-deliveredReferenced by a merged PR with no post-merge activity -- candidate for close triage (#10466)
    on Oct 28, 2026
  7. jsboige commented on Oct 29, 2026

    @jsboige
    OwnerAuthor

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

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

    Les trois volets sont sur main, verifies par lecture directe des artefacts.

    • D1 (--diff-filter sans A) : le workflow markdown-claims-output-advisory.yml porte --diff-filter=AMR -- 6 occurrences comptees sur main, 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 conserve MR dans 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.
    • D2 : le lot vide rend exit=none et une notice explicite « lot vide, ce n'est pas un resultat », distincte du exit=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 commande git diff extraite du workflow parse sur un depot temporaire contenant un carnet ajoute -- un retour a MR ou a exit=0 rougit nommement.
    • D3 : l'organe check_prose_quantitative_claims.py dispose d'une carte des regions non-prose qui lit desormais cell_type et inclut le source des cellules de code a cote des outputs. 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.

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