lean-runtime 4.23.0__tar.gz → 4.24.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/CHANGELOG.md +13 -0
  2. {lean_runtime-4.23.0/lean_runtime.egg-info → lean_runtime-4.24.0}/PKG-INFO +1 -1
  3. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/cli.py +30 -2
  4. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/environments.py +17 -4
  5. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/health.py +15 -0
  6. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/lake_cache.py +2 -2
  7. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/oci.py +18 -1
  8. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/project_execution.py +7 -1
  9. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/project_sharing.py +36 -1
  10. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/projects.py +1 -1
  11. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/references.py +1 -1
  12. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/resolver.py +1 -1
  13. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/runtime.py +25 -21
  14. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/shared_projects.py +125 -13
  15. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/store.py +58 -0
  16. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/toolchain_oci.py +11 -1
  17. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/toolchains.py +68 -17
  18. {lean_runtime-4.23.0 → lean_runtime-4.24.0/lean_runtime.egg-info}/PKG-INFO +1 -1
  19. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/pyproject.toml +1 -1
  20. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/cleanup-v1.schema.json +2 -1
  21. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_bundles.py +45 -2
  22. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_events_health.py +1 -0
  23. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_header_cache.py +4 -1
  24. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_interactive.py +3 -0
  25. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_lake_cache.py +9 -0
  26. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_projects.py +124 -14
  27. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_references.py +3 -0
  28. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_runtime.py +3 -0
  29. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_store.py +25 -0
  30. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_toolchain_oci.py +3 -0
  31. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_toolchain_slim.py +2 -9
  32. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_toolchains.py +74 -1
  33. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/LICENSE +0 -0
  34. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/MANIFEST.in +0 -0
  35. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/README.md +0 -0
  36. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/environments.toml +0 -0
  37. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/core-v4.32.2.lock.json +0 -0
  38. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/leancert-v4.30.0.5.lock.json +0 -0
  39. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/leancert-v4.31.0.lock.json +0 -0
  40. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/leancert-v4.32.2.4.lock.json +0 -0
  41. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/leancert-v4.33.0.lock.json +0 -0
  42. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/mathlib-v4.30.0.lock.json +0 -0
  43. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/mathlib-v4.31.0.lock.json +0 -0
  44. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/mathlib-v4.32.2.lock.json +0 -0
  45. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/mathlib-v4.33.0.lock.json +0 -0
  46. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/catalog/locks/mathlib-v4.33.1.lock.json +0 -0
  47. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/README.md +0 -0
  48. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/lake-artifact-cache-4.33.0.json +0 -0
  49. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/mathlib-4.32.2.json +0 -0
  50. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/mathlib-4.32.2.toml +0 -0
  51. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/mathlib-4.33.0.json +0 -0
  52. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/compatibility/mathlib-4.33.0.toml +0 -0
  53. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/concepts/context-selection.md +0 -0
  54. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/index.md +0 -0
  55. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/reference/commands.md +0 -0
  56. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/reference/python-advanced.md +0 -0
  57. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/reference/python-api.md +0 -0
  58. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/reference/storage.md +0 -0
  59. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/trust.md +0 -0
  60. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/tutorial.md +0 -0
  61. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/workflows/check-files.md +0 -0
  62. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/docs/workflows/lake-projects.md +0 -0
  63. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/examples/mathlib.toml +0 -0
  64. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/__init__.py +0 -0
  65. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/__main__.py +0 -0
  66. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/_git.py +0 -0
  67. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/_paths.py +0 -0
  68. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/backends.py +0 -0
  69. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/bundles.py +0 -0
  70. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/capsules.py +0 -0
  71. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/comparison.py +0 -0
  72. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/console.py +0 -0
  73. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/context_resolution.py +0 -0
  74. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/decisions.py +0 -0
  75. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/declaration_index.py +0 -0
  76. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/declaration_index_build.py +0 -0
  77. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/declaration_index_oci.py +0 -0
  78. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/diagnostics.py +0 -0
  79. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/__init__.py +0 -0
  80. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/analyzer.py +0 -0
  81. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/api.py +0 -0
  82. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/candidate.py +0 -0
  83. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/catalog.py +0 -0
  84. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/catalog_build.py +0 -0
  85. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/catalog_cli.py +0 -0
  86. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/catalog_manifest.py +0 -0
  87. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/data/catalog.json +0 -0
  88. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/defaults.py +0 -0
  89. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/engine.py +0 -0
  90. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/errors.py +0 -0
  91. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/history.py +0 -0
  92. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/memo.py +0 -0
  93. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/module_inventory.py +0 -0
  94. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/planner.py +0 -0
  95. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/policy.py +0 -0
  96. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/probe.py +0 -0
  97. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/py.typed +0 -0
  98. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/result.py +0 -0
  99. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/schema_resources.py +0 -0
  100. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/schemas/catalog-v1.schema.json +0 -0
  101. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/schemas/plan-v1.schema.json +0 -0
  102. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/discovery/schemas/result-v1.schema.json +0 -0
  103. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/errors.py +0 -0
  104. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/events.py +0 -0
  105. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/facade.py +0 -0
  106. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/frontmatter.py +0 -0
  107. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/header_cache.py +0 -0
  108. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/identifier_resolver.py +0 -0
  109. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/import_syntax.py +0 -0
  110. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/lake.py +0 -0
  111. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/lockfiles.py +0 -0
  112. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/locking.py +0 -0
  113. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/matrix.py +0 -0
  114. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/models.py +0 -0
  115. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/oci_protocol.py +0 -0
  116. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/packs.py +0 -0
  117. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/policies.py +0 -0
  118. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/profiling.py +0 -0
  119. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/programs.py +0 -0
  120. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/publisher_verification.py +0 -0
  121. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/py.typed +0 -0
  122. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/run_cli.py +0 -0
  123. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/schema_resources.py +0 -0
  124. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/serialization.py +0 -0
  125. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/specs.py +0 -0
  126. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/timings.py +0 -0
  127. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/toolchain_slim.py +0 -0
  128. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/verification.py +0 -0
  129. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime/wire.py +0 -0
  130. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime.egg-info/SOURCES.txt +0 -0
  131. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime.egg-info/dependency_links.txt +0 -0
  132. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime.egg-info/entry_points.txt +0 -0
  133. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime.egg-info/requires.txt +0 -0
  134. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/lean_runtime.egg-info/top_level.txt +0 -0
  135. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/mkdocs.yml +0 -0
  136. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/attestation-v1.schema.json +0 -0
  137. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/check-batch-v1.schema.json +0 -0
  138. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/comparison-v1.schema.json +0 -0
  139. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/execution-v1.schema.json +0 -0
  140. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/inspect-v1.schema.json +0 -0
  141. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/matrix-v1.schema.json +0 -0
  142. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/plan-v1.schema.json +0 -0
  143. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/profile-v1.schema.json +0 -0
  144. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/publication-v1.schema.json +0 -0
  145. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/schemas/verify-v1.schema.json +0 -0
  146. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/announcement_gate.py +0 -0
  147. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/catalog_update.py +0 -0
  148. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/lake_cache_gate.py +0 -0
  149. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/prepare_declaration_index.py +0 -0
  150. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/publish_declaration_index.py +0 -0
  151. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/registry_preflight.py +0 -0
  152. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/run_compatibility.py +0 -0
  153. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/run_v1_case_study.py +0 -0
  154. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/scripts/smoke_wheel.py +0 -0
  155. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/setup.cfg +0 -0
  156. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/conftest.py +0 -0
  157. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/e2e/test_real_discovery.py +0 -0
  158. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_analyzer.py +0 -0
  159. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_catalog.py +0 -0
  160. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_catalog_build.py +0 -0
  161. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_discovery_schema_resources.py +0 -0
  162. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_engine.py +0 -0
  163. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_memo.py +0 -0
  164. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_planner.py +0 -0
  165. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/discovery/test_policy.py +0 -0
  166. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_announcement_gate.py +0 -0
  167. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_backend.py +0 -0
  168. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_capsules.py +0 -0
  169. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_cli.py +0 -0
  170. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_console.py +0 -0
  171. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_context_resolution.py +0 -0
  172. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_declaration_index.py +0 -0
  173. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_diagnostics.py +0 -0
  174. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_environment_integration.py +0 -0
  175. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_facade.py +0 -0
  176. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_frontmatter.py +0 -0
  177. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_git.py +0 -0
  178. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_identifier_resolver.py +0 -0
  179. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_lockfiles.py +0 -0
  180. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_locking.py +0 -0
  181. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_models.py +0 -0
  182. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_multifile.py +0 -0
  183. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_oci_protocol.py +0 -0
  184. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_packs.py +0 -0
  185. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_paths.py +0 -0
  186. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_prebuilt_policy.py +0 -0
  187. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_programs.py +0 -0
  188. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_project_bootstrap.py +0 -0
  189. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_project_publish.py +0 -0
  190. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_run_cli.py +0 -0
  191. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_schema_resources.py +0 -0
  192. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_schemas.py +0 -0
  193. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_signatures.py +0 -0
  194. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_specs.py +0 -0
  195. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_v1_precision.py +0 -0
  196. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_verification_inventory.py +0 -0
  197. {lean_runtime-4.23.0 → lean_runtime-4.24.0}/tests/test_verification_probe.py +0 -0
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.24.0 - 2026-08-26
6
+
7
+ - Key shared Lake package artifacts by the exact installed Lean and Lake
8
+ executable digests. Existing `/1` package artifacts are source-only donors;
9
+ the first build of each project re-shares artifacts under the `/2` key.
10
+ - Let an exact registered dependency graph donate source across toolchain
11
+ versions, while stripping its build tree, and refresh existing project
12
+ attachments when their computed workspace identity changes.
13
+ - Report obsolete `/1` project artifacts in `doctor`, reclaim them through
14
+ `clean`, and refuse publishing moving toolchain spellings such as `master`
15
+ or undated `nightly`. Publication also fails early when its input is an
16
+ acquired sparse capsule rather than a source build.
17
+
5
18
  ## 4.23.0 - 2026-08-24
