lean-runtime 2.0.8__tar.gz → 2.1.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.
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/CHANGELOG.md +45 -1
- {lean_runtime-2.0.8/lean_runtime.egg-info → lean_runtime-2.1.0}/PKG-INFO +19 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/README.md +18 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/cli.md +36 -3
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/getting-started.md +12 -2
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/python-api.md +23 -2
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/standalone-files.md +25 -4
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/trust-and-limitations.md +1 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/cli.py +37 -2
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/diagnostics.py +22 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/__init__.py +2 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/engine.py +102 -84
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/policy.py +11 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/probe.py +46 -6
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/environments.py +21 -4
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/facade.py +8 -3
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/models.py +48 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/run_cli.py +45 -8
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/runtime.py +43 -7
- lean_runtime-2.1.0/lean_runtime/toolchain_slim.py +209 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/toolchains.py +127 -3
- {lean_runtime-2.0.8 → lean_runtime-2.1.0/lean_runtime.egg-info}/PKG-INFO +19 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime.egg-info/SOURCES.txt +3 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/pyproject.toml +1 -1
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_engine.py +143 -8
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_cli.py +15 -0
- lean_runtime-2.1.0/tests/test_diagnostics.py +36 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_facade.py +17 -0
- lean_runtime-2.1.0/tests/test_models.py +61 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_prebuilt_policy.py +16 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_run_cli.py +36 -0
- lean_runtime-2.1.0/tests/test_toolchain_slim.py +125 -0
- lean_runtime-2.0.8/tests/test_diagnostics.py +0 -18
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/LICENSE +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/MANIFEST.in +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/catalog/environments.toml +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/catalog/locks/core-v4.32.2.lock.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/catalog/locks/mathlib-v4.30.0.lock.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/catalog/locks/mathlib-v4.31.0.lock.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/catalog/locks/mathlib-v4.32.2.lock.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/compatibility/README.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/compatibility/mathlib-4.32.2.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/compatibility/mathlib-4.32.2.toml +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/architecture.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/captures.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/case-study-v1.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/development.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/environments.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/index.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/local-projects.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/portable-copies.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/ready-programs.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/docs/v1-precision.md +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/examples/mathlib.toml +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/__init__.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/__main__.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/_git.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/_paths.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/backends.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/bundles.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/comparison.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/decisions.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/analyzer.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/api.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/candidate.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/catalog.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/catalog_build.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/catalog_cli.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/catalog_manifest.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/data/catalog.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/defaults.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/errors.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/module_inventory.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/planner.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/py.typed +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/result.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/schema_resources.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/schemas/catalog-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/schemas/plan-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/discovery/schemas/result-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/errors.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/events.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/frontmatter.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/health.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/lake.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/lockfiles.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/locking.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/matrix.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/oci.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/policies.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/profiling.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/programs.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/projects.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/publisher_verification.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/py.typed +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/references.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/resolver.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/schema_resources.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/serialization.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/specs.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/store.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/timings.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/verification.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime/wire.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime.egg-info/dependency_links.txt +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime.egg-info/entry_points.txt +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime.egg-info/requires.txt +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/lean_runtime.egg-info/top_level.txt +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/mkdocs.yml +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/cleanup-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/comparison-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/execution-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/inspect-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/matrix-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/profile-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/schemas/verify-v1.schema.json +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/scripts/run_compatibility.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/scripts/run_v1_case_study.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/scripts/smoke_wheel.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/setup.cfg +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/conftest.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/e2e/test_real_discovery.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_analyzer.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_catalog.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_catalog_build.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_discovery_schema_resources.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_planner.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/discovery/test_policy.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_backend.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_bundles.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_environment_integration.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_events_health.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_frontmatter.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_git.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_interactive.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_lockfiles.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_multifile.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_paths.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_programs.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_projects.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_references.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_runtime.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_schema_resources.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_schemas.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_signatures.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_specs.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_store.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_toolchains.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_v1_precision.py +0 -0
- {lean_runtime-2.0.8 → lean_runtime-2.1.0}/tests/test_verification_inventory.py +0 -0
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2.1.0 - 2026-08-11
|
|
4
|
+
|
|
5
|
+
- Add `lean-runtime toolchain-slim` and
|
|
6
|
+
`ToolchainManager.materialize_slim()`: a verified check-profile toolchain
|
|
7
|
+
copy that drops editor indexes, static libraries, bundled LLVM/clang, and
|
|
8
|
+
sources (about 2.6 GB → 2.1 GB for v4.32.2 once `--prune-original` removes
|
|
9
|
+
the full Elan copy). Materialization hardlinks files, verification runs a
|
|
10
|
+
capability corpus with the slim copy's own `lean`, and checking transparently
|
|
11
|
+
routes through the slim copy when the full toolchain is absent. Lean v4.32
|
|
12
|
+
requires all `.olean` facets and per-module IR for ordinary elaboration, so
|
|
13
|
+
those remain; source builds of new environments still need the full
|
|
14
|
+
toolchain.
|
|
15
|
+
|
|
16
|
+
- Exempt environment acquisition (downloads, toolchain installs, source
|
|
17
|
+
builds) from the discovery search budget: a slow first-time download can no
|
|
18
|
+
longer expire an otherwise healthy search. Acquisition is bounded separately
|
|
19
|
+
by `DiscoveryPolicy.acquisition_timeout_seconds` and `lean-run
|
|
20
|
+
--acquire-timeout`; `--search-timeout` and `--check-timeout` name the
|
|
21
|
+
remaining budgets, with `--discovery-timeout` and `--timeout` kept as
|
|
22
|
+
deprecated aliases.
|
|
23
|
+
- Split the candidate probe into `acquire` and `check` phases and announce
|
|
24
|
+
first-time toolchain installation through a `toolchain.install_started`
|
|
25
|
+
event and a `lean-run` progress line. `CandidateProbe` implementers must now
|
|
26
|
+
provide both methods; `acquire` returns the newly exported
|
|
27
|
+
`AcquiredCandidate`, and its acquisition budget is forwarded to
|
|
28
|
+
`open_exact(build_timeout=...)` so `--acquire-timeout` governs source
|
|
29
|
+
builds.
|
|
30
|
+
- Add `ExecutionResult.errors`, `.warnings`, and `.first_error` severity views,
|
|
31
|
+
a `Diagnostic.location` property, and compact `repr` output for both types.
|
|
32
|
+
`to_dict()` payloads are unchanged.
|
|
33
|
+
- Report structured diagnostics against the caller's logical input names
|
|
34
|
+
(for example `Main.lean`) instead of staged sandbox paths, and rewrite the
|
|
35
|
+
staged entrypoint path back to the user's file in `lean-run` text output.
|
|
36
|
+
Raw `stdout`/`stderr` remain authoritative and unmodified in results.
|
|
37
|
+
- Add `lean.setup(toolchain="v4.32.2")` and `Runtime.open_toolchain()` for
|
|
38
|
+
reusable core-only environments; the empty-`deps` error now teaches the
|
|
39
|
+
toolchain form.
|
|
40
|
+
- Accept an exact lock path anywhere `lean-runtime profile` previously
|
|
41
|
+
required an environment name (`Runtime.subject_environment`); a path that
|
|
42
|
+
exists on disk always wins over an environment name.
|
|
43
|
+
|
|
44
|
+
## 2.0.9 - 2026-08-10
|
|
45
|
+
|
|
46
|
+
- Allow callers and `build-and-publish --timeout` to raise the per-step limit
|
|
47
|
+
for artifact hydration and environment builds on slower platforms.
|
|
4
48
|
|
|
5
49
|
## 2.0.8 - 2026-08-10
|
|
6
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-runtime
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.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
|
|
@@ -131,6 +131,24 @@ result = env.check(
|
|
|
131
131
|
result.raise_for_error()
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
Rejected proofs carry parsed diagnostics:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
result = env.check(broken_proof)
|
|
138
|
+
|
|
139
|
+
for error in result.errors:
|
|
140
|
+
print(error.file, error.line, error.message)
|
|
141
|
+
|
|
142
|
+
result.raise_for_error() # raises LeanCheckError with the same detail
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Core-only work does not need a dependency:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
core = lean.setup(toolchain="v4.32.2")
|
|
149
|
+
core.check("example : 2 + 2 = 4 := rfl").raise_for_error()
|
|
150
|
+
```
|
|
151
|
+
|
|
134
152
|
Batch and asyncio APIs reuse that prepared environment:
|
|
135
153
|
|
|
136
154
|
```python
|
|
@@ -96,6 +96,24 @@ result = env.check(
|
|
|
96
96
|
result.raise_for_error()
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
Rejected proofs carry parsed diagnostics:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
result = env.check(broken_proof)
|
|
103
|
+
|
|
104
|
+
for error in result.errors:
|
|
105
|
+
print(error.file, error.line, error.message)
|
|
106
|
+
|
|
107
|
+
result.raise_for_error() # raises LeanCheckError with the same detail
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Core-only work does not need a dependency:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
core = lean.setup(toolchain="v4.32.2")
|
|
114
|
+
core.check("example : 2 + 2 = 4 := rfl").raise_for_error()
|
|
115
|
+
```
|
|
116
|
+
|
|
99
117
|
Batch and asyncio APIs reuse that prepared environment:
|
|
100
118
|
|
|
101
119
|
```python
|
|
@@ -18,9 +18,14 @@ lean-run Main.lean --offline
|
|
|
18
18
|
|
|
19
19
|
Without explicit context or a pinned Lake project, `lean-run` automatically
|
|
20
20
|
searches its bundled exact-environment catalog. Use `--lock-out` to retain the
|
|
21
|
-
successful lock, `--no-discover` to require explicit context,
|
|
22
|
-
`--catalog PATH` to override the catalog
|
|
23
|
-
`--
|
|
21
|
+
successful lock, `--no-discover` to require explicit context, and
|
|
22
|
+
`--catalog PATH` to override the catalog. Three budgets bound the work:
|
|
23
|
+
`--search-timeout` covers ranking and compiler probes, `--check-timeout`
|
|
24
|
+
covers each Lean invocation, and `--acquire-timeout` independently bounds
|
|
25
|
+
downloading, installing, or building one candidate environment, so a slow
|
|
26
|
+
first-time download cannot expire the search. `--discovery-timeout` and
|
|
27
|
+
`--timeout` remain as deprecated aliases of `--search-timeout` and
|
|
28
|
+
`--check-timeout`. See
|
|
24
29
|
[Standalone Lean files](standalone-files.md) for routing and policy details.
|
|
25
30
|
|
|
26
31
|
## `lean-runtime`
|
|
@@ -79,6 +84,34 @@ identity, package Git trees, computer compatibility, and Lean probe before
|
|
|
79
84
|
making the environment available. See [Portable copies and environment
|
|
80
85
|
libraries](portable-copies.md) for its trust boundary.
|
|
81
86
|
|
|
87
|
+
## Slim toolchains
|
|
88
|
+
|
|
89
|
+
The official Lean toolchain is roughly 2.6 GB installed. Proof checking does
|
|
90
|
+
not need all of it: editor indexes, static libraries, the bundled LLVM/clang,
|
|
91
|
+
and toolchain sources are only used by editors and native compilation.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
lean-runtime toolchain-slim v4.32.2
|
|
95
|
+
lean-runtime toolchain-slim v4.32.2 --prune-original
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`toolchain-slim` materializes a separate check-profile copy by hardlinking the
|
|
99
|
+
kept files (near-zero extra disk), then verifies it against a capability
|
|
100
|
+
corpus — elaboration, core tactics, `decide`, `#eval`, metaprogramming, and
|
|
101
|
+
`Std` imports — using the slim copy's own `lean`. A copy that fails any probe
|
|
102
|
+
is removed and reported.
|
|
103
|
+
|
|
104
|
+
`--prune-original` uninstalls the full Elan toolchain after verification,
|
|
105
|
+
reducing the v4.32.2 footprint from about 2.6 GB to about 2.1 GB. All checking
|
|
106
|
+
continues through the slim copy. Lean v4.32 loads every `.olean` facet and
|
|
107
|
+
per-module IR during ordinary elaboration, so those artifact classes must
|
|
108
|
+
stay; larger reductions require upstream facet-loading changes.
|
|
109
|
+
|
|
110
|
+
After pruning, source builds of *new* environments and native compilation
|
|
111
|
+
need the full toolchain again; reinstall it with `lean-runtime install`.
|
|
112
|
+
Downloads are unaffected: first-time installation still transfers the full
|
|
113
|
+
official release before a slim copy can be derived from it.
|
|
114
|
+
|
|
82
115
|
## Replay
|
|
83
116
|
|
|
84
117
|
```bash
|
|
@@ -75,17 +75,27 @@ result.raise_for_error()
|
|
|
75
75
|
`setup()` resolves and ensures the environment once. Further checks reuse that
|
|
76
76
|
handle, including batch and asynchronous calls.
|
|
77
77
|
|
|
78
|
-
The same entry point opens local projects, exact locks,
|
|
79
|
-
environments:
|
|
78
|
+
The same entry point opens local projects, exact locks, previously named
|
|
79
|
+
environments, and core-only toolchains:
|
|
80
80
|
|
|
81
81
|
```python
|
|
82
82
|
project = lean.setup(project="./my-project")
|
|
83
83
|
locked = lean.setup(lock="environment.lock.json")
|
|
84
84
|
existing = lean.setup(environment="research-stack")
|
|
85
|
+
core = lean.setup(toolchain="v4.32.2")
|
|
85
86
|
```
|
|
86
87
|
|
|
87
88
|
Exactly one context must be supplied.
|
|
88
89
|
|
|
90
|
+
Rejected checks expose parsed diagnostics directly, with `file` naming your
|
|
91
|
+
logical input:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
result = environment.check(broken_proof)
|
|
95
|
+
for error in result.errors:
|
|
96
|
+
print(error.file, error.line, error.message)
|
|
97
|
+
```
|
|
98
|
+
|
|
89
99
|
## Lock for CI
|
|
90
100
|
|
|
91
101
|
Resolve a friendly dependency declaration and retain its exact graph:
|
|
@@ -12,9 +12,13 @@ environment = lean.setup(["mathlib@v4.32.2"])
|
|
|
12
12
|
project = lean.setup(project="./my-project")
|
|
13
13
|
locked = lean.setup(lock="environment.lock.json")
|
|
14
14
|
existing = lean.setup(environment="research-stack")
|
|
15
|
+
core = lean.setup(toolchain="v4.32.2")
|
|
15
16
|
```
|
|
16
17
|
|
|
17
|
-
Exactly one of `deps`, `project`, `lock`,
|
|
18
|
+
Exactly one of `deps`, `project`, `lock`, `environment`, or a bare
|
|
19
|
+
`toolchain` is required. A bare `toolchain` prepares the core-only
|
|
20
|
+
environment for that Lean release; combined with `deps` or `project` it
|
|
21
|
+
remains an override. The
|
|
18
22
|
default `Runtime` is created lazily on the first operation; importing
|
|
19
23
|
`lean_runtime` has no filesystem or network side effects. Supply
|
|
20
24
|
`runtime=Runtime(...)` to any façade function for explicit configuration.
|
|
@@ -53,7 +57,24 @@ runs. Planning metadata narrows candidates but never asserts compatibility.
|
|
|
53
57
|
|
|
54
58
|
## Results
|
|
55
59
|
|
|
56
|
-
Results remain inspectable values.
|
|
60
|
+
Results remain inspectable values. Failed checks expose their parsed
|
|
61
|
+
diagnostics directly:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
result = environment.check(source)
|
|
65
|
+
|
|
66
|
+
for error in result.errors:
|
|
67
|
+
print(error.file, error.line, error.message)
|
|
68
|
+
|
|
69
|
+
if result.first_error is not None:
|
|
70
|
+
print(result.first_error.location)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`errors` and `warnings` are severity-filtered views of `diagnostics`;
|
|
74
|
+
`first_error` returns the first error or `None`. Diagnostic `file` values name
|
|
75
|
+
the caller's logical input (for example `Main.lean`); raw compiler output
|
|
76
|
+
remains authoritative on `stdout`/`stderr` and may contain physical sandbox
|
|
77
|
+
paths. Scripts that prefer exceptions can use:
|
|
57
78
|
|
|
58
79
|
```python
|
|
59
80
|
result.raise_for_error()
|
|
@@ -15,9 +15,21 @@ environment is compatible. Runtime materializes each bounded candidate and
|
|
|
15
15
|
Lean authoritatively checks the source before discovery can succeed.
|
|
16
16
|
|
|
17
17
|
The bundled bootstrap catalog contains core Lean v4.32.2 plus exact Mathlib
|
|
18
|
-
v4.32.2, v4.31.0, and v4.30.0 locks. `--catalog PATH` selects another validated
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
v4.32.2, v4.31.0, and v4.30.0 locks. `--catalog PATH` selects another validated
|
|
19
|
+
catalog and `--no-discover` restores strict explicit-context behavior.
|
|
20
|
+
|
|
21
|
+
Three budgets bound the work independently:
|
|
22
|
+
|
|
23
|
+
- `--max-candidates` and `--search-timeout` bound the search itself: ranking
|
|
24
|
+
plus compiler probes.
|
|
25
|
+
- `--check-timeout` bounds each Lean invocation.
|
|
26
|
+
- `--acquire-timeout` bounds downloading, installing, or building one
|
|
27
|
+
candidate environment, including its source build. Acquisition never
|
|
28
|
+
consumes the search budget, so a slow first-time toolchain download cannot
|
|
29
|
+
expire an otherwise healthy search.
|
|
30
|
+
|
|
31
|
+
`--discovery-timeout` and `--timeout` remain as deprecated aliases of
|
|
32
|
+
`--search-timeout` and `--check-timeout`.
|
|
21
33
|
|
|
22
34
|
## Frontmatter format
|
|
23
35
|
|
|
@@ -53,12 +65,21 @@ environments only.
|
|
|
53
65
|
|
|
54
66
|
## Output
|
|
55
67
|
|
|
56
|
-
Human output is concise
|
|
68
|
+
Human output is concise, and error locations name the file you passed rather
|
|
69
|
+
than an internal staging path:
|
|
57
70
|
|
|
58
71
|
```text
|
|
59
72
|
✓ Main.lean accepted in 0.42s
|
|
60
73
|
```
|
|
61
74
|
|
|
75
|
+
```text
|
|
76
|
+
proofs/Bad.lean:1:23: error: Type mismatch
|
|
77
|
+
✗ proofs/Bad.lean rejected in 0.35s
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Only the checked file's own path is rewritten; dependency paths in compiler
|
|
81
|
+
output are shown as Lean printed them.
|
|
82
|
+
|
|
62
83
|
Cold operations report meaningful dependency, cache, and build phases on
|
|
63
84
|
stderr. `--quiet` suppresses progress and `--json` emits the complete structured
|
|
64
85
|
`ExecutionResult` without progress messages.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Trusted local execution
|
|
4
4
|
|
|
5
|
-
Lean Runtime
|
|
5
|
+
Lean Runtime orchestrates trusted code. It is not a sandbox.
|
|
6
6
|
|
|
7
7
|
Lake configurations are executable, dependencies may contain native code and
|
|
8
8
|
custom build targets, and explicit artifact commands run subprocesses. Lockfile
|
|
@@ -142,6 +142,12 @@ def parser() -> argparse.ArgumentParser:
|
|
|
142
142
|
push.add_argument("--publish-to", required=True)
|
|
143
143
|
push.add_argument("--tag", action="append", default=[])
|
|
144
144
|
push.add_argument("--name")
|
|
145
|
+
push.add_argument(
|
|
146
|
+
"--timeout",
|
|
147
|
+
type=float,
|
|
148
|
+
default=1800,
|
|
149
|
+
help="maximum seconds for each artifact hydration and the environment build",
|
|
150
|
+
)
|
|
145
151
|
push.add_argument(
|
|
146
152
|
"--platform-only",
|
|
147
153
|
action="store_true",
|
|
@@ -265,8 +271,21 @@ def parser() -> argparse.ArgumentParser:
|
|
|
265
271
|
diff.add_argument("right")
|
|
266
272
|
diff.add_argument("--json", action="store_true")
|
|
267
273
|
|
|
274
|
+
toolchain_slim = commands.add_parser(
|
|
275
|
+
"toolchain-slim",
|
|
276
|
+
help="materialize a verified slim check-profile copy of a toolchain",
|
|
277
|
+
)
|
|
278
|
+
toolchain_slim.add_argument("toolchain", help="Lean toolchain, e.g. v4.32.2")
|
|
279
|
+
toolchain_slim.add_argument(
|
|
280
|
+
"--prune-original",
|
|
281
|
+
action="store_true",
|
|
282
|
+
help="uninstall the full Elan toolchain after verification; checking keeps "
|
|
283
|
+
"working through the slim copy, but source builds of new environments and "
|
|
284
|
+
"native compilation need the full toolchain again",
|
|
285
|
+
)
|
|
286
|
+
|
|
268
287
|
profile = commands.add_parser("profile", help="measure repeated checks in one environment")
|
|
269
|
-
profile.add_argument("environment")
|
|
288
|
+
profile.add_argument("environment", help="environment name or exact lock path")
|
|
270
289
|
profile.add_argument("file", type=Path)
|
|
271
290
|
profile.add_argument("--warmup", type=int, default=1)
|
|
272
291
|
profile.add_argument("--repeat", type=int, default=5)
|
|
@@ -368,7 +387,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
368
387
|
_json(environment.inspect().to_dict())
|
|
369
388
|
return 0
|
|
370
389
|
if args.command == "build-and-publish":
|
|
371
|
-
environment = runtime.open_exact(
|
|
390
|
+
environment = runtime.open_exact(
|
|
391
|
+
EnvironmentLock.load(args.lock),
|
|
392
|
+
name=args.name,
|
|
393
|
+
build_timeout=args.timeout,
|
|
394
|
+
)
|
|
372
395
|
_json(
|
|
373
396
|
runtime.publish_environment(
|
|
374
397
|
environment.id,
|
|
@@ -509,6 +532,18 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
509
532
|
if args.command == "storage":
|
|
510
533
|
_json(runtime.store_status().to_dict())
|
|
511
534
|
return 0
|
|
535
|
+
if args.command == "toolchain-slim":
|
|
536
|
+
manifest = runtime.toolchains.materialize_slim(args.toolchain)
|
|
537
|
+
if args.prune_original:
|
|
538
|
+
runtime.toolchains.prune_original(args.toolchain)
|
|
539
|
+
_json(
|
|
540
|
+
{
|
|
541
|
+
**manifest.to_dict(),
|
|
542
|
+
"path": str(runtime.toolchains.slim_path(args.toolchain)),
|
|
543
|
+
"pruned_original": args.prune_original,
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
return 0
|
|
512
547
|
if args.command == "doctor":
|
|
513
548
|
doctor_report = runtime.doctor()
|
|
514
549
|
_json(doctor_report.to_dict())
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from dataclasses import replace
|
|
6
8
|
|
|
7
9
|
from .models import Diagnostic, Severity
|
|
8
10
|
|
|
@@ -46,6 +48,26 @@ def parse_diagnostics(output: str) -> tuple[Diagnostic, ...]:
|
|
|
46
48
|
return tuple(diagnostics)
|
|
47
49
|
|
|
48
50
|
|
|
51
|
+
def map_diagnostic_paths(
|
|
52
|
+
diagnostics: tuple[Diagnostic, ...],
|
|
53
|
+
path_map: Mapping[str, str] | None,
|
|
54
|
+
) -> tuple[Diagnostic, ...]:
|
|
55
|
+
"""Rewrite staged physical paths to the caller's logical input names.
|
|
56
|
+
|
|
57
|
+
Only exact staged-file matches are rewritten; package files may
|
|
58
|
+
legitimately share basenames, and raw compiler output remains authoritative
|
|
59
|
+
on the execution result.
|
|
60
|
+
"""
|
|
61
|
+
if not path_map:
|
|
62
|
+
return diagnostics
|
|
63
|
+
return tuple(
|
|
64
|
+
replace(item, file=path_map[item.file])
|
|
65
|
+
if item.file is not None and item.file in path_map
|
|
66
|
+
else item
|
|
67
|
+
for item in diagnostics
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
49
71
|
def error_diagnostic(message: str) -> Diagnostic:
|
|
50
72
|
"""Create an infrastructure diagnostic."""
|
|
51
73
|
severity: Severity = "error"
|
|
@@ -21,7 +21,7 @@ from .defaults import DEFAULT_CATALOG_PATH, default_catalog
|
|
|
21
21
|
from .errors import CatalogError, DiscoveryError, PolicyError
|
|
22
22
|
from .planner import Planner
|
|
23
23
|
from .policy import DiscoveryPolicy
|
|
24
|
-
from .probe import CandidateProbe, LeanRuntimeProbe, ProbeOutcome
|
|
24
|
+
from .probe import AcquiredCandidate, CandidateProbe, LeanRuntimeProbe, ProbeOutcome
|
|
25
25
|
from .result import (
|
|
26
26
|
RESULT_SCHEMA,
|
|
27
27
|
CandidateAttempt,
|
|
@@ -39,6 +39,7 @@ __all__ = [
|
|
|
39
39
|
"AvailabilityObservation",
|
|
40
40
|
"Candidate",
|
|
41
41
|
"CandidateAttempt",
|
|
42
|
+
"AcquiredCandidate",
|
|
42
43
|
"CandidateProbe",
|
|
43
44
|
"CandidateReason",
|
|
44
45
|
"Catalog",
|
|
@@ -68,6 +68,61 @@ def _diagnostic(code: str, detail: str) -> tuple[ResultDiagnostic, ...]:
|
|
|
68
68
|
return (ResultDiagnostic(code=code, detail=detail),)
|
|
69
69
|
|
|
70
70
|
|
|
71
|
+
def _failed_attempt(
|
|
72
|
+
exc: Exception,
|
|
73
|
+
*,
|
|
74
|
+
candidate_id: str,
|
|
75
|
+
lock_id: str,
|
|
76
|
+
attempt_started: float,
|
|
77
|
+
cancelled_externally: bool,
|
|
78
|
+
timed_out: bool,
|
|
79
|
+
) -> tuple[CandidateAttempt, str]:
|
|
80
|
+
"""Map one probe exception to an attempt and an action: continue, failed, or cancelled."""
|
|
81
|
+
duration = time.monotonic() - attempt_started
|
|
82
|
+
if isinstance(exc, ProbeIntegrityFailure):
|
|
83
|
+
status: AttemptStatus = "environment_integrity_failure"
|
|
84
|
+
code = CANDIDATE_INTEGRITY_FAILURE
|
|
85
|
+
action = "failed"
|
|
86
|
+
elif isinstance(exc, ProbeUnavailable):
|
|
87
|
+
if cancelled_externally:
|
|
88
|
+
status = "cancelled"
|
|
89
|
+
code = CANDIDATE_CANCELLED
|
|
90
|
+
action = "cancelled"
|
|
91
|
+
elif timed_out:
|
|
92
|
+
status = "timeout"
|
|
93
|
+
code = CANDIDATE_TIMEOUT
|
|
94
|
+
action = "continue"
|
|
95
|
+
else:
|
|
96
|
+
status = "environment_unavailable"
|
|
97
|
+
code = CANDIDATE_UNAVAILABLE
|
|
98
|
+
action = "continue"
|
|
99
|
+
elif isinstance(exc, LeanRuntimeError):
|
|
100
|
+
if cancelled_externally:
|
|
101
|
+
status = "cancelled"
|
|
102
|
+
code = CANDIDATE_CANCELLED
|
|
103
|
+
action = "cancelled"
|
|
104
|
+
elif timed_out:
|
|
105
|
+
status = "timeout"
|
|
106
|
+
code = CANDIDATE_TIMEOUT
|
|
107
|
+
action = "continue"
|
|
108
|
+
else:
|
|
109
|
+
status = "runtime_error"
|
|
110
|
+
code = CANDIDATE_RUNTIME_ERROR
|
|
111
|
+
action = "failed"
|
|
112
|
+
else: # defensive adapter boundary
|
|
113
|
+
status = "runtime_error"
|
|
114
|
+
code = CANDIDATE_RUNTIME_ERROR
|
|
115
|
+
action = "generic_failed"
|
|
116
|
+
attempt = CandidateAttempt(
|
|
117
|
+
candidate_id=candidate_id,
|
|
118
|
+
lock_id=lock_id,
|
|
119
|
+
status=status,
|
|
120
|
+
duration_seconds=duration,
|
|
121
|
+
diagnostics=_diagnostic(code, str(exc)),
|
|
122
|
+
)
|
|
123
|
+
return attempt, action
|
|
124
|
+
|
|
125
|
+
|
|
71
126
|
def _rejection_detail(stderr: str, stdout: str) -> str:
|
|
72
127
|
return stderr.strip() or stdout.strip() or "Lean rejected the source"
|
|
73
128
|
|
|
@@ -130,114 +185,77 @@ def discover(
|
|
|
130
185
|
duration_seconds=time.monotonic() - started,
|
|
131
186
|
diagnostics=_diagnostic(DISCOVERY_TIME_LIMIT, "total search budget expired"),
|
|
132
187
|
)
|
|
133
|
-
timeout = min(
|
|
134
|
-
remaining,
|
|
135
|
-
plan.policy.candidate_timeout_seconds or remaining,
|
|
136
|
-
)
|
|
137
188
|
attempt_started = time.monotonic()
|
|
138
|
-
|
|
189
|
+
acquisition_timeout = plan.policy.acquisition_timeout_seconds
|
|
190
|
+
with _AttemptCancellation(acquisition_timeout, cancel) as acquisition_cancel:
|
|
139
191
|
try:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
candidate_id=candidate.entry.id,
|
|
149
|
-
lock_id=candidate.entry.lock.lock_id,
|
|
150
|
-
status="environment_integrity_failure",
|
|
151
|
-
duration_seconds=time.monotonic() - attempt_started,
|
|
152
|
-
diagnostics=_diagnostic(CANDIDATE_INTEGRITY_FAILURE, str(exc)),
|
|
153
|
-
)
|
|
154
|
-
attempts.append(attempt)
|
|
155
|
-
return DiscoveryResult(
|
|
156
|
-
status="failed",
|
|
157
|
-
confidence="exhausted",
|
|
158
|
-
plan=plan,
|
|
159
|
-
attempts=tuple(attempts),
|
|
160
|
-
duration_seconds=time.monotonic() - started,
|
|
161
|
-
diagnostics=attempt.diagnostics,
|
|
162
|
-
)
|
|
163
|
-
except ProbeUnavailable as exc:
|
|
164
|
-
if cancel is not None and cancel.is_set():
|
|
165
|
-
attempt = CandidateAttempt(
|
|
166
|
-
candidate_id=candidate.entry.id,
|
|
167
|
-
lock_id=candidate.entry.lock.lock_id,
|
|
168
|
-
status="cancelled",
|
|
169
|
-
duration_seconds=time.monotonic() - attempt_started,
|
|
170
|
-
diagnostics=_diagnostic(CANDIDATE_CANCELLED, str(exc)),
|
|
171
|
-
)
|
|
172
|
-
attempts.append(attempt)
|
|
173
|
-
return DiscoveryResult(
|
|
174
|
-
status="cancelled",
|
|
175
|
-
confidence="exhausted",
|
|
176
|
-
plan=plan,
|
|
177
|
-
attempts=tuple(attempts),
|
|
178
|
-
duration_seconds=time.monotonic() - started,
|
|
179
|
-
diagnostics=attempt.diagnostics,
|
|
192
|
+
# Acquisition (downloads, toolchain installs, builds) is
|
|
193
|
+
# budgeted separately; every acquisition outcome, including
|
|
194
|
+
# failures, credits its elapsed time back to the search budget.
|
|
195
|
+
try:
|
|
196
|
+
acquired = probe.acquire(
|
|
197
|
+
candidate,
|
|
198
|
+
timeout_seconds=acquisition_timeout,
|
|
199
|
+
cancel=acquisition_cancel.cancel,
|
|
180
200
|
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
attempts.append(
|
|
188
|
-
CandidateAttempt(
|
|
189
|
-
candidate_id=candidate.entry.id,
|
|
190
|
-
lock_id=candidate.entry.lock.lock_id,
|
|
191
|
-
status=unavailable_status,
|
|
192
|
-
duration_seconds=time.monotonic() - attempt_started,
|
|
193
|
-
diagnostics=_diagnostic(code, str(exc)),
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
continue
|
|
197
|
-
except LeanRuntimeError as exc:
|
|
198
|
-
if cancel is not None and cancel.is_set():
|
|
199
|
-
error_status: AttemptStatus = "cancelled"
|
|
200
|
-
code = CANDIDATE_CANCELLED
|
|
201
|
-
elif attempt_cancel.timed_out.is_set():
|
|
202
|
-
error_status = "timeout"
|
|
203
|
-
code = CANDIDATE_TIMEOUT
|
|
204
|
-
else:
|
|
205
|
-
error_status = "runtime_error"
|
|
206
|
-
code = CANDIDATE_RUNTIME_ERROR
|
|
207
|
-
attempt = CandidateAttempt(
|
|
201
|
+
finally:
|
|
202
|
+
deadline += time.monotonic() - attempt_started
|
|
203
|
+
except Exception as exc: # defensive adapter boundary
|
|
204
|
+
attempt, action = _failed_attempt(
|
|
205
|
+
exc,
|
|
208
206
|
candidate_id=candidate.entry.id,
|
|
209
207
|
lock_id=candidate.entry.lock.lock_id,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
208
|
+
attempt_started=attempt_started,
|
|
209
|
+
cancelled_externally=cancel is not None and cancel.is_set(),
|
|
210
|
+
timed_out=acquisition_cancel.timed_out.is_set(),
|
|
213
211
|
)
|
|
214
212
|
attempts.append(attempt)
|
|
215
|
-
if
|
|
213
|
+
if action == "continue":
|
|
216
214
|
continue
|
|
217
215
|
return DiscoveryResult(
|
|
218
|
-
status="cancelled" if
|
|
216
|
+
status="cancelled" if action == "cancelled" else "failed",
|
|
219
217
|
confidence="exhausted",
|
|
220
218
|
plan=plan,
|
|
221
219
|
attempts=tuple(attempts),
|
|
222
220
|
duration_seconds=time.monotonic() - started,
|
|
223
|
-
diagnostics=
|
|
221
|
+
diagnostics=_diagnostic(DISCOVERY_RUNTIME_ERROR, str(exc))
|
|
222
|
+
if action == "generic_failed"
|
|
223
|
+
else attempt.diagnostics,
|
|
224
|
+
)
|
|
225
|
+
remaining = deadline - time.monotonic()
|
|
226
|
+
timeout = min(
|
|
227
|
+
remaining,
|
|
228
|
+
plan.policy.candidate_timeout_seconds or remaining,
|
|
229
|
+
)
|
|
230
|
+
with _AttemptCancellation(timeout, cancel) as attempt_cancel:
|
|
231
|
+
try:
|
|
232
|
+
outcome = probe.check(
|
|
233
|
+
acquired,
|
|
234
|
+
source,
|
|
235
|
+
timeout_seconds=timeout,
|
|
236
|
+
cancel=attempt_cancel.cancel,
|
|
224
237
|
)
|
|
225
238
|
except Exception as exc: # defensive adapter boundary
|
|
226
|
-
attempt =
|
|
239
|
+
attempt, action = _failed_attempt(
|
|
240
|
+
exc,
|
|
227
241
|
candidate_id=candidate.entry.id,
|
|
228
242
|
lock_id=candidate.entry.lock.lock_id,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
243
|
+
attempt_started=attempt_started,
|
|
244
|
+
cancelled_externally=cancel is not None and cancel.is_set(),
|
|
245
|
+
timed_out=attempt_cancel.timed_out.is_set(),
|
|
232
246
|
)
|
|
233
247
|
attempts.append(attempt)
|
|
248
|
+
if action == "continue":
|
|
249
|
+
continue
|
|
234
250
|
return DiscoveryResult(
|
|
235
|
-
status="failed",
|
|
251
|
+
status="cancelled" if action == "cancelled" else "failed",
|
|
236
252
|
confidence="exhausted",
|
|
237
253
|
plan=plan,
|
|
238
254
|
attempts=tuple(attempts),
|
|
239
255
|
duration_seconds=time.monotonic() - started,
|
|
240
|
-
diagnostics=_diagnostic(DISCOVERY_RUNTIME_ERROR, str(exc))
|
|
256
|
+
diagnostics=_diagnostic(DISCOVERY_RUNTIME_ERROR, str(exc))
|
|
257
|
+
if action == "generic_failed"
|
|
258
|
+
else attempt.diagnostics,
|
|
241
259
|
)
|
|
242
260
|
|
|
243
261
|
result = outcome.execution_result
|