#000037 Phase 1.c: kernel_cost/llm_cost split + sweep_weights profile
Per §22 Finding 2: flat capital_cost=1.0 made every utility negative in the dry-run sim. Split capital_cost into kernel_cost (~0.05 kernel re-probe) + llm_cost (~1.0 LLM witness fan-out); ControllerBranch now exposes effective_cost = kernel_cost + llm_cost when either is positive, falling back to legacy capital_cost when both are zero. Pre-1.c callers don't migrate. _utility() reads effective_cost so split-cost and legacy-cost branches with the same total cost produce byte-identical utility values. Per §22 Finding 3 (partial): add sweep_weights() profile — gamma_5f=1.5, lambda_capital_cost=0.25, nu_witness_divergence=0.5. Sweep work willingly pays capital for falsification discovery and treats high witness divergence as desirable signal (§13 step 11). Registered in WEIGHT_PROFILES["sweep"]; folds into governance_policy selection alongside safe / conservative / exploratory. Tests: 6 new in tests/test_prometheus.py — effective_cost split path, legacy fallback, _utility byte-identity across the two cost shapes, kernel-only vs LLM-only ranking, sweep-vs-safe divergence on a marginal branch (DEFERRED under safe, ACCEPT under sweep), and WEIGHT_PROFILES registry now pins all four profiles.
This commit is contained in:
parent
8da29fde69
commit
4b85a0af25
2 changed files with 210 additions and 8 deletions
|
|
@ -133,19 +133,44 @@ class ControllerBranch:
|
|||
(even bet) so callers that haven't measured payoff don't need
|
||||
to set it. Tests exercise the ``b_i <= 0`` safety guard via
|
||||
this field.
|
||||
|
||||
**Cost-class split (Phase 1.c)** — ``kernel_cost`` and
|
||||
``llm_cost`` decompose what was previously a single
|
||||
``capital_cost`` field. Per dry-run Finding #2, real sweep work
|
||||
spans an order-of-magnitude cost spread (kernel-only re-probe
|
||||
≈ 0.02-0.05, full LLM witness ≈ 1.0). Splitting them lets the
|
||||
controller's utility formula reflect what the witness path
|
||||
actually spends. Back-compat: when both new fields are 0 the
|
||||
controller falls back to the legacy ``capital_cost``; existing
|
||||
callers don't have to migrate. Tests pin both paths.
|
||||
"""
|
||||
|
||||
branch_id: str
|
||||
deltas: BatteryDeltas
|
||||
witness_divergence: float
|
||||
capital_cost: float
|
||||
regression_penalty: float
|
||||
security_risk: float
|
||||
memory_invalidation: float
|
||||
capital_cost: float = 0.0
|
||||
regression_penalty: float = 0.0
|
||||
security_risk: float = 0.0
|
||||
memory_invalidation: float = 0.0
|
||||
selfmodel_calibration_gain: float = 0.0
|
||||
warrant_promotion_gain: float = 0.0
|
||||
hard_vetoes: tuple[str, ...] = ()
|
||||
payoff_b: float = 1.0
|
||||
# Phase 1.c cost-class split. Defaults to 0 → back-compat
|
||||
# mode (use legacy ``capital_cost``).
|
||||
kernel_cost: float = 0.0
|
||||
llm_cost: float = 0.0
|
||||
|
||||
@property
|
||||
def effective_cost(self) -> float:
|
||||
"""The cost value the controller spends in the utility formula.
|
||||
|
||||
Returns ``kernel_cost + llm_cost`` when either is positive;
|
||||
otherwise returns the legacy ``capital_cost`` (back-compat
|
||||
path for pre-Phase-1.c callers).
|
||||
"""
|
||||
split = self.kernel_cost + self.llm_cost
|
||||
return split if split > 0.0 else self.capital_cost
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -317,10 +342,30 @@ def exploratory_weights() -> ControllerWeights:
|
|||
)
|
||||
|
||||
|
||||
def sweep_weights() -> ControllerWeights:
|
||||
"""Sleep-sweep profile (Phase 1.c — dry-run Finding #3).
|
||||
|
||||
Tuned for Phase 3 sleep-sweep economics: sweep work deliberately
|
||||
accepts capital cost in exchange for falsification discovery, so
|
||||
``lambda_capital_cost`` drops to 0.25 (vs safe-default 1.0) and
|
||||
``gamma_5f`` bumps to 1.5 (vs 1.25) to reward falsification-
|
||||
rate improvement. Witness divergence weight drops to 0.5 (vs
|
||||
1.5) because high divergence is *desirable* signal during sweep
|
||||
— it's what surfaces fixture candidates (§13 step 11).
|
||||
"""
|
||||
return replace(
|
||||
ControllerWeights(),
|
||||
gamma_5f=1.5,
|
||||
lambda_capital_cost=0.25,
|
||||
nu_witness_divergence=0.5,
|
||||
)
|
||||
|
||||
|
||||
WEIGHT_PROFILES: dict[str, ControllerWeights] = {
|
||||
"safe": safe_weights(),
|
||||
"conservative": conservative_weights(),
|
||||
"exploratory": exploratory_weights(),
|
||||
"sweep": sweep_weights(),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -371,7 +416,12 @@ def phase_1_trigger_check(
|
|||
|
||||
|
||||
def _utility(branch: ControllerBranch, w: ControllerWeights) -> float:
|
||||
"""§5 scalar utility ``U_i`` — all 11 weighted terms."""
|
||||
"""§5 scalar utility ``U_i`` — all 11 weighted terms.
|
||||
|
||||
Uses :attr:`ControllerBranch.effective_cost` so the Phase 1.c
|
||||
kernel/llm split and the legacy capital_cost path produce
|
||||
byte-identical utility values when only one path is populated.
|
||||
"""
|
||||
d = branch.deltas
|
||||
return (
|
||||
w.alpha_5s * d.delta_5s
|
||||
|
|
@ -380,7 +430,7 @@ def _utility(branch: ControllerBranch, w: ControllerWeights) -> float:
|
|||
+ w.rho_5r * d.delta_5r
|
||||
+ w.sigma_selfmodel_calibration * branch.selfmodel_calibration_gain
|
||||
+ w.tau_warrant_promotion * branch.warrant_promotion_gain
|
||||
- w.lambda_capital_cost * branch.capital_cost
|
||||
- w.lambda_capital_cost * branch.effective_cost
|
||||
- w.mu_regression_penalty * branch.regression_penalty
|
||||
- w.nu_witness_divergence * branch.witness_divergence
|
||||
- w.xi_security_risk * branch.security_risk
|
||||
|
|
@ -887,6 +937,7 @@ __all__ = [
|
|||
"safe_weights",
|
||||
"conservative_weights",
|
||||
"exploratory_weights",
|
||||
"sweep_weights",
|
||||
# Entry points
|
||||
"controller_decide",
|
||||
"phase_1_trigger_check",
|
||||
|
|
|
|||
|
|
@ -555,11 +555,17 @@ def test_idempotent_byte_identical_output():
|
|||
|
||||
|
||||
def test_weight_profiles_registry_complete():
|
||||
"""All three named profiles present and structurally distinct."""
|
||||
assert set(WEIGHT_PROFILES) == {"safe", "conservative", "exploratory"}
|
||||
"""All four named profiles present and structurally distinct."""
|
||||
assert set(WEIGHT_PROFILES) == {
|
||||
"safe",
|
||||
"conservative",
|
||||
"exploratory",
|
||||
"sweep",
|
||||
}
|
||||
safe = WEIGHT_PROFILES["safe"]
|
||||
cons = WEIGHT_PROFILES["conservative"]
|
||||
expl = WEIGHT_PROFILES["exploratory"]
|
||||
swp = WEIGHT_PROFILES["sweep"]
|
||||
# §15.2 deltas
|
||||
assert cons.gamma_5f == 1.0
|
||||
assert cons.lambda_capital_cost == 1.5
|
||||
|
|
@ -570,6 +576,10 @@ def test_weight_profiles_registry_complete():
|
|||
assert expl.nu_witness_divergence == 0.75
|
||||
assert expl.lambda_capital_cost == 0.5
|
||||
assert expl.eta_softmax_temperature == 1.5
|
||||
# Sweep profile deltas (Phase 1.c — dry-run Finding #3)
|
||||
assert swp.gamma_5f == 1.5
|
||||
assert swp.lambda_capital_cost == 0.25
|
||||
assert swp.nu_witness_divergence == 0.5
|
||||
# Safe baseline
|
||||
assert safe.gamma_5f == 1.25
|
||||
|
||||
|
|
@ -802,3 +812,144 @@ def test_module_level_constants_match_default_weights():
|
|||
assert w.h_low == H_LOW
|
||||
assert w.h_high == H_HIGH
|
||||
assert w.kappa_memory == KAPPA_MEMORY
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Phase 1.c — kernel_cost / llm_cost split on ControllerBranch
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_effective_cost_uses_split_when_provided():
|
||||
"""When ``kernel_cost`` or ``llm_cost`` > 0, ``effective_cost``
|
||||
returns their sum and IGNORES the legacy ``capital_cost`` field.
|
||||
This is the Phase 1.c migration path."""
|
||||
b = _b("a", capital_cost=1.0)
|
||||
b_split = ControllerBranch(
|
||||
branch_id="b",
|
||||
deltas=BatteryDeltas(0, 0, 0, 0),
|
||||
witness_divergence=0.0,
|
||||
capital_cost=999.0, # SHOULD BE IGNORED
|
||||
kernel_cost=0.05,
|
||||
llm_cost=0.3,
|
||||
)
|
||||
# Legacy branch uses capital_cost directly:
|
||||
assert b.effective_cost == 1.0
|
||||
# Split branch sums kernel + llm, ignores legacy:
|
||||
assert b_split.effective_cost == 0.35
|
||||
|
||||
|
||||
def test_effective_cost_falls_back_when_split_zero():
|
||||
"""When both new fields are 0, ``effective_cost`` reads
|
||||
``capital_cost`` — preserving pre-Phase-1.c behavior."""
|
||||
b = ControllerBranch(
|
||||
branch_id="legacy",
|
||||
deltas=BatteryDeltas(0, 0, 0, 0),
|
||||
witness_divergence=0.0,
|
||||
capital_cost=0.42,
|
||||
# kernel_cost + llm_cost both default to 0.0
|
||||
)
|
||||
assert b.effective_cost == 0.42
|
||||
|
||||
|
||||
def test_utility_uses_effective_cost():
|
||||
"""The utility formula reads effective_cost so a split-cost
|
||||
branch and a legacy-cost branch with the same TOTAL cost produce
|
||||
byte-identical utility values."""
|
||||
legacy = ControllerBranch(
|
||||
branch_id="legacy",
|
||||
deltas=BatteryDeltas(0.1, 0.1, 0.1, 0.1),
|
||||
witness_divergence=0.0,
|
||||
capital_cost=0.5,
|
||||
)
|
||||
split = ControllerBranch(
|
||||
branch_id="split",
|
||||
deltas=BatteryDeltas(0.1, 0.1, 0.1, 0.1),
|
||||
witness_divergence=0.0,
|
||||
capital_cost=0.0,
|
||||
kernel_cost=0.1,
|
||||
llm_cost=0.4,
|
||||
)
|
||||
w = safe_weights()
|
||||
from arborist.substrate.prometheus import _utility
|
||||
assert _utility(legacy, w) == _utility(split, w)
|
||||
|
||||
|
||||
def test_kernel_only_branch_cheaper_than_llm_branch():
|
||||
"""Two branches with identical signal but different cost class
|
||||
(kernel-only vs LLM-witness) produce different utilities,
|
||||
favoring the kernel-only path."""
|
||||
kernel_only = ControllerBranch(
|
||||
branch_id="kernel",
|
||||
deltas=BatteryDeltas(0, 0, 0.05, 0),
|
||||
witness_divergence=0.0,
|
||||
kernel_cost=0.05,
|
||||
llm_cost=0.0,
|
||||
payoff_b=10.0,
|
||||
)
|
||||
llm_only = ControllerBranch(
|
||||
branch_id="llm",
|
||||
deltas=BatteryDeltas(0, 0, 0.05, 0),
|
||||
witness_divergence=0.0,
|
||||
kernel_cost=0.0,
|
||||
llm_cost=1.0,
|
||||
payoff_b=10.0,
|
||||
)
|
||||
from arborist.substrate.prometheus import _utility
|
||||
w = safe_weights()
|
||||
assert _utility(kernel_only, w) > _utility(llm_only, w)
|
||||
|
||||
|
||||
def test_split_cost_branches_route_through_controller():
|
||||
"""End-to-end: a split-cost branch can be fed to controller_decide
|
||||
and the decision uses effective_cost in the utility math."""
|
||||
branches = (
|
||||
ControllerBranch(
|
||||
branch_id="kernel",
|
||||
deltas=BatteryDeltas(0, 0, 0.10, 0),
|
||||
witness_divergence=0.0,
|
||||
kernel_cost=0.05,
|
||||
llm_cost=0.0,
|
||||
payoff_b=10.0,
|
||||
),
|
||||
ControllerBranch(
|
||||
branch_id="llm",
|
||||
deltas=BatteryDeltas(0, 0, 0.05, 0),
|
||||
witness_divergence=0.0,
|
||||
kernel_cost=0.0,
|
||||
llm_cost=1.0,
|
||||
payoff_b=1.0,
|
||||
),
|
||||
)
|
||||
d = controller_decide(_inp(branches))
|
||||
# The kernel branch is unambiguously the winner under safe
|
||||
# weights: higher delta_5f, lower cost.
|
||||
assert d.selected_branch_id == "kernel"
|
||||
|
||||
|
||||
def test_sweep_profile_accepts_what_safe_profile_defers():
|
||||
"""The Phase-1.c sweep profile drops lambda_capital_cost from
|
||||
1.0 → 0.25 so the sweep is willing to pay capital for
|
||||
falsification discovery. A marginal-cost branch that DEFERREDs
|
||||
under safe weights ACCEPTs under sweep weights."""
|
||||
from arborist.substrate.prometheus import sweep_weights
|
||||
|
||||
# Build a branch whose utility under safe weights is barely
|
||||
# negative (DEFERRED via Kelly's p_i > 0.5 unmet); under sweep
|
||||
# weights its utility flips positive.
|
||||
# safe: U = 1.25 * 0.10 - 1.0 * 0.30 = -0.175 (< 0)
|
||||
# sweep: U = 1.50 * 0.10 - 0.25 * 0.30 = +0.075 (> 0)
|
||||
branches = (
|
||||
ControllerBranch(
|
||||
branch_id="marginal",
|
||||
deltas=BatteryDeltas(0, 0, 0.10, 0),
|
||||
witness_divergence=0.0,
|
||||
capital_cost=0.30,
|
||||
payoff_b=10.0,
|
||||
),
|
||||
)
|
||||
safe_d = controller_decide(_inp(branches, weights=safe_weights()))
|
||||
sweep_d = controller_decide(_inp(branches, weights=sweep_weights()))
|
||||
# Safe-profile decision rejects the marginal branch.
|
||||
assert safe_d.label == "REJECT"
|
||||
# Sweep-profile decision accepts it.
|
||||
assert sweep_d.label in ("ACCEPT", "MARGINAL")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue