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:
parent
47e7eb4e65
commit
45539e62d5
15 changed files with 2020 additions and 20 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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", ""))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
|
|
|||
282
docs/adr/ADR-0246-induced-identity-action-and-path-integrity.md
Normal file
282
docs/adr/ADR-0246-induced-identity-action-and-path-integrity.md
Normal 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.
|
||||
105
docs/audit/adr-0246-acceptance-packet-2026-07-17.md
Normal file
105
docs/audit/adr-0246-acceptance-packet-2026-07-17.md
Normal 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)_ |
|
||||
111
docs/audit/artifacts/adr-0246-grounding-feasibility-report.json
Normal file
111
docs/audit/artifacts/adr-0246-grounding-feasibility-report.json
Normal 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
|
||||
}
|
||||
}
|
||||
45
docs/audit/artifacts/adr-0246-slice1-complete-runlog.txt
Normal file
45
docs/audit/artifacts/adr-0246-slice1-complete-runlog.txt
Normal 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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
479
evals/adr_0246_grounding_feasibility/__init__.py
Normal file
479
evals/adr_0246_grounding_feasibility/__init__.py
Normal 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), n≈13.
|
||||
* HELD-OUT — versors from ``PARAPHRASE_PROBE_SEQUENCE`` (independently worded,
|
||||
same semantic register), n≈12. 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 n≈13 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."
|
||||
)
|
||||
40
evals/adr_0246_grounding_feasibility/__main__.py
Normal file
40
evals/adr_0246_grounding_feasibility/__main__.py
Normal 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())
|
||||
186
tests/test_adr_0246_action_record.py
Normal file
186
tests/test_adr_0246_action_record.py
Normal 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
|
||||
138
tests/test_adr_0246_grounding_feasibility.py
Normal file
138
tests/test_adr_0246_grounding_feasibility.py
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
202
tests/test_adr_0246_path_serve_integration.py
Normal file
202
tests/test_adr_0246_path_serve_integration.py
Normal 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
|
||||
Loading…
Reference in a new issue