diff --git a/MyIA.AI.Notebooks/QuantConnect/requirements.txt b/MyIA.AI.Notebooks/QuantConnect/requirements.txt index 7645621ec3..1b4fbe78b7 100644 --- a/MyIA.AI.Notebooks/QuantConnect/requirements.txt +++ b/MyIA.AI.Notebooks/QuantConnect/requirements.txt @@ -154,6 +154,18 @@ cloudpickle>=3.0.0 # - Requires Docker Desktop running # # Version Compatibility: -# - Python 3.11+ recommended +# - Python 3.11 recommended (canon #19181, aligne sur QuantConnect cloud) # - Compatible with QuantConnect cloud (Python 3.11) +# - L'execution locale des carnets se fait en Python 3.13 (l'interpreteur +# de la flotte, qui ecrit ``language_info.version`` a chaque rejeu). +# Deux canons par design, pas un seul. +# - La table CANONICAL_LANGUAGE_TRANSITIONS du cliquet +# ``scripts/notebook_tools/check_kernel_drift.py`` accepte les +# transitions 3.10 -> 3.11 (migration historique vers QC Cloud), +# 3.11 -> 3.13 (convergence vers l'interpreteur local), le saut +# direct 3.10 -> 3.13 (cas fondateur #19181, PR #19163 : 3.10.11 +# -> 3.13.3), et 3.12 -> 3.13 (anticipation de convergence, pas de +# carnet `python3` a 3.12 mesuré a l'instant -- ICT-47 sur main est +# a `py310-gpu` 3.12.13, kernelspec distinct de `python3`, donc hors +# scope). La direction inverse reste rouge. # - NumPy < 2.0 for compatibility with some libraries diff --git a/scripts/notebook_tools/check_kernel_drift.py b/scripts/notebook_tools/check_kernel_drift.py index a65c263040..1ae3d36d1f 100644 --- a/scripts/notebook_tools/check_kernel_drift.py +++ b/scripts/notebook_tools/check_kernel_drift.py @@ -8,10 +8,10 @@ - Kernel change: ``metadata.kernelspec.name`` or ``metadata.language_info.version`` differs at major.minor level - (Python 3.11 -> 3.13). Patch-only drift (3.13.3 -> 3.13.15) is NOT - flagged: the venv patch evolves under the canonical interpreter and - never changes repr() semantics (#17371). Outputs may format - ``repr(np.float64(0.9999999999999999))`` instead + (Python 3.10.11 -> 3.13.3, 3.11 -> 3.13). Patch-only drift (3.13.3 -> + 3.13.15) is NOT flagged: the venv patch evolves under the canonical + interpreter and never changes repr() semantics (#17371). Outputs may + format ``repr(np.float64(0.9999999999999999))`` instead of ``[1.0, 1.0, ...]`` even when the cell computes the same values. - Float format drift: NumPy 1.x prints ``[1.0, 1.0, 1.0]``; NumPy 2.x prints ``[1.0, 0.9999999999999999, 1.0]``. The values are within @@ -221,12 +221,55 @@ def diff_kernel(base_info, head_info): # coordinateur 2026-09-26) : la derive va dans le sens du canon decide -- # ce n'est pas une regression, pas plus qu'une derive de patch (#17371). # La DIRECTION porte l'acceptation : la transition inverse reste rouge. -# Portee au kernel .NET C# mesure dans le depot (kernelspec -# ``.net-csharp`` : 141 notebooks a 13.0, 7 a 12.0 au 2026-09-27) -- la -# classe fondatrice Python 3.11 -> 3.13 reste hors table. +# Portee mesuree au 2026-10-05 : +# - C# ``.net-csharp`` : 141 notebooks a 13.0, 7 a 12.0 (#17679). +# - Python ``python3`` (serie QC/Python) : 11 versions heterogenes +# (3.8.10, 3.9.0, 3.10.0, 3.10.11, 3.10.19, 3.11.0, 3.11.9, 3.11.14, +# 3.11.15, 3.13.3, 3.13.7, 3.13.12, 3.13.14) ; 60 carnets ``python3`` + 2 +# ``conda-torch``. +# - Decision duale sur le canon (coord. 2026-10-05T03:43:46Z, DM +# msg-20261005T034346-qyldsl) : **deux canons**, pas un seul -- +# (a) **canon des algorithmes deployes sur QC Cloud = Python 3.11**, +# documente dans ``MyIA.AI.Notebooks/QuantConnect/requirements.txt`` ; +# (b) **canon de l'execution locale des carnets = Python 3.13**, qui est +# l'interpreteur de la flotte -- c'est lui qui ecrit +# ``language_info.version`` a chaque rejeu. +# Les deux roles sont separes par design ; la migration historique +# ``3.10 -> 3.11`` reste couverte, et la convergence future +# ``3.11 -> 3.13`` aussi, plus le saut direct ``3.10 -> 3.13`` +# (cas fondateur de #19181, PR #19163 : 3.10.11 -> 3.13.3). +# Le saut ``3.12 -> 3.13`` est couvert : ICT-47 (PainAxisDistillation) +# sur main est a ``language_info.version = 3.12.13`` (mesure directe +# sur le carnet, 2026-10-05), mais son kernelspec est ``py310-gpu`` +# (distinct de ``python3``), donc le tuple ``("python3", "3.12", "3.13")`` +# ne s'applique pas a ce carnet -- la table exige le meme nom de +# kernel entre base et tete. L'entree anticipe la convergence vers +# le canon pour un futur carnet ``python3`` a 3.12.x ; aucun carnet +# de cette forme n'est encore mesuré sur main. Le saut +# ``3.11 -> 3.12`` reste implicite (3.12 = release courante de +# plusieurs carnets), mais n'est pas ajoute en l'absence d'un carnet +# de reference ``python3`` qui le pratique. +# Les sauts ``3.8 -> 3.13`` et ``3.9 -> 3.13`` sont **exclus** : la +# serie ICT (#5635, ICT-24) reste sur ``requires-python >=3.9,<3.10`` +# (contrainte ``pyphi==1.2.0``, mesure #19160), et le cliquet n'a +# pas de scope par chemin/série -- ajouter ces transitions ferait +# passer vert un carnet ICT rejoue par erreur sous 3.13 (reserve +# Hermes PRR_kwDOH2Odns8AAAABQnPfEQ, 2026-10-05). Les 2 rescapes +# ``3.8.10`` et ``3.9.0`` (1 carnet chacun, mesure body) sont +# traites au cas par cas. +# La transition inverse (3.13 -> 3.12, 3.13 -> 3.11, 3.11 -> 3.10, +# 3.13 -> 3.10) reste rouge. CANONICAL_LANGUAGE_TRANSITIONS = { (".net-csharp", "12.0", "13.0"): "C# 12.0 -> 13.0 : convergence vers le canon C# 13.0 (#17679)", + ("python3", "3.10", "3.11"): + "Python 3.10 -> 3.11 : convergence vers le canon QC/Python 3.11 (#19181)", + ("python3", "3.11", "3.13"): + "Python 3.11 -> 3.13 : convergence vers le canon d'execution 3.13 (#19181)", + ("python3", "3.10", "3.13"): + "Python 3.10 -> 3.13 : convergence directe vers le canon d'execution 3.13 (#19181, cas fondateur)", + ("python3", "3.12", "3.13"): + "Python 3.12 -> 3.13 : convergence vers le canon d'execution 3.13 (#19181, anticipation -- pas de carnet `python3` a 3.12 mesure)", } diff --git a/scripts/notebook_tools/tests/test_check_kernel_drift.py b/scripts/notebook_tools/tests/test_check_kernel_drift.py index 6e9bb4b8b4..e86139ea5d 100644 --- a/scripts/notebook_tools/tests/test_check_kernel_drift.py +++ b/scripts/notebook_tools/tests/test_check_kernel_drift.py @@ -317,11 +317,125 @@ def test_canonical_transition_kernelspec_change_refused(): assert ckd.accepted_canonical_transition(a, b) is False -def test_canonical_transition_python_minor_stays_out(): - # La classe fondatrice (Python 3.11 -> 3.13) reste hors table : la - # table est scoped au kernel, pas au couple de numeros. +# --- #19181 : table d'acceptation canon Python (QC/Python) ----------------- +# Mesure du 2026-10-05 sur la serie QC/Python : 11 versions heterogenes +# (3.8.10 a 3.13.14) ; 60 carnets ``python3`` + 2 ``conda-torch``. +# Decision duale coord. (2026-10-05T03:43:46Z, DM +# msg-20261005T034346-qyldsl) : +# - canon des algorithmes deployes sur QC Cloud = Python 3.11 +# (documented in ``MyIA.AI.Notebooks/QuantConnect/requirements.txt``) ; +# - canon de l'execution locale des carnets = Python 3.13 (l'interpreteur +# de la flotte qui ecrit ``language_info.version`` a chaque rejeu). +# Transitions acceptees en direction du canon : 3.10 -> 3.11 (historique +# QC Cloud), 3.11 -> 3.13 (convergence locale), 3.10 -> 3.13 (cas +# fondateur de #19181, PR #19163 : 3.10.11 -> 3.13.3), et 3.12 -> 3.13 +# (anticipation de convergence ; ICT-47 sur main est a `py310-gpu` +# 3.12.13, kernelspec distinct de `python3`, donc hors scope de ce +# tuple -- l'entree anticipe un futur carnet `python3` a 3.12). La +# direction inverse reste rouge. + + +def test_canonical_transition_python_311_to_313_accepted(): + # Positif 1 : la convergence vers Python 3.13 (canon d'execution) est + # couverte. a = {"language_version": "3.11.16", "kernelspec_name": "python3"} b = {"language_version": "3.13.3", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is True + # la derive brute existe bien -- c'est la table qui l'accepte. + assert ckd.diff_kernel(a, b) + + +def test_canonical_transition_python_310_to_311_accepted(): + # Positif 2 : la migration historique vers le canon QC Cloud 3.11 est + # couverte. + a = {"language_version": "3.10.19", "kernelspec_name": "python3"} + b = {"language_version": "3.11.9", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is True + + +def test_canonical_transition_python_31011_to_3133_accepted(): + # Positif 3 (cas fondateur #19181, PR #19163) : la convergence directe + # 3.10.11 -> 3.13.3 est couverte. La flotte rejette les carnets en + # 3.13 (interpreteur local), donc cette transition est le passage + # reel ; les transitions via 3.11 sont possibles mais ne se + # composent pas (cle tuple exact). + a = {"language_version": "3.10.11", "kernelspec_name": "python3"} + b = {"language_version": "3.13.3", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is True + + +def test_canonical_transition_python_38_to_313_refused(): + # Negatif 5 : 3.8 -> 3.13 reste rouge par construction. Le saut 3.8 + # n'est pas couvert par la table (serie ICT pinnée <3.10, pyphi==1.2.0, + # mesure #19160 ; sans scope par chemin/série, ajouter la transition + # ferait passer vert un carnet ICT rejoue par erreur -- reserve Hermes + # PRR_kwDOH2Odns8AAAABQnPfEQ, 2026-10-05). Les 2 rescapes 3.8.10/3.9.0 + # sont traites au cas par cas. + a = {"language_version": "3.8.10", "kernelspec_name": "python3"} + b = {"language_version": "3.13.3", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_39_to_313_refused(): + # Negatif 6 : 3.9 -> 3.13 reste rouge (meme raison que 3.8). + a = {"language_version": "3.9.0", "kernelspec_name": "python3"} + b = {"language_version": "3.13.3", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_313_to_311_refused(): + # Negatif 1 : la direction inverse est une regression du canon. + a = {"language_version": "3.13.3", "kernelspec_name": "python3"} + b = {"language_version": "3.11.16", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_311_to_310_refused(): + # Negatif 2 : retombada sur 3.10 apres adoption du canon 3.11. + a = {"language_version": "3.11.9", "kernelspec_name": "python3"} + b = {"language_version": "3.10.19", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_313_to_38_refused(): + # Negatif 3 : retomber de 3.13 a 3.8 reste rouge, meme si 3.8 -> 3.13 + # est accepte dans l'autre sens. + a = {"language_version": "3.13.3", "kernelspec_name": "python3"} + b = {"language_version": "3.8.10", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_31213_to_3133_accepted(): + # Positif : la transition 3.12 -> 3.13 est couverte. Anticipation : + # aucun carnet main n'exerce aujourd'hui le tuple (`python3`, `3.12`, + # `3.13`) — le seul carnet a 3.12 mesure est ICT-47 + # (PainAxisDistillation), dont le kernelspec est `py310-gpu` (distinct + # de `python3`), donc hors scope de ce tuple (le cliquet exige le meme + # nom de kernel entre base et tete). L'entree est ajoutee pour + # preparer la convergence d'un futur carnet `python3` a 3.12.x. Le + # temoin effectif du test est le tuple synthetique ci-dessous, pas + # ICT-47. Ajout a la demande du coordinateur (commentaire 5988053521, + # 2026-10-05T04:22Z), oubli du dispatch initial qui ne listait pas + # 3.12 dans les couverts. + a = {"language_version": "3.12.13", "kernelspec_name": "python3"} + b = {"language_version": "3.13.3", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is True + + +def test_canonical_transition_python_313_to_312_refused(): + # Negatif : la direction inverse 3.13 -> 3.12 reste rouge (n'a pas + # ete ajoutee a la table ; convention : la table n'accepte que les + # convergences vers le canon, pas la descente). + a = {"language_version": "3.13.3", "kernelspec_name": "python3"} + b = {"language_version": "3.12.13", "kernelspec_name": "python3"} + assert ckd.accepted_canonical_transition(a, b) is False + + +def test_canonical_transition_python_kernelspec_change_refused(): + # Negatif 4 : un changement de kernelspec.name reste rouge, meme avec + # des versions couvertes par la table. + a = {"language_version": "3.11.9", "kernelspec_name": "python3"} + b = {"language_version": "3.13.3", "kernelspec_name": "python311"} assert ckd.accepted_canonical_transition(a, b) is False