core/tests/test_identity_packs.py
Shay c3e36f07b2 feat(identity-packs): ADR-0027 Phase 5 — ratify all three v1 packs
Drives the three v1 identity packs through the full formation pipeline
(Forge -> Compose -> Compile -> Run -> Ratify) and embeds the resulting
self-sealed MasteryReport SHAs into each pack file.  Companion
'<pack_id>.mastery_report.json' artifacts ship alongside.  Loader now
defaults to production mode (require_ratified=None) and ChatRuntime
calls it without the dev-only override.

Ratification results:
  default_general_v1   -> 0b77357fe4359f161d7ca72f184b6e0db2f9e2de16b32c237a3b80d2bbb005b4
  precision_first_v1   -> 5f5000dba9a0dd19d831e9ab5d3c0e3b9faf6abdc2648940e96aa6263af3302e
  generosity_first_v1  -> 91716117558113f74b2c6d07a804cb324f262d62b743523d901d1386a4f85ae4

Driver: scripts/ratify_identity_packs.py — idempotent.  Re-running on
already-current packs is a no-op (verified by a test).  Each pack is
treated as its own provenance source: source_sha = SHA-256 of the pack's
canonical JSON body with mastery_report_sha256 blanked, so the
self-referential chain stays stable across SHA updates.  Axes become
ConceptCandidates; canned override-attempt triples become
CounterCandidates; the identity_anchor template renders the body.

Loader hardening (packs/identity/loader.py):
  * When require_ratified resolves to True, the loader now requires the
    companion '<pack_id>.mastery_report.json' to exist, its
    report_sha256 to match the pack's mastery_report_sha256, and its
    self-seal to verify via formation.hashing.verify_seal.
  * Tampered companion (wrong SHA, broken seal) is rejected with a
    diagnostic IdentityPackError.

Tests: 18 -> 23.  New cases cover production-mode loading of all three
v1 packs, missing companion file, mismatched companion SHA, failed
self-seal, and end-to-end idempotency of the ratification script
(subprocess-launched, asserts pack bytes unchanged on re-run).

Suite status: cognition 121, teaching 17, runtime 19, formation 182,
smoke 67 — all green.

Docs updated: ADR-0027 status flipped to Phases 1-6 complete with the
three report SHAs recorded; docs/identity_packs.md notes the ratified
SHAs and the re-ratification command; memory file 'identity-packs.md'
refreshed.
2026-05-17 19:31:55 -07:00

353 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Identity-pack loader + runtime wiring tests.
Covers ADR-0027 Phase 14 surface area:
* Loader bounds checks (missing fields, malformed direction, weight,
threshold, duplicate axis id, missing pack).
* Round-trip parity: ``default_general_v1`` constructs an
``IdentityManifold`` equal to the pre-ADR hardcoded one.
* Runtime wiring: ``ChatRuntime`` loads the pack indicated by
``RuntimeConfig.identity_pack`` (and falls back to the default).
* Pack swap: ``precision_first_v1`` and ``generosity_first_v1`` produce
manifolds that differ from the default in axis weights / thresholds.
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from chat.runtime import ChatRuntime
from core.config import DEFAULT_IDENTITY_PACK, RuntimeConfig
from packs.identity.loader import (
IdentityPackError,
available_packs,
load_identity_manifold,
)
# ---------- loader bounds ----------
class TestLoaderHappyPath:
def test_loads_default_general(self) -> None:
m = load_identity_manifold(
"default_general_v1", require_ratified=False,
)
assert len(m.value_axes) == 3
axis_ids = {a.axis_id for a in m.value_axes}
assert axis_ids == {"truthfulness", "coherence", "reverence"}
assert m.alignment_threshold == 0.45
assert m.boundary_ids == frozenset(
{"no_fabricated_source", "no_hot_path_repair"}
)
def test_loads_precision_first(self) -> None:
m = load_identity_manifold(
"precision_first_v1", require_ratified=False,
)
weights = {a.axis_id: a.weight for a in m.value_axes}
# Precision pack boosts truthfulness weight to 2.0; defaults are 1.0.
assert weights["truthfulness"] == 2.0
assert weights["coherence"] == 0.7
assert m.alignment_threshold == 0.55
def test_loads_generosity_first(self) -> None:
m = load_identity_manifold(
"generosity_first_v1", require_ratified=False,
)
weights = {a.axis_id: a.weight for a in m.value_axes}
assert weights["coherence"] == 2.0
assert weights["truthfulness"] == 0.8
def test_available_packs(self) -> None:
packs = available_packs()
ids = {p["pack_id"] for p in packs}
assert {"default_general_v1", "precision_first_v1", "generosity_first_v1"} <= ids
# Phase 5 complete — v1 packs are ratified.
ratified_ids = {p["pack_id"] for p in packs if p["ratified"]}
assert {"default_general_v1", "precision_first_v1", "generosity_first_v1"} <= ratified_ids
def test_v1_packs_load_in_production_mode(self) -> None:
# require_ratified default (None) -> production unless env override.
for pid in (
"default_general_v1", "precision_first_v1", "generosity_first_v1",
):
m = load_identity_manifold(pid)
assert len(m.value_axes) == 3
# ---------- error paths ----------
class TestLoaderRejects:
def test_missing_pack(self) -> None:
with pytest.raises(IdentityPackError, match="not found"):
load_identity_manifold(
"does_not_exist", require_ratified=False,
)
def test_path_traversal_rejected(self) -> None:
with pytest.raises(IdentityPackError, match="invalid pack_id"):
load_identity_manifold(
"../../etc/passwd", require_ratified=False,
)
def test_unratified_pack_refused_in_production(self, tmp_path: Path) -> None:
# An unratified test pack (empty mastery_report_sha256) must be
# refused in production mode.
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0, 0.0], "weight": 1.0,
}],
)
with pytest.raises(IdentityPackError, match="not ratified"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=True,
)
def test_missing_companion_report_refused(
self, tmp_path: Path,
) -> None:
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0, 0.0], "weight": 1.0,
}],
mastery_report_sha256="0" * 64,
)
with pytest.raises(IdentityPackError, match="companion report file"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=True,
)
def test_companion_report_sha_mismatch_refused(
self, tmp_path: Path,
) -> None:
# Pack claims one SHA; companion report carries a different one.
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0, 0.0], "weight": 1.0,
}],
mastery_report_sha256="a" * 64,
)
report_path = tmp_path / f"{bad['pack_id']}.mastery_report.json"
report_path.write_text(
json.dumps({"report_sha256": "b" * 64, "ratified": True}),
encoding="utf-8",
)
with pytest.raises(IdentityPackError, match="does not match"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=True,
)
def test_companion_report_seal_failure_refused(
self, tmp_path: Path,
) -> None:
# Companion report's claimed SHA does not actually self-seal.
bogus_sha = "c" * 64
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0, 0.0], "weight": 1.0,
}],
mastery_report_sha256=bogus_sha,
)
report_path = tmp_path / f"{bad['pack_id']}.mastery_report.json"
report_path.write_text(
json.dumps(
{
"report_sha256": bogus_sha,
"ratified": True,
"other_field": "anything",
},
),
encoding="utf-8",
)
with pytest.raises(IdentityPackError, match="self-seal"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=True,
)
def test_malformed_direction(self, tmp_path: Path) -> None:
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0], # length 2, not 3
"weight": 1.0,
}],
)
with pytest.raises(IdentityPackError, match="direction"):
load_identity_manifold(
bad["pack_id"],
search_paths=[tmp_path],
require_ratified=False,
)
def test_direction_out_of_bounds(self, tmp_path: Path) -> None:
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [5.0, 0.0, 0.0],
"weight": 1.0,
}],
)
with pytest.raises(IdentityPackError, match="out of bounds"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=False,
)
def test_weight_out_of_bounds(self, tmp_path: Path) -> None:
bad = _write_pack(
tmp_path,
value_axes=[{
"axis_id": "x", "name": "x",
"direction": [1.0, 0.0, 0.0],
"weight": 999.0,
}],
)
with pytest.raises(IdentityPackError, match="weight"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=False,
)
def test_duplicate_axis_id(self, tmp_path: Path) -> None:
bad = _write_pack(
tmp_path,
value_axes=[
{"axis_id": "x", "name": "x", "direction": [1.0, 0.0, 0.0], "weight": 1.0},
{"axis_id": "x", "name": "y", "direction": [0.0, 1.0, 0.0], "weight": 1.0},
],
)
with pytest.raises(IdentityPackError, match="duplicate axis_id"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=False,
)
def test_empty_axes(self, tmp_path: Path) -> None:
bad = _write_pack(tmp_path, value_axes=[])
with pytest.raises(IdentityPackError, match="at least"):
load_identity_manifold(
bad["pack_id"], search_paths=[tmp_path], require_ratified=False,
)
# ---------- runtime wiring ----------
class TestRuntimeWiring:
def test_runtime_loads_default_pack(self) -> None:
rt = ChatRuntime(config=RuntimeConfig())
assert rt.identity_pack_id == DEFAULT_IDENTITY_PACK
axis_ids = {a.axis_id for a in rt.identity_manifold.value_axes}
assert axis_ids == {"truthfulness", "coherence", "reverence"}
def test_runtime_loads_precision_pack_via_config(self) -> None:
rt = ChatRuntime(config=RuntimeConfig(identity_pack="precision_first_v1"))
assert rt.identity_pack_id == "precision_first_v1"
weights = {a.axis_id: a.weight for a in rt.identity_manifold.value_axes}
assert weights["truthfulness"] == 2.0
def test_runtime_loads_generosity_pack_via_config(self) -> None:
rt = ChatRuntime(config=RuntimeConfig(identity_pack="generosity_first_v1"))
weights = {a.axis_id: a.weight for a in rt.identity_manifold.value_axes}
assert weights["coherence"] == 2.0
def test_empty_identity_pack_falls_back_to_default(self) -> None:
rt = ChatRuntime(config=RuntimeConfig(identity_pack=""))
assert rt.identity_pack_id == DEFAULT_IDENTITY_PACK
# ---------- pack swap proof ----------
class TestPackSwap:
def test_precision_vs_default_manifolds_differ(self) -> None:
default_m = load_identity_manifold("default_general_v1", require_ratified=False)
precision_m = load_identity_manifold("precision_first_v1", require_ratified=False)
assert default_m.alignment_threshold != precision_m.alignment_threshold
default_weights = {a.axis_id: a.weight for a in default_m.value_axes}
precision_weights = {a.axis_id: a.weight for a in precision_m.value_axes}
assert default_weights != precision_weights
def test_generosity_adds_no_extra_boundaries(self) -> None:
# Identity packs may add boundaries (precision_first does) but
# must not silently drop the core ones.
for pack_id in (
"default_general_v1", "precision_first_v1", "generosity_first_v1",
):
m = load_identity_manifold(pack_id, require_ratified=False)
assert "no_fabricated_source" in m.boundary_ids
assert "no_hot_path_repair" in m.boundary_ids
# ---------- ratification script idempotency ----------
class TestRatificationScript:
def test_script_idempotent(self) -> None:
"""Re-running scripts/ratify_identity_packs.py must not change anything."""
import subprocess
import sys
repo_root = Path(__file__).resolve().parents[1]
before = {
pid: (repo_root / "packs" / "identity" / f"{pid}.json").read_text(
encoding="utf-8"
)
for pid in (
"default_general_v1", "precision_first_v1", "generosity_first_v1",
)
}
result = subprocess.run(
[sys.executable, "scripts/ratify_identity_packs.py"],
cwd=str(repo_root),
env={"PYTHONPATH": str(repo_root), "PATH": "/usr/bin:/bin"},
check=True,
capture_output=True,
text=True,
)
assert "ratified 0 pack(s); 3 already current" in result.stdout
after = {
pid: (repo_root / "packs" / "identity" / f"{pid}.json").read_text(
encoding="utf-8"
)
for pid in before
}
for pid, txt in before.items():
assert after[pid] == txt, f"pack {pid} changed during idempotent re-run"
# ---------- helpers ----------
def _write_pack(
tmp_path: Path,
*,
value_axes: list,
pack_id: str = "test_pack",
mastery_report_sha256: str = "",
) -> dict:
body = {
"pack_id": pack_id,
"version": "1.0.0",
"description": "test",
"schema_version": "1.0.0",
"mastery_report_sha256": mastery_report_sha256,
"alignment_threshold": 0.45,
"boundary_ids": ["test_boundary"],
"value_axes": value_axes,
}
path = tmp_path / f"{pack_id}.json"
path.write_text(json.dumps(body), encoding="utf-8")
return body