Skip to content

Commit e260b53

Browse files
authored
refactor(lean): share proof-state validation (#761)
1 parent 27f800b commit e260b53

5 files changed

Lines changed: 383 additions & 199 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Validation of immutable proof states against a caller-selected Lean profile."""
2+
3+
from __future__ import annotations
4+
5+
from collections.abc import Mapping
6+
from typing import Protocol
7+
8+
from pydantic import ValidationError
9+
10+
from jacobian.capability_service import CapabilityInvocationError
11+
from jacobian.contracts.capabilities import CapabilityDiagnostic
12+
from jacobian.contracts.lean import LeanEnvironment
13+
from jacobian.contracts.lean_exploration import LeanProofStateArtifact
14+
from jacobian.lean_frontend.artifacts import (
15+
_environment_imports,
16+
_source_digest,
17+
_state_digest_payload,
18+
)
19+
from jacobian.references import LeanCheckerInstallation
20+
from jacobian.storage.errors import StorageError
21+
from jacobian.storage.repository import ArtifactRepository
22+
23+
24+
class _StoredProofStateResources(Protocol):
25+
@property
26+
def store(self) -> ArtifactRepository: ...
27+
28+
@property
29+
def semantics_uri(self) -> str: ...
30+
31+
@property
32+
def state_schema_uri(self) -> str: ...
33+
34+
@property
35+
def installations(
36+
self,
37+
) -> Mapping[LeanEnvironment, LeanCheckerInstallation]: ...
38+
39+
40+
def _load_validated_proof_state(
41+
resources: _StoredProofStateResources,
42+
state_uri: str,
43+
*,
44+
expected_environment: LeanEnvironment,
45+
expected_environment_digest: str,
46+
invalid_state_hint: str,
47+
) -> LeanProofStateArtifact:
48+
"""Load one state bound to the profile selected by the current request."""
49+
50+
try:
51+
stored = resources.store.get(state_uri)
52+
if (
53+
stored.manifest.schema_uri != resources.state_schema_uri
54+
or stored.manifest.semantics_uri != resources.semantics_uri
55+
):
56+
raise ValueError("artifact is not a Lean proof state")
57+
state = LeanProofStateArtifact.model_validate(stored.payload)
58+
except (StorageError, ValidationError, ValueError) as exc:
59+
raise CapabilityInvocationError(
60+
CapabilityDiagnostic(
61+
code="INVALID_LEAN_PROOF_STATE",
62+
stage="state_loading",
63+
message="The supplied state artifact is unavailable or invalid.",
64+
hint=invalid_state_hint,
65+
)
66+
) from exc
67+
68+
installation = resources.installations[expected_environment]
69+
if (
70+
state.environment is not expected_environment
71+
or state.environment_digest != expected_environment_digest
72+
or state.imports != _environment_imports(expected_environment)
73+
or state.lean_version != installation.lean_version
74+
or state.lean_commit != installation.lean_commit
75+
or state.mathlib_commit != installation.mathlib_commit
76+
or state.source_digest != _source_digest(state.statement, state.tactic_prefix)
77+
or state.state_digest != _state_digest_payload(state)
78+
):
79+
raise CapabilityInvocationError(
80+
CapabilityDiagnostic(
81+
code="STALE_LEAN_PROOF_STATE",
82+
stage="state_validation",
83+
message=(
84+
"The proof state no longer matches its source or the "
85+
"current pinned Lean environment."
86+
),
87+
hint="Recreate the proof state under the current environment.",
88+
)
89+
)
90+
return state
91+
92+
93+
__all__ = ["_StoredProofStateResources", "_load_validated_proof_state"]

src/jacobian/lean_frontend/metavariable_fields.py

Lines changed: 6 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
CapabilityResult,
3434
CapabilityScope,
3535
)
36-
from jacobian.contracts.lean import LeanEnvironment
37-
from jacobian.contracts.lean_exploration import LeanProofStateArtifact
3836
from jacobian.contracts.lean_metavariable_fields import (
3937
LeanElaborationContext,
4038
LeanMetavariableFieldsArtifact,
@@ -43,20 +41,18 @@
4341
LeanStructuredMetavariable,
4442
)
4543
from jacobian.contracts.results import Execution, ExecutionStatus
44+
from jacobian.lean_frontend._state_validation import _load_validated_proof_state
4645
from jacobian.lean_frontend.artifacts import (
4746
_environment_digest,
48-
_environment_imports,
4947
_proof_state_command,
5048
_source_digest,
51-
_state_digest_payload,
5249
)
5350
from jacobian.lean_frontend.exploration import (
5451
_Resources,
5552
_runtime_ms,
5653
_validate_source_parts,
5754
)
5855
from jacobian.lean_frontend.repl import _response_errors
59-
from jacobian.storage.errors import StorageError
6056

6157

6258
class LeanMetavariableFieldsAdapter:
@@ -113,10 +109,14 @@ def invoke(self, request: CapabilityRequest) -> CapabilityResult:
113109
validated.environment,
114110
installation,
115111
)
116-
bound_state = self._load_bound_state(
112+
bound_state = _load_validated_proof_state(
113+
self.resources,
117114
validated.state_uri,
118115
expected_environment=validated.environment,
119116
expected_environment_digest=environment_digest,
117+
invalid_state_hint=(
118+
"Use a state URI returned by a proof-state capability."
119+
),
120120
)
121121
if bound_state.completed:
122122
raise CapabilityInvocationError(
@@ -326,56 +326,6 @@ def invoke(self, request: CapabilityRequest) -> CapabilityResult:
326326
artifact_uris=(validated.state_uri, artifact.artifact_uri),
327327
)
328328

329-
def _load_bound_state(
330-
self,
331-
state_uri: str,
332-
*,
333-
expected_environment: LeanEnvironment,
334-
expected_environment_digest: str,
335-
) -> LeanProofStateArtifact:
336-
try:
337-
stored = self.resources.store.get(state_uri)
338-
if (
339-
stored.manifest.schema_uri != self.resources.state_schema_uri
340-
or stored.manifest.semantics_uri != self.resources.semantics_uri
341-
):
342-
raise ValueError("artifact is not a Lean proof state")
343-
state = LeanProofStateArtifact.model_validate(stored.payload)
344-
except (StorageError, ValidationError, ValueError) as exc:
345-
raise CapabilityInvocationError(
346-
CapabilityDiagnostic(
347-
code="INVALID_LEAN_PROOF_STATE",
348-
stage="state_loading",
349-
message="The supplied state artifact is unavailable or invalid.",
350-
hint="Use a state URI returned by a proof-state capability.",
351-
)
352-
) from exc
353-
installation = self.resources.installations[expected_environment]
354-
expected_imports = _environment_imports(expected_environment)
355-
if (
356-
state.environment is not expected_environment
357-
or state.environment_digest != expected_environment_digest
358-
or state.imports != expected_imports
359-
or state.lean_version != installation.lean_version
360-
or state.lean_commit != installation.lean_commit
361-
or state.mathlib_commit != installation.mathlib_commit
362-
or state.source_digest
363-
!= _source_digest(state.statement, state.tactic_prefix)
364-
or state.state_digest != _state_digest_payload(state)
365-
):
366-
raise CapabilityInvocationError(
367-
CapabilityDiagnostic(
368-
code="STALE_LEAN_PROOF_STATE",
369-
stage="state_validation",
370-
message=(
371-
"The proof state no longer matches its source or the "
372-
"current pinned Lean environment."
373-
),
374-
hint="Recreate the proof state under the current environment.",
375-
)
376-
)
377-
return state
378-
379329

380330
def install_lean_metavariable_fields_capability(
381331
resources: _Resources,

src/jacobian/lean_frontend/proof_state.py

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,19 @@
2323
CapabilityResult,
2424
CapabilityScope,
2525
)
26-
from jacobian.contracts.lean import LeanEnvironment
2726
from jacobian.contracts.lean_exploration import (
28-
LeanProofStateArtifact,
2927
LeanProofStateOutput,
3028
LeanProofStateRequest,
3129
LeanProofStateTransitionArtifact,
3230
LeanProofSuccessorState,
3331
LeanTypedGoal,
3432
)
3533
from jacobian.contracts.results import Execution, ExecutionStatus
34+
from jacobian.lean_frontend._state_validation import _load_validated_proof_state
3635
from jacobian.lean_frontend.artifacts import (
3736
_environment_digest,
38-
_environment_imports,
3937
_proof_state_command,
4038
_source_digest,
41-
_state_digest_payload,
4239
_state_payload,
4340
)
4441
from jacobian.lean_frontend.exploration import (
@@ -49,7 +46,6 @@
4946
_validate_source_parts,
5047
)
5148
from jacobian.lean_frontend.repl import _response_errors
52-
from jacobian.storage.errors import StorageError
5349

5450

5551
class LeanProofStateAdapter:
@@ -132,10 +128,12 @@ def invoke(self, request: CapabilityRequest) -> CapabilityResult:
132128
proof_prefix = validated.proof_prefix
133129
bound_state = None
134130
else:
135-
bound_state = self._load_bound_state(
131+
bound_state = _load_validated_proof_state(
132+
self.resources,
136133
validated.state_uri,
137134
expected_environment=validated.environment,
138135
expected_environment_digest=environment_digest,
136+
invalid_state_hint="Use a state URI returned by this capability.",
139137
)
140138
if bound_state.completed:
141139
raise CapabilityInvocationError(
@@ -402,55 +400,5 @@ def invoke(self, request: CapabilityRequest) -> CapabilityResult:
402400
artifact_uris=artifact_uris,
403401
)
404402

405-
def _load_bound_state(
406-
self,
407-
state_uri: str,
408-
*,
409-
expected_environment: LeanEnvironment,
410-
expected_environment_digest: str,
411-
) -> LeanProofStateArtifact:
412-
try:
413-
stored = self.resources.store.get(state_uri)
414-
if (
415-
stored.manifest.schema_uri != self.resources.state_schema_uri
416-
or stored.manifest.semantics_uri != self.resources.semantics_uri
417-
):
418-
raise ValueError("artifact is not a Lean proof state")
419-
state = LeanProofStateArtifact.model_validate(stored.payload)
420-
except (StorageError, ValidationError, ValueError) as exc:
421-
raise CapabilityInvocationError(
422-
CapabilityDiagnostic(
423-
code="INVALID_LEAN_PROOF_STATE",
424-
stage="state_loading",
425-
message="The supplied state artifact is unavailable or invalid.",
426-
hint="Use a state URI returned by this capability.",
427-
)
428-
) from exc
429-
installation = self.resources.installations[expected_environment]
430-
expected_imports = _environment_imports(expected_environment)
431-
if (
432-
state.environment is not expected_environment
433-
or state.environment_digest != expected_environment_digest
434-
or state.imports != expected_imports
435-
or state.lean_version != installation.lean_version
436-
or state.lean_commit != installation.lean_commit
437-
or state.mathlib_commit != installation.mathlib_commit
438-
or state.source_digest
439-
!= _source_digest(state.statement, state.tactic_prefix)
440-
or state.state_digest != _state_digest_payload(state)
441-
):
442-
raise CapabilityInvocationError(
443-
CapabilityDiagnostic(
444-
code="STALE_LEAN_PROOF_STATE",
445-
stage="state_validation",
446-
message=(
447-
"The proof state no longer matches its source or the "
448-
"current pinned Lean environment."
449-
),
450-
hint="Recreate the proof state under the current environment.",
451-
)
452-
)
453-
return state
454-
455403

456404
__all__ = ["LeanProofStateAdapter"]

0 commit comments

Comments
 (0)