Skip to content

Check the UC allowance response on the award before deductions - #614

Draft
MaxGhenis wants to merge 9 commits into
mainfrom
w4/runner-uc-award-check
Draft

MaxGhenis wants to merge 9 commits into
mainfrom
w4/runner-uc-award-check

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #613

Stacked on #612. These files belong to workstream W1 (the UK Budget reform contract: budget_reform_population.py). W1's session is idle, so W4 opens this change by the coordinator's ruling. Unlike #612, this changes a verdict. It is a declared change citing the regulations, not an exemption. Hold: this merges only after the UK hub's legal check (done: AGREE_WITH_CHANGES) and this PR's independent review both agree.

What changes

population_uc_standard_allowance_plus_10 used to require that no universal_credit amount fall when every UC standard allowance rises 10% for 2027-28. universal_credit is the amount paid, net of deductions, and the law lets the amount paid fall. The old assertion was therefore blind to the award and wrong for capped claimants with deductions.

It is replaced by two assertions that hold for every benefit unit:

  1. No award before deductions falls by more than a penny. The award is universal_credit_pre_benefit_cap minus benefit_cap_reduction. The benefit cap reduces the award by the excess of welfare benefits over the cap (Universal Credit Regulations 2013, SI 2013/376, reg 81), counting Universal Credit before any deduction (reg 80(2)), so a capped award stays flat.
  2. Each payment moves by its award's change less its deductions' change, to the penny. Deductions are taken from the award (Claims and Payments Regulations 2013, SI 2013/380, Sch 6). With the award flat, a capped claimant's payment falls by exactly as much as their deductions rise. The comparison is signed, so a deduction released without the payment rising also fails.

As before, the allowance must scale by exactly 10% and at least one payment must rise.

Not checked here: how much deductions rise. An earlier commit on this branch bounded that by the deduction rate times the allowance rise. That bound encoded the model's formula, not the law, and it failed lawful cases on the full 1.58.1 file:

  • 56 units whose baseline award was nil start deductions with the new award;
  • in 53 units the one-penny minimum payable binds.

