UK local solve doctrine: uniform operator, declared bounds, past-cap census (#495 increment 4) - #503
Conversation
…d a past-cap census Increment 4 of the UK dense/local epic (#495), per the #492/#493 doctrine: no per-target calibration knobs. past_cap_census (in local_solver, carried on every StackedLocalSolveResult) makes the rows a solve wrote off first-class diagnostics: past-cap at init/final, escaped, frozen, and the pushed-out dumping-ground class listed row by row with area/metric labels. local_doctrine adds the release path's solve surface: UKLocalSolveDoctrine (frozen, self-validating, closed rule vocabularies) with the declared contract UK_LOCAL_TARGET_LOSS_CAP=10.0 / UK_LOCAL_MAX_WEIGHT_RATIO=100.0 (current defaults promoted to an explicit reviewed contract), and solve_uk_local_weights_under_doctrine, whose signature exposes no per-target weight/scale/cap/ratio parameters at all — the refusal is structural. The low-level solver remains the research harness. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt, uniform-surface refusal, doctrine-mode runner, unbounded pushed-out list Adjudicated findings from the sol review of #503 (all three blockers confirmed): - solve_uk_local_weights_under_doctrine no longer takes a doctrine parameter at all: the bounds always come from the reviewed module constant, so dataclasses.replace()-minted contracts cannot route through the release path (this also moots the copy/subclass tampering class). - The doctrine path validates the target surface as one uniform grid — duplicate (area_type, area_code, metric) rows would be implicit per-target weights and are refused with named examples. - build_local_candidate gains under_doctrine=True (the release mode): per-target/bound solver options are refused by name and the solve routes through the doctrine wrapper; summarize_local_candidate reports past-cap counts and write_local_candidate_outputs persists past_cap_census.json, so candidates no longer drop the observability. - past_cap_census lists every pushed-out row by default (max_listed_rows is an explicit opt-in bound with a truncation flag) and mirrors the canonical loss's refusals: non-finite estimates/targets and zero or non-finite scales raise instead of silently classifying rows inside the cap. Exact-tie semantics pinned (strictly greater, matching the torch gradient). - Brittle test assertions replaced (no Adam monotonicity claim; finite checks before max). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Cross-family review completed (sol adversarial pass, fable adjudication) — all three blockers confirmed and fixed in the second commit:
Should-fixes: census now mirrors the canonical loss's refusals (non-finite estimates/targets, zero/non-finite scales raise instead of silently classifying inside the cap — and the solver's own canonical-loss call would refuse such inputs before the census anyway, so the two stay consistent); the doctrine path refuses duplicate (area_type, area_code, metric) rows as implicit per-target weights (finding 6); Sol's no-issue verifications worth keeping on record: |
What
Increment 4 of the UK dense/local epic (#495): the local solve becomes one uniform operator with declared bounds and first-class written-off-row diagnostics, per the #492/#493 doctrine — no per-target calibration knobs.
Past-cap census (#492 A, ported to the local solve)
past_cap_censusinlocal_solver.py: counts of target rows past the loss cap at initialization and at the final weights, the rows that escaped back inside, the rows frozen past the cap throughout, and — the silent-triage class #492 measured on Build N — the rows pushed out during the solve, listed row by row with area/metric labels and before/after scaled errors (bounded list with an explicit truncation flag). Everysolve_stacked_local_weightsresult now carries the census (StackedLocalSolveResult.past_cap_census, additive field), so both the research harness and the doctrine path get the observability for free.The doctrine surface (
local_doctrine.py)UKLocalSolveDoctrine— a frozen, self-validating contract: declared loss cap, declared weight-ratio stretch bound, and closed vocabularies for the scale rule (default_target_loss_scalesonly — the canonical target-defined scales) and target weighting (uniformonly). A future family-level weighting would be a new reviewed rule name here, never a per-target vector.UK_LOCAL_SOLVE_DOCTRINE— the reviewed instance;UK_LOCAL_TARGET_LOSS_CAP = 10.0andUK_LOCAL_MAX_WEIGHT_RATIO = 100.0declare the current defaults as the explicit contract. Revising either is a doctrine change that must edit this module and its pinned test, which forces review. (max_weight_ratio anchors differently per arm: 5x vs design (dense) but ~25x effective (sparse refit re-anchors) — declare one stretch contract #493's one-stretch-contract adjudication may revise the ratio; the increment-6 candidate review adjudicates the cap against measured fit — the point of this increment is that the values are declared and uniform, not what they are.)solve_uk_local_weights_under_doctrine— the release path's solve. Structurally knob-free:target_loss_weights,target_loss_scales,target_loss_cap, andmax_weight_ratiodo not exist on its signature; passing them is aTypeErrorby construction, not a runtime flag. A tampered doctrine instance fails its own validation before any solve runs.The low-level
solve_stacked_local_weightsremains the research harness (experiments legitimately pass explicit vectors); the doctrine wrapper is what increment 6 wires into the release build.Testing
7 new tests: census transition classification on a hand-built 4-row case (escaped/frozen/pushed-out each exercised, row labels checked), default-scale semantics + shape validation, census carried on a real small solve, pinned doctrine constants, tampered-bound refusals (cap ≤ 0/NaN, ratio ≤ 1, unknown rules), the no-knobs signature guarantee, and declared-bound application (stretch bound holds on solved weights; non-doctrine object refused). Existing solver/runner tests untouched and green;
ruff checkclean.Part of #495.
🤖 Generated with Claude Code