core/tests/test_identity_continuity_verification.py
Shay f2dac1dc5c feat(identity): L11 identity continuity — same identity across reboot, not just same bytes
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).
2026-06-05 13:52:57 -07:00

75 lines
2.7 KiB
Python

"""Reboot identity verification — L11 Phase 3.
On reboot the engine recomputes its identity from the current ratified substrate
and compares it to the identity stamped in the checkpoint. A match = continuous
identity. A mismatch = the substrate changed while the engine was down (a pack
mutated), so resuming would carry the lived state into a DIFFERENT identity —
surfaced (warn + ``identity_continuity_break`` flag) by default, refused under
``strict_identity_continuity``.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from chat.runtime import ChatRuntime
from core.cognition.pipeline import CognitiveTurnPipeline
from core.config import RuntimeConfig
from core.engine_identity import IdentityContinuityError
def _drive(state_dir: Path, n: int = 2, config: RuntimeConfig | None = None) -> None:
runtime = ChatRuntime(config=config or RuntimeConfig(), engine_state_path=state_dir)
pipe = CognitiveTurnPipeline(runtime=runtime)
for _ in range(n):
pipe.run("What causes light?")
def test_same_substrate_reboot_has_no_break(tmp_path: Path) -> None:
state_dir = tmp_path / "es"
_drive(state_dir)
rt = ChatRuntime(config=RuntimeConfig(), engine_state_path=state_dir)
assert rt.identity_continuity_break is False
def test_substrate_change_during_downtime_surfaces_break(tmp_path: Path) -> None:
state_dir = tmp_path / "es"
_drive(state_dir) # stamps the default identity
# Boot under a different identity pack over the same checkpoint dir.
with pytest.warns(RuntimeWarning, match="identity continuity break"):
rt = ChatRuntime(
config=RuntimeConfig(identity_pack="precision_first_v1"),
engine_state_path=state_dir,
)
assert rt.identity_continuity_break is True
def test_strict_mode_refuses_on_identity_mismatch(tmp_path: Path) -> None:
state_dir = tmp_path / "es"
_drive(state_dir)
with pytest.raises(IdentityContinuityError):
ChatRuntime(
config=RuntimeConfig(
identity_pack="precision_first_v1", strict_identity_continuity=True
),
engine_state_path=state_dir,
)
def test_strict_mode_allows_matching_identity(tmp_path: Path) -> None:
state_dir = tmp_path / "es"
_drive(state_dir)
# Same substrate + strict mode -> boots cleanly, no break.
rt = ChatRuntime(
config=RuntimeConfig(strict_identity_continuity=True),
engine_state_path=state_dir,
)
assert rt.identity_continuity_break is False
def test_genesis_runtime_has_no_break(tmp_path: Path) -> None:
# No checkpoint to compare against -> no break.
rt = ChatRuntime(config=RuntimeConfig(), engine_state_path=tmp_path / "fresh")
assert rt.identity_continuity_break is False