imprimatur

Spike S1: external mode

Result: works on all three motivating toolchains. External mode stays the recommended default (DESIGN §6.3 A). Run on 2026-10-17.

Question

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).

What was built

A Lake package, built once per toolchain outside every host project, with two parts:

The notation file imports the host modules and SpikeLib, then uses latex_spike on a constructor and a theorem.

Results

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:

Findings that change the design or the plan

  1. One import, not two. Step 6 is redundant. The environment from elaborating the notation file already contains the host modules at 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.
  2. 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/.
  3. The frontend API is the same on all three toolchains for what external mode needs. Positional 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.
  4. Memory is the real cost. Elaborating a notation file that imports Mathlib-based modules peaks at about 7 GB and takes about 90 s on this WSL machine. The page cache was cold for the first run. Most of this is presumably loading Mathlib, which any tool that imports these modules pays. The spike didn’t time plain 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.
  5. 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).
  6. Matching the toolchain. Compare the host’s lean-toolchain with "leanprover/lean4:v" ++ Lean.versionString. Lean.versionStringCore drops the -rc1 suffix and must not be used for this.

Not tested

The spike code is not kept in the repository (PLAN §3).