feat(adr-0246): completion — §4.1 records, path serve integration, §11 feasibility (honest NULL), ADR body Proposed

Final Ring-1 unit (Sonnet 5 handoff scope, completed by Fable 5). Stacked on
feat/adr-0246-slice1-hardened. ADR stays Proposed — no self-Accept; packet §8
RULING PENDING for Shay.

§4.1/§4.2 telemetry:
  + IdentityActionRecord (schema identity_action_v1): full-SHA-256 field/record/
    pack-content digests (LE f64, canonical JSON, no default=str),
    policy_version=AdmissionPolicy.version_id(), A_raw + all measures, admitted,
    multi-condition refusal_reason (';'-joined — documented widening),
    lawful_action in {I,none}, path_break
  + manifold_content_digest + GEOMETRY_VERSION/GATE_VERSION (§3.5 scope keys)
  + IdentityScore.action_record; JSONL serializer emits identity_action_*/
    identity_path_* keys ONLY when the paths ran (flag-off wire byte-identical)
§3.4 step-2 compliance (F1-adjacent):
  + advance_identity_path(admitted=) — a policy-refused turn breaks even with
    small d_stab (was a real gap: leakage-refused turns could compose); pinned
Path serve integration (OBSERVE-ONLY):
  + advance_session_identity_path: scope from manifold digest + version ids;
    runtime advances the session ledger only when identity_wave_gate AND
    identity_action_surface are on; instance lifetime = session boundary;
    session_admit is telemetry, never egress (epsilon_session uncertified)
§11 grounding-feasibility study (evals/adr_0246_grounding_feasibility):
  fixed TRAIN(13)/HELD-OUT(12)/ADVERSARIAL(8); bivector generator proxy
  (numpy-only); SAMPLE-SIZE-CALIBRATED null (200 noise-pair trials at real n)
  + shared-basis positive control (a real bug caught RED: per-call fresh bases
  made the positive pair meaningless at 0.53). RESULT: honest NULL with the
  method validated — positive control 0.9995 (100th pctile, null p95 0.60) but
  real cross-cohort cosine 0.52 = 87th pctile of chance; AUC 0.49 [0.21,0.77];
  generator energy spread across all 10 planes; precision immaterial (6.9e-7).
  No stable generator subspace at this n; threshold tuning cannot discriminate.
ADR + packet:
  + docs/adr/ADR-0246-induced-identity-action-and-path-integrity.md (Proposed;
    F1 semantics + ||.||_G convention + turn-ownership + refusal_reason rulings
    requested; binding claims language 'lawfulness relative to the declared
    frozen frame'; honest §6.3 + §11 numbers; machine-readable operational
    status: live_activation not_authorized, both flags default-off)
  + docs/audit/adr-0246-acceptance-packet-2026-07-17.md (§10 checklist, §8 PENDING)
  + spatial_foreign uncertainty RESOLVED + pinned (tautologically zero for the
    full-span default pack; fires for reduced-support packs)

[Verification]: uv run core test --suite smoke -q => 176 passed; full battery
(all 9 ADR-0246 suites + D4 identity surfaces + gamma calibration +
identity_gate + telemetry) => 228 passed; §6.1/6.2 eval 14/14; §11 artifact +
run log under docs/audit/artifacts/.
This commit is contained in:
Shay 2026-07-17 23:36:41 -07:00
parent 47e7eb4e65
commit 45539e62d5
15 changed files with 2020 additions and 20 deletions

View file

@ -80,6 +80,7 @@ from core.physics.identity import (
IdentityScore,
TurnEvent,
)
from core.physics.identity import advance_session_identity_path
from core.physics.identity_action import AdmissionPolicy
from packs.ethics.check import EthicsCheck, EthicsContext
from packs.ethics.loader import (
@ -706,6 +707,11 @@ class ChatRuntime:
self.identity_manifold,
)
self._last_refusal_was_typed: bool = True
# ADR-0246 §3.4/§3.5 — lawful-only session identity-path ledger
# (observe-only; advanced per-turn only when identity_action_surface +
# identity_wave_gate are both on). Instance lifetime IS the §3.5
# session boundary: a fresh runtime starts from None → hard break.
self._identity_path_ledger = None
self.turn_log: List[TurnEvent] = []
from chat.thread_context import ThreadContext
self.thread_context = ThreadContext()
@ -2684,21 +2690,38 @@ class ChatRuntime:
# path (byte-identical). The boundary_ids intersection needs the
# safety/ethics verdicts, which are computed below — it is supplemented
# after those run.
# ADR-0246 §3.7 — fuller admit surface, flag-gated + default-off. The
# policy is placeholder/uncalibrated (calibrated=False); it only acts
# when identity_wave_gate is also on (a wave_field exists).
_admission_policy = (
AdmissionPolicy.placeholder_default()
if self.config.identity_action_surface
else None
)
identity_score = self._identity_check.check(
reasoning_trajectory,
self.identity_manifold,
wave_field=(
result.final_state.F if self.config.identity_wave_gate else None
),
# ADR-0246 §3.7 — fuller admit surface, flag-gated + default-off. The
# policy is placeholder/uncalibrated (calibrated=False); it only acts
# when identity_wave_gate is also on (a wave_field exists).
admission_policy=(
AdmissionPolicy.placeholder_default()
if self.config.identity_action_surface
else None
),
admission_policy=_admission_policy,
turn_id=self._context.turn,
pack_id=self.identity_pack_id,
)
# ADR-0246 §3.4/§3.5 — lawful-only session identity path (OBSERVE-ONLY,
# same flags). Refused turns break; scope changes hard-break; the
# ledger's session_admit is telemetry, never an egress decision
# (epsilon_session is an uncertified placeholder; live activation
# remains unauthorized).
identity_path_ledger = None
if _admission_policy is not None and identity_score.wave_mode_active:
self._identity_path_ledger, _path_turn = advance_session_identity_path(
self._identity_path_ledger,
self.identity_manifold,
result.final_state.F,
_admission_policy,
)
identity_path_ledger = self._identity_path_ledger
flagged = identity_score.flagged
cycle_cost = CycleCost(
cycle_index=self._context.turn,
@ -3022,6 +3045,7 @@ class ChatRuntime:
normative_clearance=main_normative_clearance,
normative_detail=main_normative_detail,
reach_level=main_reach_level,
identity_path=identity_path_ledger,
)
self.turn_log.append(turn_event)
self._emit_turn_event(turn_event)

View file

