Skip to content

fix(represent): the margin and the registry share one typed vocabulary (BS2-F2) - #376

Merged
chrishayuk merged 1 commit into
mainfrom
fix/bs2-f2-typed-statistic-keys
Sep 1, 2026
Merged

fix(represent): the margin and the registry share one typed vocabulary (BS2-F2)#376
chrishayuk merged 1 commit into
mainfrom
fix/bs2-f2-typed-statistic-keys

Conversation

@chrishayuk

Copy link
Copy Markdown
Owner

BS2-F2 — found by the real-bank integration smoke test that BALANCED-SEARCH-2's pre-registration demanded, before rung 4 starts.

The defect

The calibration registry keyed "route flip rate". ConstraintVector::of emitted "route flips". They never matched, so lookup missed and evidence_for fell through:

if let Some(e) = self.lookup(statistic, scale) { return e.verdict; }
if status.is_priceable() { Direct } else { Unusable }

A route-flips margin is a count, counts have no tail to be thin, so measurement_status is Measured, so is_priceable() is true — and the statistic ROUTE-CAL-1 established as ordering-only came back Direct.

That is the invariant the whole evidence ladder exists to protect, violated by the exact statistic the doctrine named it about: a proxy affects promotion class and never becomes price, or route_flip_rate slowly becomes a shadow behavioural contract.

Demonstrated, not argued — the same scenario run against pre-fix code:

PRE   margin.what="route flips"  evidence=Direct         priceable=true
POST  Statistic::RouteFlips      evidence=OrderingProxy  priceable=false

Two of the four registry keys did match (kl p99, routed mixture moved at p99), which is what made the mechanism look like it worked.

The fix

Statistic — a typed vocabulary — replaces the free string on all three joins:

join was now
Margin::what String Statistic
SearchCalibration::statistic String Statistic
ProxyObservation::for_criterion String Statistic

That third one had the same latent defect: proxy_for joins on the same string, and a mismatch silently yields "no proxy" — which ranks Uninformed, below ProxyRisky.

Statistic is deliberately finer than Criterion: RouteDisplacement carries both a p99 and a max limit that bind independently, and at 256 positions the p99 is a maximum wearing a percentile's name while the max is exactly what it says.

Admission::Refused keeps its Vec<String> — it reports, it does not join, and keeping it preserves the refusal report's alphabetical ordering.

Why the fixtures missed it

They passed their own literal to both sides of the join, so they were green throughout. The new regression test takes its key from a margin ConstraintVector::of actually built.

Not addressed: BS2-F1

No gate sets route_flip_maxkimi_logit_v3 sets it None and balanced-v1 inherits that — so in practice this margin is absent entirely. v3's doc says it "keeps the counts as DIAGNOSTICS and drops them as authority", but there is no diagnostic-side vehicle, so a criterion kept "as a diagnostic" produces no margin. That is a gate-design question, and it had to come second: setting route_flip_max first would have started pricing a proxy.

Verification

fmt --check, clippy -D warnings, E0 (10 passed), full suite (3618 passed, 0 failed), cargo check --workspace --all-targets. Coverage policy passed — search_evidence.rs 100.00%, constraint.rs 99.43%, assessment.rs 99.20%, promotion.rs 97.09%, quality.rs 96.52%; crate 93.45%.

BS2-F2, found by the real-bank smoke test the pre-registration demanded.

The calibration registry keyed `"route flip rate"` while
`ConstraintVector::of` emitted `"route flips"`. The lookup missed,
`evidence_for` fell through to its `is_priceable()` arm, and route flips
— a COUNT, so always `Measured` — came back `Direct`. The one statistic
ROUTE-CAL-1 established as ordering-ONLY was silently PRICED, which is
the failure the evidence ladder exists to prevent:

  PRE   margin.what="route flips"  evidence=Direct        priceable=true
  POST  Statistic::RouteFlips      evidence=OrderingProxy priceable=false

