Audit of the one-mutation-path invariant (ADR-0021 §3) found three leaks
where pack authority or session-state writes could substitute for coherence
judgment. All three landed fixes or partial closures in this push.
Leaks closed:
- Leak A: pack vocab defaulted to COHERENT — flipped to SPECULATIVE in
language_packs/{compiler,schema}.py; docstring corrected to align with
ADR-0021 (it was rationalizing the leak).
- Leak B: vault.recall was epistemic-blind — VaultStore.store() now stamps
every entry with EpistemicStatus (default SPECULATIVE); recall(min_status=)
filters to admissible-as-evidence tier. All 4 vault-write sites updated.
- Leak C (write-side): generate/proposition.py:198 stored articulated
propositions unmarked — now stamps SPECULATIVE, breaking the
fabrication-feedback loop in principle. Read-side audit of 5 call sites
is the residual.
New architectural invariants (tests/test_architectural_invariants.py):
- INV-21: one-mutation-path allowlist (caught Leak C on first run)
- INV-22: pack lexicon default is SPECULATIVE (Leak A guard)
- INV-23: vault recall epistemic-aware (Leak B guard)
New eval lanes:
- teaching_injection_resistance — ships GREEN at 1.00/1.00/0 (the
structural anti-injection claim is real and measurable)
- refusal_calibration — honest gap: 0% refusal, 0% fabrication
- contradiction_detection — honest gap: 50% flag via versor-delta heuristic,
100% false-positive; motivates the proper coherence-checker
- articulation_of_status — honest gap: 0% speculative articulation, 60%
false certainty; output-side leak surface
New benchmarks:
- benchmarks/footprint.py — total deployed runtime is 7.06 MiB
(109,358x smaller than Llama 3.1 405B, runs offline, no GPU)
- benchmarks/learning_curve.py — monotonic + replay-deterministic curve
per lane
Documentation:
- docs/truth_seeking_schema.md — foundational architectural commitment,
five rules, mapped to human failure modes, leaks published openly
- evals/CLAIMS.md — five-tier public claims doc; Tier 4.5 publishes
known gaps with named fixes; verification contract at top
- README.md — new pillar between algebraic substrate and language pillar
Includes in-flight formation pipeline scaffolding (formation/, tests/formation/,
docs/formation_pipeline_plan.md) and minor CLI/contracts/gitignore edits
that were already in the working tree at session start.
Verification: 798 passed, 2 skipped, 1 deselected (pre-existing pack-count
test drift unrelated to schema changes).
78 lines
2.6 KiB
Python
78 lines
2.6 KiB
Python
"""Tests for ``formation.hashing`` — the content-addressing foundation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from formation.hashing import (
|
|
canonical_json,
|
|
self_seal,
|
|
sha256_of,
|
|
verify_seal,
|
|
)
|
|
|
|
|
|
class TestCanonicalJson:
|
|
def test_sorted_keys(self) -> None:
|
|
assert canonical_json({"b": 1, "a": 2}) == b'{"a":2,"b":1}'
|
|
|
|
def test_tight_separators(self) -> None:
|
|
assert canonical_json([1, 2, 3]) == b"[1,2,3]"
|
|
|
|
def test_nested_stability(self) -> None:
|
|
payload = {"z": [3, 2, {"y": 1, "x": 2}], "a": "hello"}
|
|
# Same content, different construction order, must serialize identically.
|
|
twin = {"a": "hello", "z": [3, 2, {"x": 2, "y": 1}]}
|
|
assert canonical_json(payload) == canonical_json(twin)
|
|
|
|
def test_utf8(self) -> None:
|
|
assert canonical_json({"k": "wisdom λόγος"}) == '{"k":"wisdom λόγος"}'.encode()
|
|
|
|
def test_floats_forbidden(self) -> None:
|
|
with pytest.raises(TypeError, match="float values are forbidden"):
|
|
canonical_json({"x": 1.5})
|
|
|
|
def test_floats_forbidden_nested(self) -> None:
|
|
with pytest.raises(TypeError):
|
|
canonical_json([1, 2, [3, 4.0]])
|
|
|
|
def test_non_string_dict_keys_forbidden(self) -> None:
|
|
with pytest.raises(TypeError, match="dict keys must be strings"):
|
|
canonical_json({1: "x"})
|
|
|
|
|
|
class TestSha256Of:
|
|
def test_deterministic(self) -> None:
|
|
payload = {"a": 1, "b": [1, 2, 3]}
|
|
assert sha256_of(payload) == sha256_of(payload)
|
|
|
|
def test_different_content_different_sha(self) -> None:
|
|
assert sha256_of({"a": 1}) != sha256_of({"a": 2})
|
|
|
|
def test_hex_64_chars(self) -> None:
|
|
sha = sha256_of({"a": 1})
|
|
assert len(sha) == 64
|
|
assert all(c in "0123456789abcdef" for c in sha)
|
|
|
|
|
|
class TestSelfSeal:
|
|
def test_seal_then_verify(self) -> None:
|
|
sealed = self_seal({"course_id": "x", "report_sha256": ""})
|
|
assert verify_seal(sealed) is True
|
|
assert sealed["report_sha256"] != ""
|
|
|
|
def test_tamper_breaks_seal(self) -> None:
|
|
sealed = self_seal({"course_id": "x", "report_sha256": ""})
|
|
tampered = dict(sealed)
|
|
tampered["course_id"] = "y"
|
|
assert verify_seal(tampered) is False
|
|
|
|
def test_blank_sha_fails_verify(self) -> None:
|
|
assert verify_seal({"course_id": "x", "report_sha256": ""}) is False
|
|
|
|
def test_missing_field_raises_on_seal(self) -> None:
|
|
with pytest.raises(ValueError, match="missing required field"):
|
|
self_seal({"course_id": "x"})
|
|
|
|
def test_missing_field_returns_false_on_verify(self) -> None:
|
|
assert verify_seal({"course_id": "x"}) is False
|