@ -151,6 +151,33 @@ def serialize_turn_event(
out["identity_boundary_violations"] = sorted(
getattr(identity_score, "boundary_violations", ()) or ()
)
# ADR-0246 §3.7/§4.1 — induced-action admit-surface telemetry.
# Emitted only when the surface ran this turn (behind the separate
# default-off ``identity_action_surface`` flag); absent otherwise, so
# the D4-only wire format above stays byte-identical.
if getattr(identity_score, "action_surface_active", False):
out["identity_d_orth"] = float(getattr(identity_score, "d_orth", 0.0))
out["identity_d_stab"] = float(getattr(identity_score, "d_stab", 0.0))
record = getattr(identity_score, "action_record", None)
if record is not None:
out["identity_action_admitted"] = bool(record.admitted)
out["identity_action_lawful"] = str(record.lawful_action)
out["identity_action_refusal_reason"] = record.refusal_reason
out["identity_action_record_digest"] = record.record_digest()
# ADR-0246 §3.4/§3.5/§4.2 — session identity-path ledger telemetry
# (observe-only). Emitted only when the path ran this turn; absent
# otherwise, so the flag-off wire format stays byte-identical.
ledger = getattr(event, "identity_path", None)
if ledger is not None:
out["identity_path_chain_id"] = str(getattr(ledger, "chain_id", ""))
out["identity_path_d_stab"] = float(getattr(ledger, "d_stab_path", 0.0))
out["identity_path_composed_turns"] = int(
getattr(ledger, "composed_turn_count", 0)
)
out["identity_path_breaks"] = int(getattr(ledger, "break_count", 0))
out["identity_path_session_admit"] = bool(
getattr(ledger, "session_admit", True)
)
if include_content:
out["input_tokens"] = list(getattr(event, "input_tokens", ()))
out["surface"] = str(getattr(event, "surface", ""))

View file

@ -13,16 +13,28 @@ CORE's identity is not a description of CORE. It is CORE, expressed geometricall
from __future__ import annotations
import functools
import hashlib
import json
import math
import warnings
from dataclasses import dataclass
from typing import Dict, FrozenSet, List, Optional, Tuple
from typing import Any, Dict, FrozenSet, List, Optional, Tuple
import numpy as np
from algebra.cl41 import N_COMPONENTS
from core.physics.identity_manifold import IdentityManifoldGeometry
from core.physics.identity_action import AdmissionPolicy, evaluate_admission
from core.physics.identity_action import (
AdmissionPolicy,
IdentityActionRecord,
IdentityChainScope,
IdentityPathLedger,
PathBudget,
PLACEHOLDER_EPSILON_SESSION,
advance_identity_path,
build_identity_action_record,
evaluate_admission,
)
# ADR-0244 §2.2 / §4a / §2.4 — wave-gate thresholds.
#
@ -85,6 +97,94 @@ def _geometry_for_manifold(manifold: "IdentityManifold") -> IdentityManifoldGeom
return _geometry_for_axis_directions(directions)
# ADR-0246 §3.5/§4.1 — version identifiers for the hard-break ledger scope and
# the per-turn IdentityActionRecord. ``geometry_version`` identifies the
# ``IdentityManifoldGeometry`` construction contract (Gram/lift semantics);
# ``gate_version`` identifies the §3.7 admit-surface DECISION LOGIC in
# ``evaluate_admission`` (as opposed to its threshold VALUES, which are
# ``AdmissionPolicy.version_id()``). Bump either only on a genuine contract
# change — these are code-identity tags, not calibration numbers.
GEOMETRY_VERSION: str = "identity_manifold_geometry_v1"
GATE_VERSION: str = "adr_0246_admit_surface_v1"
def manifold_content_digest(manifold: "IdentityManifold") -> str:
"""Full-SHA-256 content digest of the declared value-axis frame.
ADR-0246 §3.5 a new identity-action chain must start whenever the
identity pack content changes. ``IdentityManifold`` carries no digest of
its own (the pack loader doesn't compute one), so this hashes the exact
content that defines the frame: each axis's id/name/direction/weight, the
boundary ids, and the alignment threshold canonical JSON (sorted keys, no
``default=str``), full 64-hex digest (ADR-0245 §2.3, no truncation).
"""
payload = {
"value_axes": [
{
"axis_id": str(getattr(axis, "axis_id", getattr(axis, "name", ""))),
"name": str(getattr(axis, "name", "")),
"direction": [float(x) for x in getattr(axis, "direction", ()) or ()],
"weight": float(getattr(axis, "weight", 1.0)),
}
for axis in manifold.value_axes
],
"boundary_ids": sorted(manifold.boundary_ids),
"alignment_threshold": float(manifold.alignment_threshold),
}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
# §3.5 session scoping note: the live ledger object is held BY the runtime
# instance, so the session boundary is enforced by object lifetime (a new
# runtime/session starts from ``ledger=None`` → hard break). The constant
# session_id below therefore only needs to be stable WITHIN an instance;
# pack/geometry/policy changes mid-instance still hard-break via the scope
# comparison in ``advance_identity_path``.
_LIVE_SESSION_SCOPE_ID: str = "live_runtime_session"
def advance_session_identity_path(
ledger: "IdentityPathLedger | None",
manifold: "IdentityManifold",
wave_field,
policy: "AdmissionPolicy",
*,
boundary_breach: bool = False,
) -> tuple["IdentityPathLedger", dict]:
"""Advance the live session's lawful-only identity path by one turn
(ADR-0246 §3.4/§3.5 serve integration OBSERVE-ONLY).
Builds the §3.5 chain scope from the manifold's content digest + the
geometry/gate/policy version ids, evaluates the §3.7 admit surface for the
§3.4-step-2 ``admitted`` gate, and folds the turn's induced action into the
ledger (lawful-only composition; refused turns are break markers).
OBSERVE-ONLY: the returned ledger's ``session_admit`` is telemetry, not an
egress decision ``epsilon_session`` is an UNCERTIFIED placeholder and
live activation of any identity gate remains unauthorized (D4 ratification
+ the §6.3 discrimination evidence). No refusal is derived from the path
here; that requires calibrated budgets + explicit human ratification.
"""
geometry = _geometry_for_manifold(manifold)
F = np.asarray(wave_field, dtype=np.float64)
result = evaluate_admission(geometry, F, policy, boundary_breach=boundary_breach)
action = geometry.induced_action(F)
scope = IdentityChainScope(
pack_content_digest=manifold_content_digest(manifold),
geometry_version=GEOMETRY_VERSION,
policy_version=f"{GATE_VERSION}:{policy.version_id()}",
session_id=_LIVE_SESSION_SCOPE_ID,
)
budget = PathBudget(
epsilon_turn=policy.epsilon_turn,
epsilon_session=PLACEHOLDER_EPSILON_SESSION,
)
return advance_identity_path(
ledger, scope, action, geometry.gram, budget, admitted=result.admitted
)
@dataclass(frozen=True)
class ValueAxis:
"""Compatibility value-axis shape for identity-gate tests and fixtures.
@ -130,6 +230,10 @@ class IdentityScore:
action_surface_active: bool = False
d_orth: float = 0.0
d_stab: float = 0.0
# ADR-0246 §4.1 — the full per-turn IdentityActionRecord (typed residual
# channels, digests, admit verdict). ``None`` unless the §3.7 surface ran
# (``action_surface_active=True``); legacy/flag-off callers are unaffected.
action_record: "IdentityActionRecord | None" = None
@property
def value(self) -> float:
@ -283,6 +387,8 @@ class IdentityCheck:
trajectory_id: str,
boundary_violations: FrozenSet[str],
admission_policy: "AdmissionPolicy | None" = None,
turn_id: int = 0,
pack_id: str = "",
) -> IdentityScore:
"""Operator-preservation identity score for a live versor (ADR-0244 §2.2/§4a).
@ -297,7 +403,9 @@ class IdentityCheck:
is additionally applied: a versor failing it folds into ``flagged`` (the
existing ``would_violate`` refusal path abstains admit-or-abstain, no
corrector). When ``None`` (default) the result is byte-identical to the D4
wave path.
wave path. ``turn_id``/``pack_id`` (ADR-0246 §4.1) are forwarded into the
per-turn ``IdentityActionRecord`` when the surface is active; both default
to empty/zero so omitting them never affects behavior.
"""
F = self._validate_wave_field(wave_field)
geometry = _geometry_for_manifold(manifold)
@ -328,6 +436,7 @@ class IdentityCheck:
action_surface_active = False
d_orth = 0.0
d_stab = 0.0
action_record: "IdentityActionRecord | None" = None
if admission_policy is not None:
result = evaluate_admission(
geometry,
@ -339,6 +448,23 @@ class IdentityCheck:
d_orth = result.d_orth
d_stab = result.d_stab
flagged = flagged or not result.admitted
# ADR-0246 §4.1 per-turn telemetry record. Re-evaluates the (cheap,
# pure) admit surface rather than threading ``result`` through, so
# the already-audited ``evaluate_admission`` call above stays
# untouched — see docs/audit/adr-0246-slice1-opus-audit-and-hardening.md.
action_record = build_identity_action_record(
geometry,
F.astype(np.float64),
admission_policy,
turn_id=turn_id,
trajectory_id=trajectory_id,
pack_id=pack_id,
pack_content_digest=manifold_content_digest(manifold),
geometry_version=GEOMETRY_VERSION,
gate_version=GATE_VERSION,
wave_mode_active=True,
boundary_breach=bool(boundary_violations),
)
return IdentityScore(
score=score,
flagged=flagged,
@ -351,6 +477,7 @@ class IdentityCheck:
action_surface_active=action_surface_active,
d_orth=d_orth,
d_stab=d_stab,
action_record=action_record,
)
def check(
@ -361,6 +488,8 @@ class IdentityCheck:
wave_field=None,
violated_boundary_ids: FrozenSet[str] = frozenset(),
admission_policy: "AdmissionPolicy | None" = None,
turn_id: int = 0,
pack_id: str = "",
) -> IdentityScore:
"""Check a trajectory against the IdentityManifold (ADR-0010 / ADR-0244).
@ -371,7 +500,9 @@ class IdentityCheck:
``admission_policy`` (ADR-0246 §3.7, flag-gated behind
``identity_action_surface``) is forwarded to the wave path only; ``None``
(default) keeps every caller byte-identical to the D4 gate.
(default) keeps every caller byte-identical to the D4 gate. ``turn_id``/
``pack_id`` (ADR-0246 §4.1) are cosmetic identifiers for the per-turn
record and default to ``0``/``""`` omitting them changes nothing.
``violated_boundary_ids`` (the turn's safety/ethics violated boundaries)
is intersected with the manifold's committed ``boundary_ids``; a non-empty
@ -396,7 +527,7 @@ class IdentityCheck:
if wave_field is not None:
return self._wave_field_score(
wave_field, resolved_manifold, trajectory_id, boundary_violations,
admission_policy=admission_policy,
admission_policy=admission_policy, turn_id=turn_id, pack_id=pack_id,
)
confidence = float(getattr(trajectory, "total_coherence_delta", 0.0))
confidence += self._mean_frame_coherence(trajectory)
@ -614,3 +745,9 @@ class TurnEvent:
composer_atom_set_hash: str = ""
graph_atom_set_hash: str = ""
composer_graph_atom_overlap_count: int = 0
# ADR-0246 §3.4/§3.5/§4.2 — the session identity-path ledger snapshot after
# this turn (an ``IdentityPathLedger``), populated only when the
# identity_action_surface path ran. ``None`` (default) on legacy/flag-off
# turns keeps the wire format byte-identical. Typed as ``object`` to
# preserve identity.py's low-coupling value-type role in TurnEvent.
identity_path: object = None

View file

@ -255,15 +255,19 @@ def advance_identity_path(
action: np.ndarray,
gram: np.ndarray,
budget: PathBudget,
*,
admitted: bool = True,
) -> tuple[IdentityPathLedger, dict[str, Any]]:
"""Fold one turn's raw induced action into the lawful-only path (§3.4/§3.5).
Returns ``(new_ledger, turn_record)``. ``turn_record`` reports this turn's
``lawful`` / ``d_stab_turn`` / ``path_break`` / ``hard_break``. A turn is
lawful iff ``d_stab(action) epsilon_turn`` under the locked singleton
``H_id={I}``; only lawful turns compose. A scope change (or an absent prior
ledger) is a hard break that starts a fresh chain the previous path is NOT
continued. Immutable: ``ledger`` is never mutated.
lawful iff it was ``admitted`` by the caller's per-turn policy (§3.4 step 2;
default ``True`` for pure geometric callers) AND ``d_stab(action)
epsilon_turn`` under the locked singleton ``H_id={I}``; only lawful turns
compose. A scope change (or an absent prior ledger) is a hard break that
starts a fresh chain the previous path is NOT continued. Immutable:
``ledger`` is never mutated.
**Composition semantics (ratification-relevant ADR-0246 §3.4).** A lawful
turn composes its *actual certified* induced action ``A_t`` (``a_path = A_t @
@ -285,7 +289,13 @@ def advance_identity_path(
dimension = action.shape[0]
stabilizer = IdentityStabilizer.singleton(dimension)
d_stab_turn = stabilizer_defect(action, gram, stabilizer)
lawful = d_stab_turn <= budget.epsilon_turn
# §3.4 step 2 then step 3: a turn must be ADMITTED by the per-turn policy
# (/d_orth/per-axis — the caller's §3.7 verdict, default True for pure
# geometric callers) BEFORE the stabilizer criterion can certify it lawful.
# A refused turn is a break marker even when its d_stab happens to be small
# (e.g. refused on leakage alone) — otherwise a policy-refused action would
# compose into "identity holonomy", the §3.4 category error.
lawful = admitted and d_stab_turn <= budget.epsilon_turn
hard_break = ledger is None or ledger.scope != scope
if hard_break:
@ -361,6 +371,7 @@ CERTIFIED_GAMMA_ID: float = 0.2126624458513829
# False`, and no serve path may admit on them until they are certified.
PLACEHOLDER_ORTH_TOL: float = 1e-6
PLACEHOLDER_EPSILON_TURN: float = 0.1
PLACEHOLDER_EPSILON_SESSION: float = 0.3 # §3.4 path budget — UNCERTIFIED
PLACEHOLDER_TAU_MAX: float = 0.2126624458513829 # per-axis leakage cap (= γ_id placeholder)
PLACEHOLDER_S_MIN: float = 0.0 # self-alignment floor (matches D4 _WAVE_SELF_ALIGNMENT_FLOOR)
PLACEHOLDER_UNCLASSIFIED_TOL: float = 1e-6
@ -397,6 +408,25 @@ class AdmissionPolicy:
calibrated=False,
)
def version_id(self) -> str:
"""Full-SHA-256 identifier of this exact threshold set (ADR-0246 §4.1
``policy_version`` / §3.5 "gate/policy version changed" hard-break key).
Changes iff any threshold or the ``calibrated`` flag changes canonical
JSON, no ``default=str`` (ADR-0245 §2.3).
"""
payload = {
"orth_tol": self.orth_tol,
"epsilon_turn": self.epsilon_turn,
"gamma_id": self.gamma_id,
"tau_max": self.tau_max,
"s_min": self.s_min,
"unclassified_tol": self.unclassified_tol,
"calibrated": self.calibrated,
}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
@dataclass(frozen=True)
class AdmissionResult:
@ -483,3 +513,165 @@ def evaluate_admission(
min_self_alignment=min_self_alignment,
typed_channels=channels,
)
# -- ADR-0246 §4.1/§4.3 per-turn identity-action telemetry record --------------
def _field_digest(versor: np.ndarray) -> str:
"""Full-SHA-256 digest of the versor's bytes (LE float64, ADR-0245 §2.3).
Same convention as :func:`core.physics.holographic_vault._default_mode_id`:
explicit little-endian coercion (platform-stable, not implicit host
endianness) and no truncation.
"""
le_bytes = np.ascontiguousarray(versor, dtype=np.dtype("<f8")).tobytes()
return hashlib.sha256(le_bytes).hexdigest()
@dataclass(frozen=True)
class IdentityActionRecord:
"""Per-turn identity-action telemetry record (ADR-0246 §4.1).
Built only when the §3.7 admit surface actually ran (mirrors
``IdentityScore.action_surface_active``) there is no "empty" record; its
mere existence signals the surface was active this turn. Immutable; every
array/measure is the RAW value (never a soft-projected stand-in).
**Deviation from the brief's literal singular ``refusal_reason``:**
:func:`evaluate_admission` can name MULTIPLE failed conditions
(``AdmissionResult.refusal_reasons``, a tuple); this record joins them with
``";"`` into one string (``None`` when admitted) so no information from a
multi-condition refusal is silently dropped.
**``lawful_action``** reflects the §3.4 path-ledger's own narrower
criterion ``d_stab epsilon_turn`` under the locked ``H_id={I}`` which
is a STRICT SUBSET of the fuller §3.7 ``admitted`` verdict (which also
checks leakage/orth/self-align/boundary/unclassified). A turn can be
"admitted" but still ``lawful_action="none"`` if only the stabilizer
criterion fails to also hold within budget though in practice the two
move together since ``d_stab>epsilon_turn`` is itself one of the admission
reasons. Never a soft projection: exactly ``"I"`` or ``"none"``.
"""
schema_version: str
turn_id: int
trajectory_id: str
pack_id: str
pack_content_digest: str
geometry_version: str
gate_version: str
policy_version: str
wave_mode_active: bool
a_raw: np.ndarray
d_orth: float
d_stab: float
leakage: tuple[float, ...]
leakage_rms: float
max_leakage: float
self_align: tuple[float, ...]
min_self_alignment: float
typed_residual_energy: dict[str, float]
admitted: bool
refusal_reason: str | None
lawful_action: str
path_break: bool
field_digest: str
def _identity_payload(self) -> dict[str, Any]:
"""Everything except ``record_digest`` itself (avoids self-reference)."""
return {
"schema_version": self.schema_version,
"turn_id": self.turn_id,
"trajectory_id": self.trajectory_id,
"pack_id": self.pack_id,
"pack_content_digest": self.pack_content_digest,
"geometry_version": self.geometry_version,
"gate_version": self.gate_version,
"policy_version": self.policy_version,
"wave_mode_active": self.wave_mode_active,
"A_raw": [[float(x) for x in row] for row in self.a_raw],
"d_orth": float(self.d_orth),
"d_stab": float(self.d_stab),
"leakage": [float(x) for x in self.leakage],
"leakage_rms": float(self.leakage_rms),
"max_leakage": float(self.max_leakage),
"self_align": [float(x) for x in self.self_align],
"min_self_alignment": float(self.min_self_alignment),
"typed_residual_energy": {
k: float(v) for k, v in self.typed_residual_energy.items()
},
"admitted": self.admitted,
"refusal_reason": self.refusal_reason,
"lawful_action": self.lawful_action,
"path_break": self.path_break,
"field_digest": self.field_digest,
}
def record_digest(self) -> str:
"""Full-SHA-256 content id over the record (ADR-0245 §2.3 — no
truncation, canonical JSON, no ``default=str``)."""
canonical = json.dumps(
self._identity_payload(), sort_keys=True, separators=(",", ":")
)
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def as_dict(self) -> dict[str, Any]:
out = self._identity_payload()
out["record_digest"] = self.record_digest()
return out
def build_identity_action_record(
geometry: IdentityManifoldGeometry,
versor: np.ndarray,
policy: AdmissionPolicy,
*,
turn_id: int = 0,
trajectory_id: str = "",
pack_id: str = "",
pack_content_digest: str = "",
geometry_version: str = "",
gate_version: str = "",
wave_mode_active: bool = True,
path_break: bool = False,
boundary_breach: bool = False,
) -> IdentityActionRecord:
"""Build the full ADR-0246 §4.1 per-turn record for one versor.
Runs :func:`evaluate_admission` (the single source of truth for the admit
verdict) plus :meth:`IdentityManifoldGeometry.induced_action` /
``axis_response`` for the raw per-axis vectors the aggregate result doesn't
expose. ``path_break`` defaults to ``False`` a caller not integrated with
the §3.4/§3.5 path ledger has no path to report a break against; a
path-integrated caller should pass the ledger's own ``turn_record["path_break"]``.
"""
result = evaluate_admission(geometry, versor, policy, boundary_breach=boundary_breach)
leakage, self_align = geometry.axis_response(versor)
lawful_action = "I" if result.d_stab <= policy.epsilon_turn else "none"
refusal_reason = ";".join(result.refusal_reasons) if result.refusal_reasons else None
return IdentityActionRecord(
schema_version="identity_action_v1",
turn_id=turn_id,
trajectory_id=trajectory_id,
pack_id=pack_id,
pack_content_digest=pack_content_digest,
geometry_version=geometry_version,
gate_version=gate_version,
policy_version=policy.version_id(),
wave_mode_active=wave_mode_active,
a_raw=geometry.induced_action(versor),
d_orth=result.d_orth,
d_stab=result.d_stab,
leakage=tuple(leakage),
leakage_rms=result.leakage_rms,
max_leakage=result.max_leakage,
self_align=tuple(self_align),
min_self_alignment=result.min_self_alignment,
typed_residual_energy=result.typed_channels,
admitted=result.admitted,
refusal_reason=refusal_reason,
lawful_action=lawful_action,
path_break=path_break,
field_digest=_field_digest(versor),
)

View file

@ -0,0 +1,282 @@
# ADR-0246: Induced Identity Action and Path Integrity
**Status**: **Proposed** — pending explicit human ratification (provenance guard: no self-Accept; ruling record in the acceptance packet is PENDING)
**Date**: 2026-07-17
**Authors**: Joshua Shay + multi-model R&D (Fable 5 scaffold → Opus 4.8 adversarial audit + hardening → Sonnet 5/Fable 5 completion; per-model provenance in §9)
**Preflight**: `docs/briefs/ADR-0246-induced-identity-action-and-path-integrity-preflight.md` (locked decisions §3, non-goals §7 — all honored; execution log §0a)
**Depends on**: ADR-0244 (operator-preservation identity gate, Accepted), ADR-0245 (mechanical sympathy + semantic rigor, Accepted)
**Related**: slice-0 evidence `docs/audit/adr-0246-slice0-mismatch-diagnostic-2026-07-17.md` (merged, quarantined diagnostic); Opus audit `docs/audit/adr-0246-slice1-opus-audit-and-hardening.md`
---
## 1. Context and problem
D4 (ADR-0244) built the per-turn operator-preservation floor: does the live
cognitive versor `F` preserve the frozen identity frame `I = span(a₁…aₙ)`,
measured by per-axis subspace leakage `ℓᵢ` and signed self-alignment `sᵢ`? Two
gaps were deliberately left open (preflight §2):
1. **Lawfulness inside the span** — leakage is blind to in-span reshuffles: a
rotor permuting `e1→e2` or inverting `e1→e1` has `≈0` yet is not the
identity-preserving action.
2. **Accumulation across turns** — per-turn thresholds cannot see slow drift:
many individually-admissible small rotations compose into a large one.
This ADR closes both with the **induced action** `A(F)` on the frame, two
never-collapsed diagnostics (`d_orth`, `d_stab`), a locked lawful-stabilizer
policy `H_id={I}`, typed residual channels, a lawful-only path ledger with hard
breaks, a per-turn admit surface, and full per-turn/per-session telemetry — all
flag-gated default-off at serve.
**Claims language (binding, §10 #9 of the preflight):** everything this ADR
instruments is **lawfulness relative to the declared frozen frame**. It does
NOT establish, and must never be quoted as establishing, any semantic
inalienability of the value labels themselves — the shipped axes
(`truthfulness=e1`, `coherence=e2`, `reverence=e3`) remain placeholder
orthonormal directions with no demonstrated dynamical or semantic grounding
(see §6, §7).
## 2. Decisions (as implemented; preflight §3 locked decisions honored)
### 2.1 Induced action matrix `A(F)`
`A_kj(F) = (G⁻¹)_km ⟨a_m, F a_j F̃⟩₀` — the full in-subspace action of the
versor on the frame (`core/physics/identity_manifold.py::IdentityManifoldGeometry.induced_action`).
Raw/unnormalized: a boost's cosh-stretch shows in column norms and in `d_orth`,
never hidden. Bit-exactness against an independent re-derivation was verified
in the Opus audit (max discrepancy 0.00e+00).
### 2.2 Two diagnostics, never collapsed
- `d_orth(F) = ‖A(F)ᵀ G A(F) G‖_F` (`orthogonality_defect`) — G-isometry /
numerical-integrity check. **Not** an authorization policy.
- `d_stab(F) = min_{H∈H_id} ‖A(F) H‖_G` (`identity_action.py::stabilizer_defect`)
— the lawfulness distance from the permitted identity actions.
**Norm convention (ruling requested — §7 item 1):** `‖M‖_G ≡ ‖G^{1/2} M G^{-1/2}‖_F`,
computed via `eigh` of the SPD Gram; reduces exactly to Frobenius at `G=I`
(the only shipped pack). This is the metric-consistent choice (invariant under
G-orthogonal reparameterization of the axes); ratifying this ADR ratifies the
convention.
### 2.3 Lawful stabilizer — locked singleton
`H_id = {I}` (`IdentityStabilizer.singleton`), hardcoded in the path-advance
(no enlargement parameter exists). `I`, permutations, `O(n)`/`SO(n)`
rotations, and reweightings are excluded; enlarging `H_id` is a future explicit
reviewed pack/policy change. No soft-projection of an unlawful `A` onto `I`
exists anywhere (grep-audited; pinned by tests).
### 2.4 Path integrity — lawful-only composition (F1 semantics, ratification-relevant)
`advance_identity_path` (§3.4/§3.5) composes the session path
`A_path = A_T ⋯ A_1` from **only** the turns certified lawful, where lawful ≡
**admitted by the per-turn policy (§3.4 step 2)** AND `d_stab(A_t) ≤ ε_turn`.
Refused or ill-conditioned turns insert a **break marker** and are excluded —
never a soft-projected `I` masquerading as a pass; the raw product is exposed
only as `raw_path_product` (forensic; a test fails if it ever equals the lawful
path in a mixed sequence).
**F1 composition semantics (audit finding, hereby put to ratification):** a
lawful turn composes its *actual certified action* `A_t`, not a literal element
of `H_id`. This is required, not convenience — composing literal `I`s would
make `A_path ≡ I` and blind the ledger to exactly the slow drift it exists to
catch. "Compose lawful `H_t`" in preflight §3.4-step-4 is READ as "compose the
per-turn actions that were certified lawful."
**Hard breaks (§3.5):** a new chain (fresh `chain_id`, path not continued)
starts on any change of pack content digest / geometry version / gate+policy
version / session — each dimension is pinned by a test. **Turn ownership at a
hard break (ruling requested — §7 item 3):** the boundary turn belongs to the
NEW chain (it composes into the fresh path if lawful). Biography holonomy stays
a separate process; nothing here rewrites identity axes.
**Budgets:** `d_stab(A_t) ≤ ε_turn` per turn and `d_stab(A_path) ≤ ε_session`
per session — both ε are **UNCERTIFIED placeholders** (§5), so the session
verdict (`session_admit`) is telemetry only.
### 2.5 Typed residual channels — pinned blade map
On each axis rejection `r = F a F̃ P_I(F a F̃)` (`typed_residual_energy`),
energy splits into: `null_or_conformal` (e4 = grade-1 index **4**),
`boost_like` (e5 = grade-1 index **5**), `spatial_foreign` (grade-1 spatial
slots **1/2/3** outside the pack's axis support), `unclassified` (everything
else — fail-closed, **no correction policy ever attaches to it**). Blade
indices are pinned against `algebra.cl41.basis_vector` in tests
(`test_blade_index_constants_match_algebra`). For the default full-span pack,
`spatial_foreign` is **tautologically zero** (the rejection is orthogonal to
the whole spatial block); it fires correctly for reduced-support packs —
resolved and pinned (§7 item 2).
### 2.6 Per-turn admit surface (§3.7) — admit-or-abstain only
`evaluate_admission(geometry, F, policy)` admits iff `d_orth ≤ orth_tol` AND
`d_stab ≤ ε_turn` AND `leakage_rms ≤ γ_id` AND `max ℓᵢ ≤ τ_max` AND
`min sᵢ ≥ s_min` AND no boundary breach AND no unclassified-channel firing;
otherwise refuses naming every failed condition. **No geometric `C_id`
corrector exists** — the egress model remains D4's
`conjugate_correct(refuse=True)` abstention. A malformed versor raises
`MalformedVersorError` (fail-closed; never a silent legacy fallback when a
wave field was supplied).
### 2.7 Serve wiring — flag-gated, default-off, byte-identical off
- New `RuntimeConfig.identity_action_surface: bool = False` (separate from
`identity_wave_gate`; acts only when the wave gate is also on).
- `chat/runtime.py → IdentityCheck.check(admission_policy=…) → _wave_field_score`:
a §3.7 refusal folds into `flagged`, so the existing `would_violate` egress
abstains. `IdentityScore` gains `action_surface_active`/`d_orth`/`d_stab`/
`action_record` with legacy defaults — flag-off is byte-identical (all D4
gate surfaces green unchanged; smoke green post-wiring).
- The session path ledger is advanced per wave-path turn under the same flags,
**observe-only** (`advance_session_identity_path`): `session_admit` is
telemetry, never an egress decision, because `ε_session` is uncertified and
live activation remains unauthorized.
### 2.8 Telemetry (§4.1/§4.2/§4.3)
- `IdentityActionRecord` (schema `identity_action_v1`): turn/trajectory/pack
ids, **full-SHA-256** pack-content digest + `field_digest` (LE f64 bytes) +
`record_digest` (canonical JSON, no `default=str`), geometry/gate/policy
versions (`policy_version = AdmissionPolicy.version_id()`, full SHA-256 of
the exact threshold set), `A_raw`, `d_orth`, `d_stab`, leakage/self-align
vectors, typed channels, `admitted`, `refusal_reason` (all failed conditions
joined with `;` — a deliberate widening of the preflight's singular field so
multi-condition refusals lose nothing), `lawful_action ∈ {"I","none"}`
(never a matrix), `path_break`.
- `IdentityPathLedger` (schema `identity_path_v1`): `chain_id` (full SHA-256
of scope + chain index), lawful path, `d_stab_path`, composed/break counts,
`session_admit`, `ledger_digest`.
- The JSONL turn serializer emits `identity_d_*`/`identity_action_*`/
`identity_path_*` keys **only when the respective path ran** — the flag-off
wire format is byte-identical (pinned by tests).
## 3. Evidence — §6 eval matrix (all green) and the honest discrimination result
- **§6.1 synthetic geometric suite** (9 cases) + **§6.2 path/holonomy suite**
(5 cases): `evals/adr_0246_geometric_suite` — 14/14; every construction in
the preflight tables pinned individually (identity versor, π-inversion
`s=1`, 90° permutation `s=0`, mild drift, e14 tilt, e15 boost,
near-singular Gram → `ManifoldConditioningError`, malformed F → typed error;
lawful sequence, accumulation-to-session-refusal, interleaved breaks,
pack-change hard break, raw≠lawful forensic).
- **§6.3 discrimination report** (`evals/adr_0246_discrimination`;
live benign n=13, adversarial n=8, synthetic-near-identity control n=5;
artifact `docs/audit/artifacts/adr-0246-slice1-discrimination-report.json`):
| benign pass | false refusal | adversarial detect | control pass | d_stab AUC (95% CI) |
|---|---|---|---|---|
| **0.00** | **1.00** | 1.00 | **1.00** | **0.375** [0.15, 0.62] |
**Honest finding:** on the declared placeholder frame the admit surface
refuses benign and adversarial versors **alike** — benign cognition sits
~18× farther from the frame (mean `d_stab` 27.8) than the crafted attacks
(1.55), so `d_stab` discriminates *below chance*. The near-identity control
passing 100% proves the mechanism is sound; the failure is entirely that
benign cognition does not live near the declared frame. A refuse-everything
gate trivially "detects" attacks but is **not a discriminator**.
- **§11 grounding-feasibility study** (`evals/adr_0246_grounding_feasibility`;
artifact `docs/audit/artifacts/adr-0246-grounding-feasibility-report.json`):
fixed TRAIN (live benign, n=13) / HELD-OUT (independent paraphrase cohort,
n=12) / ADVERSARIAL (n=8) splits; bivector-block generator proxy (numpy-only;
exact for simple rotors/boosts, first-order otherwise); sample-size-calibrated
null distribution (200 trials of independent pure-noise cohort pairs at the
real n) with a shared-basis positive recovery control; precision pairs
(f64↔f32) and per-plane generator-energy analysis.
**Result: NULL, with the method itself validated.** The positive control
(two independent cohorts sharing one true rank-2 subspace) recovers at
cosine 0.9995 — the 100th percentile of the same-sample-size null (p95 =
0.60) — so the instrument can find real structure at n=12. The real cohorts
do not show it: TRAIN→HELD-OUT top-2 cosine 0.52 sits at only the **87th
percentile of pure chance** (need ≥ 95th); residual-based discrimination is
AUC **0.49** (chance; CI [0.21, 0.77]); generator energy is spread across
all 10 bivector planes in both cohorts; precision transport is immaterial
(max bivector delta 6.9e-7). Benign cognition has no small, stable,
cohort-independent generator subspace detectable at this sample size — the
D4/slice-0/§6.3 finding now holds at the GENERATOR level too. Threshold
tuning cannot produce a discriminating gate; a revised implementation
contract is not justified on this evidence. The full `honest_finding` text
in the artifact is binding for any downstream claim; a much larger,
pre-registered cohort would be needed to rule out a subtle real effect.
## 4. Operational status (machine-readable)
```yaml
identity_action_surface:
implementation: proposed # this ADR; code merged flag-off
live_activation: not_authorized # D4 ratified limitation carries over
default: off
blocker: >
§6.3 shows no benign/adversarial separation on the declared frame
(AUC 0.375, benign false-refusal 1.00); thresholds beyond gamma_id
are uncertified placeholders.
activation_requires:
- held-out-stable, safety-relevant grounding result (§11 programme)
- calibrated epsilon_turn/epsilon_session/tau_max/s_min certificates
- renewed discrimination evidence with acceptable benign refusal rate
- explicit human ratification
identity_wave_gate:
unchanged: true # ADR-0244 Accepted-with-limitation posture untouched
```
## 5. Uncertified placeholders (enumerated; never live defaults)
| Constant | Value | Status |
|---|---|---|
| `gamma_id` | 0.2126624458513829 | **CERTIFIED** (D4 Phase 3 Fibonacci certificate `0079b5f2…`); pinned equal to `identity._WAVE_LEAKAGE_BOUND` by test |
| `epsilon_turn` | 0.1 | placeholder |
| `epsilon_session` | 0.3 | placeholder |
| `tau_max` | = γ_id | placeholder |
| `s_min` | 0.0 | matches D4's fixed orientation floor (geometric invariant, not tuned) |
| `orth_tol` | 1e-6 | placeholder |
| `unclassified_tol` | 1e-6 | placeholder |
`AdmissionPolicy.placeholder_default()` carries `calibrated=False`; a serve
gate must never treat an uncalibrated policy as license to activate.
## 6. What this ADR does NOT do (preflight §7 non-goals — all honored)
No geometric `C_id`/corrector; no Ring-2 residual protocol; no semantic axis
grounding or pack redesign; no biography-holonomy merge; no atlas/VSWR work;
no analytic cast reactance; no grade-1 versor projection (proven vacuous); no
indefinite-norm leakage; no `H_id` enlargement; no soft-projection; no
default-on serve gate; no Smith-chart algebra; no status flip of ADR-0244/0245.
## 7. Resolved and open questions
1. **`‖·‖_G` convention** — implemented as `‖G^{1/2}MG^{-1/2}‖_F`; **ratify
with this ADR** (§2.2).
2. **`spatial_foreign` channel** — RESOLVED: tautologically zero for the
default full-span pack; fires correctly for reduced-support packs (pinned:
`test_spatial_foreign_channel_fires_for_reduced_support_pack`).
3. **Hard-break turn ownership** — implemented as new-chain; **ratify with
this ADR** (§2.4).
4. **Malformed-F/gate routing** — the D4 wave-path validator raises before the
admission surface runs; `MalformedVersorError` from the pure primitives
propagates fail-closed. No silent legacy fallback exists when a wave field
was supplied. Unifying the two typed errors is left to a future cleanup.
5. **OPEN (future ADR):** semantic axis grounding — blocked on a positive §11
result at adequate sample size; `ε` calibration; `H_id` policy products.
## 8. Consequences
- The identity organ now measures *everything* the preflight demanded —
in-span lawfulness, isometry integrity, typed foreign leakage, and
path accumulation — with serve byte-identity preserved and zero live policy
change. What it measures says, honestly: the declared frame is not the
structure live cognition preserves, so the gate stays off and the next
investment belongs to grounding, not thresholds.
- All future identity work inherits content-addressed, full-digest telemetry
(records + ledger) and a single pure source of truth for the §3 primitives.
## 9. Provenance
Fable 5: slice-0 diagnostic; §3 primitives + §3.4/3.5 ledger + §6.1/6.2 suites
(RED-first); this completion pass. Opus 4.8: adversarial audit (bit-exact math
re-derivation; H_id/soft-projection/scope-creep verification), F1 finding,
§3.7 surface + serve wiring, §6.3 discrimination report. Sonnet 5: §4.1/§4.2
telemetry records, `admitted` gate on the ledger (F1-adjacent §3.4-step-2
compliance), path serve integration (observe-only), §11 feasibility study
design. Every stage: local-first CI (smoke + targeted suites) before commit;
no self-Accept at any point.

View file

@ -0,0 +1,105 @@
# ADR-0246 Acceptance Packet — Induced Identity Action and Path Integrity
**Date:** 2026-07-17
**ADR:** `docs/adr/ADR-0246-induced-identity-action-and-path-integrity.md` (**Proposed**)
**Preflight:** `docs/briefs/ADR-0246-induced-identity-action-and-path-integrity-preflight.md`
**Provenance chain:** Fable 5 (slice-0 diagnostic; §3 primitives; §3.4/3.5 ledger; §6.1/6.2 suites; completion pass) → Opus 4.8 (adversarial audit VERDICT PASS — bit-exact math re-derivation; F1 finding; §3.7 surface + serve wiring; §6.3 discrimination) → Sonnet 5/Fable 5 (§4.1/§4.2 telemetry; §3.4-step-2 `admitted` gate; path serve integration; §11 feasibility study). No self-Accept at any stage.
---
## 1. Scope of what is being put to ruling
Accepting this ADR accepts: the §3 primitives and their pinned blade map; the
locked `H_id={I}` stabilizer; the **`‖·‖_G = ‖G^{1/2}MG^{-1/2}‖_F` norm
convention**; the **F1 composition semantics** (lawful turns compose their raw
*certified* action, never a literal `I`); the **admitted-gate** on composition
(§3.4 step 2); **hard-break turn ownership = new chain**; the §3.7
admit-or-abstain surface; the flag-gated serve wiring (default-off,
byte-identical off); and the §4.1/§4.2 telemetry contracts.
It does **NOT** accept or authorize: live activation of `identity_wave_gate`
or `identity_action_surface` (both stay default-off; the D4 ratified
limitation carries over); any calibration of the placeholder thresholds; any
semantic claim about the value labels ("lawfulness relative to the declared
frozen frame" only); any `H_id` enlargement or geometric corrector.
## 2. §10 acceptance criteria — status
| # | Criterion | Status |
|---|---|---|
| 1 | §6 synthetic + path suites green; discrimination report attached | ✅ 14/14 eval cases; §6.3 + §11 artifacts under `docs/audit/artifacts/` |
| 2 | `H_id={I}` enforced, no silent enlargement | ✅ singleton hardcoded; no enlargement path; grep-audited + pinned |
| 3 | Lawful-only composition proven by tests that fail if raw product used | ✅ `test_lawful_path_equals_lawful_subproduct_not_raw`, `test_raw_product_differs_from_lawful`, §3.4-step-2 pin |
| 4 | Ledger hard-breaks on pack/geometry/policy/session change | ✅ parametrized over every scope dimension |
| 5 | Flag-off serve path byte-identical | ✅ egress/action-record/path tests + all D4 gate surfaces green unchanged + smoke |
| 6 | No geometric `C_id`; admit-or-abstain only | ✅ no corrector exists; refusal folds into the existing `would_violate` egress |
| 7 | Typed residual channels pinned to explicit blade indices | ✅ e4=idx 4, e5=idx 5, spatial=idx 1/2/3; pinned vs `algebra.cl41` |
| 8 | Smoke + relevant lanes local-first; `[Verification]` on commits | ✅ every commit in the stack; final run log `docs/audit/artifacts/adr-0246-slice1-complete-runlog.txt` |
| 9 | Claims language: lawfulness-relative-to-frame, never semantic inalienability | ✅ binding in ADR §1; enforced in report code + tests |
| 10 | Explicit human ratification for status flip | ⏳ THIS PACKET — §8 below |
## 3. Verification summary (see run log for actual output)
- §6.1/§6.2 eval harness: **14/14** `all_passed=True`.
- ADR-0246 test suites (induced-action incl. spatial-foreign resolution,
path-ledger incl. raw-sneak + admitted-gate, geometric suite, admission +
honest-verdict pins, egress wiring, action-record §4.1, path serve
integration, grounding feasibility, mismatch diagnostic): **all green**.
- Adjacent D4 identity surfaces + telemetry suites: **green unchanged**
(serve byte-identity).
- `uv run core test --suite smoke -q`: **green** (appended to run log).
## 4. The honest §6.3 discrimination numbers (binding)
Benign pass **0.00** · false refusal **1.00** · adversarial detect 1.00 ·
near-identity control pass **1.00** · `d_stab` AUC **0.375** [0.15, 0.62] —
benign cognition sits ~18× farther from the declared frame than crafted
attacks. The gate mechanism is sound; the frame is not what live cognition
preserves. A refuse-everything gate is not a discriminator; activation stays
unauthorized.
## 5. The §11 grounding-feasibility verdict (binding, verbatim from the artifact)
> NULL (n_train=13, n_held_out=12): the top-2 generator-proxy subspace found
> on TRAIN does NOT reliably reproduce on the independently-collected HELD-OUT
> cohort — cosine similarity 0.52 sits at only the 87th percentile of what two
> INDEPENDENT pure-noise cohorts of the same size produce by chance (need >=
> 95th) and/or does not clear the discrimination bar (AUC 0.49, 95% CI [0.21,
> 0.77]). This is consistent with — and sharpens — the D4/slice-0/§6.3 finding
> at the GENERATOR level (not just the induced-action level): benign cognition
> does not have a small, stable, cohort-independent generator subspace
> detectable at this sample size. Threshold tuning on the current pack cannot
> produce a discriminating gate; this feasibility study does not find grounds
> to draft a revised ADR-0246 implementation contract. A much larger cohort
> (this study used n<=13 per real cohort) would be needed to rule out a real
> but subtle effect, rather than to overturn this null.
Method validated by sample-size-calibrated controls: shared-basis positive
pair recovers at cosine 0.9995 (100th percentile of the null; null p95 0.60 at
n=12). Precision transport immaterial (6.9e-7).
## 6. Rulings requested alongside Proposed→Accepted
1. `‖·‖_G` convention (ADR §2.2).
2. F1 composition semantics + admitted-gate reading of §3.4 (ADR §2.4).
3. Hard-break turn ownership = new chain (ADR §2.4).
4. The refusal_reason multi-condition widening (ADR §2.8).
## 7. Operational limitation carried forward
`identity_wave_gate` AND `identity_action_surface` remain **default-off / live
activation NOT authorized**. Activation prerequisites (all required): a
positive, held-out-stable, safety-relevant grounding result at adequate sample
size; calibrated ε/τ/s certificates; renewed discrimination evidence with
acceptable benign refusal; explicit human ratification.
## 8. RULING RECORD
**PENDING** — awaiting explicit ruling by Joshua Shay.
| Field | Value |
|---|---|
| Ruling | _(pending)_ |
| By | _(pending)_ |
| Date | _(pending)_ |
| Notes | _(pending)_ |

View file

@ -0,0 +1,111 @@
{
"cohorts": {
"adversarial_n": 8,
"held_out_n": 12,
"train_n": 13
},
"cross_cohort_cosine_null_distribution": {
"mean": 0.377394,
"n_trials": 200,
"p95": 0.599818
},
"cross_cohort_cosine_percentile_in_null": 0.87,
"cross_cohort_top2_cosine_similarity": 0.524901,
"discrimination_auc_adversarial_vs_heldout": 0.489583,
"discrimination_auc_ci95": [
0.208333,
0.770833
],
"held_out_eigenvalues": [
79.571248,
20.218919,
7.368282,
3.067749,
1.251647,
1.014757,
0.487365,
0.269996,
0.000952,
5.4e-05
],
"held_out_stability_null_percentile_floor": 0.95,
"held_out_variance_explained_top_2": 0.881142,
"method": {
"generator_proxy": "bivector (grade-2) coefficient block, 10 planes",
"note": "approximates the Lie generator to first order; exact for single-plane simple rotors/boosts, approximate for compound multi-generator turns; no scipy / matrix-log dependency"
},
"null_calibration_sample_size": 12,
"plane_energy_fractions": {
"held_out": {
"e12": 0.197125,
"e13": 0.130823,
"e14": 0.068146,
"e15": 0.112533,
"e23": 0.10294,
"e24": 0.123708,
"e25": 0.07482,
"e34": 0.004372,
"e35": 0.143569,
"e45": 0.041963
},
"train": {
"e12": 0.16094,
"e13": 0.082142,
"e14": 0.12553,
"e15": 0.050535,
"e23": 0.088179,
"e24": 0.168982,
"e25": 0.025171,
"e34": 0.03812,
"e35": 0.12034,
"e45": 0.140062
}
},
"precision_transport": {
"max_bivector_delta": 6.86e-07,
"significant": false
},
"recovery_controls": {
"method_recovers_true_structure": true,
"null_distribution": {
"mean": 0.382094,
"n_trials": 200,
"p50": 0.369315,
"p95": 0.60089
},
"positive_control_cross_cohort_cosine": 0.999463,
"positive_control_percentile_in_null": 1.0,
"sample_size": 12
},
"residual_from_train_top2_subspace": {
"adversarial": {
"mean": 0.817659
},
"held_out": {
"mean": 0.74434
},
"train": {
"mean": 0.656085
}
},
"schema_version": "adr_0246_grounding_feasibility_v1",
"train_eigenvalues": [
17.063268,
8.003419,
0.574088,
0.435995,
0.187462,
0.031598,
0.014257,
0.004931,
0.003561,
0.00118
],
"train_variance_explained_top_2": 0.95239,
"verdict": {
"held_out_stable_structure_found": false,
"honest_finding": "NULL (n_train=13, n_held_out=12): the top-2 generator-proxy subspace found on TRAIN does NOT reliably reproduce on the independently-collected HELD-OUT cohort \u2014 cosine similarity 0.52 sits at only the 87th percentile of what two INDEPENDENT pure-noise cohorts of the same size produce by chance (need >= 95th) and/or does not clear the discrimination bar (AUC 0.49, 95% CI [0.21, 0.77]). This is consistent with \u2014 and sharpens \u2014 the D4/slice-0/\u00a76.3 finding at the GENERATOR level (not just the induced-action level): benign cognition does not have a small, stable, cohort-independent generator subspace detectable at this sample size. Threshold tuning on the current pack cannot produce a discriminating gate; this feasibility study does not find grounds to draft a revised ADR-0246 implementation contract. A much larger cohort (this study used n<=13 per real cohort) would be needed to rule out a real but subtle effect, rather than to overturn this null.",
"recovery_method_validated": true,
"safety_relevant": false
}
}

View file

@ -0,0 +1,45 @@
ADR-0246 slice-1 COMPLETION — final verification run log
branch: feat/adr-0246-slice1-complete (stacked on slice1-hardened; full Ring-1 stack)
pre-commit HEAD: 47e7eb4e65703b186e4cbfcccd0add25b625b0fa
=== §6.1/§6.2 eval harness ===
[geometric_suite]
PASS identity_versor
PASS inplane_pi_inversion_e12
PASS inplane_90deg_permutation_e12
PASS mild_inplane_drift_e12_0.02
PASS alien_tilt_e14_1.5
PASS boost_e15_1.0
PASS near_singular_gram
PASS malformed_f_nan
PASS malformed_f_wrong_shape
[path_suite]
PASS lawful_near_identity_sequence
PASS small_rotations_accumulate_to_session_refusal
PASS interleaved_refuse_admit
PASS hard_break_on_pack_change
PASS raw_product_differs_from_lawful
14/14 cases passed; all_passed=True
placeholders (uncertified): {'epsilon_turn': 0.1, 'epsilon_session': 0.3, 'note': 'UNCERTIFIED — D4 Phase 3 certified only gamma_id; ε not calibrated'}
=== §11 grounding-feasibility (live) — summary of artifact ===
cohorts: {'adversarial_n': 8, 'held_out_n': 12, 'train_n': 13}
recovery: positive_cosine= 0.999463 pctile= 1.0 null_p95= 0.60089
real: cosine= 0.524901 pctile= 0.87
auc= 0.489583 ci= [0.208333, 0.770833]
verdict: {'held_out_stable_structure_found': False, 'recovery_method_validated': True, 'safety_relevant': False}
=== pytest: ALL ADR-0246 suites + adjacent D4 identity + telemetry ===
........................................................................ [ 31%]
........................................................................ [ 63%]
........................................................................ [ 94%]
............ [100%]
228 passed in 131.10s (0:02:11)
=== uv run core test --suite smoke -q ===
........................................................................ [ 81%]
................................ [100%]
176 passed in 130.16s (0:02:10)

View file

@ -57,8 +57,9 @@ structure is stabilized would instrument lawfulness on a frame the dynamics igno
| **§3.4/§3.5 path ledger** | lawful-only composition + hard breaks: `PathBudget`, `IdentityChainScope`, `IdentityPathLedger`, `advance_identity_path` in `identity_action.py`; refused turns = break markers (never soft-projected `I`); scope change = hard break onto new chain | **committed** `feat/adr-0246-path-ledger` (RED→GREEN, off-serving), stacked on primitives — awaiting review |
| **§6.1/§6.2 eval matrix (scaffold)** (this unit) | runnable synthetic geometric + path/holonomy suites (`evals/adr_0246_geometric_suite/`), every §6.1/§6.2 case pinned; malformed-F fail-closed (`MalformedVersorError`) | **draft scaffold** `feat/adr-0246-slice1-scaffold` (14/14 eval cases + 114 identity-surface tests + smoke green) — awaiting Opus/Shay audit (`docs/handoff/adr-0246-slice1-scaffold-notes.md`) |
| **§3.7 gate admit surface + §6.3 discrimination** (Opus audit+hardening) | `AdmissionPolicy`/`evaluate_admission` (§3.7 pure surface); wired into `identity.py`/`chat/runtime.py` behind new default-off `identity_action_surface` (byte-identical flag-off, admit-or-abstain, no corrector); §6.3 discrimination report | **committed** `feat/adr-0246-slice1-hardened` — audit PASS, honest finding: gate refuses benign+adversarial alike (AUC 0.375, benign d_stab 18× adversarial), stays off; awaiting Shay ratification (`docs/audit/adr-0246-slice1-opus-audit-and-hardening.md`) |
| §11 grounding-feasibility (Sonnet) | does a held-out-stable, safety-relevant dynamics-invariant structure exist? (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) — the only path to a discriminating gate | next; §6.3 shows threshold tuning cannot get there |
| §4.1 per-turn record + ADR-0246 body + acceptance packet (Sonnet) | `IdentityActionRecord` telemetry; ADR body as **Proposed** with §10 claims language + honest §6.3 numbers; §10 packet | last; no self-Accept |
| **§4.1/§4.2 telemetry + path serve integration** (completion pass) | `IdentityActionRecord` (full digests, `policy_version=version_id()`, multi-condition `refusal_reason`); `manifold_content_digest` + geometry/gate version ids; §3.4-step-2 `admitted` gate on the ledger; `advance_session_identity_path` observe-only serve wiring (same flags, instance lifetime = session boundary); telemetry emits `identity_action_*`/`identity_path_*` keys only when the paths ran | **committed** `feat/adr-0246-slice1-complete` — flag-off byte-identical; all suites green |
| **§11 grounding-feasibility** (completion pass) | fixed TRAIN(13)/HELD-OUT(12)/ADVERSARIAL(8) splits; bivector generator proxy (numpy-only); **sample-size-calibrated null** (200 noise-pair trials at real n) + shared-basis positive recovery control; precision pairs; per-plane energy | **DONE — honest NULL, method validated**: positive control 0.9995 (100th pctile of null) but real cross-cohort cosine 0.52 = 87th pctile of chance; AUC 0.49; no stable generator subspace at this n. Artifact: `docs/audit/artifacts/adr-0246-grounding-feasibility-report.json` |
| **ADR-0246 body + acceptance packet** | `docs/adr/ADR-0246-induced-identity-action-and-path-integrity.md` (**Proposed**; F1 semantics, ‖·‖_G convention, turn-ownership + refusal_reason rulings requested; binding claims language; honest §6.3 + §11 numbers; machine-readable operational-status block); packet `docs/audit/adr-0246-acceptance-packet-2026-07-17.md` §8 **RULING PENDING** | **committed** — no self-Accept; awaiting Shay ruling |
Nothing in the reordering relaxes a §7 non-goal: no `C_id` corrector, no `H_id`
enlargement, no pack/axis redesign, no gate activation. The §11 grounding study

View file

@ -0,0 +1,479 @@
"""ADR-0246 §11 grounding-feasibility study (research, off-serving, evidence-only).
The brief's §11 defers "semantic axis grounding" as a later workstream because
"instruments ≠ meaning." Slice 0 and the §6.3 discrimination report both showed
that NO fixed spatial 3-frame (declared or random) is dynamically preserved by
benign cognition. This study asks the prior, narrower, and answerable question:
Does *any* held-out-stable, low-dimensional structure exist in what the
live versor's generator actually does — independent of which frame we
declare and if so, does it discriminate benign traffic from adversarial
geometric attacks?
This is explicitly a FEASIBILITY STUDY, not an implementation. Per the Opus
handoff (§4c item 4) it is "the only path to a gate that discriminates"; the
brief's own instruction (research-question authority, not this module) is that
only a positive, held-out-stable, safety-relevant finding here would justify
drafting a revised ADR-0246 implementation contract. This module does not
draft one it reports what the data shows, honestly, including a null result.
**Method (fixed cohort splits, no resampling of the same pool):**
* TRAIN benign versors from ``LIVE_PROBE_SEQUENCE`` (D4 Phase 3 / slice-0's
pinned probe set), n13.
* HELD-OUT versors from ``PARAPHRASE_PROBE_SEQUENCE`` (independently worded,
same semantic register), n12. A genuine generalization test: any structure
found on TRAIN must ALSO appear on HELD-OUT, not merely be re-discovered by
refitting the same pool.
* ADVERSARIAL the existing crafted geometric-attack cohort (tilts, boosts,
inversions, permutations), n=8, reused from ``evals.adr_0246_discrimination``.
**Generator proxy (no scipy; numpy-only per the routing instruction).** Rather
than a matrix logarithm, this study uses the versor's own GRADE-2 (bivector)
coefficient vector (Cl(4,1) indices 6..15, 10 dims) as the generator proxy: for
a versor close to a simple exponential ``F = exp(B/2)``, the bivector block of
``F`` is proportional to ``B`` to leading order, and it is EXACT for the single-
plane simple rotors/boosts used throughout D4/ADR-0246 (this is the same
quantity ``versor_plane_occupancy`` already groups by plane in the slice-0
diagnostic). This is an approximation for compound multi-generator turns and is
documented as such not a claim of an exact Lie-algebra recovery.
**Honesty constraint (same as §6.3):** with n13 samples in a 10-dimensional
proxy space, a covariance fit on TRAIN alone is not evidence of structure
almost any small sample admits a low-rank-looking in-sample fit purely from
degrees of freedom. The only evidence this study credits is CROSS-COHORT
agreement: does the dominant direction found on TRAIN also explain variance on
the independently-collected HELD-OUT cohort? A held-out-stable finding is
reported only if it does; a null finding is reported plainly otherwise.
Off-serving; deterministic (fixed RNG seed for synthetic controls; live cohorts
via the existing lazy ``chat.runtime`` collectors never imported by serve).
"""
from __future__ import annotations
from typing import Any, Sequence
import numpy as np
from algebra.cl41 import N_COMPONENTS
from evals.adr_0246_discrimination import (
_auc_bootstrap_ci,
_roc_auc,
adversarial_cohort,
)
from evals.adr_0246_mismatch_diagnostic import (
IDX_E12,
IDX_E13,
IDX_E14,
IDX_E15,
IDX_E23,
IDX_E24,
IDX_E25,
IDX_E34,
IDX_E35,
IDX_E45,
PARAPHRASE_PROBE_SEQUENCE,
collect_live_versors,
)
from evals.adr_0244_gamma_calibration import LIVE_PROBE_SEQUENCE
# Bivector (grade-2) block: 10 planes, indices 6..15 in the 32-component layout.
BIVECTOR_INDICES: tuple[int, ...] = (
IDX_E12, IDX_E13, IDX_E14, IDX_E15, IDX_E23, IDX_E24, IDX_E25, IDX_E34,
IDX_E35, IDX_E45,
)
BIVECTOR_DIM = len(BIVECTOR_INDICES) # 10
_PLANE_NAMES = ("e12", "e13", "e14", "e15", "e23", "e24", "e25", "e34", "e35", "e45")
RECOVERY_CONTROL_SEED = 20260717
POSITIVE_CONTROL_TRUE_RANK = 2
POSITIVE_CONTROL_NOISE_SIGMA = 0.03
N_NULL_TRIALS = 200
# "Held-out stable" requires the real train-vs-held-out cross-cohort cosine to
# exceed this percentile of the SAME-SAMPLE-SIZE null distribution (two
# independent pure-noise cohorts) — i.e. p < 0.05 one-sided that the observed
# agreement arose by chance alone — AND the discrimination AUC-CI lower bound
# to clear chance (reusing evals.adr_0246_discrimination's own 0.6 bar).
HELD_OUT_STABILITY_NULL_PERCENTILE_FLOOR = 0.95
DISCRIMINATION_AUC_CI_FLOOR = 0.6
def bivector_coefficients(versor: np.ndarray) -> np.ndarray:
"""The 10-dim bivector-block generator proxy of a versor (Cl(4,1) indices 6..15)."""
versor = np.asarray(versor, dtype=np.float64)
return np.array([versor[i] for i in BIVECTOR_INDICES], dtype=np.float64)
def bivector_covariance(versors: Sequence[np.ndarray]) -> np.ndarray:
"""Sample covariance of the bivector-proxy vectors (numpy-only, no scipy)."""
coeffs = np.array([bivector_coefficients(v) for v in versors], dtype=np.float64)
if coeffs.shape[0] < 2:
raise ValueError("covariance requires at least 2 samples")
return np.cov(coeffs, rowvar=False)
def principal_directions(cov: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""Eigenvalues (descending) and eigenvectors of a covariance matrix via
``np.linalg.eigh`` (exact for real-symmetric; no scipy dependency)."""
eigvals, eigvecs = np.linalg.eigh(cov)
order = np.argsort(eigvals)[::-1]
return eigvals[order], eigvecs[:, order]
def variance_explained(eigvals: np.ndarray, k: int) -> float:
total = float(np.sum(eigvals))
if total <= 0.0:
return 0.0
return float(np.sum(eigvals[:k])) / total
def subspace_residual_fraction(vec: np.ndarray, top_eigvecs: np.ndarray) -> float:
"""Fraction of ``vec``'s energy OUTSIDE span(top_eigvecs) — 0 = fully inside."""
total = float(np.dot(vec, vec))
if total <= 0.0:
return 0.0
projection = top_eigvecs @ (top_eigvecs.T @ vec)
residual = vec - projection
return float(np.dot(residual, residual)) / total
def cross_cohort_top_pc_cosine_similarity(
train_versors: Sequence[np.ndarray], test_versors: Sequence[np.ndarray], *, k: int = 1
) -> float:
"""|cosine similarity| between the top-``k`` principal directions of two
INDEPENDENTLY collected cohorts the actual generalization signal.
A high value means the dominant generator direction found on one cohort
also explains the other's covariance structure (real, cohort-independent
structure). A value near 0 means the two cohorts' dominant directions are
unrelated (no stable structure matches the D4/slice-0 finding at the
generator level rather than the induced-action level).
"""
_, train_vecs = principal_directions(bivector_covariance(train_versors))
_, test_vecs = principal_directions(bivector_covariance(test_versors))
# top-k subspace overlap via singular values of the k x k Gram of top directions
a = train_vecs[:, :k]
b = test_vecs[:, :k]
overlap = a.T @ b
if k == 1:
return float(abs(overlap[0, 0]))
singular_values = np.linalg.svd(overlap, compute_uv=False)
return float(np.mean(singular_values)) # mean principal angle cosine
# --- synthetic recovery controls ------------------------------------------------
def synthetic_recovery_positive_cohort(
rng: np.random.Generator, n: int, basis: np.ndarray | None = None
) -> list[np.ndarray]:
"""POSITIVE control: bivector coefficients confined to a low-rank subspace
+ small noise.
``basis`` is the true subspace. Pass the SAME basis to generate two
independent cohorts sharing one true structure (the cross-cohort recovery
control); omitting it draws a fresh random subspace two cohorts built
with separate fresh bases share NOTHING and must never be compared as a
positive pair (that was a real bug caught RED in the test suite: the
"positive" pair scored 0.53, indistinguishable from noise, because each
call invented its own subspace).
"""
if basis is None:
basis = np.linalg.qr(
rng.standard_normal((BIVECTOR_DIM, POSITIVE_CONTROL_TRUE_RANK))
)[0]
coeffs = []
for _ in range(n):
weights = rng.standard_normal(POSITIVE_CONTROL_TRUE_RANK)
vec = basis @ weights + POSITIVE_CONTROL_NOISE_SIGMA * rng.standard_normal(BIVECTOR_DIM)
coeffs.append(_embed_bivector(vec))
return coeffs
def synthetic_recovery_negative_cohort(
rng: np.random.Generator, n: int
) -> list[np.ndarray]:
"""NEGATIVE control: isotropic random bivector coefficients (no structure).
The eigen-analysis MUST NOT report a dominant low-rank subspace a false
positive here would mean the method hallucinates structure from noise."""
coeffs = [
_embed_bivector(rng.standard_normal(BIVECTOR_DIM)) for _ in range(n)
]
return coeffs
def _embed_bivector(bivector_coeffs: np.ndarray) -> np.ndarray:
"""Embed a 10-dim bivector-coefficient vector into a full 32-dim versor-shaped
array (scalar part fixed at 1.0) purely so it round-trips through
``bivector_coefficients`` identically these are SYNTHETIC generator-proxy
vectors for the recovery controls, not claims of being valid versors."""
v = np.zeros(N_COMPONENTS, dtype=np.float64)
v[0] = 1.0
for idx, coeff in zip(BIVECTOR_INDICES, bivector_coeffs):
v[idx] = coeff
return v
def null_cross_cohort_cosine_distribution(
n: int, *, n_trials: int = N_NULL_TRIALS, seed: int = RECOVERY_CONTROL_SEED, k: int = 2
) -> np.ndarray:
"""The NULL distribution of ``cross_cohort_top_pc_cosine_similarity`` between
two INDEPENDENT isotropic-noise (no-true-structure) cohorts of size ``n``
calibrated to the ACTUAL sample size under study, not a generic asymptotic
threshold.
This matters because at small ``n`` (comparable to the 10-dim generator-proxy
space), a sample covariance from PURE NOISE still shows an inflated top-k
"variance explained" from finite-sample fluctuation alone (verified
empirically: at n=20 an isotropic negative control showed ~0.44, not the
asymptotic 0.20 chance level for k=2/10). Comparing a real result against a
fixed threshold derived from large-sample asymptotics would be dishonestly
optimistic. Instead every real finding here is judged against a null
distribution generated at the SAME ``n``.
"""
rng = np.random.default_rng(seed)
cosines = np.empty(n_trials, dtype=np.float64)
for i in range(n_trials):
cohort_a = synthetic_recovery_negative_cohort(rng, n)
cohort_b = synthetic_recovery_negative_cohort(rng, n)
cosines[i] = cross_cohort_top_pc_cosine_similarity(cohort_a, cohort_b, k=k)
return cosines
def empirical_percentile(value: float, null_distribution: np.ndarray) -> float:
"""Fraction of the null distribution at or below ``value`` — a one-sided
empirical p-value complement (0.95 value exceeds 95% of pure-noise draws
at the same sample size, i.e. p < 0.05 one-sided)."""
return float(np.mean(null_distribution <= value))
def run_recovery_controls(
n: int, *, seed: int = RECOVERY_CONTROL_SEED, n_trials: int = N_NULL_TRIALS
) -> dict[str, Any]:
"""Sample-size-calibrated recovery sanity check (see module docstring).
Two independent cohorts drawn from the SAME true rank-2 subspace (+ noise)
at size ``n`` each MUST show high cross-cohort cosine similarity, and that
similarity must clear the NULL distribution (two independent noise cohorts
at the same ``n``) confirming the method can detect real shared structure
at this exact sample size, not merely at a generously large one.
"""
rng = np.random.default_rng(seed)
# ONE shared true subspace; two INDEPENDENT cohorts drawn from it.
shared_basis = np.linalg.qr(
rng.standard_normal((BIVECTOR_DIM, POSITIVE_CONTROL_TRUE_RANK))
)[0]
positive_a = synthetic_recovery_positive_cohort(rng, n, basis=shared_basis)
positive_b = synthetic_recovery_positive_cohort(rng, n, basis=shared_basis)
positive_cosine = cross_cohort_top_pc_cosine_similarity(
positive_a, positive_b, k=POSITIVE_CONTROL_TRUE_RANK
)
null_dist = null_cross_cohort_cosine_distribution(
n, n_trials=n_trials, seed=seed + 1, k=POSITIVE_CONTROL_TRUE_RANK
)
positive_percentile = empirical_percentile(positive_cosine, null_dist)
return {
"sample_size": n,
"positive_control_cross_cohort_cosine": round(positive_cosine, 6),
"null_distribution": {
"n_trials": n_trials,
"mean": round(float(np.mean(null_dist)), 6),
"p50": round(float(np.percentile(null_dist, 50)), 6),
"p95": round(float(np.percentile(null_dist, 95)), 6),
},
"positive_control_percentile_in_null": round(positive_percentile, 6),
"method_recovers_true_structure": bool(positive_percentile > 0.95),
}
# --- precision pairs -------------------------------------------------------------
def precision_pair_delta(versor: np.ndarray) -> float:
"""Max abs delta of the bivector-proxy coefficients under an f64->f32->f64
round-trip of the whole versor (same style as the slice-0 transport probe)."""
versor64 = np.asarray(versor, dtype=np.float64)
versor_roundtrip = versor64.astype(np.float32).astype(np.float64)
return float(
np.max(
np.abs(bivector_coefficients(versor64) - bivector_coefficients(versor_roundtrip))
)
)
# --- plane occupancy (typed e4/e5 generator analysis) ---------------------------
def mean_plane_energy_fractions(versors: Sequence[np.ndarray]) -> dict[str, float]:
"""Mean fraction of bivector energy in each of the 10 individual planes,
across a cohort the "typed e4/e5 generator analysis": does the generator
concentrate in specific e4/e5-mixing planes, or spread evenly?"""
fractions = np.zeros(BIVECTOR_DIM, dtype=np.float64)
for versor in versors:
coeffs = bivector_coefficients(versor)
total = float(np.dot(coeffs, coeffs))
if total > 0.0:
fractions += (coeffs ** 2) / total
fractions /= max(len(versors), 1)
return {name: round(float(f), 6) for name, f in zip(_PLANE_NAMES, fractions)}
# --- cohort collection -----------------------------------------------------------
def collect_train_cohort() -> list[np.ndarray]:
"""TRAIN: benign versors from the D4/slice-0 pinned probe sequence."""
return [v for _, v in collect_live_versors(LIVE_PROBE_SEQUENCE)]
def collect_held_out_cohort() -> list[np.ndarray]:
"""HELD-OUT: independently-worded paraphrase versors (genuine generalization test)."""
return [v for _, v in collect_live_versors(PARAPHRASE_PROBE_SEQUENCE)]
def collect_adversarial_cohort() -> list[np.ndarray]:
"""The existing crafted geometric-attack cohort, reused for consistency."""
return [v for _, v in adversarial_cohort()]
# --- full study -------------------------------------------------------------------
def build_feasibility_report(
train: Sequence[np.ndarray] | None = None,
held_out: Sequence[np.ndarray] | None = None,
adversarial: Sequence[np.ndarray] | None = None,
) -> dict[str, Any]:
"""Run the full §11 feasibility study and report an honest verdict.
``train``/``held_out``/``adversarial`` default to the live/synthetic cohorts
described in the module docstring; pass explicit cohorts for a fast/offline
run (as the test suite does).
"""
train = list(train) if train is not None else collect_train_cohort()
held_out = list(held_out) if held_out is not None else collect_held_out_cohort()
adversarial = list(adversarial) if adversarial is not None else collect_adversarial_cohort()
# Null calibration uses the SMALLER of the two real cohort sizes — the more
# conservative (harder-to-clear) choice when the sizes differ.
calibration_n = max(min(len(train), len(held_out)), 3)
recovery = run_recovery_controls(calibration_n)
train_eigvals, train_eigvecs = principal_directions(bivector_covariance(train))
held_out_eigvals, _ = principal_directions(bivector_covariance(held_out))
top_k = 2
cross_cohort_cosine = cross_cohort_top_pc_cosine_similarity(train, held_out, k=top_k)
null_dist = null_cross_cohort_cosine_distribution(calibration_n, k=top_k)
real_percentile = empirical_percentile(cross_cohort_cosine, null_dist)
top_eigvecs = train_eigvecs[:, :top_k]
train_residuals = [subspace_residual_fraction(bivector_coefficients(v), top_eigvecs) for v in train]
held_out_residuals = [subspace_residual_fraction(bivector_coefficients(v), top_eigvecs) for v in held_out]
adversarial_residuals = [subspace_residual_fraction(bivector_coefficients(v), top_eigvecs) for v in adversarial]
auc = _roc_auc(adversarial_residuals, held_out_residuals)
auc_ci = _auc_bootstrap_ci(adversarial_residuals, held_out_residuals)
precision_deltas = [precision_pair_delta(v) for v in train + held_out]
plane_energy_train = mean_plane_energy_fractions(train)
plane_energy_held_out = mean_plane_energy_fractions(held_out)
held_out_stable = bool(
real_percentile >= HELD_OUT_STABILITY_NULL_PERCENTILE_FLOOR
and np.isfinite(auc_ci[0])
and auc_ci[0] > DISCRIMINATION_AUC_CI_FLOOR
)
report = {
"schema_version": "adr_0246_grounding_feasibility_v1",
"method": {
"generator_proxy": "bivector (grade-2) coefficient block, 10 planes",
"note": "approximates the Lie generator to first order; exact for "
"single-plane simple rotors/boosts, approximate for compound "
"multi-generator turns; no scipy / matrix-log dependency",
},
"cohorts": {"train_n": len(train), "held_out_n": len(held_out), "adversarial_n": len(adversarial)},
"null_calibration_sample_size": calibration_n,
"recovery_controls": recovery,
"train_eigenvalues": [round(float(x), 6) for x in train_eigvals],
"held_out_eigenvalues": [round(float(x), 6) for x in held_out_eigvals],
"train_variance_explained_top_2": round(variance_explained(train_eigvals, top_k), 6),
"held_out_variance_explained_top_2": round(variance_explained(held_out_eigvals, top_k), 6),
"cross_cohort_top2_cosine_similarity": round(cross_cohort_cosine, 6),
"cross_cohort_cosine_null_distribution": {
"n_trials": N_NULL_TRIALS,
"mean": round(float(np.mean(null_dist)), 6),
"p95": round(float(np.percentile(null_dist, 95)), 6),
},
"cross_cohort_cosine_percentile_in_null": round(real_percentile, 6),
"held_out_stability_null_percentile_floor": HELD_OUT_STABILITY_NULL_PERCENTILE_FLOOR,
"residual_from_train_top2_subspace": {
"train": {"mean": round(float(np.mean(train_residuals)), 6)},
"held_out": {"mean": round(float(np.mean(held_out_residuals)), 6)},
"adversarial": {"mean": round(float(np.mean(adversarial_residuals)), 6)},
},
"discrimination_auc_adversarial_vs_heldout": round(auc, 6) if np.isfinite(auc) else None,
"discrimination_auc_ci95": [
round(x, 6) if np.isfinite(x) else None for x in auc_ci
],
"precision_transport": {
"max_bivector_delta": round(max(precision_deltas), 9) if precision_deltas else 0.0,
"significant": bool(precision_deltas and max(precision_deltas) > 1e-4),
},
"plane_energy_fractions": {"train": plane_energy_train, "held_out": plane_energy_held_out},
"verdict": {
"recovery_method_validated": bool(recovery["method_recovers_true_structure"]),
"held_out_stable_structure_found": held_out_stable,
"safety_relevant": bool(held_out_stable and auc > 0.5),
},
}
report["verdict"]["honest_finding"] = _honest_finding(report)
return report
def _honest_finding(report: dict[str, Any]) -> str:
v = report["verdict"]
cos = report["cross_cohort_top2_cosine_similarity"]
pct = report["cross_cohort_cosine_percentile_in_null"]
auc = report["discrimination_auc_adversarial_vs_heldout"]
ci = report["discrimination_auc_ci95"]
n = report["cohorts"]
if not v["recovery_method_validated"]:
return (
"INCONCLUSIVE: the recovery-control sanity check failed — at this "
"sample size the method cannot reliably distinguish a real shared "
"structure from chance agreement between two noise cohorts, "
"independent of the real cohorts' outcome. Do not draw a conclusion "
"from the real-cohort numbers below."
)
if v["held_out_stable_structure_found"]:
return (
f"POSITIVE (n_train={n['train_n']}, n_held_out={n['held_out_n']}): the "
f"top-2 generator-proxy subspace found on TRAIN cosine-agrees with the "
f"independently-collected HELD-OUT cohort at {cos:.2f} — the "
f"{pct * 100:.0f}th percentile of the SAME-SAMPLE-SIZE null distribution "
f"(two independent pure-noise cohorts), i.e. this agreement is unlikely "
f"to have arisen by chance alone. It also discriminates the adversarial "
f"cohort from held-out benign at AUC {auc:.2f} (95% CI [{ci[0]:.2f}, "
f"{ci[1]:.2f}], clears chance). This is evidence — NOT proof at this "
f"sample size — that a held-out-stable, safety-relevant structure may "
f"exist. A larger, pre-registered cohort study is required before "
f"drafting an ADR-0246 implementation contract on this basis."
)
return (
f"NULL (n_train={n['train_n']}, n_held_out={n['held_out_n']}): the top-2 "
f"generator-proxy subspace found on TRAIN does NOT reliably reproduce on "
f"the independently-collected HELD-OUT cohort — cosine similarity {cos:.2f} "
f"sits at only the {pct * 100:.0f}th percentile of what two INDEPENDENT "
f"pure-noise cohorts of the same size produce by chance (need >= 95th) "
f"and/or does not clear the discrimination bar (AUC {auc:.2f}, 95% CI "
f"[{ci[0]:.2f}, {ci[1]:.2f}]). This is consistent with — and sharpens — the "
f"D4/slice-0/§6.3 finding at the GENERATOR level (not just the induced-action "
f"level): benign cognition does not have a small, stable, cohort-independent "
f"generator subspace detectable at this sample size. Threshold tuning on the "
f"current pack cannot produce a discriminating gate; this feasibility study "
f"does not find grounds to draft a revised ADR-0246 implementation contract. "
f"A much larger cohort (this study used n<=13 per real cohort) would be "
f"needed to rule out a real but subtle effect, rather than to overturn this null."
)

View file

@ -0,0 +1,40 @@
"""Run the ADR-0246 §11 grounding-feasibility study and emit the report.
Usage: uv run python -m evals.adr_0246_grounding_feasibility [out.json]
Collects the live TRAIN (benign) and HELD-OUT (paraphrase) cohorts (spins up a
fresh empty-vault runtime twice), runs the recovery controls + cross-cohort
generator analysis + discrimination check, and prints the honest verdict.
"""
from __future__ import annotations
import json
import sys
from evals.adr_0246_grounding_feasibility import build_feasibility_report
def main() -> int:
report = build_feasibility_report()
summary = {
"cohorts": report["cohorts"],
"recovery_controls": report["recovery_controls"],
"cross_cohort_top2_cosine_similarity": report["cross_cohort_top2_cosine_similarity"],
"cross_cohort_cosine_percentile_in_null": report["cross_cohort_cosine_percentile_in_null"],
"discrimination_auc_adversarial_vs_heldout": report["discrimination_auc_adversarial_vs_heldout"],
"discrimination_auc_ci95": report["discrimination_auc_ci95"],
"precision_transport": report["precision_transport"],
"plane_energy_fractions": report["plane_energy_fractions"],
"verdict": report["verdict"],
}
print(json.dumps(summary, indent=2, sort_keys=True))
if len(sys.argv) > 1:
with open(sys.argv[1], "w", encoding="utf-8") as fh:
fh.write(json.dumps(report, indent=2, sort_keys=True) + "\n")
print(f"\nfull report written to {sys.argv[1]}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,186 @@
"""ADR-0246 §4.1/§4.3 — per-turn IdentityActionRecord telemetry pins.
Pins the pure record builder (`build_identity_action_record`), its full-SHA-256
digests (§4.3 no truncation, no `default=str`), the conditional-population
discipline (never built unless the wave/action surface actually ran), the
minimal serve wiring (IdentityScore.action_record, populated only when
admission_policy is supplied), and the telemetry serializer's conditional
emission (wave_mode_active AND action_surface_active both required flag-off
wire format is provably unchanged).
"""
from __future__ import annotations
import numpy as np
import pytest
from algebra.cl41 import N_COMPONENTS
from core.physics.identity import IdentityCheck, IdentityManifold, ValueAxis
from core.physics.identity_action import (
AdmissionPolicy,
build_identity_action_record,
)
from core.physics.identity_manifold import IdentityManifoldGeometry
from chat.telemetry import serialize_turn_event
_E14 = 8
def _rotor(biv, theta):
r = np.zeros(N_COMPONENTS, dtype=np.float64)
r[0] = np.cos(theta / 2.0)
r[biv] = np.sin(theta / 2.0)
return r
def _identity_versor():
v = np.zeros(N_COMPONENTS, dtype=np.float64)
v[0] = 1.0
return v
@pytest.fixture(scope="module")
def geometry():
return IdentityManifoldGeometry.from_directions(
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
)
def _manifold():
return IdentityManifold(
value_axes=(
ValueAxis(name="truthfulness", direction=(1.0, 0.0, 0.0)),
ValueAxis(name="coherence", direction=(0.0, 1.0, 0.0)),
ValueAxis(name="reverence", direction=(0.0, 0.0, 1.0)),
)
)
class _Trajectory:
trajectory_id = "record_test"
total_coherence_delta = 0.0
frames = ()
# --- pure builder pins ---------------------------------------------------------
def test_record_schema_and_shape(geometry):
policy = AdmissionPolicy.placeholder_default()
record = build_identity_action_record(
geometry, _identity_versor(), policy, trajectory_id="t1", turn_id=3,
)
d = record.as_dict()
assert d["schema_version"] == "identity_action_v1"
assert d["turn_id"] == 3 and d["trajectory_id"] == "t1"
assert set(d["typed_residual_energy"]) == {
"spatial_foreign", "boost_like", "null_or_conformal", "unclassified",
}
assert len(d["A_raw"]) == 3 and len(d["A_raw"][0]) == 3
assert d["admitted"] is True and d["refusal_reason"] is None
assert d["lawful_action"] == "I"
assert d["path_break"] is False
def test_record_refusal_reason_and_lawful_action_on_attack(geometry):
policy = AdmissionPolicy.placeholder_default()
record = build_identity_action_record(geometry, _rotor(_E14, 1.5), policy)
assert record.admitted is False
assert record.refusal_reason is not None
assert ">" in record.refusal_reason # e.g. "d_orth>orth_tol;..."
assert record.lawful_action == "none"
def test_record_digests_are_full_sha256_and_deterministic(geometry):
policy = AdmissionPolicy.placeholder_default()
r1 = build_identity_action_record(geometry, _identity_versor(), policy)
r2 = build_identity_action_record(geometry, _identity_versor(), policy)
assert len(r1.field_digest) == 64 and len(r1.record_digest()) == 64
int(r1.field_digest, 16)
int(r1.record_digest(), 16)
assert r1.field_digest == r2.field_digest
assert r1.record_digest() == r2.record_digest()
def test_record_digest_changes_with_content(geometry):
policy = AdmissionPolicy.placeholder_default()
r_id = build_identity_action_record(geometry, _identity_versor(), policy)
r_atk = build_identity_action_record(geometry, _rotor(_E14, 1.5), policy)
assert r_id.record_digest() != r_atk.record_digest()
assert r_id.field_digest != r_atk.field_digest
def test_policy_version_id_is_full_sha256_and_reflects_calibration_state(geometry):
p1 = AdmissionPolicy.placeholder_default()
assert len(p1.version_id()) == 64
int(p1.version_id(), 16)
p2 = AdmissionPolicy.placeholder_default()
assert p1.version_id() == p2.version_id() # deterministic
from dataclasses import replace
p3 = replace(p1, gamma_id=0.5)
assert p3.version_id() != p1.version_id() # threshold change -> new version
def test_manifold_content_digest_changes_on_axis_change():
from core.physics.identity import manifold_content_digest
m1 = _manifold()
m2 = IdentityManifold(value_axes=_manifold().value_axes[:2])
d1 = manifold_content_digest(m1)
d2 = manifold_content_digest(m2)
assert len(d1) == 64 and d1 != d2
assert manifold_content_digest(m1) == d1 # deterministic
# --- serve wiring: IdentityScore.action_record ---------------------------------
def test_flag_off_action_record_is_none():
check = IdentityCheck()
score = check.check(_Trajectory(), _manifold(), wave_field=_identity_versor())
assert score.action_record is None
def test_flag_on_populates_action_record():
check = IdentityCheck()
score = check.check(
_Trajectory(), _manifold(), wave_field=_identity_versor(),
admission_policy=AdmissionPolicy.placeholder_default(),
)
assert score.action_record is not None
assert score.action_record.trajectory_id == "record_test"
assert score.action_record.admitted is True
# --- telemetry serialization: conditional emission -----------------------------
class _Event:
turn = 1
identity_score = None
def test_telemetry_flag_off_has_no_action_fields():
check = IdentityCheck()
ev = _Event()
ev.identity_score = check.check(
_Trajectory(), _manifold(), wave_field=_identity_versor()
)
payload = serialize_turn_event(ev)
assert "identity_action_admitted" not in payload
assert "identity_d_orth" not in payload
assert "identity_d_stab" not in payload
def test_telemetry_flag_on_emits_action_surface_fields():
check = IdentityCheck()
ev = _Event()
ev.identity_score = check.check(
_Trajectory(), _manifold(), wave_field=_rotor(_E14, 1.5),
admission_policy=AdmissionPolicy.placeholder_default(),
)
payload = serialize_turn_event(ev)
assert payload["identity_action_admitted"] is False
assert isinstance(payload["identity_d_orth"], float)
assert isinstance(payload["identity_d_stab"], float)
assert "identity_action_record_digest" in payload
assert len(payload["identity_action_record_digest"]) == 64

View file

@ -0,0 +1,138 @@
"""ADR-0246 §11 grounding-feasibility study pins.
Pins the recovery-control sanity checks (the method must find structure when it
genuinely exists, and must not hallucinate structure from noise), the
bivector-proxy machinery, and the honest-verdict logic on injected cohorts no
live runtime required. A separate live smoke test (marked slow) exercises the
real collectors.
"""
from __future__ import annotations
import numpy as np
import pytest
from algebra.cl41 import N_COMPONENTS
from evals.adr_0246_grounding_feasibility import (
BIVECTOR_DIM,
build_feasibility_report,
bivector_coefficients,
cross_cohort_top_pc_cosine_similarity,
precision_pair_delta,
run_recovery_controls,
subspace_residual_fraction,
)
from evals.adr_0246_mismatch_diagnostic import IDX_E12, IDX_E13, IDX_E14
def _rotor(biv, theta):
r = np.zeros(N_COMPONENTS, dtype=np.float64)
r[0] = np.cos(theta / 2.0)
r[biv] = np.sin(theta / 2.0)
return r
def test_bivector_coefficients_extract_the_right_slots():
v = _rotor(IDX_E12, 0.6)
coeffs = bivector_coefficients(v)
assert coeffs.shape == (BIVECTOR_DIM,)
assert coeffs[0] == pytest.approx(v[IDX_E12])
assert np.count_nonzero(coeffs) == 1 # only e12 is populated
def test_recovery_controls_validate_the_method():
# sample-size-calibrated: two independent cohorts sharing a TRUE rank-2
# subspace must show cross-cohort cosine similarity far above what two
# independent pure-noise cohorts of the SAME size produce by chance.
result = run_recovery_controls(13)
assert result["positive_control_cross_cohort_cosine"] > 0.8
assert result["positive_control_percentile_in_null"] > 0.95
assert result["method_recovers_true_structure"] is True
# the null distribution itself should be well below the positive signal
assert result["null_distribution"]["p95"] < result["positive_control_cross_cohort_cosine"]
def test_cross_cohort_cosine_detects_shared_structure():
# both cohorts confined to the SAME e12/e13 plane pair -> high cosine
cohort_a = [_rotor(IDX_E12, 0.1 * i + 0.05) for i in range(6)]
cohort_b = [_rotor(IDX_E13, 0.1 * i + 0.05) for i in range(6)]
# mix e12/e13 in both cohorts so both share a 2-plane subspace
mixed_a = cohort_a + [_rotor(IDX_E13, 0.05 * i) for i in range(6)]
mixed_b = cohort_b + [_rotor(IDX_E12, 0.05 * i) for i in range(6)]
cosine = cross_cohort_top_pc_cosine_similarity(mixed_a, mixed_b, k=2)
assert cosine > 0.5 # shared 2-plane structure should show real overlap
def test_cross_cohort_cosine_detects_unrelated_structure():
cohort_a = [_rotor(IDX_E12, 0.1 * i + 0.05) for i in range(10)]
# cohort_b lives entirely in an orthogonal plane (e.g. e35, far from e12)
from evals.adr_0246_mismatch_diagnostic import IDX_E35
cohort_b = [_rotor(IDX_E35, 0.1 * i + 0.05) for i in range(10)]
cosine = cross_cohort_top_pc_cosine_similarity(cohort_a, cohort_b, k=1)
assert cosine < 0.3 # unrelated single-plane structure -> low overlap
def test_subspace_residual_fraction_zero_inside_span():
v = _rotor(IDX_E12, 0.5)
coeffs = bivector_coefficients(v)
basis = np.zeros((BIVECTOR_DIM, 1))
basis[0, 0] = 1.0 # e12 direction (index 0 in BIVECTOR_INDICES)
assert subspace_residual_fraction(coeffs, basis) < 1e-9
def test_precision_pair_delta_is_tiny():
for versor in (_rotor(IDX_E12, 0.5), _rotor(IDX_E14, 1.3)):
assert precision_pair_delta(versor) < 1e-4
def test_feasibility_report_null_on_unrelated_cohorts():
# TRAIN and HELD-OUT confined to unrelated planes -> expect a NULL verdict
train = [_rotor(IDX_E12, 0.1 * i + 0.05) for i in range(10)]
from evals.adr_0246_mismatch_diagnostic import IDX_E35, IDX_E45
held_out = [_rotor(IDX_E45, 0.1 * i + 0.05) for i in range(10)]
adversarial = [_rotor(IDX_E14, 1.5), _rotor(IDX_E35, 1.2)]
report = build_feasibility_report(train, held_out, adversarial)
assert report["verdict"]["recovery_method_validated"] is True
assert report["verdict"]["held_out_stable_structure_found"] is False
assert "NULL" in report["verdict"]["honest_finding"]
def test_feasibility_report_positive_on_shared_plane_cohorts():
# TRAIN and HELD-OUT share the same 2-plane structure (e12/e13); adversarial
# lives elsewhere entirely (e14/e35/e45) -> expect the structure to be found
# AND to discriminate against the adversarial cohort.
from evals.adr_0246_mismatch_diagnostic import IDX_E35, IDX_E45
rng = np.random.default_rng(7)
def shared_plane_versor():
theta12 = rng.normal(0, 0.3)
theta13 = rng.normal(0, 0.3)
v = np.zeros(N_COMPONENTS, dtype=np.float64)
v[0] = 1.0
v[IDX_E12] = theta12
v[IDX_E13] = theta13
return v
train = [shared_plane_versor() for _ in range(15)]
held_out = [shared_plane_versor() for _ in range(15)]
adversarial = [_rotor(IDX_E14, 1.5), _rotor(IDX_E35, 1.3), _rotor(IDX_E45, 1.1)]
report = build_feasibility_report(train, held_out, adversarial)
assert report["cross_cohort_top2_cosine_similarity"] > 0.7
assert "POSITIVE" in report["verdict"]["honest_finding"]
def test_report_schema_shape():
train = [_rotor(IDX_E12, 0.1 * i + 0.05) for i in range(10)]
held_out = [_rotor(IDX_E13, 0.1 * i + 0.05) for i in range(10)]
adversarial = [_rotor(IDX_E14, 1.5)]
report = build_feasibility_report(train, held_out, adversarial)
assert report["schema_version"] == "adr_0246_grounding_feasibility_v1"
assert set(report["cohorts"]) == {"train_n", "held_out_n", "adversarial_n"}
assert "honest_finding" in report["verdict"]
def test_module_is_pure_offserving():
import evals.adr_0246_grounding_feasibility as mod
assert mod.__file__ is not None
with open(mod.__file__, encoding="utf-8") as fh:
src = fh.read()
assert "import chat" not in src and "from chat" not in src

View file

@ -108,6 +108,37 @@ def test_e5_boost_fires_boost_channel_and_is_non_isometric(geometry):
assert geometry.orthogonality_defect(versor) > 0.05 # boost not a G-isometry
def test_spatial_foreign_channel_is_zero_for_default_pack_by_construction():
"""Resolves the open uncertainty from the Fable/Opus handoff notes: is
``spatial_foreign`` structurally broken? No for the DEFAULT 3-axis pack
(support = e1,e2,e3, i.e. the full spatial grade-1 block), the rejection
``rotated - project(rotated)`` is by construction orthogonal to e1/e2/e3, so
this channel is TAUTOLOGICALLY zero there is no "spatial but outside
support" direction left when the support IS all of span(e1,e2,e3).
"""
geom3 = IdentityManifoldGeometry.from_directions(
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
)
for versor in (_rotor(_E14, 1.3), _boost(_E25, 0.9), _rotor(_E12, 2.0)):
assert geom3.typed_residual_energy(versor)["spatial_foreign"] == pytest.approx(
0.0, abs=1e-12
)
def test_spatial_foreign_channel_fires_for_reduced_support_pack():
"""A pack whose declared axes do NOT span all of e1/e2/e3 (here: only
e1/e2) has a genuine "spatial but outside support" direction (e3), and a
versor tilting an axis toward it must register nonzero ``spatial_foreign``
confirming the channel is correct in general, not merely inert.
"""
geom2 = IdentityManifoldGeometry.from_directions(((1.0, 0.0, 0.0), (0.0, 1.0, 0.0)))
tilt_toward_e3 = _rotor(_E13, 1.0) # e13 tilts axis e1 toward e3 (out-of-support)
channels = geom2.typed_residual_energy(tilt_toward_e3)
assert channels["spatial_foreign"] > 0.05
assert channels["null_or_conformal"] == pytest.approx(0.0, abs=1e-12)
assert channels["boost_like"] == pytest.approx(0.0, abs=1e-12)
def test_typed_residual_energy_fractions_are_bounded_and_clean(geometry):
for versor in (_rotor(_E14, 0.7), _boost(_E25, 0.6), _rotor(_E12, 0.3)):
ch = geometry.typed_residual_energy(versor)

View file

@ -0,0 +1,202 @@
"""ADR-0246 §3.4/§3.5 serve integration pins — session identity-path ledger.
Pins the §3.4-step-2 ``admitted`` gate on the pure ledger (a policy-refused turn
must break even when its d_stab is small), the ``advance_session_identity_path``
serve helper (scope from manifold digest + version ids; observe-only), the
runtime wiring (ledger advanced only when both flags are on; instance lifetime
is the session boundary), and the telemetry emission (identity_path_* keys only
when the path ran flag-off wire format byte-identical).
"""
from __future__ import annotations
import numpy as np
import pytest
from algebra.cl41 import N_COMPONENTS
from core.config import RuntimeConfig
from core.physics.identity import (
GEOMETRY_VERSION,
IdentityManifold,
ValueAxis,
advance_session_identity_path,
manifold_content_digest,
)
from core.physics.identity_action import (
AdmissionPolicy,
IdentityChainScope,
PathBudget,
advance_identity_path,
)
from core.physics.identity_manifold import IdentityManifoldGeometry
from chat.telemetry import serialize_turn_event
_E12, _E14 = 6, 8
def _rotor(biv, theta):
r = np.zeros(N_COMPONENTS, dtype=np.float64)
r[0] = np.cos(theta / 2.0)
r[biv] = np.sin(theta / 2.0)
return r
def _boost(biv, theta):
r = np.zeros(N_COMPONENTS, dtype=np.float64)
r[0] = np.cosh(theta / 2.0)
r[biv] = np.sinh(theta / 2.0)
return r
def _identity_versor():
v = np.zeros(N_COMPONENTS, dtype=np.float64)
v[0] = 1.0
return v
def _manifold():
return IdentityManifold(
value_axes=(
ValueAxis(name="truthfulness", direction=(1.0, 0.0, 0.0)),
ValueAxis(name="coherence", direction=(0.0, 1.0, 0.0)),
ValueAxis(name="reverence", direction=(0.0, 0.0, 1.0)),
)
)
@pytest.fixture(scope="module")
def geometry():
return IdentityManifoldGeometry.from_directions(
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
)
# --- §3.4 step-2: the admitted gate on the pure ledger --------------------------
def test_policy_refused_turn_breaks_even_with_small_d_stab(geometry):
scope = IdentityChainScope(
pack_content_digest="p", geometry_version="g", policy_version="v",
session_id="s",
)
budget = PathBudget(epsilon_turn=0.1, epsilon_session=0.3)
small = geometry.induced_action(_rotor(_E12, 0.02)) # d_stab well under 0.1
# admitted=False (e.g. refused on leakage alone) MUST break, not compose
ledger, rec = advance_identity_path(
None, scope, small, geometry.gram, budget, admitted=False
)
assert rec["lawful"] is False and rec["path_break"] is True
assert ledger.composed_turn_count == 0 and ledger.break_count == 1
assert np.allclose(ledger.a_path_lawful, np.eye(3), atol=1e-12)
# same action with admitted=True composes
ledger2, rec2 = advance_identity_path(
None, scope, small, geometry.gram, budget, admitted=True
)
assert rec2["lawful"] is True and ledger2.composed_turn_count == 1
# --- advance_session_identity_path (serve helper) -------------------------------
def test_session_path_near_identity_composes():
policy = AdmissionPolicy.placeholder_default()
ledger, rec = advance_session_identity_path(
None, _manifold(), _identity_versor(), policy
)
assert rec["hard_break"] is True and rec["lawful"] is True
assert ledger.composed_turn_count == 1 and ledger.session_admit is True
assert ledger.scope.pack_content_digest == manifold_content_digest(_manifold())
assert ledger.scope.geometry_version == GEOMETRY_VERSION
def test_session_path_refused_turn_breaks():
policy = AdmissionPolicy.placeholder_default()
ledger, _ = advance_session_identity_path(
None, _manifold(), _identity_versor(), policy
)
ledger, rec = advance_session_identity_path(
ledger, _manifold(), _rotor(_E14, 1.5), policy # alien tilt: refused
)
assert rec["lawful"] is False and rec["path_break"] is True
assert ledger.break_count == 1 and ledger.composed_turn_count == 1
def test_session_path_hard_breaks_on_pack_change():
policy = AdmissionPolicy.placeholder_default()
ledger, _ = advance_session_identity_path(
None, _manifold(), _identity_versor(), policy
)
first_chain = ledger.chain_id
other_manifold = IdentityManifold(value_axes=_manifold().value_axes[:2])
ledger, rec = advance_session_identity_path(
ledger, other_manifold, _identity_versor(), policy
)
assert rec["hard_break"] is True
assert ledger.chain_id != first_chain
# --- telemetry ------------------------------------------------------------------
class _Event:
turn = 1
identity_score = None
identity_path = None
def test_telemetry_no_path_keys_when_absent():
payload = serialize_turn_event(_Event())
assert not any(k.startswith("identity_path_") for k in payload)
def test_telemetry_emits_path_keys_when_present():
policy = AdmissionPolicy.placeholder_default()
ledger, _ = advance_session_identity_path(
None, _manifold(), _identity_versor(), policy
)
ev = _Event()
ev.identity_path = ledger
payload = serialize_turn_event(ev)
assert payload["identity_path_chain_id"] == ledger.chain_id
assert payload["identity_path_composed_turns"] == 1
assert payload["identity_path_breaks"] == 0
assert payload["identity_path_session_admit"] is True
# --- runtime wiring (flag-gated; observe-only) ----------------------------------
def test_runtime_ledger_attribute_defaults_none_and_flag_off_never_advances():
from chat.runtime import ChatRuntime
runtime = ChatRuntime(config=RuntimeConfig(), no_load_state=True)
assert runtime._identity_path_ledger is None
runtime.chat("water boils")
assert runtime._identity_path_ledger is None # both flags off: never advanced
# and the emitted turn event carries no path ledger
assert runtime.turn_log[-1].identity_path is None
def test_runtime_ledger_advances_when_both_flags_on():
from chat.runtime import ChatRuntime
runtime = ChatRuntime(
config=RuntimeConfig(identity_wave_gate=True, identity_action_surface=True),
no_load_state=True,
)
# Not every turn reaches the wave-path check (first-touch turns can take
# the stub path — same reason slice-0 captured 13/16 probe turns), so run
# the duplicated-probe pattern until one main-path turn advances the ledger.
for text in ("water boils", "water boils", "birds fly", "birds fly"):
runtime.chat(text)
if runtime._identity_path_ledger is not None:
break
ledger = runtime._identity_path_ledger
assert ledger is not None
assert ledger.composed_turn_count + ledger.break_count >= 1
# observe-only: chat() raised nothing regardless of session_admit; the
# ledger-bearing turn's event serializes the path keys
ledger_events = [e for e in runtime.turn_log if e.identity_path is not None]
assert ledger_events, "no turn event carried the path ledger"
payload = serialize_turn_event(ledger_events[-1])
assert "identity_path_chain_id" in payload