6
19
 
7
20
  ## 4.22.0 - 2026-08-23
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-runtime
3
- Version: 4.23.0
3
+ Version: 4.24.0
4
4
  Summary: Run Lean 4 proofs from Python or standalone files
5
5
  Author: Alejandro Radisic
6
6
  License-Expression: Apache-2.0
@@ -179,6 +179,7 @@ def _render_cleanup(
179
179
  environments: CleanupReport,
180
180
  downloads: DownloadCleanupReport | None,
181
181
  scratch: CleanupReport | None = None,
182
+ project_artifacts: CleanupReport | None = None,
182
183
  ) -> None:
183
184
  style = styler_for(sys.stdout)
184
185
  retained_note = style.dim(
@@ -220,6 +221,17 @@ def _render_cleanup(
220
221
  )
221
222
  else:
222
223
  print("No abandoned workspaces to remove.")
224
+ if project_artifacts is not None:
225
+ if project_artifacts.candidates:
226
+ anything = True
227
+ print(
228
+ style.bold(
229
+ f"Would remove {len(project_artifacts.candidates)} obsolete project "
230
+ f"artifact tree(s) · {format_byte_size(project_artifacts.candidate_bytes)}"
231
+ )
232
+ )
233
+ else:
234
+ print("No obsolete /1 project artifacts to remove.")
223
235
  print(retained_note)
224
236
  if anything:
225
237
  print()
@@ -228,10 +240,13 @@ def _render_cleanup(
228
240
  reclaimed = environments.reclaimed_bytes + (downloads.reclaimed_bytes if downloads else 0)
229
241
  if scratch is not None:
230
242
  reclaimed += scratch.reclaimed_bytes
243
+ if project_artifacts is not None:
244
+ reclaimed += project_artifacts.reclaimed_bytes
231
245
  removed = (
232
246
  len(environments.removed)
233
247
  + (len(downloads.removed) if downloads else 0)
234
248
  + (len(scratch.removed) if scratch else 0)
249
+ + (len(project_artifacts.removed) if project_artifacts else 0)
235
250
  )
236
251
  if removed:
237
252
  parts = [f"{len(environments.removed)} environment(s)"]
@@ -239,6 +254,8 @@ def _render_cleanup(
239
254
  parts.append(f"{len(downloads.removed)} cached download(s)")
240
255
  if scratch is not None:
241
256
  parts.append(f"{len(scratch.removed)} abandoned workspace(s)")
257
+ if project_artifacts is not None:
258
+ parts.append(f"{len(project_artifacts.removed)} obsolete project artifact tree(s)")
242
259
  print(
243
260
  style.green(f"Removed {' and '.join(parts)} · reclaimed {format_byte_size(reclaimed)}")
244
261
  )
@@ -2460,12 +2477,19 @@ def main(argv: list[str] | None = None) -> int:
2460
2477
  dry_run=True,
2461
2478
  minimum_age_seconds=min(args.minimum_age_hours * 3600, 3600),
2462
2479
  )
2480
+ preview_project_artifacts = runtime.clean_legacy_project_artifacts(dry_run=True)
2463
2481
  if not args.json:
2464
- _render_cleanup(cleanup_preview, preview_downloads, preview_scratch)
2482
+ _render_cleanup(
2483
+ cleanup_preview,
2484
+ preview_downloads,
2485
+ preview_scratch,
2486
+ preview_project_artifacts,
2487
+ )
2465
2488
  has_candidates = bool(
2466
2489
  cleanup_preview.candidates
2467
2490
  or (preview_downloads and preview_downloads.candidates)
2468
2491
  or preview_scratch.candidates
2492
+ or preview_project_artifacts.candidates
2469
2493
  )
2470
2494
  if has_candidates and not args.dry_run:
2471
2495
  execute_cleanup = _confirm("Remove these files?", yes=args.yes, json_mode=args.json)
@@ -2486,11 +2510,15 @@ def main(argv: list[str] | None = None) -> int:
2486
2510
  dry_run=not execute_cleanup,
2487
2511
  minimum_age_seconds=min(args.minimum_age_hours * 3600, 3600),
2488
2512
  )
2513
+ gc_project_artifacts = runtime.clean_legacy_project_artifacts(
2514
+ dry_run=not execute_cleanup
2515
+ )
2489
2516
  if args.json:
2490
2517
  gc_payload: dict[str, Any] = {
2491
2518
  "environments": gc_report.to_dict(),
2492
2519
  "downloaded_files": gc_downloads.to_dict() if gc_downloads else None,
2493
2520
  "scratch": gc_scratch.to_dict(),
2521
+ "project_artifacts": gc_project_artifacts.to_dict(),
2494
2522
  }
2495
2523
  _json(
2496
2524
  envelope(
@@ -2500,7 +2528,7 @@ def main(argv: list[str] | None = None) -> int:
2500
2528
  )
2501
2529
  )
2502
2530
  elif execute_cleanup:
2503
- _render_cleanup(gc_report, gc_downloads, gc_scratch)
2531
+ _render_cleanup(gc_report, gc_downloads, gc_scratch, gc_project_artifacts)
2504
2532
  return 0
2505
2533
  if args.command == "check":
2506
2534
  if args.across is not None:
@@ -766,7 +766,11 @@ class Environment:
766
766
  spawn(
767
767
  resolved_command,
768
768
  cwd=instance,
769
- environment=self.manager.toolchains.environment,
769
+ environment=(
770
+ self.manager.toolchains.environment_for(self.lock.toolchain)
771
+ if requested_command[0] == "lake"
772
+ else self.manager.toolchains.environment
773
+ ),
770
774
  policy=selected_policy,
771
775
  ),
772
776
  )
