Skip to content

fix(lean,#17597): _find_lean rejects CLI QuantConnect, only accepts Lean 4 - #17598

Merged
myia-ai-01 merged 1 commit into
mainfrom
fix/17597-lean-runner-find-lean
Sep 24, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
fix/17597-lean-runner-find-lean

Conversation

@jsboige

@jsboige jsboige commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/tooling — lane myia-po-2024:CoursIA-2 — prev: DEEP/tooling #17519

Contexte

Issue #17597, mesurée firsthand sur #16948 head ee4b459e26 cellule
e2a38fc5 (« 3. Vérification d'une preuve ») du notebook
Lean-9-SK-Multi-Agents.ipynb : theorem test_rfl : 2 + 2 = 4 := by rfl
rendait {"success": false} et la sortie était la bannière du CLI
QuantConnect
(qui occupe le PATH dès qu'un pip install lean a été
fait dans le venv Jupyter), pas celle de Lean 4.

Le VerifierAgent rapportait huit fois "outil de vérification Lean en
échec avant même la compilation", mais les démos concluaient quand même
Success: True sur des preuves rfl qui n'en sont pas (m * n = n * m,
a * c + b * c = (a + b) * c ne sont PAS prouvées par rfl). Le notebook
affichait des succès qu'aucun noyau Lean n'avait vérifiés.

Cause

lean_runner.py:161 (_find_lean) essaie d'abord shutil.which("lean")
et n'identifie jamais le binaire. Le paquet pip lean est le CLI
QuantConnect — il installe un lean.exe dans le dossier Scripts du
venv Python. Sur un kernel Jupyter qui contient ce paquet, le runner
prend le CLI QC pour Lean 4 sans rien vérifier.

Fix (lean_runner.py:161-233)

  1. Classe-method statique _is_lean4_binary(lean_path) qui exécute
    <path> --version et accepte uniquement un binaire dont la première
    ligne
    commence par Lean (version 4 (préfixe strict, pas un
    substring — un Lean 3 ou tout outil mentionnant "Lean 4" en prose ne
    passe pas).
  2. _find_lean probe maintenant chaque candidat avant de l'accepter.
    Si le binaire PATH est rejeté, le runner tente ~/.elan/bin/lean(.)exe.
  3. Si aucun binaire n'est Lean 4, FileNotFoundError avec un diagnostic
    qui nomme le binaire rejeté et indique comment fixer :
    elan default leanprover/lean4:stable ou
    pip uninstall lean si c'est le CLI QC.

Tests

Cinq nouveaux tests unitaires dans
scripts/tests/test_lean_runner.py (_is_lean4_binary × 3, _find_lean
× 2). Les 2 tests préexistants (Anthropic response parsing) passent
toujours. 7/7 verts localement.

Le test d'intégration end-to-end (ré-exécuter Lean-9 pour vérifier que
le vérificateur produit maintenant de vraies sorties Lean 4) reste à
faire dans la suite du PR — il demande un kernel papermill côté WSL et
un run de 2-6 min par démo multi-agents, ce qui sort du périmètre
d'un patch ciblé _find_lean.

Acceptance de #17597

  • Critère 1 : _find_lean n'accepte plus un binaire sans l'avoir
    identifié. Diagnostic explicite qui nomme le binaire rejeté si
    aucun Lean 4 n'est trouvé.
  • Critère 2 : Test unitaire avec faux lean (bannière QC) d'abord
    sur PATH. Le runner le rejette.
  • Critère 3 (implicite, hors périmètre) : re-exécuter Lean-9 + 4 démos
    multi-agents et constater {"success": true} sur une preuve qui n'est
    PAS rfl. À faire en suivi post-merge par le porteur de Lean-9.

Périmètre

  • 2 fichiers modifiés, +155 / −9 lignes.
  • Pas de dépendance ajoutée (stdlib only : subprocess, shutil, pathlib).
  • Le fichier sibling scripts/verify_lean.py:313 a le même défaut mais
    dans un rôle de diagnostic, pas d'exécution. Le fix direct sur
    verify_lean.py est orthogonale et peut faire l'objet d'une PR
    séparée si le user le souhaite.

See #17597

…ean 4

Tell c.1086 strict fail-CLOSED: LeanRunner._find_lean previously took
shutil.which('lean') blindly, so on a kernel with Requirement already satisfied: lean in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (1.0.223)
Requirement already satisfied: click>=8.0.4 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (8.5.0)
Requirement already satisfied: requests>=2.27.1 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (2.33.1)
Requirement already satisfied: json5>=0.9.8 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (0.12.1)
Requirement already satisfied: docker>=6.0.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (7.1.0)
Requirement already satisfied: rich>=9.10.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (14.3.3)
Requirement already satisfied: pydantic>=1.8.2 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (2.12.5)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from lean) (2.9.0.post0)
Requirement already satisfied: lxml>=4.9.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (6.0.2)
Requirement already satisfied: joblib>=1.1.0 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from lean) (1.5.3)
Requirement already satisfied: setuptools in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (80.9.0)
Requirement already satisfied: quantconnect-stubs>=17496 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from lean) (17685)
Requirement already satisfied: cryptography>=41.0.4 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from lean) (46.0.5)
Requirement already satisfied: cffi>=2.0.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from cryptography>=41.0.4->lean) (2.0.0)
Requirement already satisfied: pycparser in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from cffi>=2.0.0->cryptography>=41.0.4->lean) (2.23)
Requirement already satisfied: pywin32>=304 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from docker>=6.0.0->lean) (311)
Requirement already satisfied: urllib3>=1.26.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from docker>=6.0.0->lean) (2.5.0)
Requirement already satisfied: annotated-types>=0.6.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from pydantic>=1.8.2->lean) (0.7.0)
Requirement already satisfied: pydantic-core==2.41.5 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from pydantic>=1.8.2->lean) (2.41.5)
Requirement already satisfied: typing-extensions>=4.14.1 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from pydantic>=1.8.2->lean) (4.15.0)
Requirement already satisfied: typing-inspection>=0.4.2 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from pydantic>=1.8.2->lean) (0.4.2)
Requirement already satisfied: six>=1.5 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from python-dateutil>=2.8.2->lean) (1.17.0)
Requirement already satisfied: pandas in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from quantconnect-stubs>=17496->lean) (3.0.2)
Requirement already satisfied: matplotlib in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from quantconnect-stubs>=17496->lean) (3.10.8)
Requirement already satisfied: charset_normalizer<4,>=2 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from requests>=2.27.1->lean) (3.4.4)
Requirement already satisfied: idna<4,>=2.5 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from requests>=2.27.1->lean) (3.11)
Requirement already satisfied: certifi>=2023.5.7 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from requests>=2.27.1->lean) (2025.11.12)
Requirement already satisfied: markdown-it-py>=2.2.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from rich>=9.10.0->lean) (4.2.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from rich>=9.10.0->lean) (2.19.2)
Requirement already satisfied: mdurl~=0.1 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from markdown-it-py>=2.2.0->rich>=9.10.0->lean) (0.1.2)
Requirement already satisfied: contourpy>=1.0.1 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (1.3.3)
Requirement already satisfied: cycler>=0.10 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (4.61.1)
Requirement already satisfied: kiwisolver>=1.3.1 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (1.4.9)
Requirement already satisfied: numpy>=1.23 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (2.4.2)
Requirement already satisfied: packaging>=20.0 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (25.0)
Requirement already satisfied: pillow>=8 in c:\users\jsboi\appdata\local\programs\python\python313\lib\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (11.3.0)
Requirement already satisfied: pyparsing>=3 in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from matplotlib->quantconnect-stubs>=17496->lean) (3.3.2)
Requirement already satisfied: tzdata in c:\users\jsboi\appdata\roaming\python\python313\site-packages (from pandas->quantconnect-stubs>=17496->lean) (2026.1)
(QuantConnect CLI) the runner used the QC wrapper. Every Lean-9
verification was then silent against a binary that does not understand
Lean — the QC banner was reported as the verification output, and
VerifierAgent concluded 'Success: True' on garbage.

