Repository navigation
feat(ci,#19251): mesurer les carnets RENAMED dans le balayage credited (previous_filename par REST) - #19261
Conversation
…advisory (balayage post-mortem) La branche « exemples credites » posee par #18761 etait **dormante**. Le diff exige `--base` ET `--pr-body-file` ; sous `schedule` -- seul declencheur qui subsiste apres la tranche 1 de #12817 -- il n'y a pas de contexte PR, donc `PR_NUMBER` est vide, donc `PR_BODY_FILE` reste vide, donc le test de `exercises-advisory.yml` prend la branche sans `--base` et **saute le diff**. Le label `credited-examples-lost` ne pouvait structurellement pas etre pose. ## Ce que livre la PR `scripts/notebook_tools/credited_examples_sweep.py` : balayage **post-mortem** des PRs mergees de la fenetre, rejouant le diff pour chacune avec SA base et SON body (option 1 de #19101). Cable comme etape du workflow ; l'etape existante n'est pas touchee. **Aucun `pull_request` n'est reintroduit** sur ce workflow : c'est la contrainte explicite de l'issue (le cout du clone par PR est la motivation d'origine de #12817). Le compromis -- detecter les pertes passees, pas proteger le merge -- est ecrit dans le module, pas seulement ici. ## Le piege trouve en mesurant Premiere mesure sur 24 h : **6 « erreurs de diff »** sur 38 PRs. Instruites, elles venaient toutes du meme cas : `git show <base>:<chemin>` sort en **128** parce que le carnet n'est pas a ce chemin dans la base. Ce n'etait pas cosmetique. `credited_diff_errors > 0` **bloque** la pose du label (#18761) : une erreur structurelle sur un carnet empechait la mesure reelle des carnets modifies de la meme PR. Un faux positif d'erreur produisait un faux zero de pertes. Trois cas separes, qui ne disent pas la meme chose : - `ADDED` : neuf, **rien a perdre** par construction (6 des 6 erreurs) ; - `RENAMED` : la base est a un **autre chemin**, que `gh pr view --json files` n'expose pas (`previousFilename` absent). Non comparable -- et contrairement a `ADDED`, un renommage **peut** perdre des exemples : declare NON MESURE, jamais « sans perte » ; - `DELETED` : exclu, comme le `--diff-filter=d` du workflow. Les carnets non mesurables sont **nommes** dans le rapport au lieu de disparaitre : un carnet tu se lirait comme un carnet conforme. ## Mesure Fenetre de 24 h sur `main`, `--json`, sans `--apply`. Un diff en erreur n'est PAS un zero mesure (#18761) : le rapport separe « pertes non exemptees = 0 » de « carnets non mesurables », et n'affirme jamais une couverture que la mesure ne porte pas. ## Tests `test_credited_examples_sweep.py` : tout hors ligne (reseau et git injectes). Le lot de l'API de recherche **au plafond** leve au lieu de passer pour un compte ; le label ne se pose que si **tous** les diffs ont reussi ; un `merge-base` indisponible est nomme ; un body illisible est nomme et la PR ecartee ; un `ADDED` n'est pas mesure ; un `RENAMED` est declare non mesure ET ne bloque plus les carnets modifies de la meme PR. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…c non masque Reponse a la review CHANGES_REQUESTED du 05/10 (5 conditions) : 1. changeType n'existe pas dans gh pr list --json files (mesure gh 2.83.2 sur #19040 : {additions, deletions, path} seulement). Les fichiers et leur nature viennent maintenant de GraphQL (pullRequest.files.nodes {path changeType}), pagines au curseur -- le repli « tout MODIFIED » classait GameTheory-18d (supprime par #19040) en modifie et levait FileNotFoundError, hors de tout garde. 2. check_notebooks recoit head_ref=headRefOid : le cote « apres » est la tete de la PR, pas l'arbre du moment du balayage. Le commit est amene par fetch-by-SHA s'il manque (PR squash-mergee) ; inatteignable = erreur nommee, PR ecartee -- jamais mesuree contre l'arbre du jour. 3. Tests : 17 -> 27. La forme reelle du lot (sans files/changeType) est desormais un fixture ; carnet supprime sans crash + carnets modifies de la meme PR toujours mesures ; pagination >100 fichiers ; tete inatteignable ; propagation de head_ref au check. Falsification : 3 mutants (branche DELETED retiree, head_ref non passe, pagination coupee) -> chacun cuche par au moins un test ; restore vert 27/27. 4. Le masque || echo "::warning::..." du workflow est retire : un plantage permanent laissait le run vert chaque nuit (classe de defaut #19214). set -euo pipefail ; sans effet merge (schedule/dispatch seulement). 5. Rejou de la mesure 24 h sur le corpus reel : dans le corps de la PR. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…lisible n'emporte plus le balayage Review 5411248369, deux voies demandees, les deux faites. 1. `check_notebooks` comptait les exercices sur l'ARBRE DU JOUR (`count_exercises_in_notebook(path)`) alors que le diff credite lit deja le blob de `head_ref`. Consequence mesuree : #18788 MODIFIE ICT-45-InoculationBifurcation-9B, #19153 le RENOMME ensuite -> le chemin est MODIFIED mais absent de l'arbre, et le comptage levait un FileNotFoundError qui emportait TOUT le balayage (`--hours 72`, rc=1 : les autres PR de la fenetre n'etaient pas mesurees). Le comptage porte desormais sur le blob de tete, qui existe par construction pour un chemin MODIFIED. La classification reste sur le chemin d'origine : `classify_notebook` lit les regles de REPERTOIRE, que le fichier temporaire du blob ne porte pas. Effet de bord voulu, sur le meme chemin : meme quand le carnet existe dans l'arbre, c'est la revision de la PR qui est comptee -- l'ancien code pouvait mesurer un arbre different de celui qu'il comparait. 2. Le repli par PR : un echec de `check` est NOMME (« carnet illisible depuis l'arbre du jour (FileNotFoundError: ...) -- renomme ou supprime apres merge, PR ecartee ») et la suite de la fenetre est mesuree. La portee du `except` est etroite (`OSError`, `ValueError`) : un autre type d'echec remonte, pour qu'un bug du compteur ne se derobe pas en « carnet renomme ». Tests : 27 -> 32. Les nouveaux portent sur un depot git REEL (deux commits, renommage effectif), pas sur un dict injecte : carnet MODIFIED absent de l'arbre compte depuis le blob (3 exercices) ; l'arbre porte une autre version (1 exercice) et la tete gagne ; sans `head_ref` l'arbre sert encore (retro-compatibilite) ; un `check` qui leve est nomme et les autres PR sont mesurees ; un `RuntimeError` remonte au lieu d'etre absorbe. Falsification : 3 mutants, 3 rouges -- comptage remis sur l'arbre (2 tests), repli par PR retire (1), `except` elargi a `Exception` (1). Source restauree, 32 passed. Suivi RENAMED : issue #19251 ouverte avant merge (mesurer les renommages, `previousFilename` absent du jeu GraphQL). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Le balayage post-mortem des exemples credites (#19101) declarait tout carnet RENAMED « NON MESURE » faute de connaitre son chemin de base : `previousFilename` n'etait pas demande au jeu GraphQL. Un `git mv` suivi d'une edition pouvait donc perdre un exemple credite sans que rien ne le voie. Le champ est desormais demande (`nodes { path changeType previousFilename }`). Quand il est present, le renommage est MESURE comme un MODIFIED : le diff credite lit `base:previousFilename` contre `head:path`. La correspondance passe par `base_path_of`, consultee en POSIX -- GraphQL rend des `/` et `str(Path)` des `\` sous Windows : sans la normalisation, la correspondance raterait en silence et le renommage redeviendrait non mesure (mutant M3). Quand `previousFilename` manque (renommage sous un seuil de similarite, ou reponse d'API degradee), le carnet reste nomme NON MESURE -- et surtout, sans correspondance la base est absente a ce chemin : le diff est en ERREUR, que #18761 refuse de convertir en label. Jamais un zero silencieux. Tests 32 -> 37 : le renommage mesure (un exemple credite perdu au `git mv` est vu), le renommage sans perte (pas de faux positif), et l'absence de zero silencieux sont epingles sur un depot git REEL ; le contrat de la carte en plus. Falsification : 4 mutants, 4 rouges -- mapping ignore (M1), `previousFilename` ignore (M2), cle non normalisee (doublure du bug Windows, M3), diff en erreur converti en zero silencieux (M4) ; source restauree, 37 passed. Au passage, les appels `subprocess` des tests touchés portent `encoding="utf-8", errors="replace"` (garde #13140/#12811 : un hôte cp1252 leve sur un payload UTF-8). Closes #19251 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…raphQL
Correction du commit precedent, dont l'hypothese etait fausse et que le rejeu
`--hours 72` a demasquee : `previousFilename` N'EXISTE PAS sur le type GraphQL
`PullRequestChangedFile`. Mesure, serveur :
Field 'previousFilename' doesn't exist on type 'PullRequestChangedFile'
Ses champs sont `additions, changeType, deletions, path, viewerViewedState`
(verifie par introspection). Demander le champ faisait echouer TOUTES les
lectures de fichiers : `--hours 72` rendait « 0 PR mesuree, 309 replis nommes »
au lieu des 2 PR / 4 pertes de la veille -- une regression, pas un progres.
Le classement reste donc en GraphQL (`changeType`), et le chemin de base est lu
par une SECONDE source, REST : `pulls/{n}/files`, champ `previous_filename`
(verifie sur #19153 : ICT-45 -> ICT-42b). Cette passe n'est faite que si la PR
porte au moins un renommage, et son echec laisse le renommage NON MESURE
(`previousFilename` rendu `""`) au lieu de le mesurer contre un mauvais chemin.
Tests 37 -> 40 : la passe REST enrichit un noeud RENAMED, elle est sautee quand
rien n'est renomme, et son echec ne casse rien. Falsification : 5 mutants, tous
rouges (dont M5 : passe REST supprimee).
Closes #19251
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Base != main (advisory, #10918)Cette PR ne livre pas sur Couverture CI perdue sur cette base (mesure, #16194)6 workflow(s) se declencheraient si cette PR visait
Un check absent n'est pas un check vert. |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[Hermes] VERDICT: LGTM (2 réserves mineures, aucune bloquante)
#19251 — le balayage credited mesure désormais les carnets RENAMED via previous_filename (REST), toujours nommés NON MESURÉS sinon. Review avec exécution du code du head (gate review) :
Vérifié firsthand (head d29f549f) :
- Tests 40/40 passed en local : fichiers du head extraits, harnais reconstitué (
count_exercises,check_credited_examplessiblings requis — leur absence fait silencieusement_HAS_CREDITED=False→ toutskipped, 5 faux échecs initiaux qui étaient le défaut de MON harnais, pas de la PR). - Cas motivateur reproduit :
pr_files("jsboige/CoursIA", 19153)rend le nœud RENAMED enrichiICT-42b → ICT-45+ 6 MODIFIED intacts — exactement le claim du body, mesuré contre l'API réelle. - La classe end-to-end
TestARenamedNotebookIsMeasuredAtItsOldPathexécute un vrai dépôt git (git mveffectif) : perte vue (=1), sans-perte pas faux positif (=0), absence de mapping →error:jamais zéro silencieux. Les 3 assertions cardinales du #19251. - Diff complet lu : clé POSIX avant lookup (piège Windows documenté), pagination REST explicite en GET (
-fferait basculer en POST — correct), repliprevious={}sur échec REST → NON MESURÉ nommé, pas mesuré contre le mauvais chemin. - Grain présent, gates du head verts (2 organes), issue #19251 lue.
Réserves mineures :
_previous_filenamess'arrête surlen(payload) < 100mais ne borne paspage(une API renvoyant indéfiniment 100 lignes bouclerait) — théorique, les fichiers de PR sont plafonnés à 3000 par l'API.- Les renommages hors du champ REST restent non mesurés — hors scope assumé au body, mentionné pour mémoire.
Pile #19215 : base = branche de #19215, à recibler après son merge (déjà noté au body).
[Hermes hermes-pr-review, cycle :08 05/10, host f6be46d1b7a3, sig=8d7c8f47]
… after #19215 squash-merge) # Conflicts: # scripts/notebook_tools/credited_examples_sweep.py # scripts/tests/test_credited_examples_sweep.py
|
G-VAR-2 light cap reached (advisory, non bloquant). |
|
G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels |
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
Path-collision (organ #13359/#13615)Cette PR #19261 (
Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur |
|
[ADJOINT PREFLIGHT] |
Grain: MED/guard — lane myia-po-2026:CoursIA — prev: MED/notebook-python #19259
Closes #19251
Quoi: le balayage post-mortem des exemples crédités (#19101) déclarait tout carnet RENAMED « NON MESURÉ » faute de connaître son chemin de base. Un
git mvsuivi d'une édition pouvait donc perdre un exemple crédité sans que rien ne le voie. Le chemin de base est désormais lu, et le renommage est mesuré comme un MODIFIED quand il est connu.Empilée sur #19215 (
fix/19101-credited-sweep) : même fichiers, donc pas de conflit au merge. La base de cette PR est la branche de #19215, pasmain— à recibler après son merge.Une hypothèse fausse, corrigée par le rejeu (à lire)
L'issue #19251 prescrivait de demander
previousFilenameau nœud GraphQL des fichiers. C'est faux, et le rejeu l'a montré : le type GraphQLPullRequestChangedFilen'a pas ce champ. Mesure, réponse du serveur :Ses champs sont
additions, changeType, deletions, path, viewerViewedState(vérifié par introspection du schéma). Le premier commit de cette branche demandait le champ : le rejeu--hours 72rendait alors 0 PR mesurée, 309 replis nommés, là où la veille rendait 2 PR / 4 pertes. Une régression franche, pas un progrès — corrigée au second commit, et c'est le rejeu qui l'a attrapée avant toute review.La source réelle
Le classement reste en GraphQL (
changeType: ADDED / DELETED / RENAMED / MODIFIED). Le chemin de base vient d'une seconde source, REST :pulls/{n}/files, champprevious_filename— vérifié sur #19153 (ICT-45-InoculationBifurcation-9B→ICT-42b-InoculationBifurcation-9B). Cette passe n'est faite que si la PR porte au moins un renommage.previousFilenameprésent → renommage mesuré : le diff crédité litbase:previousFilenamecontrehead:path.previousFilenameabsent (passe REST en échec) → renommage nommé NON MESURÉ. La base est alors absente à ce chemin : le diff est en erreur, que feat(guard,#18740): detecteur exemples credites + integration check_pr_exercises #18761 refuse de convertir en label. Jamais un zéro silencieux.Piège attrapé par les tests, pas par relecture : la correspondance
head_path → base_pathest clée en POSIX (GraphQL et REST rendent des/) alors questr(Path)rend des\sous Windows — sans normalisation, la correspondance raterait en silence et le renommage redeviendrait non mesuré (mutant M3).Preuves
Tests 32 → 40 : renommage mesuré (l'exemple crédité perdu au
git mvest vu), renommage sans perte (pas de faux positif), absence de zéro silencieux, contrat de la cartebase_path_of. Les trois premiers sur un dépôt git réel (git mveffectif, identités(credit, cell_id)stables), pas un dict injecté.Passe REST : enrichit un nœud RENAMED · est sautée quand rien n'est renommé · son échec ne casse rien et laisse le renommage non mesuré.
Falsification : 5 mutants, 5 rouges — mapping ignoré (M1),
previousFilenameignoré (M2), clé non normalisée (M3), diff en erreur converti en zéro silencieux (M4), passe REST supprimée (M5). Source restaurée, 40 passed.Vérification réelle de bout en bout :
pr_files("jsboige/CoursIA", 19153)rend la liste de fichiers attendue dont 1 RENAMED avec son chemin de base — exactement le cas qui a motivé l'issue.Rejeu
--hours 72(base de cette branche, mode rapport, aucun--apply) :Identique à la baseline feat(ci,#19101): reveiller la branche credited-examples de exercises-advisory (balayage post-mortem) #19215 (2 PR / 4 pertes / 0 repli) : aucune régression. Et l'angle mort est fermé : les 6 PR à renommage de la fenêtre (rename(#16231): ICT-45 -> ICT-42b -- accrétion de la famille InoculationBifurcation (chantier C) #19153, refactor(search,#17802): descente de 09d dans Discrepancy/ -- Discrepancy-02-Komlos-Lean #19151, reclass(ict,#18973): renum ICT-42-Crosscoder en ICT-41b — resolution de la collision de slot 42 #18986, feat(probas,#14873): G3 descente arc actuariel en sous-série Actuariat/ (T1-T5) + capstone DecPyMC-08 #18857, renum(complexity,#17063): tranche T1 -- 05 Aaronson-Arkhipov devient l'accretion 05b (R100) #18817, rename(#16231): tranche ICT -- suffixe noyau -Python (64 carnets + re-exec) #18649) — celles-là mêmes que feat(ci,#19101): reveiller la branche credited-examples de exercises-advisory (balayage post-mortem) #19215 déclarait « NON MESURÉ(S) » — sont désormais mesurées : 72 renommages au total (63 pour rename(#16231): tranche ICT -- suffixe noyau -Python (64 carnets + re-exec) #18649 seul), tous à 0 perte. C'est un zéro mesuré, plus un aveu d'ignorance. Les deux LABEL (fix(search,#17863): MGS-08 — bancs GA seedés, sorties reproductibles (DLL rebuildée depuis dbcd404) #18820, feat(genai,#18573): SK-08-MCP refonte PR1 -- vrai FastMCP + ClientSession + MCPStdioPlugin #18585) sont inchangés : pas de nouveau faux positif.
Perimetre:
scripts/notebook_tools/credited_examples_sweep.py(source REST + classement),scripts/notebook_tools/check_pr_exercises.py(base_path_of),scripts/tests/test_credited_examples_sweep.py. Hors scope : la mesure des renommages dont le chemin de base reste inconnu (nommés, jamais comptés à zéro).🤖 Generated with Claude Code