core/tests/test_curriculum_outcome_mix.py
Claude 71d6366fa3
feat(ledger,tests,adr): PR-14 — the outcome-mix rule, and the four expected licences dissolve under it (R-8)
Rank 6, the last docket item before the posture statements, and the throughput
frontier N-5 called "one ruling wide". The ruling landed. It closed the frontier
rather than opening it, and that is the correct outcome.

R-8 ruled C: committing to an entailment and correctly declining to commit are
DIFFERENT capabilities, licensed on DIFFERENT evidence, and may not be pooled.

THE FINDING: split, ZERO bands license — not four.

  band                              unknown   floor      entailed  floor
  philosophy_theology_contrast          652   0.989925          8   0.000000
  philosophy_theology_modal             652   0.989925          8   0.000000
  physics_causal                        653   0.989940          7   0.000000
  systems_software_causal               653   0.989940          7   0.000000
  (pooled, the old basis)               660   0.990046  -- clears by 0.000046

N-5 recorded that "four bands would earn SERVE the moment a ledger is sealed."
That is true on the POOLED basis and false on the ruled one. Those four cleared
theta_SERVE=0.99 only because 7-8 entailments were counted alongside 652-653
correct refusals to reach 660. The licence was manufactured by the pooling, not
earned by the evidence. conservative_floor(9,9) is 0.000000 — the Wilson lower
bound at nine trials is not merely below theta, it is zero.

This is exactly why the plan blocked PR-14 on R-8 instead of sealing first and
ruling after. A ledger sealed on the pooled basis would have granted four
licences that the mix rule then had to revoke — and revoking a granted licence
is the expensive direction, as PR-12 had just demonstrated across 21 deduction
bands in this same session.

DELEGATED RULING — R-8 C's entailed floor N: no new constant.
The packet recommended "C, with a floor from A applied to the entailed
capability only," and N was never named. It does not need naming. theta_SERVE
=0.99 through conservative_floor — the bar every other capability already meets,
657 distinct correct decisions — applied to each separated capability on its own
evidence licenses nothing, by a factor of 73. Inventing a second, weaker
constant for the capability that most needs the strong one would institutionalise
two standards, which is precisely the objection that sank option B. Recorded
under the standing delegation with its reasoning, so it can be overturned.

NO LEDGER IS SEALED, AND THAT IS THE DELIVERABLE. Under the rule there is
nothing to license, and the registered missing_ok=True absence already says so
once. An artifact whose only content is its own emptiness would be a second
statement of the same fact — the defect class registered as G-23 this same week.

THE USEFUL RESULT IS THE SHAPE OF THE GAP, WHICH POOLING HAD HIDDEN.
Non-commitment serving is FOUR TO FIVE distinct query atoms short per band.
Entailed serving is ~648 short. Those are content tasks of completely different
size, and a single pooled figure could not distinguish "almost there" from "two
orders of magnitude away". Four cases is a morning's work; 648 is a program.
Nobody could see that before the split.