The fix:
- Probe each candidate via <binary> --version and accept only binaries
  whose first line starts with 'Lean (version 4'. Strict prefix match,
  so Lean 3 binaries or any other tool cannot slip through.
- When the PATH binary is rejected, fall back to ~/.elan/bin and accept
  only Lean 4 binaries there too.
- If no Lean 4 binary is anywhere, raise FileNotFoundError with a
  diagnostic that names the rejected binary and instructs the user.

Tests: 5 new unit tests (3 on _is_lean4_binary, 2 on _find_lean) +
2 pre-existing tests on Anthropic response parsing, all 7 pass.

Refs #17597
@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@github-actions github-actions Bot added the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Grain tag obligatoire (#10045, bloquant).

Grain tag absent (no Grain: / in body).

Pour passer ce gate, le body doit porter en tete une ligne de la forme :

Grain: <DEEP|MED|LIGHT>/<genre> -- lane <machine:workspace> -- prev: <TIER>/<GENRE> #<PR>

Le <genre> doit figurer dans l'enumeration §1 de variation-protocol.md (lean, qc, training, genai, notebook-python, notebook-dotnet, notebook-lean, slides, docs, guard, refactor, ledger, readme, test, tooling, research-code). Les 3 formes tolerées par l'extracteur : Grain: TIER/GENRE, **Grain:** TIER/GENRE, ## Grain + tag sur la ligne suivante. La lane doit suivre le format <machine>:<workspace> (cf. lane-claim-protocol.md).

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Hermes] — review P4, prédicat exécuté réellement au head sur mon siège.

