Repository navigation
feat(catalog,#17217): champ resource_cost -- cout d'execution mesure, cout de creation proxy - #17226
Conversation
… cout de creation proxy Cinquieme axe du catalogue : ce qu'un notebook coute a EXECUTER et ce qu'il a coute a PRODUIRE. Demande user, explicitement « pas besoin d'etre hyper precis pour l'instant, ca pourra s'affiner » -- d'ou un champ qui rend ses mesures brutes a cote de sa classe, pour qu'un affinage ne reparte pas de zero. L'execution est MESUREE : 86,7 % du corpus porte des horodatages `metadata.execution` (nbclient/papermill), 6,7 % partiellement. Le cout est une somme de durees reelles, pas l'heuristique de `duree_estimee` (qui reste, elle sert a annoncer une duree a l'etudiant). Cout cumule du corpus : 38,8 h sur 1261 notebooks chronometres. La creation est un PROXY, declare comme tel dans le code : `revisions` compte les commits, accumules dans la passe `git log` que `build_git_metadata` fait deja. Le squash-merge ecrase chaque PR en un commit, donc il compte des PRs et pas des cycles ; ni les cycles d'agent sans commit, ni les tokens, ni le temps humain n'y figurent. Contrairement aux trois autres axes declaratifs (mono-values a ~90 %, cf #17217), celui-ci discrimine : execution LIGHT 73,7 / MODERATE 16,3 / HEAVY 3,0 / VERY_HEAVY 0,3 / UNKNOWN 6,7 ; creation LIGHT 30,6 / MODERATE 40,9 / HEAVY 21,9 / VERY_HEAVY 6,5. Le defaut que ce commit a failli publier : mes premiers seuils etaient calibres sur un clone SHALLOW coupe au 2026-08-25. La distribution mesuree la (mediane 3 revisions, max 13) ne decrit pas la vie des notebooks, elle decrit 27 jours. Sur l'historique complet -- 2309 notebooks remontant a 2024 -- c'est q1 2, mediane 4, q3 11, p90 27, p95 35, max 64. Les seuils initiaux auraient classe presque tout le corpus en HEAVY, soit exactement le defaut mono-value reproche aux autres axes. Ils sont recalibres, et les chiffres qui les fondent sont dans le commentaire de section pour se contester avec des mesures. Fail-closed partout : - pas d'horodatage -> UNKNOWN, JAMAIS LIGHT (un notebook non chronometre n'est pas un notebook rapide) ; - plus vieux commit sur une frontiere de greffe -> UNKNOWN/TRUNCATED, et `first_commit` TUE : sur un clone coupe c'est la date de coupe, la publier fabriquerait l'age qu'on pretend mesurer. Les comptes restent rendus, vrais pour la fenetre ; - `history_truncated` vaut True par defaut -- un appelant qui l'oublie obtient UNKNOWN, pas une classe inventee ; - `shallow_boundaries()` rend None si la question n'a pas pu etre posee, et None est traite comme coupe ; - duree negative ou > 10 h par cellule : ecartee du total ET decomptee de la couverture, jamais additionnee. `git rev-parse --is-shallow-repository` ne convient pas pour ce verdict, et c'est mesure : sur ce depot une fois approfondi il rend encore `true` a cause de six greffes residuelles dont AUCUNE n'est dans l'historique de MyIA.AI.Notebooks. S'y fier rendrait UNKNOWN partout la ou le champ marche. Le predicat est donc par notebook : son plus vieux commit est-il une greffe ? Une ressource externe est nommee sans corriger la classe : le temps de paroi d'un appel d'API est de la latence, pas le calcul brule a l'autre bout. Corriger la classe fabriquerait un chiffre que rien ne mesure. `test_generate_catalog.py` est touche parce que le format de `git log` gagne %H : son controle positif portait l'ancien format et a echoue -- c'est son role. Fixture aligne, assertions etendues aux trois champs neufs. Cout : le catalogue grossit d'environ 34 % (+299 o par entree). C'est le prix d'un champ qui rend ses mesures brutes ; le reduire a la seule classe le rendrait inaffinable. See #17217 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
[ADJOINT PREFLIGHT] Verdict BLOCKED, cause nommee. 17 check-runs dedupliques par (started_at, id) au head a57bcf7 : 9 pending, 1 rouge. Le rouge est |
|
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 |
Path-collision (organ #13359/#13615)Cette PR #17226 (
|
|
[ADJOINT PREFLIGHT] Verdict BLOCKED, causes nommees : 1 check pending residuel + 1 rouge PR gate (quota d'installation GitHub, echec de flotte). 18 check-runs dedupliques par (started_at, id) au head a57bcf7. mergeable=true, b0 rc=0. Re-emission requise apres stabilisation du pending et disparition du rouge. |
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
|
[ADJOINT PREFLIGHT] |
Grain: MED/harness -- lane myia-ai-01:CoursIA -- paths: scripts/notebook_tools/generate_catalog.py, scripts/notebook_tools/tests/test_resource_cost.py, scripts/notebook_tools/tests/test_generate_catalog.py
Ce que le champ ajoute
Un cinquième axe au catalogue :
resource_cost, ce qu'un notebook coûte à exécuter et ce qu'il a coûté à produire. Demande user, explicitement « pas besoin d'être hyper précis pour l'instant, ça pourra s'affiner » — d'où un champ qui rend ses mesures brutes à côté de sa classe, pour qu'un affinage ultérieur ne reparte pas de zéro.L'exécution est mesurée, pas estimée
86,7 % du corpus porte des horodatages
metadata.executionécrits par nbclient/papermill, 6,7 % partiellement. Le coût d'exécution est donc une somme de durées réelles, pas l'heuristique « 2 min par cellule » deduree_estimee(qui reste, elle sert à autre chose : annoncer une durée à l'étudiant).Distribution mesurée sur les 1351 notebooks du corpus :
execution.classcreation.classCoût d'exécution cumulé du corpus : 38,8 h sur 1261 notebooks chronométrés. Les quatre plus lourds :
ICT-25-InoculationRL(4,9 h),ICT-40a-TriangulationCausale(1,3 h),Sudoku-16-NeuralNetwork-Python(1,2 h),FLUX-1-Advanced-Generation(1,2 h).À comparer aux trois autres axes déclaratifs, mono-valués à ~90 % (#17217) : celui-ci discrimine réellement, notamment côté création.
La création est un proxy, et c'est écrit dans le code
revisionscompte les commits touchant le notebook, accumulés dans la passegit logquebuild_git_metadatafait déjà — elle parcourt tout l'historique pour n'en retenir que le commit le plus récent, donc compter est gratuit ; un second parcours ne l'est pas.Ce que le proxy ne voit pas, et qui est dit dans le commentaire de section plutôt que maquillé : le squash-merge écrase chaque PR en un commit, donc
revisionscompte des PRs, pas des cycles ; ni les cycles d'agent sans commit, ni les tokens brûlés, ni le temps humain n'y figurent. Les auteurs distincts ne discriminent presque rien (médiane 1, max 4) : ils sont rendus, ils ne classent pas.Le défaut que cette PR a failli publier
Mes premiers seuils étaient calibrés sur un instrument aveugle. Le clone d'ai-01 est
shallow, coupé au 2026-08-25. La distribution que j'y ai mesurée — médiane 3 révisions, maximum 13 — ne décrit pas la vie des notebooks, elle décrit 27 jours. Après approfondissement, sur 2309 notebooks remontant à 2024 : q1 2, médiane 4, q3 11, p90 27, p95 35, max 64. Les seuils initiaux auraient classé presque tout le corpus enHEAVY, c'est-à-dire reproduit exactement le défaut mono-valué que #17217 reproche aux autres axes.Deux conséquences dans le diff :
class: UNKNOWN, history: TRUNCATED, et safirst_commitest tue : sur un clone coupé c'est la date de coupe, la publier fabriquerait l'âge qu'on prétend mesurer. Les comptes, eux, restent rendus — ils sont vrais pour la fenêtre.git rev-parse --is-shallow-repositoryne convient pas pour ça, et c'est mesuré : sur ce dépôt une fois approfondi il rend encoretrueà cause de six greffes résiduelles dont aucune n'est dans l'historique deMyIA.AI.Notebooks— dont le plus vieux commit remonte à 2024 et porte un parent. S'y fier rendraitUNKNOWNpartout précisément là où le champ fonctionne. Le prédicat retenu est donc par notebook : son plus vieux commit est-il dans.git/shallow?Fail-closed, partout
UNKNOWN, jamais LIGHT. Un notebook qu'on n'a pas pu chronométrer n'est pas un notebook rapide.UNKNOWN/ABSENT.history_truncatedvaut True par défaut : un appelant qui oublie de le poser obtientUNKNOWN, pas une classe inventée.shallow_boundaries()rendNonesi la question n'a pas pu être posée, etNoneest traité comme coupé.Une ressource externe est nommée, elle ne corrige pas la classe
Le temps de paroi d'un appel d'API est de la latence réseau, pas le calcul brûlé à l'autre bout. Un notebook
requires_apiclassé LIGHT est léger pour cette machine, pas pour le monde.external: ["api","gpu"]est donc rendu à côté de la classe sans la corriger : corriger la classe fabriquerait un chiffre que rien ne mesure. La limite est écrite dans le code et gardée par un test.Validation
13 tests neufs. Les quatre propriétés qu'ils épinglent sont rangées par dégât : (1) une absence de mesure ne devient jamais « léger » ; (2) un horizon de clone n'est pas une date de création ; (3) une durée aberrante est écartée ; (4) les seuils discriminent — chacune des huit bornes est franchie dans les deux sens.
test_generate_catalog.pyest touché parce que le format degit loggagne%H: son contrôle positif portait l'ancien format et a échoué, ce qui est exactement son rôle. Le fixture est aligné et les assertions étendues aux trois champs neufs.Aucun notebook touché.
COURSE_CATALOG.generated.*non touché — byte-identique àmain.À savoir avant de merger
COURSE_CATALOG.generated.mdni dans les README de série. C'est un choix de présentation à part entière (quelle colonne céder), pas un oubli — geste séparé.See #17217
🤖 Generated with Claude Code