Delivered:
  - curriculum_serve_entailed registered in CAPABILITY_LEDGERS, missing_ok=True
    (absent = nothing licensed, the honest state), with the rule declared in the
    manifest table rather than at a call site (ADR-0263 rule 5)
  - an audit source for it — demanded immediately by the manifest's own pin
    (test_every_licensed_capability_has_an_audit_source went red the moment the
    capability was declared, which is PR-5's declared-table discipline working)
  - ADR-0264 §5 amendment recording the ruling and correcting §4.1's "four bands
    would earn SERVE" expectation. Changes no decision in that ADR;
    curriculum_serving_enabled stays False
  - tests/test_curriculum_outcome_mix.py on the gate: both capabilities pinned,
    the shortfall pinned exactly, AND the counterfactual pinned — pooling
    licenses 4 — so the argument for the rule cannot drift away from the number
    it rests on

The serving path needs no change, and that is stated rather than left implicit:
under the rule nothing is licensed, so there is no licensed-vs-disclosed branch
to route. Building that machinery now would be building for a case that cannot
occur yet.

Closes G-10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wcw2pnMBwyvmNyQg4uPEt4
2026-07-28 07:59:01 +00:00

161 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""R-8 (ruled C) / G-10 — the curriculum outcome-mix rule, and what it measures.
**The rule.** Committing to an entailment and correctly declining to commit are
different capabilities. They are licensed on different evidence and may not be
pooled. ``core.ratified_ledger.CAPABILITY_LEDGERS`` declares them as two entries;
this module measures them.
**Why the rule was needed, in one number.** Pooled, the four leading bands reach
660 committed decisions and ``conservative_floor(660, 660) = 0.990046``, clearing
θ_SERVE=0.99 — by a margin of **0.000046**. Split, the same bands hold **652653
correct UNKNOWNs** and **78 entailments**. Neither half clears. The licence was
manufactured entirely by counting correct refusals and correct commitments as one
kind of evidence.
**This overturns the plan's own prediction.** N-5 recorded that *"four bands would
earn SERVE the moment a ledger is sealed."* That is true on the pooled basis and
false on the ruled one. Sealing under R-8 C licenses **nothing** — which is exactly
why the plan blocked PR-14 on this ruling rather than sealing first and ruling
after. A ledger sealed on the pooled basis would have granted four licences that the
mix rule then had to revoke, and revoking a granted licence is the expensive
direction (see R-13 and the 21 deduction bands).
**The shortfall is small and exact, and that is the useful part.** Unknown-serving
needs **four to five** more distinct query atoms per band (657 653 for two bands,
657 652 for the other two). Entailed-serving needs roughly **648** more. Those are
content tasks of wildly different size, and before the split nobody could see that —
the pooled number hid a four-case gap behind a 648-case one.
This module deliberately does **not** seal a ledger. Under the rule there is nothing
to license, and an artifact asserting zero licences is a file whose only content is
its own emptiness; the registered ``missing_ok=True`` absence already says it, once.
"""
from __future__ import annotations
import pytest
from core.ratified_ledger import CAPABILITY_LEDGERS
from core.reliability_gate import conservative_floor
from core.reliability_gate.evidence import SERVE_VOLUME_AT_THETA_099
THETA_SERVE = 0.99
@pytest.fixture(scope="module")
def mix() -> dict[str, dict[str, int]]:
"""Per-band ``{'entailed': n, 'unknown': n}`` from the practice producer."""
from evals.curriculum_serve.practice.runner import run
report = run()
return {band: dict(c["gold_mix"]) for band, c in report["classes"].items()}
def test_both_capabilities_are_registered() -> None:
"""The split is declared in the manifest, not asserted at a call site.
ADR-0263 rule 5. If this fails, the outcome-mix rule exists only in prose —
which is the state R-8 was raised to end.
"""
assert "curriculum_serve" in CAPABILITY_LEDGERS
assert "curriculum_serve_entailed" in CAPABILITY_LEDGERS
assert CAPABILITY_LEDGERS["curriculum_serve_entailed"].missing_ok is True, (
"absent must mean 'nothing licensed' for the entailed capability — a "
"missing_ok=False entry would turn an honest absence into a broken deployment"
)
def test_the_mix_is_not_vacuous(mix: dict[str, dict[str, int]]) -> None:
"""Guard the measurement before asserting anything about it."""
assert len(mix) >= 10, f"parsed only {len(mix)} bands from the producer"
assert any(m.get("entailed", 0) for m in mix.values()), "no entailed cases parsed"
assert any(m.get("unknown", 0) for m in mix.values()), "no unknown cases parsed"
def test_no_band_licenses_entailed_serving(mix: dict[str, dict[str, int]]) -> None:
"""The committing capability, on its own evidence: nothing qualifies.
``conservative_floor(9, 9) == 0.0`` — the Wilson lower bound at nine trials is
not merely below θ, it is zero. This is the assertion that stops an entailment
licence being granted on a handful of cases, which is what pooling did.
"""
offenders = {
band: n
for band, m in mix.items()
if (n := m.get("entailed", 0)) and conservative_floor(n, n) >= THETA_SERVE
}
assert not offenders, (
f"these bands now hold enough entailed evidence to license: {offenders}. "
"That is good news and a reviewed decision — seal the entailed ledger and "
"move this pin."
)
best = max(m.get("entailed", 0) for m in mix.values())
assert best < SERVE_VOLUME_AT_THETA_099, (
f"max entailed evidence is {best}, at or above the {SERVE_VOLUME_AT_THETA_099} "
"θ_SERVE requires — the entailed capability is now earnable"
)
def test_no_band_licenses_unknown_serving_either(mix: dict[str, dict[str, int]]) -> None:
"""The declining capability, on its OWN evidence: also nothing — the finding.
This is the assertion that contradicts N-5. The four bands everyone expected to
license hold 652653 correct UNKNOWNs against a 657 requirement: **four to five
short**.
They only ever cleared because 78 entailments were pooled in to reach 660.
"""
licensed = {
band: n
for band, m in mix.items()
if (n := m.get("unknown", 0)) and conservative_floor(n, n) >= THETA_SERVE
}
assert not licensed, (
f"these bands now license unknown-serving on distinct non-commitments: "
f"{licensed}. Expected under the ruling once curriculum volume grows — seal "
"the ledger and move this pin, in the same change."
)
def test_pooling_would_have_licensed_four_bands(mix: dict[str, dict[str, int]]) -> None:
"""Pin the counterfactual, because it is the whole justification for the rule.
If this ever fails it means pooling no longer manufactures a licence, and the
mix rule has become free rather than load-bearing — worth knowing either way. It
is asserted rather than written in prose so the number cannot quietly drift away
from the argument that rests on it.
"""
pooled = {
band: total
for band, m in mix.items()
if conservative_floor(total := sum(m.values()), total) >= THETA_SERVE
}
assert len(pooled) == 4, (
f"pooled evidence licenses {len(pooled)} bands, not the 4 recorded when R-8 "
f"was ruled: {sorted(pooled)}"
)
for band, total in pooled.items():
entailed = mix[band].get("entailed", 0)
unknown = mix[band].get("unknown", 0)
assert conservative_floor(unknown, unknown) < THETA_SERVE, band
assert entailed < 10, (band, entailed)
assert total >= SERVE_VOLUME_AT_THETA_099, (band, total)
def test_the_shortfall_is_recorded_exactly(mix: dict[str, dict[str, int]]) -> None:
"""Four or five cases, not "more volume" — the gap is small, and its size is the point.
Before the split, the unknown shortfall (4-5) was hidden behind the entailed one
(~648). A single pooled figure could not distinguish "almost there" from
"two orders of magnitude away," and those call for completely different work.
"""
best_unknown = max(m.get("unknown", 0) for m in mix.values())
best_entailed = max(m.get("entailed", 0) for m in mix.values())
assert SERVE_VOLUME_AT_THETA_099 - best_unknown == 4, (
f"the unknown-serving shortfall moved: best band has {best_unknown} distinct "
f"non-commitments against {SERVE_VOLUME_AT_THETA_099} required"
)
assert SERVE_VOLUME_AT_THETA_099 - best_entailed > 600, (
f"the entailed shortfall is now {SERVE_VOLUME_AT_THETA_099 - best_entailed}; "
"if it has closed to within a few hundred, the entailed capability is worth "
"re-planning rather than deferring"
)