Skip to content

fix(tweety,#17245): retirer les generations Tweety supplantees avant telechargement - #17246

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/tweety-stale-jar-generations
Sep 21, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/tweety-stale-jar-generations

Conversation

@jsboige

@jsboige jsboige commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2023:CoursIA — prev: LIGHT/notebook-python #17238

Closes #17245.

Le défaut

download_jar sort tôt dès que local_path.exists(), et le nom local embarque la version (local_jar_name(module, version)) : changer TWEETY_VERSION ne faisait donc jamais disparaître la génération précédente, et download_tweety_jars ne comportait aucune étape de nettoyage.

Ce n'est pas cosmétique, parce que tweety_init.py:153 construit le classpath par glob :

jar_files = list(lib_dir.glob("*.jar"))
classpath_items.extend(str(j.resolve()) for j in jar_files)

Les deux générations montaient donc ensemble sur la JVM, l'ordre de résolution décidant classe par classe.

Mesure (arbre partagé, 2026-09-21)

Génération Fichiers Classes Poids
1.29 (org.tweetyproject.*-1.29-with-dependencies.jar, fat) 35 29 209 428,3 Mo
1.30 (*-1.30.jar, thin) 39 1 872 5,7 Mo
externes 3 949 —

Intersection 1.29 ∩ 1.30 = 1 829 classes — 97,7 % des classes de la version courante. Sur un arbre mis à jour, la quasi-totalité du code 1.30 est en double sur le classpath, et 428 Mo de génération morte sont montés à chaque startJVM. Un arbre neuf fait tourner un classpath 1.30 pur, un arbre mis à jour un classpath mixte : la classe de panne qui se lit « ça marche chez moi ».

Le correctif

download_tweety_jars retire les JAR Tweety d'une génération antérieure avant de télécharger, en annonçant le nombre et le poids récupérés.

  • Dépendances externes hors périmètre : EXTERNAL_DEPENDENCIES (args4j, commons-math 2.2, sat4j) est versionné indépendamment du projet et reste en place.
  • Fail-closed : un nom qui ne correspond à aucun artefact connu n'est jamais supprimé. C'est la propriété que les contrôles négatifs ci-dessous éprouvent.
  • --keep-old-generations restaure le comportement précédent, pour qui veut inspecter l'ancienne génération.
  • local_jar_name() extrait comme source unique : download_jar écrit ce nom, expected_jar_names le prédit — les deux ne peuvent plus diverger.

Vérification

Téléchargement neutralisé (on éprouve le nettoyage, pas le réseau), fixtures = les 77 noms réellement présents sur l'arbre partagé :

avant : 77 jars  ->  apres : 42 jars
OK  defaut : 5.7 Mo de 1.30 + 3 externes, residu 1.29 retire
OK  annonce : Superseded Tweety generations: 35 JAR(s), 0.0 MB
OK  --keep-old-generations : 77 jars conserves (comportement precedent)

Huit contrôles unitaires, dont quatre négatifs (le gate ne doit pas sur-supprimer) :

Nom Attendu Motif
dung-1.29.jar supprimé thin d'une version antérieure (absent du disque, forme couverte d'avance)
logics-commons-1.29.jar supprimé nom de collision, version antérieure
org.tweetyproject.tweety-full-1.29-with-dependencies.jar supprimé bundle fat, hors modules déclarés
args4j-2.33.jar · org.ow2.sat4j.core-2.3.5.jar conservé externe déclaré
commons-math-3.6.1.jar conservé externe non déclaré, stem inconnu
causal-1.30.jar conservé version courante
NotARealArtifact-1.29.jar conservé inconnu → fail-closed

python -m py_compile OK. Aucun appelant ne dépend de l'ancienne signature (keep_old_generations est optionnel) : la seule occurrence hors du script est une autre fonction download_jar dans SymbolicAI/_archive/Tweety.ipynb, sans rapport.

Signalé, non corrigé (hors périmètre)

Le garde de version en tête de fichier compare des chaînes :

if TWEETY_VERSION >= "1.29":   # ligne 95
if TWEETY_VERSION >= "1.30":   # ligne 97

"1.9" >= "1.29" est vrai lexicographiquement, et "1.100" >= "1.30" est faux. Le comportement est correct pour 1.30, donc ce n'est pas un défaut actif — mais un parseur tolérant serait nécessaire pour le corriger sans casser --version 1.30-SNAPSHOT. Laissé en place, signalé ici plutôt que corrigé au passage.

🤖 Generated with Claude Code

…telechargement

download_jar sort tot des que local_path.exists(), et le nom local embarque la
version (local_jar_name(module, version)) : changer TWEETY_VERSION ne faisait
donc jamais disparaitre la generation precedente, et download_tweety_jars ne
comportait aucune etape de nettoyage.

