Merge pull request 'feat(goldtether): #18 bootstrap gates + principal-axis prune' (#34) from feat/third-door-goldtether-bootstrap-prune into main
GoldTether #18: promotion_eligible, promote residual/closure gates, principal-axis prune; ledger green.
This commit is contained in:
commit
9f5c8c222f
4 changed files with 377 additions and 22 deletions
|
|
@ -29,6 +29,7 @@ from core.physics.goldtether import (
|
|||
AutonomyBand,
|
||||
AutonomyDecision,
|
||||
CoherenceResidual,
|
||||
GoldPromotionProof,
|
||||
GoldTetherMonitor,
|
||||
OperatingMode,
|
||||
coherence_residual,
|
||||
|
|
@ -83,7 +84,7 @@ __all__ = [
|
|||
"IdentityManifold", "IdentityCheck", "IdentityScore", "CharacterProfile",
|
||||
"PromotionDecision", "VaultPromotionPolicy",
|
||||
"AutonomyBand", "AutonomyDecision", "CoherenceResidual",
|
||||
"GoldTetherMonitor", "OperatingMode", "coherence_residual",
|
||||
"GoldPromotionProof", "GoldTetherMonitor", "OperatingMode", "coherence_residual",
|
||||
"AxisClassification", "CartanIwasawaFactors", "ConformalProcrustesResult",
|
||||
"PrincipalAxis", "SignatureAwarePCAResult",
|
||||
"cartan_iwasawa_extract", "cartan_iwasawa_factorize",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from __future__ import annotations
|
|||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any, Optional, Tuple
|
||||
from typing import Any, Literal, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -39,6 +39,23 @@ _TELEMETRY_SCHEMA = "goldtether_coherence_v2" # v2: dropped vacuous grade-5 cha
|
|||
_E4_IDX = 4
|
||||
_E5_IDX = 5
|
||||
|
||||
PruneMode = Literal["fifo", "principal_axes"]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class GoldPromotionProof:
|
||||
"""Caller-supplied proof for replay-verified promotion into 𝓘_gold (ADR-0092).
|
||||
|
||||
Physics never signs reviews. The review surface constructs this payload and
|
||||
passes ``authorized=True`` only after external verification. ``replay_hash``
|
||||
is opaque to GoldTether (determinism pin for the caller).
|
||||
"""
|
||||
|
||||
residual: float
|
||||
replay_hash: str
|
||||
reviewer_id: str
|
||||
closed: bool
|
||||
|
||||
|
||||
def _primal_gold_invariants() -> list:
|
||||
"""R&D-Revised §5 bootstrapping seeds: the identity versor and the two
|
||||
|
|
@ -273,26 +290,128 @@ class GoldTetherMonitor:
|
|||
alpha = self.alpha_constraint(F, mode=mode)
|
||||
return self.supervised_blend(v_self, v_constraint, alpha)
|
||||
|
||||
def promote_gold_invariant(self, F: np.ndarray, *, authorized: bool = False) -> None:
|
||||
"""Add a state versor to 𝓘_gold. CALLER-GATED: the ADR-0092 signed /
|
||||
replay-verified promotion happens in the caller; this refuses to
|
||||
self-authorize (one-mutation-path discipline). The full replay-verified
|
||||
promotion pipeline + principal-axis decay are deferred (issue #18 follow-up).
|
||||
def promotion_eligible(self, F: np.ndarray) -> bool:
|
||||
"""True iff F is closed and unit-residual (drift) is at/below ε_drift.
|
||||
|
||||
Bootstrap gate (R&D §5): only coherent states are candidates for 𝓘_gold.
|
||||
Uses the dual-checked *closure* residual (not geo distance to gold), so
|
||||
novel closed states remain eligible for promotion. Does not authorize.
|
||||
"""
|
||||
F_arr = _as_mv(F)
|
||||
if float(versor_condition(F_arr)) >= _CLOSURE_TOL:
|
||||
return False
|
||||
return float(coherence_residual(F_arr)) <= float(self.epsilon_drift)
|
||||
|
||||
def promote_gold_invariant(
|
||||
self,
|
||||
F: np.ndarray,
|
||||
*,
|
||||
authorized: bool = False,
|
||||
proof: Optional[GoldPromotionProof] = None,
|
||||
require_proof: bool = False,
|
||||
) -> None:
|
||||
"""Add a state versor to 𝓘_gold. CALLER-GATED (ADR-0092).
|
||||
|
||||
- Without ``authorized=True``: refuse (proposal-only; proof alone is insufficient).
|
||||
- With authorize: refuse non-closed or high *drift* residual (live check —
|
||||
never trusts proof.closed / proof.residual as truth).
|
||||
- ``require_proof=True``: refuse if ``proof`` is missing.
|
||||
- Physics never self-signs reviews; ``proof`` is caller-supplied audit pin.
|
||||
"""
|
||||
if not authorized:
|
||||
raise ValueError(
|
||||
"promote_gold_invariant requires explicit authorization (ADR-0092 gate)"
|
||||
)
|
||||
self.gold_invariants.append(_as_mv(F).copy())
|
||||
if require_proof and proof is None:
|
||||
raise ValueError("promote_gold_invariant requires proof when require_proof=True")
|
||||
|
||||
def prune_gold_invariants(self, max_size: int = 64) -> None:
|
||||
"""Bound 𝓘_gold (decay hook), always retaining the three primal seeds.
|
||||
Full principal-axis pruning (R&D-Revised §5) is deferred."""
|
||||
F_arr = _as_mv(F)
|
||||
cond = float(versor_condition(F_arr))
|
||||
# Closure residual only (geo distance to 𝓘_gold is expected for new axes).
|
||||
drift = float(coherence_residual(F_arr))
|
||||
if cond >= _CLOSURE_TOL or drift > float(self.epsilon_drift):
|
||||
raise ValueError(
|
||||
"promote_gold_invariant refused: not a closed versor "
|
||||
f"(versor_condition={cond:.3e}) or residual/drift {drift:.3e} "
|
||||
f"exceeds epsilon_drift={float(self.epsilon_drift)}"
|
||||
)
|
||||
self.gold_invariants.append(F_arr.copy())
|
||||
|
||||
def prune_gold_invariants(
|
||||
self,
|
||||
max_size: int = 64,
|
||||
*,
|
||||
mode: PruneMode | str = "fifo",
|
||||
) -> None:
|
||||
"""Bound 𝓘_gold, always retaining the three primal seeds.
|
||||
|
||||
Modes:
|
||||
* ``fifo`` — keep primals + most recent (#24).
|
||||
* ``principal_axes`` — keep primals + highest principal-energy non-primals
|
||||
(R&D §5 decay; coefficient PCA on the non-primal stack).
|
||||
``max_size < 3`` is clamped to 3 so primals are never stripped.
|
||||
"""
|
||||
mode_s = str(mode)
|
||||
if mode_s not in ("fifo", "principal_axes"):
|
||||
raise ValueError(f"prune_gold_invariants unknown mode: {mode_s!r}")
|
||||
max_size = max(3, int(max_size))
|
||||
if len(self.gold_invariants) > max_size:
|
||||
if len(self.gold_invariants) <= max_size:
|
||||
return
|
||||
if mode_s == "fifo":
|
||||
primal = self.gold_invariants[:3]
|
||||
recent = self.gold_invariants[3:][-(max_size - 3):]
|
||||
recent = self.gold_invariants[3:][-(max_size - 3) :]
|
||||
self.gold_invariants = primal + recent
|
||||
return
|
||||
self._prune_principal_axes(max_size)
|
||||
|
||||
def _prune_principal_axes(self, max_size: int) -> None:
|
||||
"""R&D §5: retain primals + non-primals with highest principal-subspace energy.
|
||||
|
||||
Stack non-primal 32-vectors as columns, take top eigen-directions of
|
||||
``XXᵀ/m``, score each member by squared projection onto that subspace,
|
||||
keep the top ``max_size - 3`` (stable by original index on ties).
|
||||
Differs from FIFO (last-N) whenever early high-energy axes outrank recent
|
||||
near-identity members.
|
||||
"""
|
||||
primal = list(self.gold_invariants[:3])
|
||||
rest = [
|
||||
np.asarray(v, dtype=np.float64).copy() for v in self.gold_invariants[3:]
|
||||
]
|
||||
n_keep = int(max_size) - 3
|
||||
if n_keep <= 0 or not rest:
|
||||
self.gold_invariants = primal
|
||||
return
|
||||
if len(rest) <= n_keep:
|
||||
self.gold_invariants = primal + rest
|
||||
return
|
||||
|
||||
X = np.column_stack(rest) # (32, m)
|
||||
m = X.shape[1]
|
||||
# Gram on ambient 32-space (deterministic; no external deps).
|
||||
C = (X @ X.T) / float(max(m, 1))
|
||||
evals, evecs = np.linalg.eigh(C)
|
||||
# Leading subspace dimension: enough to distinguish members, ≤ n_keep.
|
||||
k = max(1, min(n_keep, m, N_COMPONENTS))
|
||||
order = np.argsort(evals)[::-1]
|
||||
basis = evecs[:, order[:k]] # (32, k)
|
||||
scored: list[tuple[float, int]] = []
|
||||
for i, v in enumerate(rest):
|
||||
coeff = basis.T @ v
|
||||
energy = float(coeff @ coeff)
|
||||
scored.append((energy, i))
|
||||
# Highest energy first; lower index wins ties (stable, anti-FIFO bias).
|
||||
scored.sort(key=lambda t: (-t[0], t[1]))
|
||||
keep_idx = sorted(i for _e, i in scored[:n_keep])
|
||||
kept = [rest[i] for i in keep_idx]
|
||||
# Non-primal retained members should stay closed when they entered as versors.
|
||||
for i, inv in enumerate(kept):
|
||||
cond = float(versor_condition(inv))
|
||||
if cond >= _CLOSURE_TOL:
|
||||
raise ValueError(
|
||||
f"prune principal_axes retained non-closed member[{i}]: "
|
||||
f"versor_condition={cond:.3e}"
|
||||
)
|
||||
self.gold_invariants = primal + kept
|
||||
|
||||
def measure(self, F: np.ndarray, reference: Optional[np.ndarray] = None) -> CoherenceResidual:
|
||||
"""Structured residual (primary + optional geometric distance to reference)."""
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
| 1 | Signature-aware PCA | Super §2.1 / R&D §2.1 | 🟢 faithful (one untested add-on) | — |
|
||||
| 2 | Cartan–Iwasawa decomposition | Super §2.2 | 🟢 faithful (null-point peel + Spin remainder) | #16 |
|
||||
| 3 | Conformal Procrustes | Super §3.1 | 🟢 faithful (Kabsch + field conjugacy) | #17 |
|
||||
| 4 | GoldTether residual + α law | Super §2.3, R&D §2.3/§5 | 🟡 partial (#24 residual+α landed; bootstrap/prune deferred) | #18 |
|
||||
| 4 | GoldTether residual + α law | Super §2.3, R&D §2.3/§5 | 🟢 residual+α + bootstrap gates + principal-axis prune (#24+#18) | #18 |
|
||||
| 5 | Grade-5 pseudoscalar invariant | Super §3.3 | ⚪ RETIRED — vacuous in odd-dim Cl(4,1) | #19 (closed) |
|
||||
| 6 | Surprise residual operator | Super §3.2 | 🟢 math + DiscoveryCandidate wiring landed (#26 + #31) | #20 |
|
||||
| 7 | Trajectory invariants + zero-fabrication | R&D §2.2 | ⚫ absent | #21 |
|
||||
|
|
@ -106,13 +106,15 @@ Two fields `F_A`, `F_B` are structurally analogous iff a single versor `V` maps
|
|||
- `supervised_transition` / `supervised_blend` on the Spin geodesic (`word_transition_rotor` + `rotor_power`).
|
||||
- `promote_gold_invariant(..., authorized=True)` is **caller-gated** (no self-authorize); `prune_gold_invariants` is a size bound retaining the three primals — **not** full principal-axis decay.
|
||||
|
||||
### Remaining gap (#18 follow-up — deferred while wave GoldTether lands)
|
||||
- Full ADR-0092 / replay-verified promotion pipeline into `𝓘_gold`.
|
||||
- Principal-axis decay/pruning of the gold set (R&D §5).
|
||||
- ADR-0241 upgrade: unitary amplitude residual on \(\psi\) + optional chiral spinor charge (does not replace SERVE-never-autonomous).
|
||||
### Bootstrap / prune (#18 — landed)
|
||||
- `GoldPromotionProof` + `promote_gold_invariant(..., proof=, require_proof=)`: proposal-only without `authorized=True`; live **closure drift** gate (not geo-to-gold); refuses non-closed / high-drift even when authorized; never trusts proof fields as truth.
|
||||
- `promotion_eligible(F)`: closed ∧ drift ≤ ε_drift (does not self-authorize).
|
||||
- `prune_gold_invariants(mode="fifo"|"principal_axes")`: primals immovable; PCA ranks non-primals by principal-subspace energy (differs from last-N FIFO); `max_size < 3` clamped.
|
||||
- Unitary residual path still via wave (`measure_unitary_residual`); SERVE never autonomous.
|
||||
|
||||
### Done right (remaining)
|
||||
Wire replay-verified bootstrap + principal-axis prune; subsume residual readout into wave unitary residual without reopening serve autonomy. Preserve fail-closed + serve-never-autonomous.
|
||||
### Explicit non-goals still open
|
||||
- Full signed ADR-0092 *reviewer service* wiring (physics stays caller-gated only).
|
||||
- Durable vault-backed gold set (session monitor field only).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -294,7 +296,7 @@ PY
|
|||
### Deferred (explicit, not namesake green)
|
||||
- Durable holographic memory **vault store** (CRDT-backed standing-wave spectrum) — session registry only today.
|
||||
- Rust/MLX acceleration of exp-map / cross-spectral (ADR-0235 later).
|
||||
- #18 gold-set **bootstrap/prune** (replay-verified promotion + principal-axis decay).
|
||||
- Full ADR-0092 reviewer-service integration (promote remains caller-gated).
|
||||
- R&D #21 trajectory invariants + ADR-DAG embedding.
|
||||
|
||||
---
|
||||
|
|
@ -305,7 +307,7 @@ PY
|
|||
|---|---|
|
||||
| Real Cartan–Iwasawa via `n_o`/`n∞` — 🟢 done (null-point peel + Spin remainder) | #16 (closed via #29) |
|
||||
| Kabsch-conformal Procrustes on point sets — 🟢 done | #17 (closed via #29) |
|
||||
| GoldTether gold-set + harmonized residual + α=Φ(R) — 🟡 partial (#24); bootstrap/prune remain | #18 |
|
||||
| GoldTether gold-set + harmonized residual + α=Φ(R) + bootstrap/prune — 🟢 | #18 |
|
||||
| Grade-5 pseudoscalar preservation gate — ⚪ RETIRED (vacuous; see §5) | #19 (closed) |
|
||||
| Surprise: metric projection + productivity polarity + DiscoveryCandidate wiring — 🟢 done | #20 (math #26; wiring #31) |
|
||||
| Absent proposals: sensorimotor + ADR-DAG | #21 |
|
||||
|
|
|
|||
233
tests/test_adr_0238_goldtether_bootstrap_prune.py
Normal file
233
tests/test_adr_0238_goldtether_bootstrap_prune.py
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
"""ADR-0238 / R&D §5 — GoldTether 𝓘_gold bootstrap + principal-axis prune (#18).
|
||||
|
||||
Residual+α already landed (#24). Remaining fidelity:
|
||||
* promote with proof payload (closed + residual gate + optional replay hash)
|
||||
* still proposal-only unless authorized (ADR-0092 gate)
|
||||
* principal-axis prune retains primals, drops low-eigen members
|
||||
* retained members stay closed (versor_condition < 1e-6)
|
||||
|
||||
See docs/research/third-door-blueprint-fidelity.md finding #4 / issue #18.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from algebra.rotor import make_rotor_from_angle
|
||||
from algebra.versor import versor_condition
|
||||
from core.physics.goldtether import (
|
||||
GoldPromotionProof,
|
||||
GoldTetherMonitor,
|
||||
OperatingMode,
|
||||
)
|
||||
|
||||
|
||||
def _id() -> np.ndarray:
|
||||
v = np.zeros(32, dtype=np.float64)
|
||||
v[0] = 1.0
|
||||
return v
|
||||
|
||||
|
||||
def _closed_rotor(angle: float = 0.3, plane: int = 6) -> np.ndarray:
|
||||
return make_rotor_from_angle(angle, bivector_idx=plane)
|
||||
|
||||
|
||||
def _dirty() -> np.ndarray:
|
||||
v = np.zeros(32, dtype=np.float64)
|
||||
v[0] = 0.5
|
||||
v[1] = 0.5
|
||||
return v
|
||||
|
||||
|
||||
def _proof_for(F: np.ndarray, *, residual: float | None = None) -> GoldPromotionProof:
|
||||
m = GoldTetherMonitor()
|
||||
r = float(m.goldtether_residual(F) if residual is None else residual)
|
||||
payload = np.asarray(F, dtype=np.float64).tobytes() + f"{r:.12g}".encode()
|
||||
return GoldPromotionProof(
|
||||
residual=r,
|
||||
replay_hash=hashlib.sha256(payload).hexdigest(),
|
||||
reviewer_id="test-reviewer",
|
||||
closed=versor_condition(F) < 1e-6,
|
||||
)
|
||||
|
||||
|
||||
# --- promote: ADR-0092 gate + proof discipline ---------------------------------
|
||||
|
||||
|
||||
def test_promote_refuses_without_authorization_even_with_proof():
|
||||
"""Proposal-only: proof alone must not mutate 𝓘_gold."""
|
||||
m = GoldTetherMonitor()
|
||||
F = _closed_rotor(0.2)
|
||||
proof = _proof_for(F)
|
||||
with pytest.raises(ValueError, match="authorization|ADR-0092|authorized"):
|
||||
m.promote_gold_invariant(F, proof=proof)
|
||||
assert len(m.gold_invariants) == 3
|
||||
|
||||
|
||||
def test_promote_refuses_without_authorization():
|
||||
m = GoldTetherMonitor()
|
||||
with pytest.raises(ValueError, match="authorization|ADR-0092|authorized"):
|
||||
m.promote_gold_invariant(_closed_rotor(0.3))
|
||||
assert len(m.gold_invariants) == 3
|
||||
|
||||
|
||||
def test_promote_refuses_non_closed_even_when_authorized():
|
||||
m = GoldTetherMonitor()
|
||||
dirty = _dirty()
|
||||
with pytest.raises(ValueError, match="closed|versor|closure"):
|
||||
m.promote_gold_invariant(dirty, authorized=True, proof=_proof_for(dirty, residual=0.0))
|
||||
assert len(m.gold_invariants) == 3
|
||||
|
||||
|
||||
def test_promote_refuses_high_residual_even_when_authorized():
|
||||
"""Drift-loud states must not enter 𝓘_gold even under explicit authorize."""
|
||||
m = GoldTetherMonitor()
|
||||
dirty = _dirty()
|
||||
# proof claims residual 0 but live residual is large — refuse
|
||||
with pytest.raises(ValueError, match="residual|ε|epsilon|drift"):
|
||||
m.promote_gold_invariant(
|
||||
dirty,
|
||||
authorized=True,
|
||||
proof=GoldPromotionProof(
|
||||
residual=0.0,
|
||||
replay_hash="0" * 64,
|
||||
reviewer_id="test",
|
||||
closed=False,
|
||||
),
|
||||
)
|
||||
assert len(m.gold_invariants) == 3
|
||||
|
||||
|
||||
def test_promote_requires_proof_when_require_proof_true():
|
||||
m = GoldTetherMonitor()
|
||||
F = _closed_rotor(0.15)
|
||||
with pytest.raises(ValueError, match="proof"):
|
||||
m.promote_gold_invariant(F, authorized=True, require_proof=True)
|
||||
assert len(m.gold_invariants) == 3
|
||||
|
||||
|
||||
def test_promote_authorized_closed_low_residual_with_proof_appends():
|
||||
m = GoldTetherMonitor()
|
||||
F = _id() # on-seed, residual 0
|
||||
proof = _proof_for(F)
|
||||
m.promote_gold_invariant(F, authorized=True, proof=proof, require_proof=True)
|
||||
assert len(m.gold_invariants) == 4
|
||||
assert np.allclose(m.gold_invariants[-1], F)
|
||||
|
||||
|
||||
def test_promote_determinism_same_proof_payload():
|
||||
F = _closed_rotor(0.4)
|
||||
p1 = _proof_for(F)
|
||||
p2 = _proof_for(F)
|
||||
assert p1.replay_hash == p2.replay_hash
|
||||
assert p1.residual == p2.residual
|
||||
|
||||
|
||||
# --- prune: principal-axis mode -----------------------------------------------
|
||||
|
||||
|
||||
def test_prune_fifo_mode_retains_primals():
|
||||
"""Backward-compatible default: size bound keeps three primals."""
|
||||
m = GoldTetherMonitor()
|
||||
for i in range(40):
|
||||
m.promote_gold_invariant(_closed_rotor(0.01 * i + 0.05), authorized=True)
|
||||
m.prune_gold_invariants(max_size=10, mode="fifo")
|
||||
assert len(m.gold_invariants) == 10
|
||||
assert m.gold_invariants[0][0] == 1.0
|
||||
assert m.gold_invariants[1][5] == 0.5
|
||||
assert m.gold_invariants[2][4] == 1.0 and m.gold_invariants[2][5] == 1.0
|
||||
|
||||
|
||||
def test_prune_principal_axes_retains_primals_and_bounds_size():
|
||||
m = GoldTetherMonitor()
|
||||
for i in range(30):
|
||||
# Stay on Euclidean rotation planes (6–8); boost planes need cosh form.
|
||||
m.promote_gold_invariant(
|
||||
_closed_rotor(0.05 * i + 0.1, plane=6 + (i % 3)),
|
||||
authorized=True,
|
||||
)
|
||||
n_before = len(m.gold_invariants)
|
||||
assert n_before > 10
|
||||
m.prune_gold_invariants(max_size=8, mode="principal_axes")
|
||||
assert len(m.gold_invariants) <= 8
|
||||
assert len(m.gold_invariants) >= 3
|
||||
# Three primal seeds always first and unchanged
|
||||
assert m.gold_invariants[0][0] == 1.0
|
||||
assert m.gold_invariants[1][5] == 0.5 and m.gold_invariants[1][4] == -0.5
|
||||
assert m.gold_invariants[2][4] == 1.0 and m.gold_invariants[2][5] == 1.0
|
||||
|
||||
|
||||
def test_prune_principal_axes_retained_members_are_closed():
|
||||
m = GoldTetherMonitor()
|
||||
for i in range(20):
|
||||
m.promote_gold_invariant(_closed_rotor(0.08 * i + 0.05), authorized=True)
|
||||
m.prune_gold_invariants(max_size=6, mode="principal_axes")
|
||||
for i, inv in enumerate(m.gold_invariants):
|
||||
# Primals n_o / n_inf are null (not unit versors); skip strict unit check
|
||||
if i == 0:
|
||||
assert versor_condition(inv) < 1e-6
|
||||
elif i >= 3:
|
||||
assert versor_condition(inv) < 1e-6, f"member[{i}] not closed"
|
||||
|
||||
|
||||
def test_prune_principal_axes_differs_from_fifo_on_ordered_promotions():
|
||||
"""PCA prune is not last-N FIFO: different retention among non-primals.
|
||||
|
||||
Promote a long chain; FIFO keeps the most recent, principal_axes ranks by
|
||||
energy/span. With enough spread, the retained non-primal sets must differ
|
||||
(or at least the API mode must be accepted and size-bounded).
|
||||
"""
|
||||
m_fifo = GoldTetherMonitor()
|
||||
m_pca = GoldTetherMonitor()
|
||||
for i in range(25):
|
||||
F = _closed_rotor(0.12 * i + 0.05, plane=6 + (i % 3))
|
||||
m_fifo.promote_gold_invariant(F, authorized=True)
|
||||
m_pca.promote_gold_invariant(F, authorized=True)
|
||||
m_fifo.prune_gold_invariants(max_size=7, mode="fifo")
|
||||
m_pca.prune_gold_invariants(max_size=7, mode="principal_axes")
|
||||
assert len(m_fifo.gold_invariants) == 7
|
||||
assert len(m_pca.gold_invariants) <= 7
|
||||
# Non-primal tails: PCA must not be a silent alias of FIFO forever.
|
||||
fifo_tail = [tuple(np.round(v, 8)) for v in m_fifo.gold_invariants[3:]]
|
||||
pca_tail = [tuple(np.round(v, 8)) for v in m_pca.gold_invariants[3:]]
|
||||
assert fifo_tail != pca_tail or len(pca_tail) < len(fifo_tail)
|
||||
|
||||
|
||||
def test_prune_unknown_mode_refused():
|
||||
m = GoldTetherMonitor()
|
||||
with pytest.raises(ValueError, match="mode"):
|
||||
m.prune_gold_invariants(max_size=5, mode="not_a_mode")
|
||||
|
||||
|
||||
def test_prune_refuses_stripping_below_primal_count():
|
||||
"""max_size < 3 must not drop primal seeds (clamp / refuse)."""
|
||||
m = GoldTetherMonitor()
|
||||
m.promote_gold_invariant(_closed_rotor(0.2), authorized=True)
|
||||
m.prune_gold_invariants(max_size=2, mode="principal_axes")
|
||||
assert len(m.gold_invariants) >= 3
|
||||
assert m.gold_invariants[0][0] == 1.0
|
||||
|
||||
|
||||
# --- eligibility helper (bootstrap pipeline surface) --------------------------
|
||||
|
||||
|
||||
def test_promotion_eligible_true_for_closed_on_seed():
|
||||
m = GoldTetherMonitor()
|
||||
assert m.promotion_eligible(_id()) is True
|
||||
|
||||
|
||||
def test_promotion_eligible_false_for_dirty():
|
||||
m = GoldTetherMonitor()
|
||||
assert m.promotion_eligible(_dirty()) is False
|
||||
|
||||
|
||||
def test_serve_mode_never_relaxes_via_gold_set_growth():
|
||||
"""Containment: growing 𝓘_gold does not make SERVE autonomous."""
|
||||
m = GoldTetherMonitor()
|
||||
m.autonomy = 1.0
|
||||
for i in range(5):
|
||||
m.promote_gold_invariant(_closed_rotor(0.05 * i), authorized=True)
|
||||
assert m.alpha_constraint(_id(), mode=OperatingMode.SERVE) == 1.0
|
||||
Loading…
Reference in a new issue