@@ -905,7 +909,12 @@ class Environment:
905
909
  preliminary: list[BackendResult] = []
906
910
  path_map = {str(instance / name): name for name in files}
907
911
  staging_started = time.monotonic()
908
- execution_environment = self.manager.toolchains.environment
912
+ execution_environment = (
913
+ self.manager.toolchains.environment_for(self.lock.toolchain)
914
+ if operation == "build"
915
+ or (requested_command and requested_command[0] == "lake")
916
+ else self.manager.toolchains.environment
917
+ )
909
918
  if operation == "check":
910
919
  assert entrypoint is not None
911
920
  for name, source in files.items():
@@ -1301,7 +1310,11 @@ class EnvironmentManager:
1301
1310
  result = self.backend.execute(
1302
1311
  command,
1303
1312
  cwd=workspace,
1304
- environment=self.toolchains.environment,
1313
+ environment=(
1314
+ self.toolchains.environment_for(lock.toolchain)
1315
+ if requested_command[0] == "lake"
1316
+ else self.toolchains.environment
1317
+ ),
1305
1318
  policy=build_policy,
1306
1319
  cancel=cancel,
1307
1320
  )
@@ -1327,7 +1340,7 @@ class EnvironmentManager:
1327
1340
  result = self.backend.execute(
1328
1341
  command,
1329
1342
  cwd=workspace,
1330
- environment=self.toolchains.environment,
1343
+ environment=self.toolchains.environment_for(lock.toolchain),
1331
1344
  policy=build_policy,
1332
1345
  cancel=cancel,
1333
1346
  )