tweety_init construit le classpath par glob(lib_dir/"*.jar"), les deux
generations montaient donc ensemble sur la JVM, a la merci de l'ordre de
resolution. Mesure sur l'arbre partage : 35 fat-JARs 1.29 (29 209 classes,
428,3 Mo) a cote de 39 thin-JARs 1.30 (1 872 classes, 5,7 Mo) -- soit 1 829
classes dupliquees, 97,7 % des classes de la version courante.

download_tweety_jars retire desormais les JARs Tweety d'une version anterieure
avant de telecharger, en annoncant le nombre et le poids recuperes. Les
dependances externes (EXTERNAL_DEPENDENCIES) sont hors perimetre -- versionnees
independamment du projet. Un nom inconnu n'est jamais supprime (fail-closed).
--keep-old-generations restaure le comportement precedent.

Verification (telechargement neutralise, fixtures = les 77 noms reels) :
- 77 -> 42 JAR par defaut ; 3 externes preserves ; aucun -1.30 touche ;
- 8 controles unitaires : dung-1.29 et logics-commons-1.29 detectes,
  tweety-full-1.29 detecte (hors modules declares), commons-math-3.6.1 /
  args4j-2.33 / org.ow2.sat4j.core-2.3.5 / nom inconnu preserves ;
- --keep-old-generations : 77 JAR conserves.

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

jsboige commented Sep 21, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2025:CoursIA-2
pr: 17246
head: e7ae86e
complete: true
body: read
comments-reviewed: 0
reviews-reviewed: 0
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: 0c6b3fa1b273318458532f6296cab5567e3048a07d49aa052f325d1e324a5097
diff-files: 2
diff-additions: 96
diff-deletions: 14
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Verdict READY au 2026-09-21T18:45Z. Mesures au head e7ae86e : 28 check-runs dedupliques (started_at, id), pages fusionnees, 0 pending, 0 rouge — le rouge de quota (FAIL cannot establish check state, anterieur au retablissement 16:49Z) a disparu au rejeu de la lane, PR gate PASS. b0 rc=0, blocking vide (organe origin/main 068a739e). mergeable=true. Porteur : myia-po-2023:CoursIA, distinct de la lane emettrice.

@myia-ai-01
myia-ai-01 merged commit 0650f2a into main Sep 21, 2026
28 of 31 checks passed
myia-ai-01 pushed a commit that referenced this pull request Sep 23, 2026
…te son budget (#17275)

`wait_and_decide` est concu pour ne pas conclure trop tot : 90 polls de 30 s,
une canari de livraison, et meme une relecture a l'echeance ajoutee par #11751
parce qu'un timeout tombant entre deux polls fabriquait un faux FAIL. Et
pourtant une SEULE erreur d'API transitoire abandonnait les 90 polls :
`fetch_checks` appelle `_gh_api` une fois par page plus une fois pour `/status`,
aucun des deux ne retente, et la boucle n'entourait son fetch d'aucun `try`.

Quatre PRs (#17224, #17228, #17246, #17260) ont porte ce rouge le 2026-09-21
sans defaut de contenu : le gate n'a pas pu lire, et a rendu un FAIL
indiscernable d'un echec de check.

Correctif : un retry borne sur la lecture de la boucle, arme uniquement sur une
signature transitoire RECONNUE. La classification est volontairement
asymetrique -- toute erreur inconnue, et toute erreur permanente (404), reste
fatale immediatement. Le defaut d'une erreur non reconnue est donc « fatal »,
qui est le cote qui preserve la regle 1.

Deux bornes, et la regle 1 intacte : le retry s'arrete a la MEME echeance
absolue (--timeout-min n'est jamais etendu) et a un plafond d'echecs
CONSECUTIFS, remis a zero par une lecture reussie -- une API qui flappe n'est
pas une API qui a epuise son budget. Une lecture en echec ne touche PAS
quiet_streak : la compter comme un poll calme laisserait une panne faire
conclure PASS, la seule direction que la regle 1 interdit.

Mesures :
- falsification : retry retire -> 4 rouges (acceptance 1, acceptance 2, les deux
  invariants), 13 verts conserves (ce sont des pins, pas des discriminateurs) ;
  restaure -> 17 verts.
- non-regression : 251 verts sur la famille pr_gate complete.
- acceptance 3 mesuree deux fois : a l'echeance depassee ZERO retry n'est
  depense (1 appel) ; le chemin de retry lit l'echeance absolue, il ne la
  recalcule pas.
- diff : scripts/pr_gate.py +99/-1, 0 CRLF.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@jsboige
jsboige deleted the fix/tweety-stale-jar-generations branch October 16, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

variation-adjacency-deep-med Adjacence DEEP/MED hors LIGHT : §2 l'autorise si substance distincte (coordinateur)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants