lean-runtime 2.9.0__tar.gz → 2.9.1__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 (184) hide show
  1. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/CHANGELOG.md +7 -0
  2. {lean_runtime-2.9.0/lean_runtime.egg-info → lean_runtime-2.9.1}/PKG-INFO +1 -1
  3. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/cli.md +1 -0
  4. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/portable-copies.md +3 -0
  5. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/cli.py +16 -3
  6. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/oci.py +38 -21
  7. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/schema_resources.py +1 -0
  8. {lean_runtime-2.9.0 → lean_runtime-2.9.1/lean_runtime.egg-info}/PKG-INFO +1 -1
  9. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime.egg-info/SOURCES.txt +1 -0
  10. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/pyproject.toml +1 -1
  11. lean_runtime-2.9.1/schemas/publication-v1.schema.json +70 -0
  12. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_bundles.py +52 -0
  13. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_cli.py +58 -0
  14. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_schema_resources.py +1 -0
  15. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_schemas.py +58 -0
  16. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/LICENSE +0 -0
  17. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/MANIFEST.in +0 -0
  18. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/README.md +0 -0
  19. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/environments.toml +0 -0
  20. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/core-v4.32.2.lock.json +0 -0
  21. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/leancert-v4.30.0.5.lock.json +0 -0
  22. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/leancert-v4.31.0.lock.json +0 -0
  23. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/leancert-v4.32.2.4.lock.json +0 -0
  24. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/leancert-v4.33.0.lock.json +0 -0
  25. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/mathlib-v4.30.0.lock.json +0 -0
  26. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/mathlib-v4.31.0.lock.json +0 -0
  27. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/mathlib-v4.32.2.lock.json +0 -0
  28. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/catalog/locks/mathlib-v4.33.0.lock.json +0 -0
  29. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/README.md +0 -0
  30. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/lake-artifact-cache-4.33.0.json +0 -0
  31. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/mathlib-4.32.2.json +0 -0
  32. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/mathlib-4.32.2.toml +0 -0
  33. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/mathlib-4.33.0.json +0 -0
  34. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/compatibility/mathlib-4.33.0.toml +0 -0
  35. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/architecture.md +0 -0
  36. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/captures.md +0 -0
  37. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/case-study-v1.md +0 -0
  38. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/development.md +0 -0
  39. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/environments.md +0 -0
  40. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/getting-started.md +0 -0
  41. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/index.md +0 -0
  42. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/lake-artifact-cache-design.md +0 -0
  43. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/local-projects.md +0 -0
  44. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/project-publishing.md +0 -0
  45. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/python-api.md +0 -0
  46. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/ready-programs.md +0 -0
  47. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/standalone-files.md +0 -0
  48. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/trust-and-limitations.md +0 -0
  49. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/docs/v1-precision.md +0 -0
  50. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/examples/mathlib.toml +0 -0
  51. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/__init__.py +0 -0
  52. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/__main__.py +0 -0
  53. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/_git.py +0 -0
  54. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/_paths.py +0 -0
  55. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/backends.py +0 -0
  56. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/bundles.py +0 -0
  57. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/capsules.py +0 -0
  58. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/comparison.py +0 -0
  59. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/console.py +0 -0
  60. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/decisions.py +0 -0
  61. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/diagnostics.py +0 -0
  62. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/__init__.py +0 -0
  63. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/analyzer.py +0 -0
  64. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/api.py +0 -0
  65. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/candidate.py +0 -0
  66. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/catalog.py +0 -0
  67. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/catalog_build.py +0 -0
  68. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/catalog_cli.py +0 -0
  69. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/catalog_manifest.py +0 -0
  70. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/data/catalog.json +0 -0
  71. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/defaults.py +0 -0
  72. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/engine.py +0 -0
  73. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/errors.py +0 -0
  74. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/module_inventory.py +0 -0
  75. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/planner.py +0 -0
  76. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/policy.py +0 -0
  77. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/probe.py +0 -0
  78. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/py.typed +0 -0
  79. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/result.py +0 -0
  80. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/schema_resources.py +0 -0
  81. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/schemas/catalog-v1.schema.json +0 -0
  82. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/schemas/plan-v1.schema.json +0 -0
  83. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/discovery/schemas/result-v1.schema.json +0 -0
  84. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/environments.py +0 -0
  85. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/errors.py +0 -0
  86. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/events.py +0 -0
  87. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/facade.py +0 -0
  88. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/frontmatter.py +0 -0
  89. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/header_cache.py +0 -0
  90. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/health.py +0 -0
  91. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/identifier_resolver.py +0 -0
  92. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/lake.py +0 -0
  93. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/lake_cache.py +0 -0
  94. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/lockfiles.py +0 -0
  95. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/locking.py +0 -0
  96. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/matrix.py +0 -0
  97. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/models.py +0 -0
  98. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/oci_protocol.py +0 -0
  99. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/packs.py +0 -0
  100. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/policies.py +0 -0
  101. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/profiling.py +0 -0
  102. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/programs.py +0 -0
  103. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/project_execution.py +0 -0
  104. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/project_sharing.py +0 -0
  105. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/projects.py +0 -0
  106. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/publisher_verification.py +0 -0
  107. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/py.typed +0 -0
  108. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/references.py +0 -0
  109. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/resolver.py +0 -0
  110. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/run_cli.py +0 -0
  111. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/runtime.py +0 -0
  112. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/serialization.py +0 -0
  113. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/shared_projects.py +0 -0
  114. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/specs.py +0 -0
  115. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/store.py +0 -0
  116. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/timings.py +0 -0
  117. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/toolchain_oci.py +0 -0
  118. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/toolchain_slim.py +0 -0
  119. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/toolchains.py +0 -0
  120. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/verification.py +0 -0
  121. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime/wire.py +0 -0
  122. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime.egg-info/dependency_links.txt +0 -0
  123. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime.egg-info/entry_points.txt +0 -0
  124. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime.egg-info/requires.txt +0 -0
  125. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/lean_runtime.egg-info/top_level.txt +0 -0
  126. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/mkdocs.yml +0 -0
  127. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/cleanup-v1.schema.json +0 -0
  128. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/comparison-v1.schema.json +0 -0
  129. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/execution-v1.schema.json +0 -0
  130. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/inspect-v1.schema.json +0 -0
  131. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/matrix-v1.schema.json +0 -0
  132. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/profile-v1.schema.json +0 -0
  133. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/schemas/verify-v1.schema.json +0 -0
  134. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/announcement_gate.py +0 -0
  135. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/lake_cache_gate.py +0 -0
  136. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/registry_preflight.py +0 -0
  137. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/run_compatibility.py +0 -0
  138. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/run_v1_case_study.py +0 -0
  139. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/scripts/smoke_wheel.py +0 -0
  140. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/setup.cfg +0 -0
  141. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/conftest.py +0 -0
  142. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/e2e/test_real_discovery.py +0 -0
  143. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_analyzer.py +0 -0
  144. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_catalog.py +0 -0
  145. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_catalog_build.py +0 -0
  146. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_discovery_schema_resources.py +0 -0
  147. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_engine.py +0 -0
  148. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_planner.py +0 -0
  149. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/discovery/test_policy.py +0 -0
  150. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_backend.py +0 -0
  151. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_capsules.py +0 -0
  152. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_console.py +0 -0
  153. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_diagnostics.py +0 -0
  154. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_environment_integration.py +0 -0
  155. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_events_health.py +0 -0
  156. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_facade.py +0 -0
  157. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_frontmatter.py +0 -0
  158. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_git.py +0 -0
  159. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_header_cache.py +0 -0
  160. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_identifier_resolver.py +0 -0
  161. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_interactive.py +0 -0
  162. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_lake_cache.py +0 -0
  163. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_lockfiles.py +0 -0
  164. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_models.py +0 -0
  165. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_multifile.py +0 -0
  166. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_oci_protocol.py +0 -0
  167. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_packs.py +0 -0
  168. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_paths.py +0 -0
  169. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_prebuilt_policy.py +0 -0
  170. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_programs.py +0 -0
  171. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_project_publish.py +0 -0
  172. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_projects.py +0 -0
  173. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_references.py +0 -0
  174. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_run_cli.py +0 -0
  175. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_runtime.py +0 -0
  176. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_signatures.py +0 -0
  177. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_specs.py +0 -0
  178. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_store.py +0 -0
  179. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_toolchain_oci.py +0 -0
  180. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_toolchain_slim.py +0 -0
  181. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_toolchains.py +0 -0
  182. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_v1_precision.py +0 -0
  183. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_verification_inventory.py +0 -0
  184. {lean_runtime-2.9.0 → lean_runtime-2.9.1}/tests/test_verification_probe.py +0 -0
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.9.1 - 2026-08-15
6
+
7
+ - Make GHCR credential discovery a single bounded GitHub CLI status read so a
8
+ transient identity lookup cannot silently downgrade an authenticated account
9
+ to anonymous; add a validated `lean-runtime.publication/v1` envelope behind
10
+ `publish environment --json`.
11
+
5
12
  ## 2.9.0 - 2026-08-15