@@ -107,6 +107,20 @@ def diagnose(toolchains: ToolchainManager, store: EnvironmentStore) -> DoctorRep
107
107
  )
108
108
  else:
109
109
  checks.append(DoctorCheck("scratch", "pass", "No abandoned workspaces"))
110
+ legacy_artifacts = store.clean_legacy_project_artifacts(dry_run=True)
111
+ if legacy_artifacts.candidates:
112
+ checks.append(
113
+ DoctorCheck(
114
+ "project-artifacts-v1",
115
+ "warning",
116
+ f"{len(legacy_artifacts.candidates)} package artifact tree(s) use the obsolete "
117
+ "toolchain-insensitive /1 key; reclaim them with `lean-runtime clean --yes`",
118
+ )
119
+ )
120
+ else:
121
+ checks.append(
122
+ DoctorCheck("project-artifacts-v1", "pass", "No obsolete /1 project artifacts")
123
+ )
110
124
  store_status = store.status()
111
125
  cutoff = datetime.now(timezone.utc).timestamp() - STALE_ENVIRONMENT_AGE_HOURS * 3600
112
126
  stale = tuple(
@@ -145,6 +159,7 @@ def repair(toolchains: ToolchainManager, store: EnvironmentStore) -> DoctorRepor
145
159
  include_legacy=True,
146
160
  legacy_minimum_age_seconds=LEGACY_SCRATCH_MINIMUM_AGE_SECONDS,
147
161
  )
162
+ store.clean_legacy_project_artifacts(dry_run=False)
148
163
  with suppress(ToolchainError):
149
164
  toolchains.elan_path(bootstrap=True)
150
165
  return diagnose(toolchains, store)
@@ -160,7 +160,7 @@ class LakeArtifactCache:
160
160
  try:
161
161
  process = subprocess.run(
162
162
  self.toolchains.command(toolchain, "lake", *arguments),
163
- env=self.toolchains.environment,
163
+ env=self.toolchains.environment_for(toolchain),
164
164
  text=True,
165
165
  stdout=subprocess.PIPE,
166
166
  stderr=subprocess.STDOUT,
@@ -183,7 +183,7 @@ class LakeArtifactCache:
183
183
 
184
184
  def environment(self, context: ProjectContext) -> dict[str, str]:
185
185
  """Return root-only cache settings without caching dependency packages."""
186
- environment = self.toolchains.environment
186
+ environment = self.toolchains.environment_for(context.toolchain)
187
187
  if not self.project_opted_in(context):
188
188
  return environment
189
189
  self.capabilities(context.toolchain)
@@ -28,7 +28,7 @@ from .bundles import (
28
28
  EnvironmentBundles,
29
29
  _capsule_config_object,
30
30
  )
31
- from .capsules import CapsuleManifest
31
+ from .capsules import CAPSULE_MANIFEST, CapsuleManifest
32
32
  from .errors import (
33
33
  CredentialAcquisitionError,
34
34
  DownloadLimitExceeded,
@@ -62,6 +62,7 @@ from .store import (
62
62
  platform_compatibility,
63
63
  platform_record,
64
64
  )
65
+ from .toolchains import immutable_toolchain_spelling
65
66
 
66
67
  _REPOSITORY = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*(?:/[a-z0-9]+(?:[._-][a-z0-9]+)*)+")
67
68
  _BEARER_PARAMETER = re.compile(r'([a-zA-Z]+)="([^"]*)"')
@@ -1458,6 +1459,22 @@ class OCIEnvironmentPublisher:
1458
1459
  ) -> PublicationInfo:
1459
1460
  if profile != "check-capsule":
1460
1461
  raise ValueError("publication profile must be 'check-capsule'")
1462
+ root = self.store.environment_path(environment_id)
1463
+ try:
1464
+ metadata = json.loads((root / "metadata.json").read_text(encoding="utf-8"))
1465
+ lock = self.store.load_lock(str(metadata["lock_id"]))
1466
+ except (OSError, KeyError, TypeError, json.JSONDecodeError) as exc:
1467
+ raise EnvironmentError("publication input has invalid environment metadata") from exc
1468
+ if (root / "workspace" / CAPSULE_MANIFEST).is_file():
1469
+ raise EnvironmentError(
1470
+ "publication input must be source-built; sparse acquired capsules cannot be "
1471
+ "republished"
1472
+ )
1473
+ if not immutable_toolchain_spelling(lock.toolchain):
1474
+ raise EnvironmentError(
1475
+ f"refusing to publish mutable toolchain spelling {lock.toolchain!r}; "
1476
+ "use an immutable release or dated nightly"
1477
+ )
1461
1478
  self.check_access()
1462
1479
  with tempfile.TemporaryDirectory(prefix="lean-runtime-publish-") as temporary:
1463
1480
  temporary_root = Path(temporary)
@@ -214,6 +214,7 @@ class ProjectExecutor:
214
214
  project=provenance,
215
215
  packages=context.package_provenance(),
216
216
  logical_command=("lake", "env", "lean", relative),
217
+ environment=self.runtime.toolchains.environment_for(context.toolchain),
217
218
  cancel=cancel,
218
219
  )