Two of the four registry keys DID match, so the mechanism looked like it
worked. `Statistic` replaces the free string on all three joins — the
margin's `what`, the registry's `statistic`, and a proxy observation's
`for_criterion` — so a rename cannot silently demote an entry to the
fall-through again. It is deliberately finer than `Criterion`, which
carries both a p99 and a max limit that bind independently.

The regression test takes its key from a margin `ConstraintVector::of`
actually built, rather than passing its own literal to both sides —
which is what let the fixtures stay green through the defect.

`Admission::Refused` keeps its `Vec<String>`: it reports, it does not
join.

Does NOT address BS2-F1 — no gate sets `route_flip_max`, so this margin
is absent in practice. That is a gate-design question, and fixing it
first would have started pricing a proxy.
@chrishayuk
chrishayuk merged commit 8a6ca6a into main Sep 1, 2026
29 of 31 checks passed
chrishayuk added a commit that referenced this pull request Sep 1, 2026
R4-F1, found by Rung 4's first real search.

The driver printed `PROMOTE: e26`, matching the pre-registered
prediction. Reversing the input list printed `PROMOTE: e20`, from
identical reports. All four rank keys were byte-identical —
`tier=1 within=0.702202900 frugality=-0.0` — because `within` falls back
to `gpu_ms_saved` for any non-`Priced` class and every one-step move
removed the same bytes. `cmp_rank` returned Equal and a stable sort
handed back input order. At diagnostic scale nothing is priceable BY
DESIGN, so that tie is the normal state, not an edge case. Meanwhile
both calibrated proxies separated the four candidates monotonically and
unanimously: the evidence existed and the comparator could not see it.

The invariant this pins:

  Determinism may order indistinguishable candidates for DISPLAY; it may
  never convert indistinguishability into evidence for PROMOTION.

`display_order` may use identity as its final tie-break.
`decide_promotion` is not given identity at all, so it cannot.

ORDINAL, never scalar. An OrderingProxy licenses order and not
magnitude, so a numeric proxy term in RankingScore would smuggle
magnitude back through the comparator. Instead: A proxy-dominates B iff
no worse on every comparable proxy and strictly better on at least one.
Comparability requires BOTH candidates to have observed the statistic
and the registry to say it orders at this scale.

Conflicts are REFUSED, not traded off — there is no empirical basis for
how many places of kl are worth one place of routing, so a frontier
returns Ambiguous{ConflictingOrderingProxies}. Physical gain may
separate candidates the proxies call EQUAL, and that is recorded as
`decided_by_physical_gain` so a trace never has to guess which stage
decided.

Statistic moves to its own module with a canonical `better()`/`order()`:
one derivation of which end is good, because deriving it from a gate's
LimitKind would only work for statistics a contract bounds, and
RouteFlipRate is bounded by nothing. quality.rs also went to 888 lines
in #376 — over the 800 cap — and is back to 775.

Two things the tests caught. The decision was permutation-invariant
before the evidence RECORD was: `dominated` echoed input order, so the
same round produced different traces. And sorting the table on
`cmp_rank` alone put the WORST candidate first, because every key tied
and identity decided — `display_order` now reads best-first.

On the real four reports, both permutations: PROMOTE e26, dominating
e20/e23/e24, deciding proxies [KlP99, RouteFlipRate]. No new GPU work.

Selection is not prediction. The variant is `SelectForAuthority`, not
`Promote`, and its evidence names the authority dimensions the
diagnostic scale could not speak to. `Uninformed` is ELIGIBLE by design:
authority is the mechanism that resolves those dimensions, so gating it
out would silently rewrite the doctrine into "diagnostic must predict
every authority dimension before authority may run" — impossible at 256
positions for the mass tails.

  Uninformed may be selected for authority IFF it is otherwise
  promotable, the missing dimensions are EXPLICIT, and no
  better-evidenced candidate outranks it.

Never `Uninformed => refuse`, never `Uninformed => assume free`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant