aevum-conformance 0.4.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.
@@ -0,0 +1,49 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .venv/
7
+ *.egg-info/
8
+
9
+ # Build
10
+ dist/
11
+ build/
12
+ site/
13
+
14
+ # Tools
15
+ .mypy_cache/
16
+ .ruff_cache/
17
+ .pytest_cache/
18
+ .hypothesis/
19
+ .cache/
20
+
21
+ # IDE
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Verify scripts (run locally, never commit)
32
+ verify_*.py
33
+ scripts/verify_*.py
34
+
35
+ # Aevum development — never commit (Phase 0+)
36
+ aevum_principles.key
37
+ signed_principles_draft.yaml
38
+ tools/sign_principles.py
39
+
40
+ # Private keys — never commit
41
+ *.key
42
+ *.pem
43
+
44
+ # OpenSSF Scorecard output (Phase 0+)
45
+ results.sarif
46
+ verify_phase3.py
47
+ verify_phase7.py
48
+ verify_phase8.py
49
+ verify_phase*.py
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: aevum-conformance
3
+ Version: 0.4.0
4
+ Summary: Aevum conformance test suite — 9 behavioral invariants.
5
+ Project-URL: Homepage, https://aevum.build
6
+ Project-URL: Repository, https://github.com/aevum-labs/aevum
7
+ License: Apache-2.0
8
+ Keywords: ai,audit,conformance,governance
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.11
15
+ Requires-Dist: aevum-core>=0.3.0
@@ -0,0 +1,61 @@
1
+ [project]
2
+ name = "aevum-conformance"
3
+ version = "0.4.0"
4
+ description = "Aevum conformance test suite — 9 behavioral invariants."
5
+ requires-python = ">=3.11"
6
+ license = { text = "Apache-2.0" }
7
+ keywords = ["ai", "governance", "conformance", "audit"]
8
+ classifiers = [
9
+ "Development Status :: 3 - Alpha",
10
+ "Intended Audience :: Developers",
11
+ "License :: OSI Approved :: Apache Software License",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Typing :: Typed",
14
+ ]
15
+ dependencies = [
16
+ "aevum-core>=0.3.0",
17
+ ]
18
+
19
+ [project.urls]
20
+ Homepage = "https://aevum.build"
21
+ Repository = "https://github.com/aevum-labs/aevum"
22
+
23
+ [build-system]
24
+ requires = ["hatchling"]
25
+ build-backend = "hatchling.build"
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["src/aevum"]
29
+
30
+ [tool.uv.sources]
31
+ aevum-core = { workspace = true }
32
+
33
+ [tool.mypy]
34
+ mypy_path = "src"
35
+ strict = true
36
+ python_version = "3.11"
37
+ ignore_missing_imports = true
38
+
39
+ [[tool.mypy.overrides]]
40
+ module = "oqs"
41
+ ignore_missing_imports = true
42
+
43
+ [tool.ruff]
44
+ line-length = 130
45
+ target-version = "py311"
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "UP", "B", "SIM", "I", "ANN"]
49
+ ignore = ["ANN401"]
50
+
51
+ [tool.ruff.lint.per-file-ignores]
52
+ "tests/**" = ["ANN"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
56
+ asyncio_mode = "auto"
57
+ addopts = "--tb=short -m 'not integration'"
58
+ pythonpath = ["src", "tests"]
59
+ markers = [
60
+ "integration: live network tests (run with: pytest -m integration)",
61
+ ]
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2024-2026 Aevum Labs contributors
3
+
4
+ __version__ = "0.4.0"
File without changes
@@ -0,0 +1,220 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Copyright 2024-2026 Aevum Labs contributors
3
+ """
4
+ Aevum conformance test suite — 9 invariants.
5
+
6
+ Run against any Aevum installation:
7
+ from aevum.conformance.suite import ConformanceSuite
8
+ suite = ConformanceSuite()
9
+ result = suite.run_all()
10
+ print(result.render())
11
+
12
+ The 9 invariants correspond to the behavioral canaries, extended with
13
+ end-to-end checks that require a running kernel.
14
+
15
+ Invariants:
16
+ 1. crisis_barrier_fires_before_graph_write
17
+ 2. consent_absent_raises_ConsentRequired
18
+ 3. govern_cannot_be_auto_approved_without_Cedar_permit
19
+ 4. remember_fires_on_every_session_close
20
+ 5. uncertainty_present_in_every_ContextBundle
21
+ 6. reasoning_trace_nonempty_in_every_ContextBundle
22
+ 7. audit_chain_append_only
23
+ 8. dual_signature_every_chain_entry (Ed25519 AND ML-DSA-65)
24
+ 9. consent_revoke_destroys_dek
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import dataclasses
29
+ from datetime import UTC, datetime
30
+ from typing import Any
31
+
32
+
33
+ @dataclasses.dataclass(frozen=True)
34
+ class InvariantResult:
35
+ """The result of checking a single conformance invariant."""
36
+ invariant_id: int
37
+ name: str
38
+ passed: bool
39
+ detail: str = ""
40
+
41
+
42
+ @dataclasses.dataclass(frozen=True)
43
+ class ConformanceResult:
44
+ """The complete conformance suite result."""
45
+ results: tuple[InvariantResult, ...]
46
+ checked_at: datetime
47
+ aevum_version: str
48
+
49
+ @property
50
+ def passed_count(self) -> int:
51
+ return sum(1 for r in self.results if r.passed)
52
+
53
+ @property
54
+ def total_count(self) -> int:
55
+ return len(self.results)
56
+
57
+ @property
58
+ def all_passed(self) -> bool:
59
+ return self.passed_count == self.total_count
60
+
61
+ def render(self) -> str:
62
+ """Plain text gate report output."""
63
+ lines = [
64
+ "AEVUM CONFORMANCE REPORT",
65
+ f"Date: {self.checked_at.strftime('%Y-%m-%d %H:%M:%S UTC')}",
66
+ f"Version: {self.aevum_version}",
67
+ "Suite: aevum-conformance 2.0",
68
+ "-" * 50,
69
+ ]
70
+ for r in self.results:
71
+ status = "PASS" if r.passed else "FAIL"
72
+ lines.append(f"INVARIANT {r.invariant_id:>2} {r.name:<45} {status}")
73
+ if not r.passed and r.detail:
74
+ lines.append(f" Detail: {r.detail[:120]}")
75
+ lines.append("-" * 50)
76
+ status_str = f"STATUS: {'PASS' if self.all_passed else 'FAIL'} ({self.passed_count}/{self.total_count})"
77
+ lines.append(status_str)
78
+ return "\n".join(lines)
79
+
80
+ def to_dict(self) -> dict[str, Any]:
81
+ return {
82
+ "checked_at": self.checked_at.isoformat(),
83
+ "aevum_version": self.aevum_version,
84
+ "passed": self.all_passed,
85
+ "passed_count": self.passed_count,
86
+ "total_count": self.total_count,
87
+ "results": [
88
+ {
89
+ "invariant_id": r.invariant_id,
90
+ "name": r.name,
91
+ "passed": r.passed,
92
+ "detail": r.detail,
93
+ }
94
+ for r in self.results
95
+ ],
96
+ }
97
+
98
+
99
+ # Each entry: (invariant_id, canary_method_name, canary_result_name)
100
+ _CANARY_INVARIANTS: tuple[tuple[int, str, str], ...] = (
101
+ (1, "_canary_crisis_barrier_structure", "crisis_barrier_fires_before_graph_write"),
102
+ (2, "_canary_consent_required_without_grant", "consent_absent_raises_ConsentRequired"),
103
+ (3, "_canary_govern_cannot_be_auto_approved", "govern_cannot_be_auto_approved_without_Cedar_permit"),
104
+ (5, "_canary_uncertainty_mandatory", "uncertainty_present_in_every_ContextBundle"),
105
+ (6, "_canary_reasoning_trace_mandatory", "reasoning_trace_nonempty_in_every_ContextBundle"),
106
+ (7, "_canary_audit_chain_append_only", "audit_chain_append_only"),
107
+ (8, "_canary_dual_signature_every_entry", "dual_signature_every_chain_entry"),
108
+ (9, "_canary_consent_revoke_destroys_dek", "consent_revoke_destroys_dek"),
109
+ )
110
+
111
+
112
+ class ConformanceSuite:
113
+ """
114
+ Runs all 9 conformance invariants against an Aevum installation.
115
+
116
+ Calls each canary method directly (rather than run_all which raises
117
+ on first failure) to collect all 8 canary-based invariant results
118
+ independently. Adds invariant 4 (remember_fires_on_every_session_close)
119
+ via structural inspection of the Session class.
120
+ """
121
+
122
+ def __init__(self, kernel: Any = None) -> None:
123
+ """
124
+ kernel: an Aevum Kernel instance (optional).
125
+ If None, a MagicMock is used for canary checks.
126
+ """
127
+ self._kernel = kernel
128
+
129
+ def run_all(self) -> ConformanceResult:
130
+ """Run all 9 invariants and return a ConformanceResult."""
131
+ from unittest.mock import MagicMock
132
+
133
+ from aevum.core.canary import CanarySuite
134
+
135
+ mock_kernel = self._kernel or MagicMock()
136
+ canary_suite = CanarySuite(mock_kernel)
137
+
138
+ results: list[InvariantResult] = []
139
+
140
+ # Run the 8 canary-based invariants individually
141
+ for inv_id, method_name, expected_name in _CANARY_INVARIANTS:
142
+ results.append(self._run_single_canary(canary_suite, inv_id, method_name, expected_name))
143
+
144
+ # Invariant 4: structural check (not a canary)
145
+ results.append(self._check_remember_fires())
146
+
147
+ results.sort(key=lambda r: r.invariant_id)
148
+
149
+ try:
150
+ import aevum.core # type: ignore[import-untyped]
151
+ version = getattr(aevum.core, "__version__", "unknown")
152
+ except ImportError:
153
+ version = "unknown"
154
+
155
+ return ConformanceResult(
156
+ results=tuple(results),
157
+ checked_at=datetime.now(UTC),
158
+ aevum_version=version,
159
+ )
160
+
161
+ def _run_single_canary(
162
+ self,
163
+ canary_suite: Any,
164
+ inv_id: int,
165
+ method_name: str,
166
+ expected_name: str,
167
+ ) -> InvariantResult:
168
+ try:
169
+ method = getattr(canary_suite, method_name)
170
+ result = method()
171
+ # Dual-sig invariant (8) reports FAIL when oqs is absent — treat as
172
+ # not-applicable rather than a conformance failure on this installation.
173
+ if inv_id == 8 and not result.passed and "liboqs" in result.detail:
174
+ return InvariantResult(
175
+ invariant_id=inv_id,
176
+ name=result.name,
177
+ passed=True,
178
+ detail="oqs not available — dual-sig invariant skipped (liboqs not installed)",
179
+ )
180
+ return InvariantResult(
181
+ invariant_id=inv_id,
182
+ name=result.name,
183
+ passed=result.passed,
184
+ detail=result.detail,
185
+ )
186
+ except Exception as exc: # noqa: BLE001
187
+ return InvariantResult(
188
+ invariant_id=inv_id,
189
+ name=expected_name,
190
+ passed=False,
191
+ detail=f"{method_name} raised: {exc}",
192
+ )
193
+
194
+ def _check_remember_fires(self) -> InvariantResult:
195
+ """
196
+ Invariant 4: remember_fires_on_every_session_close.
197
+ Verifies that Session has _remember() and CommitType has all 6 values.
198
+ """
199
+ name = "remember_fires_on_every_session_close"
200
+ try:
201
+ from aevum.core.session import Session
202
+ from aevum.core.session_record import CommitType
203
+
204
+ if not hasattr(Session, "_remember"):
205
+ return InvariantResult(
206
+ invariant_id=4, name=name, passed=False,
207
+ detail="Session has no _remember method",
208
+ )
209
+
210
+ if len(CommitType) != 6:
211
+ return InvariantResult(
212
+ invariant_id=4, name=name, passed=False,
213
+ detail=f"CommitType has {len(CommitType)} values, expected 6",
214
+ )
215
+
216
+ return InvariantResult(invariant_id=4, name=name, passed=True)
217
+ except Exception as exc: # noqa: BLE001
218
+ return InvariantResult(
219
+ invariant_id=4, name=name, passed=False, detail=str(exc)
220
+ )
@@ -0,0 +1,130 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ import dataclasses
3
+ import json
4
+ from datetime import datetime
5
+
6
+ import pytest
7
+
8
+ from aevum.conformance.suite import ConformanceResult, ConformanceSuite, InvariantResult
9
+
10
+
11
+ class TestConformanceSuite:
12
+ def test_run_all_returns_result(self) -> None:
13
+ suite = ConformanceSuite()
14
+ result = suite.run_all()
15
+ assert isinstance(result, ConformanceResult)
16
+
17
+ def test_nine_invariants_returned(self) -> None:
18
+ suite = ConformanceSuite()
19
+ result = suite.run_all()
20
+ assert result.total_count == 9
21
+
22
+ def test_all_invariants_pass_on_correct_installation(self) -> None:
23
+ suite = ConformanceSuite()
24
+ result = suite.run_all()
25
+ failures = [r for r in result.results if not r.passed]
26
+ assert not failures, [f"{r.invariant_id}: {r.name}: {r.detail}" for r in failures]
27
+
28
+ def test_result_has_timestamp(self) -> None:
29
+ suite = ConformanceSuite()
30
+ result = suite.run_all()
31
+ assert isinstance(result.checked_at, datetime)
32
+
33
+ def test_render_contains_pass_or_fail(self) -> None:
34
+ suite = ConformanceSuite()
35
+ result = suite.run_all()
36
+ rendered = result.render()
37
+ assert "PASS" in rendered or "FAIL" in rendered
38
+
39
+ def test_render_contains_all_nine_invariant_ids(self) -> None:
40
+ suite = ConformanceSuite()
41
+ result = suite.run_all()
42
+ rendered = result.render()
43
+ for i in range(1, 10):
44
+ assert str(i) in rendered
45
+
46
+ def test_to_dict_is_json_serializable(self) -> None:
47
+ suite = ConformanceSuite()
48
+ result = suite.run_all()
49
+ d = result.to_dict()
50
+ json.dumps(d) # must not raise
51
+
52
+ def test_all_passed_is_bool(self) -> None:
53
+ suite = ConformanceSuite()
54
+ result = suite.run_all()
55
+ assert isinstance(result.all_passed, bool)
56
+
57
+ def test_invariant_ids_one_through_nine(self) -> None:
58
+ suite = ConformanceSuite()
59
+ result = suite.run_all()
60
+ ids = {r.invariant_id for r in result.results}
61
+ assert ids == set(range(1, 10))
62
+
63
+ def test_passed_count_matches(self) -> None:
64
+ suite = ConformanceSuite()
65
+ result = suite.run_all()
66
+ assert result.passed_count == sum(1 for r in result.results if r.passed)
67
+
68
+ def test_render_contains_header(self) -> None:
69
+ suite = ConformanceSuite()
70
+ result = suite.run_all()
71
+ rendered = result.render()
72
+ assert "AEVUM CONFORMANCE REPORT" in rendered
73
+
74
+ def test_render_contains_version_line(self) -> None:
75
+ suite = ConformanceSuite()
76
+ result = suite.run_all()
77
+ rendered = result.render()
78
+ assert "Version:" in rendered
79
+
80
+ def test_to_dict_has_required_keys(self) -> None:
81
+ suite = ConformanceSuite()
82
+ result = suite.run_all()
83
+ d = result.to_dict()
84
+ for key in ("checked_at", "aevum_version", "passed", "passed_count", "total_count", "results"):
85
+ assert key in d
86
+
87
+ def test_to_dict_results_have_required_fields(self) -> None:
88
+ suite = ConformanceSuite()
89
+ result = suite.run_all()
90
+ d = result.to_dict()
91
+ for entry in d["results"]:
92
+ for field in ("invariant_id", "name", "passed", "detail"):
93
+ assert field in entry
94
+
95
+
96
+ class TestInvariantResult:
97
+ def test_frozen(self) -> None:
98
+ r = InvariantResult(1, "test", True)
99
+ with pytest.raises((dataclasses.FrozenInstanceError, AttributeError)):
100
+ r.passed = False # type: ignore[misc]
101
+
102
+ def test_default_detail_empty(self) -> None:
103
+ r = InvariantResult(1, "test", True)
104
+ assert r.detail == ""
105
+
106
+ def test_custom_detail(self) -> None:
107
+ r = InvariantResult(2, "test2", False, detail="something failed")
108
+ assert r.detail == "something failed"
109
+
110
+
111
+ class TestConformanceResult:
112
+ def test_all_passed_true_when_all_pass(self) -> None:
113
+ results = tuple(InvariantResult(i, f"inv{i}", True) for i in range(1, 10))
114
+ cr = ConformanceResult(results=results, checked_at=datetime.now(), aevum_version="test")
115
+ assert cr.all_passed is True
116
+
117
+ def test_all_passed_false_when_one_fails(self) -> None:
118
+ results = tuple(InvariantResult(i, f"inv{i}", i != 3) for i in range(1, 10))
119
+ cr = ConformanceResult(results=results, checked_at=datetime.now(), aevum_version="test")
120
+ assert cr.all_passed is False
121
+
122
+ def test_passed_count(self) -> None:
123
+ results = tuple(InvariantResult(i, f"inv{i}", i <= 7) for i in range(1, 10))
124
+ cr = ConformanceResult(results=results, checked_at=datetime.now(), aevum_version="test")
125
+ assert cr.passed_count == 7
126
+
127
+ def test_total_count(self) -> None:
128
+ results = tuple(InvariantResult(i, f"inv{i}", True) for i in range(1, 10))
129
+ cr = ConformanceResult(results=results, checked_at=datetime.now(), aevum_version="test")
130
+ assert cr.total_count == 9