draft(adr-0246): slice-1 scaffold — §6.1/§6.2 eval suite + malformed-F guard
BOUNDED AUTONOMOUS SCAFFOLD DRAFT (Fable 5) — not a PR, not merged, no status
flip, no main push. For Opus 4.8 + Shay audit before anything proceeds toward main.
Stacked on the verified §3-primitives + §3.4/3.5-ledger stack (reuse, not re-derive;
all descend from main @ 04d67ca5).
Adds (directive steps 3-4):
+ evals/adr_0246_geometric_suite/ runnable §6.1 synthetic geometric suite
(identity, pi-inversion, 90deg-permutation, mild drift, alien tilt e14,
boost e15, near-singular Gram, malformed F) + §6.2 path/holonomy suite
(lawful sequence, small-rotation session accumulation, interleaved refuse,
pack-change hard break, raw!=lawful forensic). 14/14 cases pass.
+ tests/test_adr_0246_geometric_suite.py pins every case + explicit §6.1 pins
(pi-inversion s=-1, 90deg s=0, near-singular Gram error, malformed-F error)
+ identity_manifold.py: MalformedVersorError + _validate_versor guard on
induced_action / typed_residual_energy (§6.1 fail-closed on malformed F)
+ docs/handoff/adr-0246-slice1-scaffold-notes.md placeholder list, §5-§7
uncertainties, constraint-compliance record, explicit Opus/human TODOs
+ docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt actual run output
Constraints honored: H_id={I} only; no soft-projection of unlawful A; path
composes lawful actions only (never raw product); no C_id corrector; chat/runtime,
flags, D4 gate wiring untouched (A-04 quarantine pinned); no discrimination report,
no ADR body, no claims language (deferred TODO: Opus/human); D4 plan not modified.
epsilon_turn/epsilon_session are UNCERTIFIED PLACEHOLDERS, never baked into a
module default (PathBudget is caller-supplied) — flagged in notes + run log.
[Verification]: uv run core test --suite smoke -q => 176 passed;
python -m evals.adr_0246_geometric_suite => 14/14 all_passed;
ADR-0246 suites + adjacent D4 identity surfaces => 114 passed (see run log).
This commit is contained in:
parent
6efe4ad80c
commit
ed54dddacb
7 changed files with 645 additions and 5 deletions
|
|
@ -160,6 +160,28 @@ def euclidean_norm(s: np.ndarray) -> float:
|
|||
return float(np.linalg.norm(np.asarray(s, dtype=np.float64), ord=2))
|
||||
|
||||
|
||||
class MalformedVersorError(ValueError):
|
||||
"""Raised when a versor handed to an ADR-0246 primitive is not a well-formed
|
||||
``N_COMPONENTS``-vector of finite floats.
|
||||
|
||||
Fail-closed at the primitive boundary (ADR-0246 §6.1 "Malformed F → typed
|
||||
error"): a NaN/inf or wrong-shape field must raise, never propagate a silent
|
||||
NaN into an induced action or a residual-channel split.
|
||||
"""
|
||||
|
||||
|
||||
def _validate_versor(versor: np.ndarray) -> np.ndarray:
|
||||
"""Coerce to a finite float64 ``(N_COMPONENTS,)`` array or fail closed."""
|
||||
array = np.asarray(versor, dtype=np.float64)
|
||||
if array.shape != (N_COMPONENTS,):
|
||||
raise MalformedVersorError(
|
||||
f"versor must have shape ({N_COMPONENTS},), got {array.shape}"
|
||||
)
|
||||
if not np.all(np.isfinite(array)):
|
||||
raise MalformedVersorError("versor has non-finite (NaN/inf) components")
|
||||
return array
|
||||
|
||||
|
||||
def orthogonality_defect_of_action(action: np.ndarray, gram: np.ndarray) -> float:
|
||||
"""``‖AᵀGA − G‖_F`` for a precomputed induced action ``A`` (ADR-0246 §3.2).
|
||||
|
||||
|
|
@ -270,8 +292,11 @@ class IdentityManifoldGeometry:
|
|||
When ``F`` preserves the subspace isometrically, ``A`` is ``G``-orthogonal
|
||||
(``AᵀGA = G``). Built only from existing primitives (Gram, signed inner
|
||||
product, sandwich); no new algebra.
|
||||
|
||||
Raises :class:`MalformedVersorError` on a non-finite or wrong-shape versor
|
||||
(ADR-0246 §6.1 fail-closed on malformed F).
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
versor = _validate_versor(versor)
|
||||
n = len(self.axes_psi)
|
||||
overlaps = np.empty((n, n), dtype=np.float64)
|
||||
for j, axis_j in enumerate(self.axes_psi):
|
||||
|
|
@ -310,8 +335,11 @@ class IdentityManifoldGeometry:
|
|||
|
||||
Retains the positive-definite Euclidean coefficient norm (never the
|
||||
indefinite ``⟨·,·⟩₀``) so a boost/e5 component cannot silently vanish.
|
||||
|
||||
Raises :class:`MalformedVersorError` on a non-finite or wrong-shape versor
|
||||
(ADR-0246 §6.1 fail-closed on malformed F).
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
versor = _validate_versor(versor)
|
||||
e4_energy = e5_energy = spatial_foreign = unclassified = total = 0.0
|
||||
for axis in self.axes_psi:
|
||||
rotated = sandwich(versor, axis)
|
||||
|
|
|
|||
38
docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt
Normal file
38
docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
ADR-0246 slice-1 scaffold — local run log
|
||||
branch: feat/adr-0246-slice1-scaffold
|
||||
base: feat/adr-0246-path-ledger (contains §3 primitives + §3.4/3.5 ledger); all descend from main @ 04d67ca5
|
||||
commit (pre-scaffold-commit HEAD): 6efe4ad80cc9a4914fc97e0e51ec92c1dd43ec72
|
||||
python: Python 3.12.13
|
||||
|
||||
=== §6.1/§6.2 eval harness: python -m evals.adr_0246_geometric_suite ===
|
||||
|
||||
[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'}
|
||||
|
||||
=== pytest: ADR-0246 suites + adjacent identity surfaces ===
|
||||
........................................................................ [ 63%]
|
||||
.......................................... [100%]
|
||||
114 passed in 52.42s
|
||||
|
||||
=== uv run core test --suite smoke -q ===
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
176 passed, 1 warning in 130.75s (0:02:10)
|
||||
|
|
@ -54,9 +54,10 @@ structure is stabilized would instrument lawfulness on a frame the dynamics igno
|
|||
|------|-------|--------|
|
||||
| **Slice 0** — mismatch diagnostic | evidence-only classification of the benign mismatch (foreign leakage vs in-span-unlawful vs numerical vs path vs semantic-coupling-absent) | **merged** `main` (quarantined diagnostic artifact); found: structural e4/e5 foreign leakage, declared frame dynamically unspecial |
|
||||
| **§3 primitives** | pure `A(F)`, `d_orth`, `d_stab` vs locked `H_id={I}`, typed residual channels in `core/physics/identity_manifold.py` + `identity_action.py`; slice-0 eval rewired to consume them (single source of truth) | **committed** `feat/adr-0246-induced-action-primitives` (RED→GREEN, off-serving, flag untouched) — awaiting review |
|
||||
| **§3.4/§3.5 path ledger** (this unit) | 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 | in progress — RED→GREEN, off-serving; stacked on the primitives branch |
|
||||
| §3.7 gate admit surface | wire d_orth/d_stab/typed channels into `IdentityScore` (flag default-off) | after ledger |
|
||||
| §6 eval matrix + §11 feasibility | synthetic + path suites + discrimination report; the invariant/semantic-grounding feasibility study runs here as the **first consumer** of the §3 primitives (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) | after gate surface |
|
||||
| **§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 | wire d_orth/d_stab/typed channels into `IdentityScore` (flag default-off); calibrate ε_turn/ε_session | after audit |
|
||||
| §6.3 + §11 feasibility | discrimination report; the invariant/semantic-grounding feasibility study runs here as the **first consumer** of the §3 primitives (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) | after gate surface |
|
||||
| ADR-0246 body + acceptance packet | §10 criteria; no self-Accept | last |
|
||||
|
||||
Nothing in the reordering relaxes a §7 non-goal: no `C_id` corrector, no `H_id`
|
||||
|
|
|
|||
132
docs/handoff/adr-0246-slice1-scaffold-notes.md
Normal file
132
docs/handoff/adr-0246-slice1-scaffold-notes.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# ADR-0246 Slice-1 Scaffold — Handoff Notes (Fable 5 bounded autonomous draft)
|
||||
|
||||
**Status:** DRAFT SCAFFOLD ONLY — not a PR, not merged, no status flip, no `main`
|
||||
push. For review by Opus 4.8 + Joshua Shay before anything proceeds toward `main`.
|
||||
**Branch:** `feat/adr-0246-slice1-scaffold` (unmerged).
|
||||
**D4 gate (directive stop-condition #3):** PASSED — D4 is closed. Both acceptance
|
||||
packets are on `main` (`docs/audit/adr-024{4,5}-acceptance-packet-2026-07-17.md`);
|
||||
ADR-0244 status line is `Accepted — ratified by Joshua Shay 2026-07-17`; `main @
|
||||
04d67ca5`. Proceeded.
|
||||
**Stop condition reached:** #1 — all four build steps complete and the full
|
||||
§6.1 + §6.2 matrix passes locally (smoke + new tests + eval harness), with results
|
||||
written to `docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt` (not asserted
|
||||
from memory). No §3/§7 non-goal had to be violated; no calibration numbers were
|
||||
invented into the modules (see Placeholders).
|
||||
|
||||
---
|
||||
|
||||
## 1. What was built (directive steps 1–4)
|
||||
|
||||
| Step | Deliverable | Where |
|
||||
|------|-------------|-------|
|
||||
| 1 | `induced_action(F)`, `d_orth`, typed residual energy (§3.1/3.2/3.6), pure f64 | `core/physics/identity_manifold.py` |
|
||||
| 2 | `H_id={I}` policy (`IdentityStabilizer`), `d_stab`, lawful-only path composition + hard-break ledger (§3.3–§3.5) | `core/physics/identity_action.py` |
|
||||
| 3 | Runnable §6.1 synthetic geometric suite + §6.2 path/holonomy suite | `evals/adr_0246_geometric_suite/` |
|
||||
| 4 | Unit pins for every synthetic case (fail loudly on the exact expected) | `tests/test_adr_0246_{induced_action,path_ledger,geometric_suite}.py` |
|
||||
|
||||
### Reuse note (transparent for audit)
|
||||
Steps 1–2 are the **already-verified** work from earlier this session, reused
|
||||
rather than re-derived (RED-first TDD, off-serving):
|
||||
- `feat/adr-0246-induced-action-primitives` — §3 primitives (commit `4941cf18`)
|
||||
- `feat/adr-0246-path-ledger` — §3.4/3.5 ledger (commit `6efe4ad8`, stacked)
|
||||
|
||||
`feat/adr-0246-slice1-scaffold` is stacked on top of those, so its history carries
|
||||
both commits (all descend from `main @ 04d67ca5`). The two earlier branches are
|
||||
therefore **subsumed** by this scaffold and can be pruned if the reviewers prefer
|
||||
the single-branch deliverable. This scaffold additionally adds: the malformed-F
|
||||
guard (`MalformedVersorError`), the §6.1/§6.2 eval harness, the extra §6.1 pins,
|
||||
the run log, and these notes.
|
||||
|
||||
---
|
||||
|
||||
## 2. Hard-constraint compliance (directive "Hard constraints while building")
|
||||
|
||||
- [x] `H_id = {I}` only — `IdentityStabilizer.singleton`; never enlarged; never
|
||||
soft-projects an unlawful `A` onto `I` (refused turns are break markers).
|
||||
Pinned: `test_refused_turn_is_break_and_excluded`.
|
||||
- [x] Path composed from lawful/certified actions only — never raw `A_t`.
|
||||
`advance_identity_path` composes only turns with `d_stab ≤ ε_turn`; refused/
|
||||
ill-conditioned turns get a `path_break` marker (never an identity stand-in).
|
||||
Forensic contrast pinned: `test_raw_product_differs_from_lawful`.
|
||||
- [x] No nonzero geometric `C_id` / conjugate corrector — admit-or-abstain only.
|
||||
Nothing in the modules rewrites `F` or `A`.
|
||||
- [x] `chat/runtime.py`, flag defaults, and D4 gate wiring **untouched**. Verified:
|
||||
`test_gate_flag_and_bound_untouched` (flag default-off, `_WAVE_LEAKAGE_BOUND`
|
||||
unchanged); A-04 quarantine pinned by `test_*_is_pure_offserving` /
|
||||
`test_suite_is_offserving`.
|
||||
- [x] No discrimination report, ADR-0246 body, or acceptance-packet language
|
||||
written. No "semantic inalienability" / marketing claim drafted — see §5 TODO.
|
||||
- [x] `docs/handoff/ADR-0244-D4-IMPLEMENTATION-PLAN.md` **not modified**.
|
||||
|
||||
---
|
||||
|
||||
## 3. Placeholder values used (directive: list every one + why)
|
||||
|
||||
| Placeholder | Value | Where | Why it is a placeholder |
|
||||
|-------------|-------|-------|--------------------------|
|
||||
| `epsilon_turn` | 0.1 | `evals/adr_0246_geometric_suite` `PLACEHOLDER_EPSILON_TURN`; test fixtures | **UNCERTIFIED.** D4 Phase 3 certified only `γ_id = 0.2126624458513829`. The two-level path budget (§3.4) is not yet calibrated. Value chosen only to exercise the mechanism (small single-turn drift admits; a 90° rotation refuses). NOT baked into any serve/module default — `PathBudget` is always caller-supplied. |
|
||||
| `epsilon_session` | 0.3 | same | **UNCERTIFIED.** Same status; chosen so ~7 steps of a 0.05-rad rotation accumulate past it, demonstrating the accumulation guard. Not a policy value. |
|
||||
| `_NONZERO` = 0.05 | 0.05 | eval harness | Not a calibration number — a "clearly nonzero" marker for the ">0" rows of the §6.1 table (d_stab, leakage). |
|
||||
| test construction angles (0.02, 0.05, 1.0, 1.5, π/2, π) | — | tests/evals | Case constructions, not policy. Chosen to realize the exact §6.1/§6.2 geometric signatures. |
|
||||
|
||||
**Explicitly NOT invented:** `γ_id` (already certified, unchanged), `τ_max`,
|
||||
`s_min`. The per-turn admit surface that would consume these (§3.7) is **not built
|
||||
in this scaffold** — that is the gate-wiring unit, deliberately out of scope, so no
|
||||
placeholder was needed for them.
|
||||
|
||||
---
|
||||
|
||||
## 4. Uncertainties in §5–§7 (directive: list anything you were unsure how to satisfy)
|
||||
|
||||
1. **§3.2 general-pack `‖·‖_G` convention.** The brief fixes `d_stab = ‖A−H‖_G`
|
||||
but leaves the general-pack weighted-norm convention to "ADR-0246 proper." I
|
||||
implemented `‖M‖_G = ‖G^{1/2} M G^{-1/2}‖_F` (metric-consistent; reduces exactly
|
||||
to Frobenius at `G=I`, which is the only shipped pack). **Needs review/ratification.**
|
||||
2. **§3.6 `spatial_foreign` channel.** For the default pack (support = e1/e2/e3) it
|
||||
is structurally ~0 (projection removes in-span components). Implemented generally
|
||||
(residual energy on grade-1 spatial slots outside the axis support) but it cannot
|
||||
fire until a non-default pack exists. Untested against a real non-default pack.
|
||||
3. **§4.1 `IdentityActionRecord` full telemetry** (per-turn `field_digest`,
|
||||
`record_digest`, gate/policy versions, admitted/refusal_reason) is **not built**;
|
||||
only the §4.2 `IdentityPathLedger` (with `ledger_digest`, `chain_id`) is. The
|
||||
per-turn record belongs to the gate-surface/telemetry unit (§3.7), out of scope
|
||||
here. `advance_identity_path` returns a lightweight per-turn dict, not the full record.
|
||||
4. **§6.1 "Malformed F → never silent legacy when wave field was supplied."** The
|
||||
"silent legacy" clause is about the D4 gate's dual-mode fallback in
|
||||
`identity.py` (untouched here, owned by D4). This scaffold adds a fail-closed
|
||||
`MalformedVersorError` at the *pure primitive* boundary (`induced_action` /
|
||||
`typed_residual_energy`). Whether the gate should route malformed-F to this same
|
||||
typed error is a gate-wiring decision, deferred.
|
||||
5. **Path budget semantics under a hard break.** I treated a scope change as: start
|
||||
a fresh chain, and the triggering turn is turn 1 of the new chain (its own action
|
||||
composes into the fresh `I`). The brief (§3.5) specifies a new `chain_id` and
|
||||
that the old path is not continued, but does not pin whether the boundary turn
|
||||
belongs to the old or new chain. Chose new-chain. **Confirm.**
|
||||
|
||||
---
|
||||
|
||||
## 5. Explicitly deferred to Opus/human review (NOT written here)
|
||||
|
||||
> **TODO: Opus/human review** — the following were intentionally left undone per
|
||||
> the directive; do not treat their absence as an oversight:
|
||||
- The **discrimination report** (§6.3): benign/adversarial rates, false-refusal,
|
||||
ablations, CI-bounded separation. Requires the gate-surface + live cohorts.
|
||||
- The **ADR-0246 body** and any **acceptance-packet** language.
|
||||
- Any **claim about what the axes mean** ("semantic inalienability", grounding).
|
||||
The §11 grounding-feasibility study (fixed cohort splits, synthetic recovery
|
||||
controls, generator analysis, precision pairs, adversarial discrimination) is the
|
||||
first consumer of these primitives and is **not** part of this scaffold.
|
||||
- The **gate admit-surface wiring** into `IdentityScore` (§3.7) and the calibration
|
||||
of `ε_turn`/`ε_session`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Verification (see run log for actual output)
|
||||
|
||||
`docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt`:
|
||||
- `python -m evals.adr_0246_geometric_suite` → 14/14 cases passed, `all_passed=True`.
|
||||
- pytest ADR-0246 suites + adjacent D4 identity surfaces → 114 passed.
|
||||
- `uv run core test --suite smoke -q` → (appended to the run log).
|
||||
|
||||
No PR. No merge. No status flip. Report back to Shay/Opus for the audit that
|
||||
decides what, if anything, proceeds toward `main`.
|
||||
301
evals/adr_0246_geometric_suite/__init__.py
Normal file
301
evals/adr_0246_geometric_suite/__init__.py
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
"""ADR-0246 §6.1/§6.2 synthetic geometric + path/holonomy eval suite (scaffold).
|
||||
|
||||
A runnable, deterministic harness that constructs every case in the preflight
|
||||
§6.1 synthetic geometric table and §6.2 path/holonomy table, runs the pure
|
||||
ADR-0246 primitives (induced action ``A(F)``, ``d_orth``, ``d_stab`` vs the locked
|
||||
singleton ``H_id={I}``, typed residual channels, lawful-only path ledger), and
|
||||
checks each against its expected geometric signature. Every case reports
|
||||
``{name, checks, passed}`` and the suite reports an overall ``passed``.
|
||||
|
||||
Scope (bounded scaffold draft — NOT an accepted ADR):
|
||||
* Off-serving: imports only ``algebra`` + ``core.physics.identity_{manifold,action}``;
|
||||
never ``chat.runtime`` (A-04 quarantine).
|
||||
* ``H_id={I}`` only; refused turns are break markers, never soft-projected ``I``;
|
||||
the path composes lawful actions only — never the raw product.
|
||||
* No ``C_id`` corrector; admit-or-abstain only.
|
||||
* The path-suite ε values are UNCERTIFIED PLACEHOLDERS (see
|
||||
``PLACEHOLDER_EPSILON_*``): D4 Phase 3 certified only ``γ_id``; ε_turn/ε_session
|
||||
are not yet calibrated. They exist here solely to exercise the mechanism and
|
||||
are flagged in the run log. Do NOT read them as policy.
|
||||
|
||||
No discrimination report, no claims about what the axes *mean* — that is
|
||||
explicitly deferred to Opus/human review (see the slice-1 scaffold notes).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from core.physics.identity_manifold import (
|
||||
IdentityManifoldGeometry,
|
||||
ManifoldConditioningError,
|
||||
MalformedVersorError,
|
||||
)
|
||||
from core.physics.identity_action import (
|
||||
IdentityChainScope,
|
||||
PathBudget,
|
||||
advance_identity_path,
|
||||
raw_path_product,
|
||||
stabilizer_defect_for_versor,
|
||||
)
|
||||
|
||||
# grade-2 bivector plane indices (grade-2 block starts at index 6)
|
||||
_E12, _E13, _E14, _E15, _E23, _E24, _E25 = 6, 7, 8, 9, 10, 11, 12
|
||||
|
||||
# --- UNCERTIFIED PLACEHOLDERS (flagged; not policy) ---------------------------
|
||||
# D4 Phase 3 certified γ_id only. ε_turn / ε_session are NOT calibrated; these
|
||||
# illustrative values merely exercise the two-level path budget mechanism.
|
||||
PLACEHOLDER_EPSILON_TURN: float = 0.1
|
||||
PLACEHOLDER_EPSILON_SESSION: float = 0.3
|
||||
# "clearly nonzero" marker for the >0 rows in the §6.1 table (not a threshold).
|
||||
_NONZERO = 0.05
|
||||
_ZERO = 1e-9
|
||||
|
||||
|
||||
def default_geometry() -> IdentityManifoldGeometry:
|
||||
"""The shipped default declared frame span(e1,e2,e3), Gram = I3."""
|
||||
return IdentityManifoldGeometry.from_directions(
|
||||
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
|
||||
)
|
||||
|
||||
|
||||
def rotor(biv: int, theta: float) -> np.ndarray:
|
||||
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: int, theta: float) -> np.ndarray:
|
||||
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() -> np.ndarray:
|
||||
v = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||
v[0] = 1.0
|
||||
return v
|
||||
|
||||
|
||||
def _case(name: str, checks: dict[str, bool]) -> dict[str, Any]:
|
||||
return {"name": name, "checks": checks, "passed": all(checks.values())}
|
||||
|
||||
|
||||
def _expect_raises(name: str, fn: Callable[[], Any], exc: type[BaseException]) -> dict[str, Any]:
|
||||
raised = False
|
||||
try:
|
||||
fn()
|
||||
except exc:
|
||||
raised = True
|
||||
except Exception: # wrong exception type is a failure
|
||||
raised = False
|
||||
return _case(name, {f"raises_{exc.__name__}": raised})
|
||||
|
||||
|
||||
# --- §6.1 synthetic geometric suite -------------------------------------------
|
||||
|
||||
|
||||
def run_geometric_suite(geometry: IdentityManifoldGeometry | None = None) -> list[dict[str, Any]]:
|
||||
geometry = geometry or default_geometry()
|
||||
cases: list[dict[str, Any]] = []
|
||||
|
||||
# Identity versor → A≈I, ℓ≈0, s≈+1, d_orth≈0, d_stab≈0
|
||||
v = identity_versor()
|
||||
a = geometry.induced_action(v)
|
||||
leak, self_align = geometry.axis_response(v)
|
||||
cases.append(_case("identity_versor", {
|
||||
"A_is_identity": bool(np.allclose(a, np.eye(3), atol=1e-12)),
|
||||
"leakage_zero": max(leak) < _ZERO,
|
||||
"self_align_plus_one": min(self_align) > 1.0 - _ZERO,
|
||||
"d_orth_zero": geometry.orthogonality_defect(v) < _ZERO,
|
||||
"d_stab_zero": stabilizer_defect_for_versor(geometry, v) < _ZERO,
|
||||
}))
|
||||
|
||||
# In-plane π inversion of e1/e2 → ℓ≈0, s(e1)≈-1, s(e3)≈+1, d_stab>0
|
||||
v = rotor(_E12, np.pi)
|
||||
leak, self_align = geometry.axis_response(v)
|
||||
cases.append(_case("inplane_pi_inversion_e12", {
|
||||
"leakage_zero": max(leak) < _ZERO,
|
||||
"self_align_e1_minus_one": self_align[0] < -1.0 + _ZERO,
|
||||
"self_align_e2_minus_one": self_align[1] < -1.0 + _ZERO,
|
||||
"self_align_e3_plus_one": self_align[2] > 1.0 - _ZERO,
|
||||
"d_stab_positive": stabilizer_defect_for_versor(geometry, v) > _NONZERO,
|
||||
}))
|
||||
|
||||
# In-plane 90° permutation e1→e2 → ℓ≈0, s(e1)≈0, d_stab>0
|
||||
v = rotor(_E12, np.pi / 2.0)
|
||||
leak, self_align = geometry.axis_response(v)
|
||||
cases.append(_case("inplane_90deg_permutation_e12", {
|
||||
"leakage_zero": max(leak) < _ZERO,
|
||||
"self_align_e1_zero": abs(self_align[0]) < _ZERO,
|
||||
"self_align_e2_zero": abs(self_align[1]) < _ZERO,
|
||||
"d_stab_positive": stabilizer_defect_for_versor(geometry, v) > _NONZERO,
|
||||
}))
|
||||
|
||||
# Mild in-plane drift step → small d_stab (per-turn passes ε_turn placeholder)
|
||||
v = rotor(_E12, 0.02)
|
||||
d_stab = stabilizer_defect_for_versor(geometry, v)
|
||||
leak, _ = geometry.axis_response(v)
|
||||
cases.append(_case("mild_inplane_drift_e12_0.02", {
|
||||
"leakage_zero": max(leak) < _ZERO,
|
||||
"d_stab_small_but_positive": _ZERO < d_stab < PLACEHOLDER_EPSILON_TURN,
|
||||
}))
|
||||
|
||||
# Alien tilt e14 → ℓ>0, null_or_conformal channel fires, boost channel ≈0
|
||||
v = rotor(_E14, 1.5)
|
||||
ch = geometry.typed_residual_energy(v)
|
||||
cases.append(_case("alien_tilt_e14_1.5", {
|
||||
"leakage_positive": geometry.leakage_rms(v) > _NONZERO,
|
||||
"null_or_conformal_fires": ch["null_or_conformal"] > _NONZERO,
|
||||
"boost_like_zero": ch["boost_like"] < _ZERO,
|
||||
"unclassified_clean": ch["unclassified"] < _ZERO,
|
||||
}))
|
||||
|
||||
# Boost component (e5) → ℓ,s normalized in range, boost channel fires, d_orth>0
|
||||
v = boost(_E15, 1.0)
|
||||
ch = geometry.typed_residual_energy(v)
|
||||
leak, self_align = geometry.axis_response(v)
|
||||
cases.append(_case("boost_e15_1.0", {
|
||||
"leakage_in_unit_range": 0.0 <= max(leak) <= 1.0,
|
||||
"self_align_in_range": all(-1.0 - _ZERO <= s <= 1.0 + _ZERO for s in self_align),
|
||||
"boost_like_fires": ch["boost_like"] > _NONZERO,
|
||||
"null_or_conformal_zero": ch["null_or_conformal"] < _ZERO,
|
||||
"d_orth_positive": geometry.orthogonality_defect(v) > _NONZERO,
|
||||
}))
|
||||
|
||||
# Near-singular Gram (near-parallel axes) → ManifoldConditioningError at build
|
||||
cases.append(_expect_raises(
|
||||
"near_singular_gram",
|
||||
lambda: IdentityManifoldGeometry.from_directions(
|
||||
((1.0, 0.0, 0.0), (1.0, 1e-9, 0.0), (0.0, 0.0, 1.0))
|
||||
),
|
||||
ManifoldConditioningError,
|
||||
))
|
||||
|
||||
# Malformed F → MalformedVersorError (NaN and wrong-shape)
|
||||
nan_v = identity_versor()
|
||||
nan_v[0] = np.nan
|
||||
cases.append(_expect_raises(
|
||||
"malformed_f_nan", lambda: geometry.induced_action(nan_v), MalformedVersorError
|
||||
))
|
||||
cases.append(_expect_raises(
|
||||
"malformed_f_wrong_shape",
|
||||
lambda: geometry.induced_action(np.ones(7, dtype=np.float64)),
|
||||
MalformedVersorError,
|
||||
))
|
||||
return cases
|
||||
|
||||
|
||||
# --- §6.2 path / holonomy suite -----------------------------------------------
|
||||
|
||||
|
||||
def _scope(pack: str = "packA") -> IdentityChainScope:
|
||||
return IdentityChainScope(
|
||||
pack_content_digest=pack,
|
||||
geometry_version="geomV1",
|
||||
policy_version="polV1(PLACEHOLDER_epsilons)",
|
||||
session_id="sess1",
|
||||
biography_epoch=None,
|
||||
)
|
||||
|
||||
|
||||
def run_path_suite(geometry: IdentityManifoldGeometry | None = None) -> list[dict[str, Any]]:
|
||||
geometry = geometry or default_geometry()
|
||||
budget = PathBudget(
|
||||
epsilon_turn=PLACEHOLDER_EPSILON_TURN,
|
||||
epsilon_session=PLACEHOLDER_EPSILON_SESSION,
|
||||
)
|
||||
gram = geometry.gram
|
||||
ident = geometry.induced_action(identity_versor())
|
||||
cases: list[dict[str, Any]] = []
|
||||
|
||||
# Sequence of lawful near-I turns → A_path near I; no false path refusal
|
||||
ledger = None
|
||||
for _ in range(20):
|
||||
ledger, _ = advance_identity_path(ledger, _scope(), ident, gram, budget)
|
||||
cases.append(_case("lawful_near_identity_sequence", {
|
||||
"path_near_identity": bool(np.allclose(ledger.a_path_lawful, np.eye(3), atol=1e-12)),
|
||||
"session_admit": ledger.session_admit,
|
||||
"no_breaks": ledger.break_count == 0,
|
||||
"composed_20": ledger.composed_turn_count == 20,
|
||||
}))
|
||||
|
||||
# Small in-plane rotations each < ε_turn → path eventually breaches ε_session
|
||||
small = geometry.induced_action(rotor(_E12, 0.05))
|
||||
ledger = None
|
||||
all_turns_lawful = True
|
||||
for _ in range(40):
|
||||
ledger, rec = advance_identity_path(ledger, _scope(), small, gram, budget)
|
||||
all_turns_lawful = all_turns_lawful and rec["lawful"]
|
||||
if not ledger.session_admit:
|
||||
break
|
||||
cases.append(_case("small_rotations_accumulate_to_session_refusal", {
|
||||
"each_turn_lawful": all_turns_lawful,
|
||||
"session_refused": not ledger.session_admit,
|
||||
"path_d_stab_exceeds_session": ledger.d_stab_path > PLACEHOLDER_EPSILON_SESSION,
|
||||
}))
|
||||
|
||||
# Interleaved refuse + admit → refused turns break, excluded, raw recorded
|
||||
big = geometry.induced_action(rotor(_E12, np.pi / 2.0))
|
||||
seq = [ident, big, ident, big, ident]
|
||||
ledger = None
|
||||
breaks_pattern = []
|
||||
for a in seq:
|
||||
ledger, rec = advance_identity_path(ledger, _scope(), a, gram, budget)
|
||||
breaks_pattern.append(rec["path_break"])
|
||||
cases.append(_case("interleaved_refuse_admit", {
|
||||
"composed_3": ledger.composed_turn_count == 3,
|
||||
"breaks_2": ledger.break_count == 2,
|
||||
"break_pattern": breaks_pattern == [False, True, False, True, False],
|
||||
}))
|
||||
|
||||
# Pack digest change → hard break, new chain_id, old path not continued
|
||||
ledger, _ = advance_identity_path(None, _scope("packA"), ident, gram, budget)
|
||||
ledger, _ = advance_identity_path(ledger, _scope("packA"), small, gram, budget)
|
||||
id_a, drifted = ledger.chain_id, ledger.a_path_lawful.copy()
|
||||
ledger, rec = advance_identity_path(ledger, _scope("packB"), ident, gram, budget)
|
||||
cases.append(_case("hard_break_on_pack_change", {
|
||||
"hard_break": rec["hard_break"],
|
||||
"new_chain_id": ledger.chain_id != id_a,
|
||||
"old_path_not_continued": not np.allclose(ledger.a_path_lawful, drifted),
|
||||
"fresh_chain": ledger.composed_turn_count == 1 and ledger.break_count == 0,
|
||||
}))
|
||||
|
||||
# Raw product ≠ lawful product when a refused turn is present (forensic)
|
||||
seq = [ident, big, ident]
|
||||
ledger = None
|
||||
for a in seq:
|
||||
ledger, _ = advance_identity_path(ledger, _scope(), a, gram, budget)
|
||||
raw = raw_path_product(seq)
|
||||
cases.append(_case("raw_product_differs_from_lawful", {
|
||||
"raw_neq_lawful": not np.allclose(raw, ledger.a_path_lawful),
|
||||
"lawful_excludes_refused": bool(np.allclose(ledger.a_path_lawful, np.eye(3), atol=1e-12)),
|
||||
}))
|
||||
return cases
|
||||
|
||||
|
||||
def build_suite_report() -> dict[str, Any]:
|
||||
geometry = default_geometry()
|
||||
geometric = run_geometric_suite(geometry)
|
||||
path = run_path_suite(geometry)
|
||||
all_cases = geometric + path
|
||||
return {
|
||||
"schema_version": "adr_0246_geometric_suite_v1",
|
||||
"declared_frame": ["truthfulness=e1", "coherence=e2", "reverence=e3"],
|
||||
"stabilizer": "H_id={I} (locked)",
|
||||
"placeholders": {
|
||||
"epsilon_turn": PLACEHOLDER_EPSILON_TURN,
|
||||
"epsilon_session": PLACEHOLDER_EPSILON_SESSION,
|
||||
"note": "UNCERTIFIED — D4 Phase 3 certified only gamma_id; ε not calibrated",
|
||||
},
|
||||
"geometric_suite": geometric,
|
||||
"path_suite": path,
|
||||
"case_count": len(all_cases),
|
||||
"passed_count": sum(1 for c in all_cases if c["passed"]),
|
||||
"all_passed": all(c["passed"] for c in all_cases),
|
||||
}
|
||||
42
evals/adr_0246_geometric_suite/__main__.py
Normal file
42
evals/adr_0246_geometric_suite/__main__.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
"""Run the ADR-0246 §6.1/§6.2 geometric + path eval suite and emit the report.
|
||||
|
||||
Usage: uv run python -m evals.adr_0246_geometric_suite [out.json]
|
||||
|
||||
Deterministic, off-serving. Prints a per-case pass/fail summary and the overall
|
||||
verdict; optionally writes the structured JSON report. Exit code 0 iff every case
|
||||
passed (so it can gate a run log), 1 otherwise.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
from evals.adr_0246_geometric_suite import build_suite_report
|
||||
|
||||
|
||||
def main() -> int:
|
||||
report = build_suite_report()
|
||||
for suite in ("geometric_suite", "path_suite"):
|
||||
print(f"\n[{suite}]")
|
||||
for case in report[suite]:
|
||||
mark = "PASS" if case["passed"] else "FAIL"
|
||||
print(f" {mark} {case['name']}")
|
||||
if not case["passed"]:
|
||||
for check, ok in case["checks"].items():
|
||||
if not ok:
|
||||
print(f" ✗ {check}")
|
||||
print(
|
||||
f"\n{report['passed_count']}/{report['case_count']} cases passed; "
|
||||
f"all_passed={report['all_passed']}"
|
||||
)
|
||||
print(f"placeholders (uncertified): {report['placeholders']}")
|
||||
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"report written to {sys.argv[1]}")
|
||||
return 0 if report["all_passed"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
98
tests/test_adr_0246_geometric_suite.py
Normal file
98
tests/test_adr_0246_geometric_suite.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
"""ADR-0246 §6.1/§6.2 eval-suite pins — every synthetic case fails loudly.
|
||||
|
||||
Pins the runnable ``evals.adr_0246_geometric_suite`` harness AND the specific
|
||||
§6.1 rows the preflight table calls out (π-inversion ``s≈-1``, 90° permutation
|
||||
``s≈0``, near-singular Gram → ``ManifoldConditioningError``, malformed F →
|
||||
``MalformedVersorError``), so a regression names the exact broken case.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from core.physics.identity_manifold import (
|
||||
IdentityManifoldGeometry,
|
||||
ManifoldConditioningError,
|
||||
MalformedVersorError,
|
||||
)
|
||||
from evals.adr_0246_geometric_suite import (
|
||||
build_suite_report,
|
||||
default_geometry,
|
||||
identity_versor,
|
||||
rotor,
|
||||
run_geometric_suite,
|
||||
run_path_suite,
|
||||
)
|
||||
|
||||
_E12 = 6
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def geometry() -> IdentityManifoldGeometry:
|
||||
return default_geometry()
|
||||
|
||||
|
||||
def test_full_suite_all_cases_pass():
|
||||
report = build_suite_report()
|
||||
# every case must pass; the assertion message names any that did not
|
||||
failed = [c["name"] for c in report["geometric_suite"] + report["path_suite"] if not c["passed"]]
|
||||
assert failed == [], f"failing cases: {failed}"
|
||||
assert report["all_passed"] is True
|
||||
assert report["case_count"] == report["passed_count"] == 14
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", [c["name"] for c in run_geometric_suite()])
|
||||
def test_each_geometric_case_passes(geometry, case):
|
||||
result = {c["name"]: c for c in run_geometric_suite(geometry)}[case]
|
||||
assert result["passed"], result["checks"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", [c["name"] for c in run_path_suite()])
|
||||
def test_each_path_case_passes(geometry, case):
|
||||
result = {c["name"]: c for c in run_path_suite(geometry)}[case]
|
||||
assert result["passed"], result["checks"]
|
||||
|
||||
|
||||
# --- explicit §6.1 pins (directive step 4: fail loudly on the exact expected) --
|
||||
|
||||
|
||||
def test_pi_inversion_self_align_is_minus_one(geometry):
|
||||
_, self_align = geometry.axis_response(rotor(_E12, np.pi))
|
||||
assert self_align[0] == pytest.approx(-1.0, abs=1e-9) # e1 inverted
|
||||
assert self_align[1] == pytest.approx(-1.0, abs=1e-9) # e2 inverted
|
||||
assert self_align[2] == pytest.approx(1.0, abs=1e-9) # e3 fixed
|
||||
|
||||
|
||||
def test_90deg_permutation_self_align_is_zero(geometry):
|
||||
_, self_align = geometry.axis_response(rotor(_E12, np.pi / 2.0))
|
||||
assert self_align[0] == pytest.approx(0.0, abs=1e-9) # e1 → e2, orthogonal
|
||||
assert self_align[1] == pytest.approx(0.0, abs=1e-9)
|
||||
|
||||
|
||||
def test_near_singular_gram_fails_closed():
|
||||
with pytest.raises(ManifoldConditioningError):
|
||||
IdentityManifoldGeometry.from_directions(
|
||||
((1.0, 0.0, 0.0), (1.0, 1e-9, 0.0), (0.0, 0.0, 1.0))
|
||||
)
|
||||
|
||||
|
||||
def test_malformed_versor_raises_typed_error(geometry):
|
||||
nan_v = identity_versor()
|
||||
nan_v[5] = np.inf
|
||||
with pytest.raises(MalformedVersorError):
|
||||
geometry.induced_action(nan_v)
|
||||
with pytest.raises(MalformedVersorError):
|
||||
geometry.typed_residual_energy(np.ones(N_COMPONENTS + 3, dtype=np.float64))
|
||||
|
||||
|
||||
def test_suite_is_offserving():
|
||||
import evals.adr_0246_geometric_suite as suite
|
||||
|
||||
assert suite.__file__ is not None
|
||||
with open(suite.__file__, encoding="utf-8") as fh:
|
||||
src = fh.read()
|
||||
# no actual import of serve modules (the A-04 note in the docstring names
|
||||
# chat.runtime as forbidden, so match import statements, not the substring)
|
||||
assert "import chat" not in src and "from chat" not in src
|
||||
Loading…
Reference in a new issue