core/tests/test_vault_recall_rethaw.py
Shay 1ac4284f21
feat(vault): wire vault-recall E2 re-thaw per ADR-0006 (W-004) (#251)
Closes W-004 wiring debt surfaced by L2 audit (#238) and predicted
by L1 audit's forward note (#237). ADR-0006 §"Integration Points"
states: "Vault recall re-activates the region to E2 transiently,
then lets it cool again." Prior to this commit, vault.recall()
returned entries with no energy field at all — the re-thaw was
spec-only.

Changes:
- vault/store.py: import EnergyClass / EnergyProfile from
  core.physics.energy. Define module-level _VAULT_RECALL_RETHAW_ENERGY
  singleton (raw=0.50, energy_class=E2, mid-band). Both .recall() and
  .recall_batch() stamp each returned entry with the re-thaw profile
  via a new "energy_profile" key in the result dict.
- tests/test_vault_recall_rethaw.py: 6 tests pinning the contract —
  recall returns E2 profile, recall_batch returns E2 profile,
  singleton is byte-identical across calls (replay determinism),
  empty vault is no-op, min_status filtering preserves the field,
  raw value sits unambiguously in E2 band [0.37, 0.62).

Architectural notes:
- The re-thaw is *declared* by the vault, not derived through the
  energy operator. ADR-0006 makes the assertion directly; vault
  recall is the moment the assertion applies.
- The singleton (rather than a per-call construction) preserves
  byte-identical replay: same recall sequence => identical
  EnergyProfile object => stable trace if downstream folds it.
- Cool-down per ADR-0006 is downstream field propagation's
  responsibility via FieldEnergyOperator's natural recency decay.
  Once the recalled entry is no longer being injected into the
  active field state, recency drops and energy class falls.
- "energy_profile" is added to recall result dicts, alongside the
  existing "epistemic_state" field. Existing consumers (generate/
  stream.py:169, chat/runtime.py:1643, vault/decompose.py:124,179,
  session/context.py:347) ignore unknown keys — no breakage.

Unlocks W-005 (energy-modulated surface readback) — now that E0/E2
distinction exists at the runtime data shape, downstream readback
modulation can become meaningful instead of moot.

Verification:
- tests/test_vault_recall_rethaw.py: 6 passed
- tests/test_vault_*.py: 48 passed, 4 skipped (no regression)
- core test --suite smoke: 67 passed
- core test --suite cognition: 120 passed, 1 skipped
- core test --suite algebra: 82 passed, 50 skipped
- scripts/verify_lane_shas.py: 7/7 match pinned SHAs (byte-identity preserved)
2026-05-24 19:40:29 -07:00

97 lines
3.6 KiB
Python

"""Tests for ADR-0006 vault-recall re-thaw (W-004).
ADR-0006 §"Integration Points": "Vault recall re-activates the region to E2
transiently, then lets it cool again."
These tests pin the contract that vault.recall and vault.recall_batch return
an EnergyProfile with energy_class=E2 on every returned entry, declaring
the transient re-activation.
"""
from __future__ import annotations
import numpy as np
from core.physics.energy import EnergyClass, EnergyProfile
from teaching.epistemic import EpistemicStatus
from vault.store import VaultStore, _VAULT_RECALL_RETHAW_ENERGY
def _make_versor(seed: int) -> np.ndarray:
rng = np.random.default_rng(seed)
return rng.standard_normal(32).astype(np.float32)
def test_recall_returns_energy_profile_e2() -> None:
"""Every recall result carries an EnergyProfile declaring E2 re-thaw."""
store = VaultStore()
for i in range(3):
store.store(_make_versor(i), {"label": f"entry-{i}"})
results = store.recall(_make_versor(0), top_k=3)
assert len(results) == 3
for hit in results:
assert "energy_profile" in hit, "recall result missing energy_profile"
profile = hit["energy_profile"]
assert isinstance(profile, EnergyProfile)
assert profile.energy_class is EnergyClass.E2
def test_recall_batch_returns_energy_profile_e2() -> None:
"""Batched recall stamps the same re-thaw energy on every entry."""
store = VaultStore()
for i in range(3):
store.store(_make_versor(i), {"label": f"entry-{i}"})
queries = np.stack([_make_versor(0), _make_versor(1)])
batch_results = store.recall_batch(queries, top_k=2)
assert len(batch_results) == 2
for per_query in batch_results:
assert len(per_query) == 2
for hit in per_query:
assert "energy_profile" in hit
assert hit["energy_profile"].energy_class is EnergyClass.E2
def test_rethaw_energy_singleton_byte_identical() -> None:
"""The re-thaw EnergyProfile is a deterministic singleton — no per-call
drift. Required for replay byte-identity (same recall sequence ⇒ same
energy profile on every entry)."""
store = VaultStore()
store.store(_make_versor(0), {"label": "entry-0"})
a = store.recall(_make_versor(0), top_k=1)[0]["energy_profile"]
b = store.recall(_make_versor(0), top_k=1)[0]["energy_profile"]
assert a is b, "re-thaw profile must be a stable singleton, not a fresh allocation"
assert a is _VAULT_RECALL_RETHAW_ENERGY
def test_recall_empty_vault_does_not_emit_profile() -> None:
"""Empty vault returns []; no energy_profile to attach."""
store = VaultStore()
assert store.recall(_make_versor(0), top_k=5) == []
def test_recall_with_min_status_still_carries_energy_profile() -> None:
"""min_status filtering does not strip the energy_profile field."""
store = VaultStore()
store.store(_make_versor(0), {"label": "coherent"},
epistemic_status=EpistemicStatus.COHERENT)
store.store(_make_versor(1), {"label": "speculative"},
epistemic_status=EpistemicStatus.SPECULATIVE)
results = store.recall(
_make_versor(0), top_k=5,
min_status=EpistemicStatus.COHERENT,
)
assert len(results) == 1
assert results[0]["energy_profile"].energy_class is EnergyClass.E2
def test_rethaw_profile_raw_is_in_e2_band() -> None:
"""ADR-0006 declares E2 transient re-activation. Verify the singleton's
raw value sits in the E2 band [0.37, 0.62) — so it is unambiguously E2
rather than borderline."""
profile = _VAULT_RECALL_RETHAW_ENERGY
assert 0.37 <= profile.raw < 0.62
assert profile.energy_class is EnergyClass.E2