UC deductions: FRR family re-harvested from primary sources + DB ingest - #52
Merged
Conversation
Collaborator
Review — UC deductions: FRR family re-harvestProvenance is honest and the structure is clean: 8 rows, no Confirm before merge (stacking)
Should address
Suggestion
Reviewed with Claude Code assistance. |
vahid-ahmadi
force-pushed
the
uk/uc-deductions-frr
branch
from
August 19, 2026 13:09
23bb43b to
b32f8a0
Compare
Contributor
Author
|
All three items addressed in b32f8a0:
Suite with pypdf: 182 passed / 4 skipped; ruff clean. |
Collaborator
Re-review — all addressed ✅
Clean from my side. |
MaxGhenis
force-pushed
the
uk/db-ingest-uk-externals
branch
from
August 19, 2026 20:19
0a8bc91 to
2eb5576
Compare
MaxGhenis
added a commit
that referenced
this pull request
Aug 19, 2026
… persistence Sol's round-2 probes found two mechanism defects, both fixed: 1. HMRC reckoner rows bypassed the closed unit registry (the revenue_effect branch returns before the canon call). Unit validation now runs at the top of the loop, before ANY branch; regression test injects an unknown-unit reckoner row. 2. register_baselines ran AFTER the claims transaction committed — it validated the DB but did not guard it (an unregistered-baseline probe raised with all 15,851 replacement claims already committed). Split into register_baselines_txn (bare executes, caller owns the transaction — nested `with conn:` would commit early, which is why the txn form may never open one) + the standalone wrapper. ingest() now runs delete + insert + registration gate + lane rows in ONE transaction: an unregistered world rolls back everything. Rollback test: poison claim -> raise -> DB byte-identical, poison world never registered. Also per the round-2 pass: - equivalisation + poverty_line_anchor promoted into STANDARD_CONDITIONS; semantic pins added to the integration test (anchor distribution 5,632/480/416, every mixed anchor is a window, equivalisation tracks housing costs on all 13,056 HBAI rows, UKMOD gini/income all BHC + modified-OECD, none vacuous) — count-only accounting cannot detect semantic condition loss. - Stale accounting comment corrected: 15,851 + 1,073 = 16,924 admitted; + 1,829 drops = 18,753 adapter rows. - Period comment narrowed: END-year is the LIVE claim convention; archived harvest NOTES staged some sources by start year, so archive-built ingests (#52) must translate at staging time. - Two pre-existing unused imports removed (ruff check; CI only gates format). Committed DB unchanged: this round changes validation and transaction mechanics, not staging semantics. Suite: 215. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Part of #39/#21. The original ~/scorecard-harvest/uk_deductions staging is machine-local, so the Fair Repayment Rate family is re-harvested directly from primary sources, vendored, and page-verified 2026-08-14: - sources/harvest-uk-deductions/frr/ vendors the AB2024 print (FRR passages: paras 2.30/4.111/5.134) and the AB2024 policy costings document (verified ABSENCE of an FRR costing line across all 93 pages — the measure is a financial transaction), plus 8 staged claims each carrying a verbatim quote and page locator: PSNCR +GBP385m 2029-30, 1.2m gaining households, GBP420/yr average gain, 700k families with children (HMT and DWP statements as separate citable claims), and the 2.8m households-with-deductions pre-FRR level. - scorecard_db/ingest_uk_deductions.py maps them under the harvest fail-loud contract. The PSNCR line lands as the new cash_requirement_change metric with fiscal_measure=psncr — deliberately NOT revenue_change, so no PSNB-shaped comparison can silently join against it (the #21 rule; a test enforces it). Reform rows ride policy_ref uc_fair_repayment_rate (cap 25%->15%, effective 2025-04-30 as reform detail); the pre-FRR level is a baseline claim; everything held_out. DWP quarterly deductions outturns (PE's calibration source) are not staged at all. - models.py gains cash_requirement_change / gainer_count / average_annual_gain (commented; stacked on the #33 vocabulary). Remaining from #21, documented in VERIFICATION.md: JRF floor (pre/post-FRR baseline_keys), historical cap costings, DWP/DfC screening, Policy in Practice, Citizens Advice — each needs its own page-verified pass. The policyengine-uk#1815 PE comparator attaches once its run artifacts carry engine/bundle provenance. Part of #39 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…checked - Rows 2 and 3 verified as two DISTINCT verbatim AB2024 sentences (para 4.111 vs the para 2.30/5.134 wording) by extracting the vendored PDF; a test now pins both strings so neither can drift into a reworded fragment of the other. - The 93-page costing-absence claim is machine-checked: a test extracts the vendored costings PDF and asserts zero occurrences of the measure name (importorskip(pypdf) — skips on bare CI, runs locally). - Rebased onto uk/db-ingest-uk-externals (UnitConcept.GBP dependency). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es, one transaction Rebased from the pre-repair #48 branch onto main (the two original harvest commits cherry-picked intact) and adapted to the foundation contract, fixing four defects: 1. value_kind was "usd" on GBP rows -> "gbp" (sol's #48-round-1 class). 2. The FRR reform rows rode the DEFAULT baseline (= current law), but the costing's own counterfactual is the 25%-cap law the FRR replaced — today's current law INCLUDES the FRR, so a PE result computed against it would be a cross-world comparison. The rows now carry the registered pre_frr_uc_deductions world (baselines.py entry with AB2024/DWP-PR provenance), mirrored in conditions["baseline_policy"]. 3. The £420 average annual gain was bare GBP — a per-household average a query could sum. New GBP_PER_HOUSEHOLD unit concept (mirror of USD_PER_HOUSEHOLD, same value_kind rule). 4. ingest() was a bare upsert with no gates. Now the foundation shape: ONE transaction (delete both sources + insert + the deliberate- registration gate + lane row — commit or nothing), lane-feed mirror after commit. Also: period asserted = fy END year per row (the live claim convention; staged rows verified clean, all 8); identity values (program/subgroup/geography/unit) route through the closed registry (hm_treasury + dwp registered in uk_aliases); measure + fiscal_measure promoted into STANDARD_CONDITIONS; staged unit labels validated against the expected concept per metric. Committed DB ingested: 58,461 -> 58,469 claims; registry 52 -> 53 worlds; uk-deductions-frr lane 'ingested' in DB + feed. Suite: 224. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MaxGhenis
force-pushed
the
uk/uc-deductions-frr
branch
from
August 19, 2026 21:47
b32f8a0 to
2559bfa
Compare
…ng + fy gates Sol's round-1 probes on the rework, both blockers fixed: 1. Staged conditions merged AFTER the generated identity fields, so a poison row's geography="Mars" or a baseline_policy contradicting ReformRef.baseline was accepted. Reserved keys (country, geography, fy, baseline_policy) now raise, and the generated fields merge LAST (loud + inert). Poison tests for both vectors. 2. The 2.8m "households with deductions" row REMOVED from staging. Re-verified against the live press release: the staged quote was a paraphrase (the actual sentence is "With as many as 2.8 million households seeing deductions made..."), the release states no measurement vintage (the FY2025-26/period-2026 identity was unsupported — the reviewer traced the figure to DWP's Dec-2023 to Nov-2024 administrative deductions statistics, pre-FRR), and the quantity is exactly the DWP quarterly deductions OUTTURN family relationships.py routes away from external scores. Disposition recorded in VERIFICATION.md: future Ledger lane, staged from the statistics publication itself. The reform_policy=None branch now raises — a future level row is a deliberate decision, never a silent default. Hardening per the round: - Exact per-source accounting (hm_treasury 4 + dwp 3): an empty or truncated staging fails wholesale instead of delete-and-commit-zero. - fy label validation: '2029-99' is malformed (suffix must be start year + 1), in this module AND in the foundation _fy (same class; '1999-00' still parses). DB re-ingested: 58,468 claims (7 FRR), lane detail '7 claims'. Suite: 227. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Python's \$ matches before a final newline, so the foundation _fy's re.match(r"^...\$") accepted "2029-30\n", "2029/30\n", and "FYE 2030\n" (sol round-2 probe). Both patterns now re.fullmatch, with trailing- newline regressions; no other ^...\$ match patterns remain in the package. Suite: 227. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two remaining \$-anchored call sites (round-3 probes: a trailing- newline HBAI window period and UKMOD poverty metric were both ingested): _SPAN and _UKMOD_POVERTY now fullmatch, with regressions. _UKMOD_TAX_PROGRAMS stays .match deliberately — it is a prefix pattern over canon-validated program values. And the ROOT CAUSE of the recurring committed-DB header churn, fixed rather than re-cleaned: ScorecardDB.__init__ unconditionally DROP+CREATEd the comparisons view, so every process that merely READ the committed DB (tests, exporters) wrote a schema change and bumped the SQLite header counters — that is how round 3's stray binary delta got swept into a test-only commit. _ensure_view now recreates the view only when its stored definition differs from the code's; new tests pin open/close byte-stability (x3) and stale-view rebuild. The committed blob is restored to its round-2 state and verified byte-stable across the full suite. Suite: 230. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Aug 20, 2026
Rebase completion (the branch forked before the whole UK arc — #43-#48, #52, #71, #72 all landed under it): - uk-deductions-frr (merged today, unknown to this branch) was the one committed lane without a country tag -> UK. - The UK ingests' appended lane metas now carry country explicitly (ingest_uk_externals' five lanes + ingest_uk_deductions' one), so a fresh-feed append passes the new sync_lane_feed guard and never files a UK lane under the app's missing-key US default. - app/public/data copies refreshed from data/ (they had drifted to a 270-row populations.json vs 284) and pinned: new test asserts the committed copies byte-match data/, and a second asserts every committed lane carries US|UK — the two drift classes this rebase surfaced. Suite: 241 python + 3 bun; oxlint + vite build clean; committed DB byte-stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #39 (staging handoff from #21). Stacked on #48 (base branch
uk/db-ingest-uk-externals— it builds on that PR's Metric vocabulary); merge #48 first and this rebases to a clean diff.Why a re-harvest
#21 staged the UC-deductions claims at
~/scorecard-harvest/uk_deductions/— machine-local, not vendored. Rather than transcribe from the issue text, the Fair Repayment Rate family is re-harvested from primary sources, vendored in-repo, and page-verified (2026-08-14):sources/harvest-uk-deductions/frr/autumn_budget_2024.pdf— FRR passages at paras 2.30 (p.47), 4.111 (p.110), 5.134 (p.142)autumn_budget_2024_policy_costings.pdf— verified absence: no FRR costing line in all 93 pages, consistent with UK UC deductions: external scores staged (FRR, JRF floor, historical cap costings) #21's finding that the measure is a financial transaction with no PSNB impactclaims_staged.jsonlcarries 8 claims, each with a verbatim quote and page locator: PSNCR +£385m (2029-30), 1.2m gaining households, £420/yr average gain, 700k families with children — HMT and DWP statements staged as separate citable claims — plus the 2.8m households-with-deductions pre-FRR level.VERIFICATION.mdrecords the full audit and what remains un-staged from #21 (JRF floor with its pre/post-FRRbaseline_keysplit, historical cap costings, screening counts).The ingest
scorecard_db/ingest_uk_deductions.py, under the harvest fail-loud contract:cash_requirement_changemetric withfiscal_measure=psncr— deliberately notrevenue_change, so no PSNB-shaped comparison can ever silently join against it. This is UK UC deductions: external scores staged (FRR, JRF floor, historical cap costings) #21's "benchmark against PSNCR, never PSNB" rule made structural, andtest_no_psnb_shaped_claimsenforces it.policy_ref {"policy": "uc_fair_repayment_rate"}with the cap change and effective date as reform detail; the pre-FRR deductions level is a baseline claim.held_out; the DWP quarterly deductions outturn tables (the calibration source PE's parameters consume) are not staged at all — that boundary is the other UK UC deductions: external scores staged (FRR, JRF floor, historical cap costings) #21 rule.models.pygainscash_requirement_change/gainer_count/average_annual_gain(the distributional-impact vocabulary of UK fiscal-event documents).Verification
4 new tests (headline values, PSNCR-not-PSNB, quote+publication on every claim, DB round trip); full suite 137 passed, ruff clean.
The PE comparator from policyengine-uk#1815 (0.85m benunits, £421 mean gain vs DWP's £420) attaches as a
pe_resultsrow once its run artifacts are staged with engine/bundle provenance — that, plus the remaining #21 families, keeps #39 open.🤖 Generated with Claude Code
Merge order: this PR hard-depends on #48 (
UnitConcept.GBPand the #33 metric vocabulary come from that base) — merge #48 first; the branch is rebased onto it. #65 addsUnitConcept.GBPindependently; whichever merges second takes the trivial enum conflict.