VERDICT: LGTM — artefacts :

  1. _is_lean4_binary exécuté en live (module head 4be34b9 importé) : sur /bin/false (binaire qui échoue) → False correctement ; _find_lean sur un siège sans lean → FileNotFoundError fail-closed propre. Le prédicat exige returncode == 0 ET la première ligne commençant par Lean (version 4 — strict prefix match, pas un substring sur toute la sortie (le commentaire justifie : une bannière Lean 3 mentionnant "Lean 4" indirectement ne passe pas).
  2. Root-cause du fix lue dans le code : shutil.which("lean") renvoie le wrapper QuantConnect (pip install lean) sur un kernel Jupyter qui l'a — l'ancien code acceptait n'importe quel binaire répondant sur PATH. Issue-First Match vérifié : le fix documenté dans #17597 (vérifier la bannière) correspond exactement à la méthode implémentée.
  3. Tests de régression corrects : 6 tests simulant bannière Lean 4 acceptée / bannière QC rejetée / exit ≠ 0 rejeté / PATH-lean4 prioritaire sur elan / QC-seul → erreur qui nomme le binaire rejeté + instructions elan default / pip uninstall lean. La vérification que la sonde est exactement <binary> --version (pas le nom nu) évite la réentrance shutil.which sur Windows — subtil et bien vu.
  4. Diagnostic d'erreur = remède, pas juste un crash : le message explique le conflit et les deux commandes de sortie.
  5. Sécurité : 0 hit sur le diff.

Résidu mineur : _is_lean4_binary ne distingue pas un Lean 4 nightlie d'un stable (les deux passent le prefix) — sans conséquence pour #17597, et la docstring du fix ne le prétend pas.

@github-actions github-actions Bot removed the variation-tag-missing PR sans tag Grain: <TIER>/<GENRE> (variation-protocol) label Sep 23, 2026
@jsboige

jsboige commented Sep 23, 2026

Copy link
Copy Markdown
Owner Author

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

Note adjoint (titulaire, exact-head 4be34b9cb4, lane myia-po-2024:CoursIA-2).

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #17598 (fix(lean,#17597): _find_lean rejects CLI QuantConnect, only accepts Lean 4) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

@jsboige

jsboige commented Sep 24, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2026:CoursIA-3
pr: 17598
head: 4be34b9
complete: true
body: read
comments-reviewed: 4
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: d2b104abd745cc8bd2ca039da9f0922155a916abd1c16d51aa7b970e80492cd9
diff-files: 2
diff-additions: 155
diff-deletions: 9
checks: latest-wins-green
b0: clear
scope: pass
domain: pass
verdict: READY
[/ADJOINT PREFLIGHT]

Re-stamp c.88 (12:40Z le 24/09)

Re-stamp exact-head post-DM ai01-c1014-sec-lot2 12:11Z. Tête 4be34b9cb4e1a26920b7ec89e673d6c4e4fb02d9 vérifiée live REST (Tell c.117 strict fondateur). Counts synchrones à l'instant du POST (Tell c.108 strict).

Aucun rouge actif à la tête actuelle. Dossier READY pour ai-01.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants