Builds the L11 lived-spine half on top of Shape B+ T-resume: prove the continuous/resumed life is the SAME identity, with a content-derived, hash-chained lineage and a falsifiable behavioral proof. - core/engine_identity.py (L11-1): EngineIdentity = sha256 of the ratified PERSONALITY substrate (identity/safety/ethics/register/anchor-lens pack files) + code revision. Content-derived, NOT entropy — same substrate => same identity (cross-engine portable). The "who am I" hash; bumped by a ratified identity change, NOT by lived learning (that is experience, carried by Shape B+). - engine_state + chat/runtime (L11-2): every checkpoint manifest stamps engine_identity + parent_engine_identity (git-like lineage). Stable substrate => identity == parent (one continuous life); a ratified change => the bump. - chat/runtime + config (L11-3): on reboot, recompute identity and compare to the stamped one. Mismatch (substrate changed while down) surfaces a warning + identity_continuity_break flag; strict_identity_continuity (opt-in) refuses (IdentityContinuityError). Default warns — reboot is recovery, not control flow (ADR-0157); the operator must not be bricked by a benign ratified pack swap. - tests (L11-4): the proof. Continuity is SUFFICIENT (byte-identical resume + no break under a fixed identity), identity is LOAD-BEARING (distinct packs => distinct hashes), and the CONTRAPOSITIVE holds (resuming under a different identity raises the break). Same identity <=> continuous; different => break. Test hygiene (required by L11's always-on identity stamping): conftest isolates the default engine_state dir per test; the refusal-calibration cold-start probe uses no_load_state=True. Both prevent cross-test identity-lineage pollution. 19 dedicated tests; curated smoke green (no spurious break warnings).
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
"""EngineIdentity — L11 Phase 1.
|
|
|
|
EngineIdentity is the content-derived sha256 of the engine's ratified personality
|
|
substrate (identity / safety / ethics / register / anchor-lens pack files) plus
|
|
the code revision. Content-derived, NOT entropy: same substrate -> same identity
|
|
(two engines with the same ratified packs ARE the same engine functionally).
|
|
This is the "who am I" hash the L11 lineage chain and reboot verification build on.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from core.config import DEFAULT_IDENTITY_PACK, RuntimeConfig
|
|
from core.engine_identity import (
|
|
RATIFIED_ROLES,
|
|
EngineIdentityError,
|
|
compute_engine_identity,
|
|
engine_identity_for_config,
|
|
ratified_substrate,
|
|
)
|
|
|
|
_DEFAULTS: dict[str, str] = {
|
|
"identity": "default_general_v1",
|
|
"safety": "core_safety_axes_v1",
|
|
"ethics": "default_general_ethics_v1",
|
|
"register": "default_neutral_v1",
|
|
"anchor_lens": "default_unanchored_v1",
|
|
}
|
|
|
|
|
|
def test_engine_identity_is_deterministic_and_content_derived() -> None:
|
|
a = compute_engine_identity(_DEFAULTS, git_revision="abc123")
|
|
b = compute_engine_identity(dict(_DEFAULTS), git_revision="abc123")
|
|
assert a == b # same substrate -> same identity (cross-engine portable)
|
|
assert len(a) == 64 # sha256 hex
|
|
|
|
|
|
def test_changing_identity_pack_changes_identity() -> None:
|
|
base = compute_engine_identity(_DEFAULTS, git_revision="abc123")
|
|
swapped = compute_engine_identity(
|
|
{**_DEFAULTS, "identity": "precision_first_v1"}, git_revision="abc123"
|
|
)
|
|
assert base != swapped # a different identity pack IS a different identity
|
|
|
|
|
|
def test_changing_code_revision_changes_identity() -> None:
|
|
a = compute_engine_identity(_DEFAULTS, git_revision="rev-a")
|
|
b = compute_engine_identity(_DEFAULTS, git_revision="rev-b")
|
|
assert a != b
|
|
|
|
|
|
def test_substrate_exposes_per_role_pack_content_hashes() -> None:
|
|
substrate = ratified_substrate(_DEFAULTS, git_revision="abc123")
|
|
assert set(RATIFIED_ROLES).issubset(substrate.keys())
|
|
assert substrate["code_revision"] == "abc123"
|
|
# Each role records its pack id + a 64-hex content hash of the pack file.
|
|
for role in RATIFIED_ROLES:
|
|
assert substrate[role]["pack_id"] == _DEFAULTS[role]
|
|
assert len(substrate[role]["sha256"]) == 64
|
|
# Distinct packs have distinct content hashes.
|
|
assert substrate["identity"]["sha256"] != substrate["safety"]["sha256"]
|
|
|
|
|
|
def test_missing_ratified_pack_raises() -> None:
|
|
with pytest.raises(EngineIdentityError):
|
|
compute_engine_identity(
|
|
{**_DEFAULTS, "identity": "no_such_pack_xyz"}, git_revision="abc123"
|
|
)
|
|
|
|
|
|
def test_engine_identity_for_config_resolves_defaults() -> None:
|
|
# An empty config resolves every role to its DEFAULT pack.
|
|
from_config = engine_identity_for_config(RuntimeConfig(), git_revision="abc123")
|
|
explicit = compute_engine_identity(_DEFAULTS, git_revision="abc123")
|
|
assert from_config == explicit
|
|
|
|
|
|
def test_engine_identity_for_config_honors_identity_override() -> None:
|
|
base = engine_identity_for_config(RuntimeConfig(), git_revision="abc123")
|
|
override = engine_identity_for_config(
|
|
RuntimeConfig(identity_pack="precision_first_v1"), git_revision="abc123"
|
|
)
|
|
assert base != override
|
|
assert DEFAULT_IDENTITY_PACK == "default_general_v1" # sanity on the default
|