Repair UC capital graph inputs and document calibration support gaps - #892
Conversation
76238dc to
c4d88c0
Compare
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Review pass at c4d88c00 (Claude Code, high effort; worktree checkout. This PR contains #891 — the merge-base of the two is #891's head — so I reviewed only #892's own 14 files (+8,067/−90); the ledger, target and feed changes in the 43-file stat are #891's and are reviewed there. Without the engine: 545 passed, 7 skipped on the capital, graph, redraw, measurement, parity, gates and diagnostics suites, plus 513 passed, 6 skipped on the #891-shared set; ruff check clean; ci_test_groups --verify, the parity census and the coverage manifest current; CI green apart from the wheel jobs pending.)
The graph repair is right, and #892 should own it
graph.py:195-196 adds exactly ("person", "is_benunit_head") and ("person", "is_parent") to uc_capital_coherence's consumes; the serialized fixture's structural diff touches only that node's person inputs. The new regression is real: with main's graph.py and fixture swapped in it fails with KeyError: "UC relationship person inputs missing: ['is_benunit_head', 'is_parent']" from uc_relationships.py:38, and passes with the PR. The legacy-versus-graph acceptance skips here without the engine; the engine-uk and spine-uk lanes are green. On why #883 did not catch it: _stage_slices checks only that declared consumes are available, nothing checks that a transform reads only declared columns, and #883's tests called cohere_uc_capital on a full frame — so only the graph path, which builds a minimal frame from consumed slices, could fail. The new test is stage-specific; a generic test that runs every stage in _STAGE_CONSUMES through the kernel's projection would catch the next instance of this class.
The same two-line hunk and the same fixture blob appear in #889 byte-identically, so those will not conflict, but the two PRs add differently named tests after the same line of test_uk_uc_capital_coherence.py, and git merge-tree reports that conflict plus five #891-overlap files. #889 should drop its copy and rebase after #891 and #892 land.
Findings
1. Should-fix — the headline LP evidence was produced by code not in the PR. tools/diagnose_uk_uc_support.py hard-codes fixed_mass_constraint: False (:158) and lp_probe takes a single scalar tolerance with bounds (0, cap_ratio). The committed evidence (docs/evidence/uk-uc-882/calibration-comparison.json) carries fresh_bounded_redistribution_lp_probes with fixed_household_mass_constraint: true, per-row tolerances (lone parent 5%, the rest 25%) and current_weight_multiplier_bounds [0.75, 1.25] — none of which the tool can express — and the one family that records a probe_script_sha256 (f99ca727…) does not match the committed tool (15df89f1…); the full-supported and bounded families record no script hash at all. So the tool reproduces its own two probes, but the ±25% witness, the "full supported set infeasible at 5% excluding CGT" claim and the named-opposition probes have no committed producer. Commit the probe script (or extend the tool with per-row tolerances and a fixed-mass mode) and record its sha in the evidence.
2. Should-fix — five signed fit deferrals are deleted on the strength of a non-default run, and the body says so only in passing. target_fit_reviewed_exclusions.json goes from six entries to one: the four dwp.uc.households_*@2025 deferrals and the private-pension count band are removed, leaving obr.capital_gains_tax@2025. The justification is the fresh run's errors, and the new test itself says that run is "1,500 epochs with the explicit family_equal override … not a default-doctrine fit claim". Since the gate fails stale exclusions anyway, removal is defensible — but retiring signed adjudications is a register change and the body should name the five and the basis, not fold them into "remove five obsolete terminal fit exemptions".
Verified good
- The two zero-support bands are #879's (
COUPLE_NO_CHILDREN£27.6–28.8k and £28.8–30k);uk_target_fit_gateis what refuses the export andtest_restored_fit_checks_leave_empty_payment_tail_cells_blockedpins it with CGT excluded. - The tools are development-only (no import from any package, pyproject or workflow), need the licensed replay directory, and authenticate every input sha against the receipt. The committed evidence contains no ids or row-level data: the only long arrays are target-name lists, and the tool writes ids only to files it documents as local-only.
- The LP is a
highsfeasibility problem with per-row relative bounds and independent post-verification; deterministic given the pinned scipy, with a time limit that can yield "inconclusive" on slow hardware. - The ±25% witness is labelled a diagnostic in both the evidence and the doc, no weights are exported, and no manifest or artifact is written as releasable.
Needs the licensed data
The policyengine-uk extra (seven skips including the parity acceptance and the matrix smoke), the private replay directory, and — until it is committed — the script behind the three LP evidence families.
Nothing blocks the graph fix; merge order #891 → #892 matches the branch topology. 1 is the one to settle before this is used as evidence in #882, since the strongest claims in the body currently rest on an uncommitted script; 2 is a body change plus the five names.
c4d88c0 to
1df5379
Compare
|
Vahid, the review fixes are in 1df53792, including the remaining metadata and framing nits from #891.
Local review validation: 69 LP/support tests and 464 target/ledger/spec tests passed, with two existing feed-path skips. Independent code/evidence and prose reviews are clean. At the time of posting, 20 hosted checks have passed and the two installed-wheel suites are still running, with no failures so far (CI run). These fixes make the evidence reproducible and its limits explicit; they do not close the lone-parent gap or certify a calibrated release. |
The UK source graph failed at UC capital coherence because executor projection removed
person.is_benunit_headandperson.is_parent, which the claimant/couple predicate now reads. This PR declares both dependencies and updates the serialized graph fixture. The regression runs the actual projected capital transform with positive SPI reporters and claimant roles that disagree with legal marriage, checking donor values and redraw count. Existing UK-engine integration and H2 parity already exercise all 28 real transforms on synthetic source tables; broader conditional-branch fixture coverage remains follow-up work.The fresh unsampled source build then completes all 40 graph nodes and 15 source-scope gates. This PR adds authenticated matrix/support diagnostics and aggregate evidence without private records or source IDs. #891 is merged; this PR is rebased onto
mainand carries the build/diagnostic increment plus its review fixes. It advances #882 without closing it.The population comparison receipt is the authority for the fit results below. The separate source-only receipt checks target compilation and contains no population fit results.
The review fixes also express the five paid child-count averages as explicit twelve-month source windows, removing misleading December-to-year hold records and recording source months/publication identity. All 415 compiled values/statuses and the ordered 366-target active roster remain unchanged; the other 410 references, including 101 monthly UC references, and unrelated holds are preserved.
The fresh paired comparison moves the same-paid-target lone-parent shortfall from 14.37% to 14.01%, while the ten broad count rows’ weighted mean absolute percentage error falls from 8.97% to 4.72%. These runs use 1,500 updates and
family_equalallocation, learning rate .02, seed zero, free mass, cap 10 and no sparsity penalty. They do not establish readiness under the unchanged defaults of 256 updates and uniform allocation. The allowance-based family proxy and structural classification coincide under the tested UK2.97 claimant inputs; the proxy adds no support, does not recover administrative ineligible-partner cases, and usesUNKNOWNdefensively for zero model allowance.Two childless-couple payment bands still have zero support and correctly block calibrated export. The five-plus/TCL contrast still depends on one original family. Named-subset linear programs are feasible, but the full supported system remains infeasible at 5% even after omitting the already-deferred OBR CGT target. A fixed-mass witness restricted to ±25% of current weights can put lone-parent claims within 1% while the other selected rows remain within 25%; income-tax and NI estimates fall 14.05% and 9.87% from current estimates. That witness is a diagnostic trade-off, not a releasable or optimized calibration. Zero rows and CGT are omitted only from the stated diagnostic subsets.
The PR retires these five #796 fit exemptions because their observed errors in the fresh 1,500-update candidate are inside the existing 25% bound:
dwp.uc.households_children_1@2025dwp.uc.households_children_2@2025dwp.uc.households_children_5_or_more@2025dwp.uc.households_single_with_children@2025hmrc/private_pension_income_count_income_band_100_000_to_150_000@2025The historical register preserves the original UC support/capital and related pension solver-pressure rationale, including later historical-basis annotations. The new errors do not revalidate those earlier causal claims. The gate rejects stale exemptions once their rows fit inside the bound, so retirement restores normal enforcement: any renewed absolute error above 25% blocks release. These targets remain bound in the solve. The CGT exemption under #875 remains unchanged (+44.771038% in this candidate). Replaying all 366 saved errors leaves exactly the two empty-tail failures. No calibrated H5 or passing calibration record is created; the original failed receipt is preserved.
The population receipt's
retired_fit_deferral_evidenceblock preserves the full-precision values and canonical diagnostic SHA-256971e6f8cac94b69ebf82364e83845f27d19fce38490a032524db237eb4884463behind this table.The committed tools/diagnose_uk_uc_lp_evidence.py reproduces all three published LP families: named opposition, the full supported system, and bounded redistribution with heterogeneous tolerances and fixed mass. The formatted producer exactly reproduces their deterministic constraint, status, fit, concentration and protected-outcome fields from the authenticated saved archives. New provenance binds the producer, imported helper, inputs, versions and result; original receipt/script hashes are preserved. The report's reproduction instructions give the command. This runs no population engine or Adam calibration and exports no witness weights or source IDs.
The diagnostic report records the controlled comparisons, source support, input hashes, gate evidence, and remaining age/claim/cash questions. Review validation passes 69 LP/support tests and 464 target/ledger/spec tests, with two existing default-feed-path skips; pinned national/local regeneration and exact saved-matrix LP evidence replay pass. Earlier validation covers the projected capital regression, graph/H2 integration, a real UK2.97 matrix-tool smoke case, and gate/battery/country tests. PR checks validate code contracts; they do not certify population artifacts.