219
220
 
@@ -260,6 +261,7 @@ class ProjectExecutor:
260
261
  packages=context.package_provenance(),
261
262
  logical_command=("lake", "env", "lean", safe_filename),
262
263
  path_map={relative: safe_filename, str(source_path): safe_filename},
264
+ environment=self.runtime.toolchains.environment_for(context.toolchain),
263
265
  cancel=cancel,
264
266
  )
265
267
 
@@ -349,6 +351,10 @@ class ProjectExecutor:
349
351
  shared: bool | None = None,
350
352
  restore_artifacts: bool = False,
351
353
  ) -> ExecutionResult:
354
+ # Shared preparation may select compiled donors before Lake is invoked.
355
+ # Establish the exact full build toolchain first so the entire operation,
356
+ # including nested Lake/Lean subprocesses, has one toolchain boundary.
357
+ self.runtime.toolchains.ensure_full(context.toolchain, cancel=cancel)
352
358
  selected_shared = project_sharing_enabled(context.root) if shared is None else shared
353
359
  workspace = (
354
360
  self.runtime.shared_projects.prepare(context, cancel=cancel)
@@ -501,7 +507,7 @@ class ProjectExecutor:
501
507
  process = subprocess.run(
502
508
  command,
503
509
  cwd=context.root,
504
- env=self.runtime.toolchains.environment,
510
+ env=self.runtime.toolchains.environment_for(context.toolchain),
505
511
  text=True,
506
512
  stdout=subprocess.PIPE,
507
513
  stderr=subprocess.STDOUT,
@@ -465,6 +465,7 @@ def plan_adoption(
465
465
  entry=entry,
466
466
  source_package=source_package,
467
467
  effective_entries=effective_entries,
468
+ toolchain_identity=None,
468
469
  )
469
470
  key = sha256_id("project_package", identity)
470
471
  else:
@@ -558,6 +559,41 @@ class ProjectAdopter:
558
559
  lake_dir = context.root / ".lake"
559
560
  lake_dir.mkdir(parents=True, exist_ok=True)
560
561
  packages_dir = _packages_directory(context)
562
+ marker = lake_dir / ATTACHMENT_RECORD
563
+ try:
564
+ attachment = json.loads(marker.read_text(encoding="utf-8"))
565
+ except (OSError, json.JSONDecodeError):
566
+ attachment = None
567
+ links_match = isinstance(attachment, dict) and (
568
+ attachment.get("schema") == ATTACHMENT_SCHEMA
569
+ and attachment.get("workspace_id") == workspace.workspace_id
570
+ )
571
+ if links_match:
572
+ for entry in manifest_packages:
573
+ if entry["type"] != "git":
574
+ continue
575
+ name = str(entry["name"])
576
+ package_dir = override_package_dirs.get(name)
577
+ link = packages_dir / name
578
+ if package_dir is None or not link.is_symlink():
579
+ links_match = False
580
+ break
581
+ subdir = _package_subdir(entry)
582
+ target = package_dir
583
+ if subdir is not None:
584
+ for _part in subdir.parts:
585
+ target = target.parent
586
+ if link.resolve() != target.resolve():
587
+ links_match = False
588
+ break
589
+ if links_match:
590
+ return AdoptionResult(
591
+ context.root,
592
+ "already-attached",
593
+ len(workspace.package_ids),
594
+ 0,
595
+ workspace.workspace_id,
596
+ )
561
597
  if packages_dir.parent != lake_dir:
562
598
  packages_dir.parent.mkdir(parents=True, exist_ok=True)
563
599
  token = f"{os.getpid()}.{uuid.uuid4().hex}"
@@ -567,7 +603,6 @@ class ProjectAdopter:
567
603
  staging.mkdir()
568
604
  swapped = False
569
605
  had_original = packages_dir.exists() or packages_dir.is_symlink()
570
- marker = lake_dir / ATTACHMENT_RECORD
571
606
  config = context.root / PROJECT_CONFIG
572
607
  try:
573
608
  self.shared.events.emit(
@@ -101,7 +101,7 @@ def _lake_metadata(path: Path, toolchain: str, runtime: Runtime) -> tuple[str, t
101
101
  process = subprocess.run(
102
102
  command,
103
103
  cwd=path,
104
- env=runtime.toolchains.environment,
104
+ env=runtime.toolchains.environment_for(toolchain),
105
105
  text=True,
106
106
  stdout=subprocess.PIPE,
107
107
  stderr=subprocess.STDOUT,
@@ -218,7 +218,7 @@ def _lake_metadata(
218
218
  process = subprocess.run(
219
219
  command,
220
220
  cwd=checkout,
221
- env=toolchains.environment,
221
+ env=toolchains.environment_for(toolchain),
222
222
  text=True,
223
223
  stdout=subprocess.PIPE,
224
224
  stderr=subprocess.STDOUT,
@@ -127,7 +127,7 @@ class EnvironmentResolver:
127
127
  process = self.backend.execute(
128
128
  command,
129
129
  cwd=workspace,
130
- environment=self.toolchains.environment,
130
+ environment=self.toolchains.environment_for(toolchain),
131
131
  policy=ExecutionPolicy(timeout_seconds=timeout, max_output_bytes=10_000_000),
132
132
  cancel=cancel,
133
133
  )
@@ -118,7 +118,7 @@ from .store import (
118
118
  platform_record,
119
119
  )
120
120
  from .toolchain_oci import OCIToolchainLibrary, OCIToolchainPublisher, ToolchainPublication
121
- from .toolchains import ToolchainManager, normalize_toolchain
121
+ from .toolchains import ToolchainManager, immutable_toolchain_spelling, normalize_toolchain
122
122
  from .verification import (
123
123
  VerificationCheck,
124
124
  VerificationReport,
@@ -254,7 +254,7 @@ class Runtime:
254
254
  self.home = self.toolchains.home
255
255
  self.backend = backend or LocalBackend()
256
256
  self.store = EnvironmentStore(self.home)
257
- self.shared_projects = SharedProjectManager(self.home, self.events)
257
+ self.shared_projects = SharedProjectManager(self.home, self.events, self.toolchains)
258
258
  self.lake_cache = LakeArtifactCache(self.home, self.toolchains, self.events)
259
259
  self.header_cache = LeanHeaderCache(self.home, self.toolchains, self.events)
260
260
  self.identifier_resolver = IdentifierResolver(self.home)
@@ -800,6 +800,16 @@ class Runtime:
800
800
  if sign and not finalize:
801
801
  raise ValueError("platform-only publishing cannot sign a lock index")
802
802
  environment = self.environment(identifier)
803
+ if environment.sparse:
804
+ raise EnvironmentError(
805
+ "publication input must be source-built; sparse acquired capsules cannot be "
806
+ "republished"
807
+ )
808
+ if not immutable_toolchain_spelling(environment.lock.toolchain):
809
+ raise EnvironmentError(
810
+ f"refusing to publish mutable toolchain spelling "
811
+ f"{environment.lock.toolchain!r}; use an immutable release or dated nightly"
812
+ )
803
813
  repository = OCIRepository.parse(library)
804
814
  selected_publisher = publisher or OCIEnvironmentPublisher(
805
815
  repository, self.store, self.bundles, self.events
@@ -1531,7 +1541,7 @@ class Runtime:
1531
1541
  process = subprocess.run(
1532
1542
  command,
1533
1543
  cwd=context.root,
1534
- env=self.toolchains.environment,
1544
+ env=self.toolchains.environment_for(context.toolchain),
1535
1545
  text=True,
1536
1546
  stdout=subprocess.PIPE,
1537
1547
  stderr=subprocess.STDOUT,
@@ -1569,18 +1579,6 @@ class Runtime:
1569
1579
  results: list[AdoptionResult] = []
1570
1580
  failures: list[tuple[Path, str]] = []
1571
1581
  for project in selected_plan.projects:
1572
- if project.attached:
1573
- workspace_id = discover_project(project.root).provenance().workspace_id
1574
- results.append(
1575
- AdoptionResult(
1576
- project.root,
1577
- "already-attached",
1578
- len(project.packages),
1579
- 0,
1580
- workspace_id,
1581
- )
1582
- )
1583
- continue
1584
1582
  if not project.ready:
1585
1583
  continue
1586
1584
  context = discover_project(project.root)
@@ -2210,12 +2208,7 @@ class Runtime:
2210
2208
  plan.project_name or target.name,
2211
2209
  "lib",
2212
2210
  )
2213
- environment_for = getattr(self.toolchains, "environment_for", None)
2214
- command_environment = (
2215
- environment_for(plan.toolchain)
2216
- if callable(environment_for)
2217
- else self.toolchains.environment
2218
- )
2211
+ command_environment = self.toolchains.environment_for(plan.toolchain)
2219
2212
  process = subprocess.run(
2220
2213
  command,
2221
2214
  cwd=staging,
@@ -2472,6 +2465,14 @@ class Runtime:
2472
2465
  """Reclaim abandoned disposable execution and resolution workspaces."""
2473
2466
  return self.store.clean_scratch(dry_run=dry_run, minimum_age_seconds=minimum_age_seconds)
2474
2467
 
2468
+ def clean_legacy_project_artifacts(
2469
+ self, *, dry_run: bool = True, minimum_age_seconds: float = 0
2470
+ ) -> CleanupReport:
2471
+ return self.store.clean_legacy_project_artifacts(
2472
+ dry_run=dry_run,
2473
+ minimum_age_seconds=minimum_age_seconds,
2474
+ )
2475
+
2475
2476
  def clean_downloads(
2476
2477
  self, *, dry_run: bool = True, minimum_age_seconds: float = 2_592_000
2477
2478
  ) -> DownloadCleanupReport:
@@ -2576,6 +2577,9 @@ class Runtime:
2576
2577
  source_digest=sha256_text(source),
2577
2578
  policy=policy,
2578
2579
  path_map={str(source_path): safe_filename},
2580
+ environment=(
2581
+ self.toolchains.environment_for(selected) if project_root is not None else None
2582
+ ),
2579
2583
  cancel=cancel,
2580
2584
  )
2581
2585