In law, advances are also fixed amounts (SI 2013/383 reg 10), while the model scales every deduction type with the allowance (policyengine-uk#2410). So no allowance-proportional bound holds for both law and model.

Why a penny. These amounts are float32 annual pounds, and their sums round by a few thousandths of a pound. The review found the largest award "decline" on 2.126.12 was £0.0039 a year.

The verdict is a pure function, uc_allowance_verdict. Tests cover:

  • the lawful capped case;
  • deduction rises of any size;
  • each unlawful movement;
  • rounding within a penny on both sides.

Why now

policyengine-uk 2.130.1 fails the old check on one benefit unit in the seeded 1,000-household subsample: a capped award whose deductions rise. 2.126.12 passed only because its uc_deduction_random_draw applies no deductions when benefit-unit weight sums below 1e6, as the subsample does. policyengine-uk #1905 changed that gate to built_from_data.

The full published uk-data 1.58.1 file, with every standard allowance +10% for 2027-28 (counts only):

policyengine-uk 2.126.12 policyengine-uk 2.130.1
Payments that fall 141 141
Payments that rise 5,644 5,700
Awards before deductions that fall by more than a penny 0 0
Payments not equal to award less deductions (to the penny) 0 0
New check holds holds

A correction: an earlier version of this description said 2.126.12 had two falling awards, fixed by policyengine-uk #2076. Those were float32 rounding of £0.0039 a year at most. Neither version has a real award decline.

Legal check

An independent check by the UK hub, run by an Opus 5.5 reviewer, returned AGREE_WITH_CHANGES (~/reviews/uk-hub/reviews/uc-cap-deductions-ruling/review-opus.md):

  • Universal Credit counts towards the cap before any deduction (SI 2013/376 reg 80(2)).
  • Deductions come out of the post-cap award (SI 2013/380 Sch 6).
  • Sanctions: in law a sanctioned capped award can fall (regs 110, 111). policyengine-uk applies no sanctions.
  • Childcare: the childcare costs element comes off the cap reduction (reg 81(1)(b), (2)); policyengine-uk#2089 adds it.
  • Advances: these are fixed amounts (SI 2013/383 reg 10); see policyengine-uk#2410.
  • Discretion and cap level: overpayment recovery is "not more than" a percentage (reg 11), and the deductions cap has been 15% administratively since 30 April 2025.

Invariants

  • No award before deductions falls by more than a penny.
  • Each payment change equals the award change less the deduction change, to the penny.

axiom: n/a: test-contract invariant; no policy encoding changes

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 14, 2026 08:51
W1's population_uc_standard_allowance_plus_10 required that no
universal_credit amount fall when every standard allowance rises 10%.
universal_credit is the amount paid, net of deductions. Under the benefit
cap (Universal Credit Regulations 2013, SI 2013/376, reg 81) a capped award
stays flat, and deductions are taken from the award as percentages of the
standard allowance (Claims and Payments Regulations 2013, SI 2013/380,
Sch 6), so a capped claimant with deductions is paid less. policyengine-uk
2.130.1 applies deductions to simulations built from data (#1905), so one
benefit unit in the 1,000-household subsample shows exactly that; 2.126.12
applied none below 1e6 benefit-unit weight, so the subsample never had
deductions.

The check now asserts what the law makes invariant:
- no award before deductions (universal_credit_pre_benefit_cap less
  benefit_cap_reduction) falls;
- no payment falls by more than its deductions rise;
- no deductions rise by more than the standard allowance;
plus, as before, exact 10% scaling and at least one higher payment.
The verdict is a pure function, uc_allowance_verdict, with a test for the
lawful capped case and one for each unlawful movement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UK hub pointed out that "deductions rise by no more than the standard
allowance" holds only while uc_deduction_rate <= 1: policyengine-uk 2.130.1
adds the latent excess above the deductions cap (deductions the cap does not
limit) with no bound of its own. The check now requires that no unit's
deduction rate changes under the reform (the rate does not depend on the
allowance) and that its deductions rise by no more than that rate times the
allowance rise, which is tighter for ordinary rates and still correct above
1. Tests cover a rate above 1 and a changed rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UK hub's independent legal check (AGREE_WITH_CHANGES) confirms the
invariants as model invariants: Universal Credit counts towards the cap
before any deduction (SI 2013/376 reg 80(2)); in law a sanctioned capped
award can fall (regs 110, 111), which the model, applying no sanctions,
cannot show; advances are recovered as fixed amounts (SI 2013/383 reg 10),
so the model's deduction rise is an upper bound (policyengine-uk#2410).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-1 review of #614 and the full contract run on policyengine-uk 2.130.1
show that the deduction-rate assertions encoded the model's formula, not the
law, and fail lawful cases:
- deductions start with a new award (56 units on the full 1.58.1 file whose
  rate goes from 0 because their baseline award was nil);
- the one-penny minimum payable binds (53 units);
- in law advances are fixed amounts (SI 2013/383 reg 10), so no
  allowance-proportional bound is an invariant of both law and model.

The check now requires:
- no award before deductions falls by more than a penny (float32 rounding
  in these sums is a few thousandths of a pound; the round-1 review found
  the two "falling awards" on 2.126.12 were 0.0039 a year);
- each payment moves by its award's change less its deductions' change, to
  the penny (signed, so a missing deduction release is caught);
- exact 10% scaling and at least one higher payment, as before.
How much deductions rise is documented as unchecked here. On the full
1.58.1 file both 2.126.12 and 2.130.1 hold; tests pin the penny boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 14, 2026 13:47
#612's population stub raised universal_credit under the UC allowance
reform but not universal_credit_pre_benefit_cap, so on this branch the
payment no longer equalled the award less deductions and #614's
accounting assertion rightly failed three coverage tests. The stub now
raises the award with the payment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis changed the base branch from w4/runner-assertion-ids to main October 15, 2026 02:31
MaxGhenis and others added 2 commits October 14, 2026 22:31
Round-2 review of #614:
- Medium: deductions may not reduce the amount payable below a penny an
  assessment period (SI 2013/380 Sch 6 para 3(1)(a)). The check now
  requires every payment, in both runs, to be at least the lesser of its
  award and 12p a year, so deductions that take a whole award fail.
- Medium: every value the check reads must be finite in both runs; a NaN
  deduction, cap reduction or award no longer passes the comparisons.
- Low: the award-decline case lowers the award and its payment together,
  so only the award condition catches it; new tests cover errors that
  cancel across units, the penny boundary on gains, the 10% scaling
  tolerance and a payment left at exactly the floor. Removing any one of
  the four conditions fails a test.
- Low: the population stub has no cap reduction or deductions, so its
  payments equal its awards at every level.
- #612 review Low: assertion ids are tested on extraction-error, pending
  and unsupported rows too.

On the full 1.58.1 file with policyengine-uk 2.130.1 the check holds: 0
award falls, 0 accounting breaks, 0 payments below the floor, 0
non-finite values. All 66 runner tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

Contract: UC allowance check asserts on the payment, which the law lets fall

1 participant