"""Seeded surface variation — selector determinism + decoration shape (ADR-0071, Plan Phase R4). """ from __future__ import annotations from chat.register_variation import ( DecorationResult, _select_bucket_entry, decorate_surface, decorate_surface_str, ) from packs.register.loader import UNREGISTERED, load_register_pack def test_selector_returns_empty_string_for_empty_bucket(): result = _select_bucket_entry( (), seed_text="anything", register_id="test_v1", turn_idx=0, bucket_name="openings", ) assert result == "" def test_selector_is_deterministic(): bucket = ("a", "b", "c", "d", "e") args = dict( seed_text="some surface", register_id="test_v1", turn_idx=7, bucket_name="openings", ) first = _select_bucket_entry(bucket, **args) for _ in range(50): assert _select_bucket_entry(bucket, **args) == first def test_selector_varies_with_turn_idx(): bucket = ("a", "b", "c", "d", "e") seen = set() for turn_idx in range(20): seen.add(_select_bucket_entry( bucket, seed_text="constant", register_id="test_v1", turn_idx=turn_idx, bucket_name="openings", )) # 20 turns over a 5-entry bucket should hit at least 3 entries. assert len(seen) >= 3, ( f"seed is not varying with turn_idx — only saw {seen}" ) def test_selector_varies_with_register_id(): bucket = ("a", "b", "c", "d", "e") a = _select_bucket_entry( bucket, seed_text="same", register_id="reg_A", turn_idx=0, bucket_name="openings", ) b = _select_bucket_entry( bucket, seed_text="same", register_id="reg_B", turn_idx=0, bucket_name="openings", ) # Not guaranteed for a single bucket size 5 — sweep a few # turn_idx values to ensure register_id moves the seed at all. diffs = 0 for t in range(20): a_t = _select_bucket_entry( bucket, seed_text="same", register_id="reg_A", turn_idx=t, bucket_name="openings", ) b_t = _select_bucket_entry( bucket, seed_text="same", register_id="reg_B", turn_idx=t, bucket_name="openings", ) if a_t != b_t: diffs += 1 assert diffs > 0, ( f"register_id has no effect on selector across 20 turn_idx " f"values (first picks: a={a!r}, b={b!r})" ) def test_selector_distributes_across_bucket(): """Across many distinct seed_text inputs, every entry should be selected at least once.""" bucket = ("a", "b", "c") seen: set[str] = set() for i in range(300): seen.add(_select_bucket_entry( bucket, seed_text=f"surface_{i}", register_id="test", turn_idx=0, bucket_name="openings", )) assert seen == {"a", "b", "c"} def test_selector_uses_bucket_name(): """Different bucket_name keys produce different seeds.""" bucket = ("a", "b", "c", "d", "e", "f", "g", "h") args = dict(seed_text="x", register_id="r", turn_idx=0) o = _select_bucket_entry(bucket, bucket_name="openings", **args) c = _select_bucket_entry(bucket, bucket_name="closings", **args) diffs = 0 for t in range(20): o_t = _select_bucket_entry( bucket, seed_text="x", register_id="r", turn_idx=t, bucket_name="openings", ) c_t = _select_bucket_entry( bucket, seed_text="x", register_id="r", turn_idx=t, bucket_name="closings", ) if o_t != c_t: diffs += 1 # Not strict on (o, c) at turn 0 alone — sweep guards. _ = (o, c) assert diffs > 0 def test_decorate_surface_empty_buckets_noop(): """UNREGISTERED / neutral / terse all have empty buckets.""" surface = "Light is illumination. Pack-grounded (en_core_cognition_v1)." out = decorate_surface(surface, UNREGISTERED, turn_idx=0) assert isinstance(out, DecorationResult) assert out.surface == surface assert out.variant_id == "" neutral = load_register_pack("default_neutral_v1") assert decorate_surface(surface, neutral, turn_idx=0).surface == surface assert decorate_surface(surface, neutral, turn_idx=0).variant_id == "" terse = load_register_pack("terse_v1") assert decorate_surface(surface, terse, turn_idx=0).surface == surface assert decorate_surface(surface, terse, turn_idx=0).variant_id == "" def test_decorate_surface_empty_input_noop(): """Empty input ⇒ empty output regardless of register.""" convivial = load_register_pack("convivial_v1") out = decorate_surface("", convivial, turn_idx=0) assert out.surface == "" assert out.variant_id == "" def test_decorate_surface_convivial_attaches_markers(): """Convivial register has populated openings and closings — at least one of them should attach to most surfaces.""" convivial = load_register_pack("convivial_v1") surface = "Light is illumination." out = decorate_surface(surface, convivial, turn_idx=0) # The opening (3 entries, no empty) is always non-empty. # Therefore the surface must change. assert out.surface != surface assert surface in out.surface # ADR-0072 (R5) — non-empty markers produce a non-empty variant_id. assert out.variant_id != "" assert len(out.variant_id) == 12 def test_decorate_surface_is_deterministic(): convivial = load_register_pack("convivial_v1") surface = "Light is illumination." a = decorate_surface(surface, convivial, turn_idx=0) b = decorate_surface(surface, convivial, turn_idx=0) assert a == b def test_decorate_surface_turn_idx_varies_output(): convivial = load_register_pack("convivial_v1") surface = "Light is illumination." seen: set[str] = set() for t in range(15): seen.add(decorate_surface(surface, convivial, turn_idx=t).surface) # 15 turns × 3 openings × 3 closings = 9 possible outputs; # should see at least 4 distinct. assert len(seen) >= 4, f"only {len(seen)} distinct decorations across 15 turns" # ADR-0072 (R5) — DecorationResult + variant_id + str alias. def test_decorate_surface_str_alias_returns_string(): """decorate_surface_str preserves the pre-R5 string-only API.""" convivial = load_register_pack("convivial_v1") surface = "Light is illumination." out = decorate_surface_str(surface, convivial, turn_idx=0) assert isinstance(out, str) full = decorate_surface(surface, convivial, turn_idx=0) assert out == full.surface def test_variant_id_stable_across_invocations(): """Same (opening, closing) pair ⇒ identical 12-char variant_id.""" convivial = load_register_pack("convivial_v1") surface = "Light is illumination." a = decorate_surface(surface, convivial, turn_idx=0) b = decorate_surface(surface, convivial, turn_idx=0) assert a.variant_id == b.variant_id # Stable hex prefix of 12 chars. assert len(a.variant_id) == 12 int(a.variant_id, 16) # raises if not hex def test_variant_id_distinguishes_distinct_marker_pairs(): """Different marker selections ⇒ different variant_ids.""" convivial = load_register_pack("convivial_v1") surface = "Light is illumination." seen_pairs: set[tuple[str, str]] = set() seen_variant_ids: set[str] = set() for t in range(40): result = decorate_surface(surface, convivial, turn_idx=t) seen_pairs.add((result.opening, result.closing)) if result.variant_id: seen_variant_ids.add(result.variant_id) # Bijection between non-trivial pairs and variant_ids: # the number of distinct variant_ids equals the number of # distinct non-empty pairs (collisions on 48 bits ≈ 2^-48 ⇒ none). non_trivial_pairs = {p for p in seen_pairs if p != ("", "")} assert len(seen_variant_ids) == len(non_trivial_pairs) def test_variant_id_empty_for_no_decoration(): """UNREGISTERED / neutral / terse always emit ``variant_id=""``.""" surface = "Light is illumination." for pack_id in ("default_neutral_v1", "terse_v1"): pack = load_register_pack(pack_id) for t in range(5): assert decorate_surface(surface, pack, turn_idx=t).variant_id == "" for t in range(5): assert decorate_surface(surface, UNREGISTERED, turn_idx=t).variant_id == ""