6
13
 
7
14
  - Make environment publication fail closed: verify registry push access before
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-runtime
3
- Version: 2.9.0
3
+ Version: 2.9.1
4
4
  Summary: Run Lean 4 proofs from Python or standalone files
5
5
  Author: Alejandro Radisic
6
6
  License-Expression: Apache-2.0
@@ -78,6 +78,7 @@ lean-runtime copy save research-stack --output research-stack.lean-environment
78
78
  lean-runtime --home /tmp/fresh copy open research-stack.lean-environment --name research-stack
79
79
  lean-runtime publish environment environment.lock.json --publish-to ghcr.io/owner/lean-environments
80
80
  lean-runtime publish environment --publish-to ghcr.io/owner/lean-environments --check-access
81
+ lean-runtime publish environment --publish-to ghcr.io/owner/lean-environments --check-access --json
81
82
  lean-runtime check research-stack Main.lean --json
82
83
  lean-runtime inspect research-stack --packages
83
84
  lean-runtime environments
@@ -121,6 +121,9 @@ lean-runtime publish environment \
121
121
  --check-access
122
122
  ```
123
123
 
124
+ Add `--json` for the versioned `lean-runtime.publication/v1` success or failure
125
+ envelope. The process exit code retains the classification below.
126
+
124
127
  For GHCR, credentials are selected in this order: the
125
128
  `LEAN_RUNTIME_REGISTRY_USERNAME` / `LEAN_RUNTIME_REGISTRY_PASSWORD` pair, an
126
129
  authenticated GitHub CLI session, then anonymous access. The access report names
@@ -55,6 +55,7 @@ def _schema_for(command: str) -> str:
55
55
  "matrix": "lean-runtime.matrix/v1",
56
56
  "clean": "lean-runtime.cleanup/v1",
57
57
  "inspect": "lean-runtime.inspect/v1",
58
+ "build-and-publish": "lean-runtime.publication/v1",
58
59
  }.get(command, "lean-runtime.execution/v1")
59
60
 
60
61
 
@@ -440,6 +441,9 @@ Run `lean-runtime COMMAND --help` for examples and options.""",
440
441
  publish_environment.add_argument("--accelerate", action="store_true")
441
442
  publish_environment.add_argument("--sign", action="store_true")
442
443
  publish_environment.add_argument("--attest", action="store_true")
444
+ publish_environment.add_argument(
445
+ "--json", action="store_true", help="emit a versioned JSON envelope"
446
+ )
443
447
  publish_environment.add_argument(
444
448
  "--check-access",
445
449
  action="store_true",
@@ -526,6 +530,7 @@ Run `lean-runtime COMMAND --help` for examples and options.""",
526
530
  push.add_argument(
527
531
  "--attest", action="store_true", help="publish a signed source/probe attestation"
528
532
  )
533
+ push.add_argument("--json", action="store_true", help="emit a versioned JSON envelope")
529
534
  push.add_argument(
530
535
  "--check-access",
531
536
  action="store_true",
@@ -1187,7 +1192,11 @@ def main(argv: list[str] | None = None) -> int:
1187
1192
  if args.command == "build-and-publish":
1188
1193
  access = runtime.check_publication_access(args.publish_to)
1189
1194
  if args.check_access:
1190
- _json(access.to_dict())
1195
+ _json(
1196
+ envelope(_schema_for(args.command), ok=True, data=access.to_dict())
1197
+ if args.json
1198
+ else access.to_dict()
1199
+ )
1191
1200
  return 0
1192
1201
  if args.lock is None:
1193
1202
  raise ValueError("publish environment requires LOCK unless --check-access is used")
@@ -1208,7 +1217,11 @@ def main(argv: list[str] | None = None) -> int:
1208
1217
  publication["consumer_command"] = (
1209
1218
  f"lean-runtime --library {args.publish_to} download {args.lock}"
1210
1219
  )
1211
- _json(publication)
1220
+ _json(
1221
+ envelope(_schema_for(args.command), ok=True, data=publication)
1222
+ if args.json
1223
+ else publication
1224
+ )
1212
1225
  return 0
1213
1226
  if args.command == "finalize-publication":
1214
1227
  descriptors = []
@@ -1686,7 +1699,7 @@ def main(argv: list[str] | None = None) -> int:
1686
1699
  envelope(
1687
1700
  _schema_for(args.command),
1688
1701
  ok=False,
1689
- data={"publication": exc.to_dict()},
1702
+ data=exc.to_dict(),
1690
1703
  errors=[error("publication_failed", str(exc), details=exc.to_dict())],
1691
1704
  )
1692
1705
  )
@@ -88,32 +88,49 @@ class RegistryCredential:
88
88
  return cls(username, password, "environment")
89
89
  if repository.registry == "ghcr.io" and shutil.which("gh") is not None:
90
90
  try:
91
- token = subprocess.run(
92
- ("gh", "auth", "token", "--hostname", "github.com"),
93
- text=True,
94
- capture_output=True,
95
- timeout=10,
96
- check=False,
97
- )
98
- identity = subprocess.run(
99
- ("gh", "api", "user", "--jq", ".login"),
91
+ status = subprocess.run(
92
+ (
93
+ "gh",
94
+ "auth",
95
+ "status",
96
+ "--active",
97
+ "--hostname",
98
+ "github.com",
99
+ "--json",
100
+ "hosts",
101
+ "--show-token",
102
+ ),
100
103
  text=True,
101
104
  capture_output=True,
102
- timeout=10,
105
+ timeout=5,
103
106
  check=False,
104
107
  )
105
108
  except (OSError, subprocess.TimeoutExpired):
106
- pass
107
- else:
108
- selected_token = token.stdout.strip()
109
- selected_username = identity.stdout.strip()
110
- if (
111
- token.returncode == 0
112
- and identity.returncode == 0
113
- and selected_token
114
- and selected_username
115
- ):
116
- return cls(selected_username, selected_token, "GitHub CLI")
109
+ return cls(None, None, "GitHub CLI")
110
+ try:
111
+ payload = json.loads(status.stdout)
112
+ accounts = payload.get("hosts", {}).get("github.com", [])
113
+ active = next(
114
+ (
115
+ account
116
+ for account in accounts
117
+ if isinstance(account, dict) and account.get("active") is True
118
+ ),
119
+ None,
120
+ )
121
+ except (AttributeError, json.JSONDecodeError):
122
+ return cls(None, None, "GitHub CLI")
123
+ if isinstance(active, dict):
124
+ selected_username = active.get("login")
125
+ selected_token = active.get("token")
126
+ if isinstance(selected_username, str) and selected_username:
127
+ return cls(
128
+ selected_username,
129
+ selected_token
130
+ if isinstance(selected_token, str) and selected_token
131
+ else None,
132
+ "GitHub CLI",
133
+ )
117
134
  return cls(None, None, "anonymous")
118
135
 
119
136
 
@@ -13,6 +13,7 @@ SCHEMA_NAMES = frozenset(
13
13
  "inspect-v1.schema.json",
14
14
  "matrix-v1.schema.json",
15
15
  "profile-v1.schema.json",
16
+ "publication-v1.schema.json",
16
17
  "verify-v1.schema.json",
17
18
  }
18
19
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-runtime
3
- Version: 2.9.0
3
+ Version: 2.9.1
4
4
  Summary: Run Lean 4 proofs from Python or standalone files
5
5
  Author: Alejandro Radisic
6
6
  License-Expression: Apache-2.0
@@ -124,6 +124,7 @@ schemas/execution-v1.schema.json
124
124
  schemas/inspect-v1.schema.json
125
125
  schemas/matrix-v1.schema.json
126
126
  schemas/profile-v1.schema.json
127
+ schemas/publication-v1.schema.json
127
128
  schemas/verify-v1.schema.json
128
129
  scripts/announcement_gate.py
129
130
  scripts/lake_cache_gate.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lean-runtime"
7
- version = "2.9.0"
7
+ version = "2.9.1"
8
8
  description = "Run Lean 4 proofs from Python or standalone files"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://lean-runtime.dev/schemas/publication-v1.schema.json",
4
+ "type": "object",
5
+ "required": ["schema", "ok", "data", "warnings", "errors"],
6
+ "additionalProperties": false,
7
+ "properties": {
8
+ "schema": {"const": "lean-runtime.publication/v1"},
9
+ "ok": {"type": "boolean"},
10
+ "data": {
11
+ "oneOf": [
12
+ {"$ref": "#/$defs/access"},
13
+ {"$ref": "#/$defs/publication"},
14
+ {"$ref": "#/$defs/failure"},
15
+ {"type": "object", "maxProperties": 0}
16
+ ]
17
+ },
18
+ "warnings": {"$ref": "https://lean-runtime.dev/schemas/execution-v1.schema.json#/$defs/issues"},
19
+ "errors": {"$ref": "https://lean-runtime.dev/schemas/execution-v1.schema.json#/$defs/issues"}
20
+ },
21
+ "$defs": {
22
+ "access": {
23
+ "type": "object",
24
+ "required": ["registry", "username", "credential_source", "push_verified"],
25
+ "additionalProperties": false,
26
+ "properties": {
27
+ "registry": {"type": "string"},
28
+ "username": {"type": ["string", "null"]},
29
+ "credential_source": {"type": "string"},
30
+ "push_verified": {"type": "boolean"}
31
+ }
32
+ },
33
+ "publication": {
34
+ "type": "object",
35
+ "required": ["library", "exact_environment_id", "environment_id", "computer_copy_id", "publication_id", "uploaded_files", "total_blob_bytes", "uploaded_bytes", "reused_bytes", "reuse_percent", "computer_record", "consumer_command"],
36
+ "additionalProperties": false,
37
+ "properties": {
38
+ "library": {"type": "string"},
39
+ "exact_environment_id": {"type": "string"},
40
+ "environment_id": {"type": "string"},
41
+ "computer_copy_id": {"type": "string"},
42
+ "publication_id": {"type": ["string", "null"]},
43
+ "uploaded_files": {"type": "integer", "minimum": 0},
44
+ "total_blob_bytes": {"type": "integer", "minimum": 0},
45
+ "uploaded_bytes": {"type": "integer", "minimum": 0},
46
+ "reused_bytes": {"type": "integer", "minimum": 0},
47
+ "reuse_percent": {"type": "number", "minimum": 0, "maximum": 100},
48
+ "computer_record": {"type": "object"},
49
+ "consumer_command": {"type": "string"}
50
+ }
51
+ },
52
+ "failure": {
53
+ "type": "object",
54
+ "required": ["phase", "registry", "status_code", "retryable", "published", "partial", "credential_source", "username", "hint", "message"],
55
+ "additionalProperties": false,
56
+ "properties": {
57
+ "phase": {"type": "string"},
58
+ "registry": {"type": "string"},
59
+ "status_code": {"type": ["integer", "null"]},
60
+ "retryable": {"type": "boolean"},
61
+ "published": {"type": "boolean"},
62
+ "partial": {"type": "boolean"},
63
+ "credential_source": {"type": "string"},
64
+ "username": {"type": ["string", "null"]},
65
+ "hint": {"type": ["string", "null"]},
66
+ "message": {"type": "string"}
67
+ }
68
+ }
69
+ }
70
+ }
@@ -901,6 +901,58 @@ def test_oci_publisher_denies_access_before_exporting(tmp_path: Path, monkeypatc
901
901
  assert "secret" not in json.dumps(events[-1].to_dict())
902
902
 
903
903
 
904
+ def test_ghcr_credential_discovery_reads_identity_and_token_once(monkeypatch) -> None:
905
+ monkeypatch.delenv("LEAN_RUNTIME_REGISTRY_USERNAME", raising=False)
906
+ monkeypatch.delenv("LEAN_RUNTIME_REGISTRY_PASSWORD", raising=False)
907
+ monkeypatch.setattr(shutil, "which", lambda _name: "/usr/bin/gh")
908
+ calls: list[tuple[str, ...]] = []
909
+
910
+ def status(command: tuple[str, ...], **_kwargs: object) -> subprocess.CompletedProcess[str]:
911
+ calls.append(command)
912
+ payload = {
913
+ "hosts": {
914
+ "github.com": [
915
+ {
916
+ "active": True,
917
+ "state": "success",
918
+ "login": "owner",
919
+ "token": "secret",
920
+ }
921
+ ]
922
+ }
923
+ }
924
+ return subprocess.CompletedProcess(command, 0, json.dumps(payload), "")
925
+
926
+ monkeypatch.setattr(subprocess, "run", status)
927
+ credential = RegistryCredential.discover(OCIRepository.parse("oci://ghcr.io/owner/cache"))
928
+
929
+ assert credential == RegistryCredential("owner", "secret", "GitHub CLI")
930
+ assert len(calls) == 1
931
+ assert calls[0][:3] == ("gh", "auth", "status")
932
+ assert "api" not in calls[0]
933
+
934
+
935
+ def test_ghcr_credential_discovery_keeps_known_identity_when_token_is_unavailable(
936
+ monkeypatch,
937
+ ) -> None:
938
+ monkeypatch.delenv("LEAN_RUNTIME_REGISTRY_USERNAME", raising=False)
939
+ monkeypatch.delenv("LEAN_RUNTIME_REGISTRY_PASSWORD", raising=False)
940
+ monkeypatch.setattr(shutil, "which", lambda _name: "/usr/bin/gh")
941
+ payload = {
942
+ "hosts": {
943
+ "github.com": [{"active": True, "state": "failed", "login": "owner", "token": ""}]
944
+ }
945
+ }
946
+ monkeypatch.setattr(
947
+ subprocess,
948
+ "run",
949
+ lambda command, **_kwargs: subprocess.CompletedProcess(command, 0, json.dumps(payload), ""),
950
+ )
951
+
952
+ credential = RegistryCredential.discover(OCIRepository.parse("oci://ghcr.io/owner/cache"))
953
+ assert credential == RegistryCredential("owner", None, "GitHub CLI")
954
+
955
+
904
956
  def test_oci_publisher_classifies_retryable_preflight_failure(tmp_path: Path) -> None:
905
957
  producer, _environment_id, _lock = _published_runtime(tmp_path / "producer")
906
958
  publisher = OCIEnvironmentPublisher(
@@ -318,6 +318,64 @@ def test_publish_check_access_needs_no_lock(monkeypatch, capsys) -> None:
318
318
  assert output["credential_source"] == "GitHub CLI"
319
319
 
320
320
 
321
+ def test_publish_failure_json_is_a_versioned_envelope(monkeypatch, capsys) -> None:
322
+ def denied(_self, _library: str) -> PublicationAccess:
323
+ raise PublicationError(
324
+ "registry denied push access",
325
+ phase="access_preflight",
326
+ registry="ghcr.io/owner/cache",
327
+ status_code=403,
328
+ credential_source="GitHub CLI",
329
+ username="owner",
330
+ hint="refresh scopes",
331
+ )
332
+
333
+ monkeypatch.setattr("lean_runtime.cli.Runtime.check_publication_access", denied)
334
+ result = main(
335
+ [
336
+ "publish",
337
+ "environment",
338
+ "--publish-to",
339
+ "oci://ghcr.io/owner/cache",
340
+ "--check-access",
341
+ "--json",
342
+ ]
343
+ )
344
+
345
+ assert result == 3
346
+ output = json.loads(capsys.readouterr().out)
347
+ assert output["schema"] == "lean-runtime.publication/v1"
348
+ assert output["ok"] is False
349
+ assert output["data"]["published"] is False
350
+ assert output["data"]["status_code"] == 403
351
+ assert output["errors"][0]["code"] == "publication_failed"
352
+
353
+
354
+ def test_publish_access_json_is_a_versioned_envelope(monkeypatch, capsys) -> None:
355
+ monkeypatch.setattr(
356
+ "lean_runtime.cli.Runtime.check_publication_access",
357
+ lambda _self, _library: PublicationAccess(
358
+ "ghcr.io/owner/cache", "owner", "GitHub CLI", True
359
+ ),
360
+ )
361
+ result = main(
362
+ [
363
+ "publish",
364
+ "environment",
365
+ "--publish-to",
366
+ "oci://ghcr.io/owner/cache",
367
+ "--check-access",
368
+ "--json",
369
+ ]
370
+ )
371
+
372
+ assert result == 0
373
+ output = json.loads(capsys.readouterr().out)
374
+ assert output["schema"] == "lean-runtime.publication/v1"
375
+ assert output["ok"] is True
376
+ assert output["data"]["push_verified"] is True
377
+
378
+
321
379
  def test_verbose_materialization_failure_includes_tool_output(capsys) -> None:
322
380
  failure = MaterializationError(
323
381
  "environment build failed",
@@ -15,6 +15,7 @@ def test_schema_resource_names_are_closed() -> None:
15
15
  "inspect-v1.schema.json",
16
16
  "matrix-v1.schema.json",
17
17
  "profile-v1.schema.json",
18
+ "publication-v1.schema.json",
18
19
  "verify-v1.schema.json",
19
20
  } == lean.SCHEMA_NAMES
20
21
  with pytest.raises(ValueError, match="unknown Lean Runtime schema"):
@@ -42,6 +42,7 @@ def test_every_v1_schema_compiles_eagerly() -> None:
42
42
  "inspect-v1.schema",
43
43
  "matrix-v1.schema",
44
44
  "profile-v1.schema",
45
+ "publication-v1.schema",
45
46
  "verify-v1.schema",
46
47
  }
47
48
  for path in paths:
@@ -78,6 +79,7 @@ def test_every_v1_schema_accepts_its_closed_error_envelope() -> None:
78
79
  "inspect": "lean-runtime.inspect/v1",
79
80
  "matrix": "lean-runtime.matrix/v1",
80
81
  "profile": "lean-runtime.profile/v1",
82
+ "publication": "lean-runtime.publication/v1",
81
83
  "verify": "lean-runtime.verify/v1",
82
84
  }
83
85
  for name, identifier in schemas.items():
@@ -121,6 +123,62 @@ def test_inspect_and_gc_success_fixtures_are_closed() -> None:
121
123
  _validator("cleanup-v1.schema.json").validate(gc)
122
124
 
123
125
 
126
+ def test_publication_access_and_failure_fixtures_match_v1_schema() -> None:
127
+ validator = _validator("publication-v1.schema.json")
128
+ validator.validate(
129
+ envelope(
130
+ "lean-runtime.publication/v1",
131
+ ok=True,
132
+ data={
133
+ "registry": "oci://ghcr.io/owner/cache",
134
+ "username": "owner",
135
+ "credential_source": "GitHub CLI",
136
+ "push_verified": True,
137
+ },
138
+ )
139
+ )
140
+ validator.validate(
141
+ envelope(
142
+ "lean-runtime.publication/v1",
143
+ ok=True,
144
+ data={
145
+ "library": "oci://ghcr.io/owner/cache",
146
+ "exact_environment_id": "lock_abc",
147
+ "environment_id": "env_abc",
148
+ "computer_copy_id": "sha256:" + "a" * 64,
149
+ "publication_id": "sha256:" + "b" * 64,
150
+ "uploaded_files": 1,
151
+ "total_blob_bytes": 10,
152
+ "uploaded_bytes": 4,
153
+ "reused_bytes": 6,
154
+ "reuse_percent": 60.0,
155
+ "computer_record": {},
156
+ "consumer_command": "lean-runtime download environment.lock.json",
157
+ },
158
+ )
159
+ )
160
+ failure = {
161
+ "phase": "access_preflight",
162
+ "registry": "oci://ghcr.io/owner/cache",
163
+ "status_code": 403,
164
+ "retryable": False,
165
+ "published": False,
166
+ "partial": False,
167
+ "credential_source": "GitHub CLI",
168
+ "username": "owner",
169
+ "hint": "refresh scopes",
170
+ "message": "registry denied push access",
171
+ }
172
+ validator.validate(
173
+ envelope(
174
+ "lean-runtime.publication/v1",
175
+ ok=False,
176
+ data=failure,
177
+ errors=[error("publication_failed", failure["message"], details=failure)],
178
+ )
179
+ )
180
+
181
+
124
182
  def test_timing_phase_vocabulary_and_duration_are_bounded() -> None:
125
183
  with pytest.raises(ValueError, match="unsupported timing phase"):
126
184
  PhaseTiming("other", 1) # type: ignore[arg-type]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes