core/evals/analogical_transfer/kappa_calibration.py
Shay ec2b9ae794 feat(adr-0243): Lane A — discovery wiring + live kappa-search caller
Wire the ADR-0242 §5 pinned primitives into the contemplation runner
(ADR-0243 §2.4): contemplate_surprise_history() gates the newest
caller-timed ADR-0239 dual-operator observation through
is_discovery_eligible, the measured history through
cross_band_discovery_gate (F5/F6/F7 persistence), and only then emits a
DiscoveryCandidate via the existing emit_surprise_discovery ->
DiscoveryCandidateSink. Proposal-plumbing only: the sole side effect is
sink.emit().

Surprise-signal sourcing (the Lane A open question): ContemplationFinding
carries no surprise measurement and the report-mining paths have no
geometry, so nothing is fabricated for them — the canonical carrier is
the ADR-0239 dual audit dict (SelfAuthorshipMiner / ADR-0240 harness),
consumed as explicit SurpriseObservations with caller-supplied logical
time (deterministic, no wall clock).

Give propose_kappa_line_search its live caller (ADR-0242 §5-P1):
evals/analogical_transfer/kappa_calibration.py calibrate_transfer_kappa,
confined to the ADR-0240 calibration pipeline per R-04; kappa_search_event
fires exactly once when a sink is passed. Proposal-only — no
GoldTetherMonitor mutation.

A-04 hardening found during review: chat/runtime.py idle_tick lazily
imports core.contemplation.runner inside the serve process, and the
package __init__ eagerly pulled wave_seam -> holographic_vault (a
serve-banned module) — a PRE-EXISTING process-level quarantine breach.
Closed by making the wave-seam re-exports lazy (PEP 562) and keeping the
multi_scale_energy gate import function-local; pinned by a new dynamic
idle-tick-edge test in test_serve_quarantine_transitive.py.

Pinned primitives untouched: is_discovery_eligible,
cross_band_discovery_gate, candidate_from_surprise_dual signatures
unchanged; no changes to cognitive_lifecycle.py or chat/runtime.py.
2026-07-17 11:36:41 -07:00

114 lines
4.4 KiB
Python

"""ADR-0242 §5-P1 — live κ line-search caller (calibration pipeline only).
`core.physics.goldtether.propose_kappa_line_search` and its internal
`kappa_search_event` telemetry emission were fully implemented and pinned
(`tests/test_adr_0242_carry_seams.py`) but had zero live call sites. This
module is the live caller, confined to the ADR-0240 analogical-transfer
calibration pipeline per the master plan's R-04 mitigation ("trace
generation ... limited strictly to the calibration and training-loop
pipelines" — never a hot/serve path; `chat/runtime.py` must never import
this package).
What is calibrated: in `core.physics.surprise.dual_operator` the
productive-transfer threshold is κ-scaled (``thr = productive_threshold /
κ``). The objective below proposes the in-bounds κ whose scaled threshold
sits closest to the worst measured Procrustes residual of the calibration
corpus — i.e. κ is tuned so the acceptance bar tracks the corpus's actual
structural-residual scale. The result is PROPOSAL-ONLY: nothing here
mutates `GoldTetherMonitor` state, COHERENT standing, or serve autonomy;
failures fall back to baseline κ = 1.0 inside the search itself.
"""
from __future__ import annotations
import math
from typing import Any, Sequence, cast
import numpy as np
from core.physics.dynamic_manifold import conformal_procrustes
from core.physics.goldtether import kappa_search_event, propose_kappa_line_search
from evals.analogical_transfer.harness import TransferCase, make_fixture_pair
def calibrate_transfer_kappa(
cases: Sequence[TransferCase] | None = None,
*,
productive_threshold: float = 0.35,
lower: float = 0.1,
upper: float = 2.0,
evaluation_budget: int = 16,
sink: Any | None = None,
) -> dict[str, Any]:
"""Propose a κ for the transfer corridor via Fibonacci section search.
Per-case Procrustes residuals are measured ONCE, outside the search
loop, so the κ objective is cheap arithmetic (R-04: bounded
calibration cost). Cases whose residual is not measurable
(Procrustes refusal or non-finite residual) are excluded and counted;
if none remain the calibration refuses (typed ``ValueError``) rather
than proposing κ from nothing.
When ``sink`` is provided (any ``emit(line: str)`` object), the
§5-P1 ``kappa_search_event`` fires exactly once from inside
:func:`core.physics.goldtether.propose_kappa_line_search`.
"""
corpus = tuple(cases) if cases is not None else (make_fixture_pair(),)
if not corpus:
raise ValueError("calibrate_transfer_kappa: cases must be non-empty")
threshold = float(productive_threshold)
if not math.isfinite(threshold) or threshold <= 0.0:
raise ValueError("productive_threshold must be finite and > 0")
residuals: list[float] = []
refused = 0
for case in corpus:
try:
# conformal_procrustes accepts sequences of 32-vectors (same call
# shape as the harness); its annotation is narrower than runtime.
proc_residual = float(
conformal_procrustes(
cast(np.ndarray, case.source),
cast(np.ndarray, case.target),
)[1]
)
except ValueError:
refused += 1
continue
if not math.isfinite(proc_residual):
refused += 1
continue
residuals.append(proc_residual)
if not residuals:
raise ValueError(
"calibrate_transfer_kappa refused: no admissible cases "
f"({refused} of {len(corpus)} procrustes-refused or non-finite)"
)
# Worst measured structural residual is the binding acceptance constraint.
r_star = max(residuals)
def residual_fn(kappa: float) -> float:
return (threshold / float(kappa) - r_star) ** 2
proposed_kappa, outcome = propose_kappa_line_search(
residual_fn,
lower=float(lower),
upper=float(upper),
evaluation_budget=int(evaluation_budget),
objective_id="analogical_transfer_kappa",
objective_version="v1",
sink=sink,
)
return {
"kind": "analogical_transfer_kappa_calibration",
"proposed_kappa": float(proposed_kappa),
"productive_threshold": threshold,
"max_procrustes_residual": r_star,
"case_count": len(residuals),
"refused_case_count": refused,
"event": kappa_search_event(proposed_kappa, outcome),
}
__all__ = ["calibrate_transfer_kappa"]