Result: works on all three motivating toolchains. External mode stays the recommended default (DESIGN §6.3 A). Run on 2026-10-17.
Can a prebuilt extractor combine its own .olean files with a host’s
lake env search path, elaborate a notation file in-process with Lean’s
frontend, and then see the host’s modules at private level? This must work on
v4.28.0-rc1, v4.31.0 and v4.34.1 (PLAN §3).
A Lake package, built once per toolchain outside every host project, with two parts:
SpikeLib, standing in for Imprimatur. It registers an environment
extension with initialize, and defines a command
latex_spike <ident> => "<str>". The command resolves the identifier with
realizeGlobalConstNoOverloadWithInfo, so a misspelt name is an error
(DESIGN §4, constraint 3), and records it in the extension.runner, an executable (supportInterpreter = true) that takes a host
directory, a notation file, a host module and a declaration, and then:
lean-toolchain matches Lean.versionString;lake env printenv LEAN_PATH in the host directory (elan picks the
host’s toolchain from there), and appends its own
IO.appDir / ".." / "lib" / "lean";initSearchPath (← findSysroot) path and
enableInitializersExecution;Lean.Elab.runFrontend input {} fileName Notation`;collectAxioms on it;importModules … (loadExts := true) (level := .private) and repeats
step 5.The notation file imports the host modules and SpikeLib, then uses
latex_spike on a constructor and a theorem.
| Host | Toolchain | Notation file | Steps 1–5 | Step 6 | Wall time | Peak RSS |
|---|---|---|---|---|---|---|
llmbda deep/ (LambdaCalculus.Semantics.BigStepProofs) |
v4.28.0-rc1 | 11 s | ✓ | ✓ | 27 s | 4.1 GB |
PLanet (Planet.Paper, Mathlib) |
v4.31.0 | 92 s | ✓ | ✓ after fix below | 2 min 24 s | 11.4 GB |
math catalogue (…Uniformization.RiemannDiskSeed, a module file, Mathlib) |
v4.34.1 | 95 s | ✓ | ✓ after fix below | 3 min 11 s | 10.8 GB |
In every run:
.olean files and compiled elaborators combine with the host’s search path;collectAxioms
returned [propext, Classical.choice, Quot.sound]. For the math catalogue the
target is in a module file, so this confirms that a non-module notation
file sees private data (DESIGN §6.2);git status unchanged in all
three).private level. Importing them again doubled peak memory, from about 6.5–7
to about 11 GB on the Mathlib hosts. The extractor should take its
environment from the notation file, or from a generated one when there is no
notation file, and never import twice.runFrontend turns initializer execution off again on v4.31.0 and
v4.34.1, but not on v4.28.0-rc1. Calling importModules (loadExts := true)
afterwards fails with “enableInitializersExecution must be run before
calling importModules (loadExts := true)”. Any later import must call
enableInitializersExecution again. This belongs in Compat/.runFrontend input opts fileName mainModuleName
returns IO (Option Environment) everywhere; later versions only add
optional parameters. importModules has loadExts and
level := .private on all three, which also confirms the Appendix A claim
for v4.28 and v4.31.lake env lean on the same file for
comparison. README should state the memory needed. Running two Mathlib
hosts in parallel on a 16 GB machine isn’t safe.String.trim is already deprecated in v4.28.0-rc1 (replaced by
String.trimAscii). This is another API to avoid in code that must build
across the toolchain range (see CONTRIBUTING.md).lean-toolchain with
"leanprover/lean4:v" ++ Lean.versionString. Lean.versionStringCore drops
the -rc1 suffix and must not be used for this.@[latex_view] functions, which would need to
be compiled, or interpreted with evalConst. Sidecar mode remains the
answer for those (DESIGN §6.3 B).precompileModules or native plugins.The spike code is not kept in the repository (PLAN §3).