Align Child Benefit claim exports and opt-out draws with the UK model - #1107
juaristi22 wants to merge 5 commits into
Conversation
Automated review pass (Claude Code, high effort) — round 1 at
|
Automated review pass (Claude Code, high effort) — round 2 at
|
| Item | Status | Evidence |
|---|---|---|
| Should-fix: taper opt-outs are paid under the new encoding | Open | assign_child_benefit_opt_outs (child_benefit_take_up.py:667-700) still fills the shortfall from the taper pool, whatever supports_opt_out says. Under registered_claims those families keep would_claim_child_benefit. With policyengine-uk's default opt_out_charge_share of 1, the engine pays them, because only fully charged families stay opted out. So once the encoding switches, the paid caseload rises by the taper pool's opted-out mass, and so does the amount obr.child_benefit calibrates against, while the receipt's "paid" count (claims & ~opt_out, :452) understates what the engine pays. Fix, either: draw only from fully_charged when supports_opt_out is true (recording the shortfall); or report the engine-paid caseload next to the stage count in the receipt and confirm the Child Benefit rows still fit. |
| Nit: docstring wrap | Open | The module docstring still breaks at "income. This stage" (:6). |
| New: refreshed hashes | Fine | 4d03a7a4 updates the coverage manifest's source_manifest_sha256 entries. 6a981144 updates the synthetic UK graph's Child Benefit contract hash in the parity fixture. Both follow from the stage change. |
The capability check still matches policyengine-uk. #2140 merged on 5 October (fdad84ca) and ships in 2.121.0. policyengine_uk/parameters/gov/hmrc/child_benefit/opt_out_charge_share.yaml is the name this PR tests for ("opt_out_charge_share" in parameters.gov.hmrc.child_benefit.children, :268-269), and child_benefit again uses defined_for = "would_claim_child_benefit". That is the semantics registered_claims assumes.
Still dormant. uv.lock pins policyengine-uk 2.100.0, so releases keep the legacy encoding until the build moves to ≥ 2.121.0. That move would bring about twenty releases of benefit changes with it, so it belongs on #1095 with its own measurement arm. The taper fix above should land before it, so that the switch doesn't move the Child Benefit fit unannounced.
Ran locally at this head: test_uk_child_benefit_take_up.py and test_uk_release_input_coverage_manifest.py (31 passed). CI: integration-uk, lint, select-countries and wheels are green; engine-free and engine-uk/us were still running.
|
@vahid-ahmadi Both points in your latest review are addressed in The opt-out draw follows your option (a), using the installed Local validation passed the full UK group (164 tests) and synthetic integration (2 tests). After formatting tests/helpers, affected reruns passed 32 engine-free, 13 locked-engine and 13 opt-out-aware engine tests; production code and contract pins were unchanged. The synthetic dataset cases check actual model-paid families, children and cash at shares 1, 0.5, 0 and 1.1. Remote CI: The registered-claim export complements the gate merged in UK#2140. I added the rollout checklist to this PR, linked to #1095: pin the engine, rebuild or explicitly migrate exports, report shortages, and measure population model-paid counts/cash and OBR fit separately from broader upgrade effects. Production adjusted net income comes from UK |
Child Benefit payment flags previously erased registered opt-outs from the claim population, preventing charge-relief reforms from restoring their payments. This PR preserves registered claims when the installed engine supports opt-outs, complementing the independent claims gate merged in PolicyEngine/policyengine-uk#2140 for #2065. Older engines retain the existing payment-flag export.
It also addresses Vahid's latest review: the new contract's taper fallback could select families that the model still paid. The stage now reads and records the installed
opt_out_charge_share, requiring a positive charge fraction at least that share. At the default share of 1, only fully charged families qualify; an insufficient pool produces an explicit weighted shortfall. Partial shares, zero-charge boundaries, shares above 1 and invalid values have regression coverage. Legacy pool selection, taper fallback and random streams remain unchanged. The module-docstring wrap is fixed, and canonical declarations, synthetic fixtures and dependent hashes are synchronized.Validation
fdad84ca3fd2a81774211e888dc81565a64d4b22/core 3.32.16; tests check synthetic exported-dataset paid families, children and cash at shares 1, 0.5, 0 and 1.1, plus charge-relief restoration.Head:
9ee55c08815231a512b1b1cda601252d0005a260. Remote CI:All 10 checks passed on9ee55c0([CI run](https://github.com/PolicyEngine/microcosm/actions/runs/37327907801))..These synthetic checks supply matched adjusted net income directly. The production adapter calculates adjusted net income through a UK
Microsimulation; the stage then takes its maximum over members who are not eligible children, while the UKchild_benefitformula uses all members. Receipts audit draws before calibration. Population cash caseload and calibration fit therefore require measurement. This PR does not rebuild or publish population data or change dependency locks. Existing folded exports require a rebuild or an explicit migration before using the registered-claim contract.Rollout checklist
Related follow-up: #1095.