aetherproof-arch 0.1.0a1__py3-none-any.whl
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.
- aetherproof/__init__.py +76 -0
- aetherproof/__version__.py +1 -0
- aetherproof/cli.py +83 -0
- aetherproof/csr/__init__.py +6 -0
- aetherproof/csr/record.py +12 -0
- aetherproof/csr/relations.py +46 -0
- aetherproof/models.py +172 -0
- aetherproof/obligations/__init__.py +6 -0
- aetherproof/obligations/generator.py +68 -0
- aetherproof/obligations/odg.py +115 -0
- aetherproof/proof/__init__.py +7 -0
- aetherproof/proof/engine.py +77 -0
- aetherproof/proof/invalidation.py +66 -0
- aetherproof/proof/pdg.py +90 -0
- aetherproof/protocol/__init__.py +5 -0
- aetherproof/protocol/messages.py +64 -0
- aetherproof/remediation/__init__.py +7 -0
- aetherproof/remediation/cycle.py +107 -0
- aetherproof/remediation/diagnosis.py +56 -0
- aetherproof/remediation/guardrails.py +59 -0
- aetherproof/review/__init__.py +5 -0
- aetherproof/review/roles.py +86 -0
- aetherproof_arch-0.1.0a1.dist-info/METADATA +622 -0
- aetherproof_arch-0.1.0a1.dist-info/RECORD +29 -0
- aetherproof_arch-0.1.0a1.dist-info/WHEEL +5 -0
- aetherproof_arch-0.1.0a1.dist-info/entry_points.txt +2 -0
- aetherproof_arch-0.1.0a1.dist-info/licenses/AUTHORS.md +48 -0
- aetherproof_arch-0.1.0a1.dist-info/licenses/LICENSE +26 -0
- aetherproof_arch-0.1.0a1.dist-info/top_level.txt +1 -0
aetherproof/__init__.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AetherProof v2.0.0 — Empirical Implementation of the Formal Architecture.
|
|
3
|
+
|
|
4
|
+
Reference implementation of the formal architecture specified in:
|
|
5
|
+
|
|
6
|
+
Baladi, S. (2026). AetherProof: Autonomous Formal Verification and
|
|
7
|
+
Invariant Synthesis for Multi-Agent Software Ecosystems.
|
|
8
|
+
Zenodo. https://doi.org/10.5281/zenodo.24013124
|
|
9
|
+
|
|
10
|
+
This is a reference implementation, not a production system.
|
|
11
|
+
No real-world deployment has been evaluated.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from aetherproof.__version__ import __version__
|
|
15
|
+
|
|
16
|
+
from aetherproof.models import (
|
|
17
|
+
CSRecord,
|
|
18
|
+
Obligation,
|
|
19
|
+
Counterexample,
|
|
20
|
+
Verdict,
|
|
21
|
+
ObligationType,
|
|
22
|
+
CounterexampleType,
|
|
23
|
+
Layer,
|
|
24
|
+
)
|
|
25
|
+
from aetherproof.csr.record import CSRRecord
|
|
26
|
+
from aetherproof.csr.relations import SemanticRelation, RelationType
|
|
27
|
+
from aetherproof.obligations.generator import generate_obligations
|
|
28
|
+
from aetherproof.obligations.odg import ODG, EdgeType
|
|
29
|
+
from aetherproof.proof.pdg import PDG, PDGNode, NodeType
|
|
30
|
+
from aetherproof.proof.invalidation import invalidate
|
|
31
|
+
from aetherproof.proof.engine import ProofEngine
|
|
32
|
+
from aetherproof.remediation.diagnosis import diagnose
|
|
33
|
+
from aetherproof.remediation.guardrails import check_guardrails, ViolationType, Patch
|
|
34
|
+
from aetherproof.remediation.cycle import RemediationCycle
|
|
35
|
+
from aetherproof.protocol.messages import Message, MessageType, Envelope
|
|
36
|
+
from aetherproof.review.roles import Role, Authority, AUTHORITY_MATRIX
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"__version__",
|
|
40
|
+
# Models
|
|
41
|
+
"CSRecord",
|
|
42
|
+
"Obligation",
|
|
43
|
+
"Counterexample",
|
|
44
|
+
"Verdict",
|
|
45
|
+
"ObligationType",
|
|
46
|
+
"CounterexampleType",
|
|
47
|
+
"Layer",
|
|
48
|
+
# CSR
|
|
49
|
+
"CSRRecord",
|
|
50
|
+
"SemanticRelation",
|
|
51
|
+
"RelationType",
|
|
52
|
+
# Obligations
|
|
53
|
+
"generate_obligations",
|
|
54
|
+
"ODG",
|
|
55
|
+
"EdgeType",
|
|
56
|
+
# Proof
|
|
57
|
+
"PDG",
|
|
58
|
+
"PDGNode",
|
|
59
|
+
"NodeType",
|
|
60
|
+
"invalidate",
|
|
61
|
+
"ProofEngine",
|
|
62
|
+
# Remediation
|
|
63
|
+
"diagnose",
|
|
64
|
+
"check_guardrails",
|
|
65
|
+
"ViolationType",
|
|
66
|
+
"Patch",
|
|
67
|
+
"RemediationCycle",
|
|
68
|
+
# Protocol
|
|
69
|
+
"Message",
|
|
70
|
+
"MessageType",
|
|
71
|
+
"Envelope",
|
|
72
|
+
# Review
|
|
73
|
+
"Role",
|
|
74
|
+
"Authority",
|
|
75
|
+
"AUTHORITY_MATRIX",
|
|
76
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.0.0"
|
aetherproof/cli.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Command-line interface for AetherProof v2.0.0."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from aetherproof.__version__ import __version__
|
|
7
|
+
from aetherproof.models import CSRecord, Layer, Verdict
|
|
8
|
+
from aetherproof.csr.relations import SemanticRelation, RelationType
|
|
9
|
+
from aetherproof.obligations.generator import generate_obligations
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv=None) -> int:
|
|
13
|
+
parser = argparse.ArgumentParser(
|
|
14
|
+
prog="aetherproof",
|
|
15
|
+
description="AetherProof v2.0.0 — Reference Implementation",
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--version", action="version", version=f"%(prog)s {__version__}"
|
|
19
|
+
)
|
|
20
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
21
|
+
|
|
22
|
+
# csr command
|
|
23
|
+
csr = sub.add_parser("csr", help="CSR record operations")
|
|
24
|
+
csr.add_argument("--layer", choices=["L1", "L2", "L3"], required=True)
|
|
25
|
+
csr.add_argument("--language", required=True)
|
|
26
|
+
csr.add_argument("--content", required=True)
|
|
27
|
+
csr.add_argument("--role", required=True)
|
|
28
|
+
|
|
29
|
+
# obligation command
|
|
30
|
+
obl = sub.add_parser("obligation", help="Obligation operations")
|
|
31
|
+
obl.add_argument("--csr-id", required=True)
|
|
32
|
+
obl.add_argument("--property", required=True)
|
|
33
|
+
|
|
34
|
+
# relation command
|
|
35
|
+
rel = sub.add_parser("relation", help="Semantic relation operations")
|
|
36
|
+
rel.add_argument("--source", required=True)
|
|
37
|
+
rel.add_argument("--target", required=True)
|
|
38
|
+
rel.add_argument(
|
|
39
|
+
"--type",
|
|
40
|
+
choices=[r.value for r in RelationType],
|
|
41
|
+
required=True,
|
|
42
|
+
)
|
|
43
|
+
rel.add_argument("--loss-description")
|
|
44
|
+
|
|
45
|
+
args = parser.parse_args(argv)
|
|
46
|
+
|
|
47
|
+
if args.command == "csr":
|
|
48
|
+
layer = Layer(args.layer)
|
|
49
|
+
record = CSRecord.create(
|
|
50
|
+
layer=layer,
|
|
51
|
+
language=args.language,
|
|
52
|
+
content=args.content,
|
|
53
|
+
semantic_role=args.role,
|
|
54
|
+
)
|
|
55
|
+
print(f"CSR ID: {record.csr_id}")
|
|
56
|
+
print(f"Artifact Hash: {record.artifact_hash}")
|
|
57
|
+
print(f"Semantic ID: {record.semantic_id}")
|
|
58
|
+
print(f"Layer: {record.layer.value}")
|
|
59
|
+
print(f"Language: {record.language}")
|
|
60
|
+
|
|
61
|
+
elif args.command == "obligation":
|
|
62
|
+
# In a real CLI, this would fetch the CSR record
|
|
63
|
+
print(f"Obligation for CSR {args.csr_id}: {args.property}")
|
|
64
|
+
|
|
65
|
+
elif args.command == "relation":
|
|
66
|
+
try:
|
|
67
|
+
relation = SemanticRelation(
|
|
68
|
+
source=args.source,
|
|
69
|
+
target=args.target,
|
|
70
|
+
relation_type=RelationType(args.type),
|
|
71
|
+
loss_description=args.loss_description,
|
|
72
|
+
)
|
|
73
|
+
print(f"Relation created: {relation.source} "
|
|
74
|
+
f"-> {relation.target} ({relation.relation_type.value})")
|
|
75
|
+
except ValueError as e:
|
|
76
|
+
print(f"Error: {e}")
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
return 0
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
sys.exit(main())
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Semantic Relations — Definition 2 implementation.
|
|
3
|
+
|
|
4
|
+
Seven typed relations: refines, instantiates, approximates, implies,
|
|
5
|
+
contradicts, depends_on, supersedes.
|
|
6
|
+
|
|
7
|
+
See manuscript Section 3.3.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RelationType(str, Enum):
|
|
18
|
+
"""Seven semantic relations (Section 3.3)."""
|
|
19
|
+
REFINES = "refines"
|
|
20
|
+
INSTANTIATES = "instantiates"
|
|
21
|
+
APPROXIMATES = "approximates"
|
|
22
|
+
IMPLIES = "implies"
|
|
23
|
+
CONTRADICTS = "contradicts"
|
|
24
|
+
DEPENDS_ON = "depends_on"
|
|
25
|
+
SUPERSEDES = "supersedes"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class SemanticRelation:
|
|
30
|
+
"""A semantic relation between two CSR records."""
|
|
31
|
+
source: str # csr_id
|
|
32
|
+
target: str # csr_id
|
|
33
|
+
relation_type: RelationType
|
|
34
|
+
loss_description: Optional[str] = None # required for approximates
|
|
35
|
+
justification: str = ""
|
|
36
|
+
|
|
37
|
+
def __post_init__(self):
|
|
38
|
+
if self.relation_type == RelationType.APPROXIMATES:
|
|
39
|
+
if not self.loss_description:
|
|
40
|
+
raise ValueError(
|
|
41
|
+
"approximates relation requires a loss_description "
|
|
42
|
+
"(Section 3.3.1)"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ["SemanticRelation", "RelationType"]
|
aetherproof/models.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core data models for AetherProof v2.0.0.
|
|
3
|
+
|
|
4
|
+
Implements Definitions 1–6 from the manuscript.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ─── Enums ───
|
|
17
|
+
|
|
18
|
+
class Verdict(str, Enum):
|
|
19
|
+
"""Verification verdict (Section 4.6)."""
|
|
20
|
+
PROVEN = "proven"
|
|
21
|
+
REFUTED = "refuted"
|
|
22
|
+
UNKNOWN = "unknown"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ObligationType(str, Enum):
|
|
26
|
+
"""Obligation types (Section 4.2)."""
|
|
27
|
+
INVARIANCE = "invariance"
|
|
28
|
+
PRE_POST = "pre_post"
|
|
29
|
+
SATISFIABILITY = "satisfiability"
|
|
30
|
+
CONSISTENCY = "consistency"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CounterexampleType(str, Enum):
|
|
34
|
+
"""Counterexample types (Section 6.1)."""
|
|
35
|
+
CODE_DEFECT = "code_defect"
|
|
36
|
+
SPECIFICATION_ERROR = "specification_error"
|
|
37
|
+
ASSUMPTION_VIOLATION = "assumption_violation"
|
|
38
|
+
MODEL_ERROR = "model_error"
|
|
39
|
+
FLAKY_REFUTATION = "flaky_refutation"
|
|
40
|
+
UNKNOWN = "unknown"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Layer(str, Enum):
|
|
44
|
+
"""Functional layers (Section 3.1)."""
|
|
45
|
+
L1 = "L1" # System-level properties (TLA+)
|
|
46
|
+
L2 = "L2" # Functional contracts (Why3/Lean)
|
|
47
|
+
L3 = "L3" # Logical constraints (SMT-LIB/Z3)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ─── Core Models ───
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True)
|
|
53
|
+
class CSRecord:
|
|
54
|
+
"""
|
|
55
|
+
Canonical Specification Representation (Definition 1).
|
|
56
|
+
|
|
57
|
+
Three identities:
|
|
58
|
+
- Record identity (csr_id)
|
|
59
|
+
- Artifact identity (artifact_hash)
|
|
60
|
+
- Semantic identity (semantic_id)
|
|
61
|
+
"""
|
|
62
|
+
csr_id: str
|
|
63
|
+
artifact_hash: str
|
|
64
|
+
semantic_id: str
|
|
65
|
+
layer: Layer
|
|
66
|
+
language: str
|
|
67
|
+
content: str
|
|
68
|
+
semantic_role: str
|
|
69
|
+
requirements_refs: tuple[str, ...] = field(default_factory=tuple)
|
|
70
|
+
assumptions: tuple[str, ...] = field(default_factory=tuple)
|
|
71
|
+
relations: tuple[str, ...] = field(default_factory=tuple)
|
|
72
|
+
version: str = "1.0.0"
|
|
73
|
+
provenance: str = ""
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def create(
|
|
77
|
+
cls,
|
|
78
|
+
layer: Layer,
|
|
79
|
+
language: str,
|
|
80
|
+
content: str,
|
|
81
|
+
semantic_role: str,
|
|
82
|
+
requirements_refs: tuple[str, ...] = (),
|
|
83
|
+
assumptions: tuple[str, ...] = (),
|
|
84
|
+
) -> "CSRecord":
|
|
85
|
+
"""Create a new CSR record with computed identities."""
|
|
86
|
+
artifact_hash = hashlib.sha256(content.encode()).hexdigest()[:16]
|
|
87
|
+
semantic_id = hashlib.sha256(
|
|
88
|
+
f"{semantic_role}:{content}".encode()
|
|
89
|
+
).hexdigest()[:16]
|
|
90
|
+
csr_id = f"csr-{layer.value}-{artifact_hash[:8]}"
|
|
91
|
+
|
|
92
|
+
return cls(
|
|
93
|
+
csr_id=csr_id,
|
|
94
|
+
artifact_hash=artifact_hash,
|
|
95
|
+
semantic_id=semantic_id,
|
|
96
|
+
layer=layer,
|
|
97
|
+
language=language,
|
|
98
|
+
content=content,
|
|
99
|
+
semantic_role=semantic_role,
|
|
100
|
+
requirements_refs=requirements_refs,
|
|
101
|
+
assumptions=assumptions,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class Obligation:
|
|
107
|
+
"""
|
|
108
|
+
Proof obligation (Definition 4).
|
|
109
|
+
|
|
110
|
+
O = (id, property, assumptions, source_csr, discharged_by, verdict)
|
|
111
|
+
"""
|
|
112
|
+
id: str
|
|
113
|
+
property: str
|
|
114
|
+
assumptions: tuple[str, ...]
|
|
115
|
+
source_csr: str
|
|
116
|
+
discharged_by: Optional[str] = None
|
|
117
|
+
verdict: Verdict = Verdict.UNKNOWN
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def create(
|
|
121
|
+
cls,
|
|
122
|
+
property: str,
|
|
123
|
+
assumptions: tuple[str, ...],
|
|
124
|
+
source_csr: str,
|
|
125
|
+
) -> "Obligation":
|
|
126
|
+
"""Create a new obligation."""
|
|
127
|
+
oid = hashlib.sha256(
|
|
128
|
+
f"{property}:{source_csr}".encode()
|
|
129
|
+
).hexdigest()[:16]
|
|
130
|
+
return cls(
|
|
131
|
+
id=f"O-{oid}",
|
|
132
|
+
property=property,
|
|
133
|
+
assumptions=assumptions,
|
|
134
|
+
source_csr=source_csr,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass(frozen=True)
|
|
139
|
+
class Counterexample:
|
|
140
|
+
"""
|
|
141
|
+
Counterexample (Definition 6).
|
|
142
|
+
|
|
143
|
+
C = (id, obligation, witness, refutation_evidence, type, provenance)
|
|
144
|
+
"""
|
|
145
|
+
id: str
|
|
146
|
+
obligation: str
|
|
147
|
+
witness: str
|
|
148
|
+
refutation_evidence: str
|
|
149
|
+
type: CounterexampleType
|
|
150
|
+
provenance: str = ""
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def create(
|
|
154
|
+
cls,
|
|
155
|
+
obligation: str,
|
|
156
|
+
witness: str,
|
|
157
|
+
refutation_evidence: str,
|
|
158
|
+
type: CounterexampleType = CounterexampleType.UNKNOWN,
|
|
159
|
+
provenance: str = "",
|
|
160
|
+
) -> "Counterexample":
|
|
161
|
+
"""Create a new counterexample."""
|
|
162
|
+
cid = hashlib.sha256(
|
|
163
|
+
f"{obligation}:{witness}".encode()
|
|
164
|
+
).hexdigest()[:16]
|
|
165
|
+
return cls(
|
|
166
|
+
id=f"C-{cid}",
|
|
167
|
+
obligation=obligation,
|
|
168
|
+
witness=witness,
|
|
169
|
+
refutation_evidence=refutation_evidence,
|
|
170
|
+
type=type,
|
|
171
|
+
provenance=provenance,
|
|
172
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Obligation Generator — Section 4.2 implementation.
|
|
3
|
+
|
|
4
|
+
Generates proof obligations from CSR records according to their
|
|
5
|
+
semantic role.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from aetherproof.models import (
|
|
11
|
+
CSRecord,
|
|
12
|
+
Obligation,
|
|
13
|
+
ObligationType,
|
|
14
|
+
Layer,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_obligations(record: CSRecord) -> list[Obligation]:
|
|
19
|
+
"""
|
|
20
|
+
Generate obligations from a CSR record.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
record : CSRecord
|
|
25
|
+
The CSR record.
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
list[Obligation]
|
|
30
|
+
Generated obligations.
|
|
31
|
+
"""
|
|
32
|
+
if record.semantic_role == "invariant" and record.layer == Layer.L1:
|
|
33
|
+
return [
|
|
34
|
+
Obligation.create(
|
|
35
|
+
property=f"Invariance({record.content})",
|
|
36
|
+
assumptions=record.assumptions,
|
|
37
|
+
source_csr=record.csr_id,
|
|
38
|
+
)
|
|
39
|
+
]
|
|
40
|
+
elif record.semantic_role == "contract" and record.layer == Layer.L2:
|
|
41
|
+
return [
|
|
42
|
+
Obligation.create(
|
|
43
|
+
property=f"PrePost({record.content})",
|
|
44
|
+
assumptions=record.assumptions,
|
|
45
|
+
source_csr=record.csr_id,
|
|
46
|
+
)
|
|
47
|
+
]
|
|
48
|
+
elif record.semantic_role == "constraint" and record.layer == Layer.L3:
|
|
49
|
+
return [
|
|
50
|
+
Obligation.create(
|
|
51
|
+
property=f"Satisfiability({record.content})",
|
|
52
|
+
assumptions=record.assumptions,
|
|
53
|
+
source_csr=record.csr_id,
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
elif record.semantic_role == "translation":
|
|
57
|
+
return [
|
|
58
|
+
Obligation.create(
|
|
59
|
+
property=f"Consistency({record.content})",
|
|
60
|
+
assumptions=record.assumptions,
|
|
61
|
+
source_csr=record.csr_id,
|
|
62
|
+
)
|
|
63
|
+
]
|
|
64
|
+
else:
|
|
65
|
+
return []
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
__all__ = ["generate_obligations"]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Obligation Dependency Graph (ODG) — Section 4.3 implementation.
|
|
3
|
+
|
|
4
|
+
The ODG is a directed acyclic graph over obligations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from aetherproof.models import Obligation, Verdict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EdgeType(str, Enum):
|
|
17
|
+
"""ODG edge types (Section 4.3)."""
|
|
18
|
+
REQUIRES = "requires"
|
|
19
|
+
REFINES = "refines"
|
|
20
|
+
ASSUMES = "assumes"
|
|
21
|
+
CONTRADICTS = "contradicts"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ODG:
|
|
26
|
+
"""Obligation Dependency Graph."""
|
|
27
|
+
obligations: dict[str, Obligation] = field(default_factory=dict)
|
|
28
|
+
edges: list[tuple[str, str, EdgeType]] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
def add_obligation(self, obligation: Obligation) -> None:
|
|
31
|
+
"""Add an obligation to the graph."""
|
|
32
|
+
self.obligations[obligation.id] = obligation
|
|
33
|
+
|
|
34
|
+
def add_edge(
|
|
35
|
+
self,
|
|
36
|
+
source_id: str,
|
|
37
|
+
target_id: str,
|
|
38
|
+
edge_type: EdgeType,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Add an edge between two obligations."""
|
|
41
|
+
if source_id not in self.obligations:
|
|
42
|
+
raise ValueError(f"Source obligation {source_id} not found")
|
|
43
|
+
if target_id not in self.obligations:
|
|
44
|
+
raise ValueError(f"Target obligation {target_id} not found")
|
|
45
|
+
|
|
46
|
+
# Check for cycles in dependency-bearing edges
|
|
47
|
+
if edge_type in (EdgeType.REQUIRES, EdgeType.REFINES, EdgeType.ASSUMES):
|
|
48
|
+
if self._would_create_cycle(source_id, target_id):
|
|
49
|
+
raise ValueError(
|
|
50
|
+
f"Adding edge ({source_id} -> {target_id}) "
|
|
51
|
+
f"would create a cycle (Proposition P3)"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
self.edges.append((source_id, target_id, edge_type))
|
|
55
|
+
|
|
56
|
+
def _would_create_cycle(self, source_id: str, target_id: str) -> bool:
|
|
57
|
+
"""Check if adding an edge would create a cycle."""
|
|
58
|
+
# Simple DFS from target_id to see if we can reach source_id
|
|
59
|
+
visited: set[str] = set()
|
|
60
|
+
stack = [target_id]
|
|
61
|
+
|
|
62
|
+
while stack:
|
|
63
|
+
current = stack.pop()
|
|
64
|
+
if current == source_id:
|
|
65
|
+
return True
|
|
66
|
+
if current in visited:
|
|
67
|
+
continue
|
|
68
|
+
visited.add(current)
|
|
69
|
+
|
|
70
|
+
for s, t, e in self.edges:
|
|
71
|
+
if s == current and e in (
|
|
72
|
+
EdgeType.REQUIRES, EdgeType.REFINES, EdgeType.ASSUMES
|
|
73
|
+
):
|
|
74
|
+
stack.append(t)
|
|
75
|
+
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def invalidate(self, obligation_id: str) -> list[str]:
|
|
79
|
+
"""
|
|
80
|
+
Invalidate an obligation and all dependents.
|
|
81
|
+
|
|
82
|
+
Returns list of invalidated obligation IDs.
|
|
83
|
+
|
|
84
|
+
The invalidation propagates FORWARD: if O1 requires O2 (O1 depends on O2),
|
|
85
|
+
then invalidating O2 invalidates O1. The edge direction is (source, target),
|
|
86
|
+
meaning "source depends on target". So if we invalidate `obligation_id`,
|
|
87
|
+
we need to find all `source` that point to `obligation_id` as their target.
|
|
88
|
+
"""
|
|
89
|
+
if obligation_id not in self.obligations:
|
|
90
|
+
raise ValueError(f"Obligation {obligation_id} not found")
|
|
91
|
+
|
|
92
|
+
invalidated = []
|
|
93
|
+
to_invalidate = {obligation_id}
|
|
94
|
+
|
|
95
|
+
while to_invalidate:
|
|
96
|
+
current = to_invalidate.pop()
|
|
97
|
+
if current in invalidated:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
invalidated.append(current)
|
|
101
|
+
|
|
102
|
+
# Find dependents: obligations that require/refine/assume current
|
|
103
|
+
# Edge direction is (source, target), where source depends on target.
|
|
104
|
+
# So if target == current, then source is a dependent.
|
|
105
|
+
for s, t, e in self.edges:
|
|
106
|
+
if t == current and e in (
|
|
107
|
+
EdgeType.REQUIRES, EdgeType.REFINES, EdgeType.ASSUMES
|
|
108
|
+
):
|
|
109
|
+
if s not in invalidated:
|
|
110
|
+
to_invalidate.add(s)
|
|
111
|
+
|
|
112
|
+
return invalidated
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
__all__ = ["ODG", "EdgeType"]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Proof Dependency Graph (PDG) and differential proving."""
|
|
2
|
+
|
|
3
|
+
from aetherproof.proof.pdg import PDG, NodeType
|
|
4
|
+
from aetherproof.proof.invalidation import invalidate
|
|
5
|
+
from aetherproof.proof.engine import ProofEngine
|
|
6
|
+
|
|
7
|
+
__all__ = ["PDG", "NodeType", "invalidate", "ProofEngine"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Differential Proof Engine — Section 5 implementation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
|
|
9
|
+
from aetherproof.models import Obligation, Verdict
|
|
10
|
+
from aetherproof.proof.pdg import PDG
|
|
11
|
+
from aetherproof.proof.invalidation import invalidate
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ProofEngine:
|
|
16
|
+
"""
|
|
17
|
+
Incremental differential proof engine.
|
|
18
|
+
|
|
19
|
+
Implements cost-aware tiered proving (Section 5.6).
|
|
20
|
+
"""
|
|
21
|
+
pdg: PDG
|
|
22
|
+
proof_budget: dict = field(default_factory=lambda: {
|
|
23
|
+
"time": 60.0,
|
|
24
|
+
"cost": 100.0,
|
|
25
|
+
"human_attention": 5.0,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
def discharge(
|
|
29
|
+
self,
|
|
30
|
+
obligation: Obligation,
|
|
31
|
+
evidence: str,
|
|
32
|
+
) -> Obligation:
|
|
33
|
+
"""Discharge an obligation with evidence."""
|
|
34
|
+
return Obligation(
|
|
35
|
+
id=obligation.id,
|
|
36
|
+
property=obligation.property,
|
|
37
|
+
assumptions=obligation.assumptions,
|
|
38
|
+
source_csr=obligation.source_csr,
|
|
39
|
+
discharged_by=evidence,
|
|
40
|
+
verdict=Verdict.PROVEN,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def refute(
|
|
44
|
+
self,
|
|
45
|
+
obligation: Obligation,
|
|
46
|
+
counterexample: str,
|
|
47
|
+
) -> Obligation:
|
|
48
|
+
"""Refute an obligation with a counterexample."""
|
|
49
|
+
return Obligation(
|
|
50
|
+
id=obligation.id,
|
|
51
|
+
property=obligation.property,
|
|
52
|
+
assumptions=obligation.assumptions,
|
|
53
|
+
source_csr=obligation.source_csr,
|
|
54
|
+
discharged_by=counterexample,
|
|
55
|
+
verdict=Verdict.REFUTED,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def reuse_conditions_met(
|
|
59
|
+
self,
|
|
60
|
+
obligation_id: str,
|
|
61
|
+
new_changeset: str,
|
|
62
|
+
) -> bool:
|
|
63
|
+
"""
|
|
64
|
+
Check if an invalidated obligation can be reused.
|
|
65
|
+
|
|
66
|
+
Implements the five reuse conditions (Section 5.4).
|
|
67
|
+
"""
|
|
68
|
+
# In a real implementation, this would check:
|
|
69
|
+
# 1. Assumption preservation
|
|
70
|
+
# 2. Evidence compatibility
|
|
71
|
+
# 3. Property stability
|
|
72
|
+
# 4. Semantic relation preservation
|
|
73
|
+
# 5. Artifact stability (proposed)
|
|
74
|
+
return False # Conservative: always re-discharge
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
__all__ = ["ProofEngine"]
|