Skip to content

Write policyengine-uk's alcohol, tobacco and gambling duty inputs from the LCFS stage - #1381

Open
juaristi22 wants to merge 24 commits into
mainfrom
uk-excise-quantities-1359
Open

juaristi22 wants to merge 24 commits into
mainfrom
uk-excise-quantities-1359

Conversation

@juaristi22

@juaristi22 juaristi22 commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1359.

policyengine-uk charges alcohol, tobacco and gambling duty on household inputs that Microcosm never wrote, so all three duties scored zero on Microcosm data. With this PR the LCFS stage writes those inputs, levelled to published FY2024-25 quantities and duty bases. The gambling inputs sit on the households the Gambling Commission's survey says play, not only on the fifth the diary records. No spending column moves.

Updated 2026-10-25. A new step brings the two gambling keys to the Gambling Commission's participation (GSGB 2025), on the feed re-pinned to Chronicle 272c987 (PolicyEngine/chronicle#391), and main is merged in (#1205). #1383 locks policyengine-uk 2.151.0, the first release with the gambling duties (PolicyEngine/policyengine-uk#2590). This PR also carries the open items of Vahid's third review of #1383 (see below).

What changes

Five scratch shares in the LCFS chain. The chain draws them on the donor's diary lines, appended after its twenty targets (the eighteen spend targets and #1383's two heating fuels), so every existing draw is bit-identical:

  • tobacco within P602's off-trade lines;
  • on-trade alcohol within P611;
  • gambling stakes within P609;
  • the National Lottery's share of the stakes;
  • the betting and gaming share of the rest. Society and Irish lotteries pay no UK gambling duty and carry no base.

A household's parts are its shares of the totals it carries.

The new level_excise_quantities step. It shares each FY2024-25 national figure out to households in proportion to the spending that buys it, at prior weights. The figures are vendored as excise_facts.json:

  • Alcohol: litres of beer, cider, wine, spirits and other fermented products are HMRC's clearances, in litres of pure alcohol, over the engine's own strengths. The beer and cider draught shares come from the draught-relief clearances.
  • Tobacco: cigarettes a week, and grams a week of hand-rolling tobacco, cigars and other tobacco, are HMRC's clearances over 52 weeks.
  • Gambling: each of the 13 duty bases is HMRC's cash receipts for the duty, or Machine Games Duty's liabilities for the band, over the engine's FY2024-25 rate, so the engine charges back the published receipts. Lottery Duty's base follows the National Lottery stakes and the other twelve follow the betting and gaming stakes. General Betting Duty's base is split over its five inputs by the Gambling Commission's gross gambling yield for Great Britain.

Sparkling cider, heated tobacco and spread betting have no published quantity and keep the engine's zero.

The new top_up_gambling_participation step (María's ruling of 2026-10-25). The diary records a stake for about a fifth of the households that GSGB's four-week participation implies. Under any steady rate of play, a two-week diary sees at least half of the four-week players, so the gap is play the diary misses, not its window. Before the levelling, the step brings the two gambling keys to GSGB 2025:

  • Adult rates. Each adult's chance of playing in four weeks and in twelve months is GSGB's rate for its sex and age band (tables B.1 and B.3). A key's rate is the union of its activities (table B.9), taken as independent within the population that plays any of them.
  • Households. A household's chance combines its adults at theta, fitted on the donor's ratio of two-adult to one-adult household rates: 0.9 for the National Lottery (close to independent adults) and 0.08 for betting and gaming (close to one player per household).
  • The walk. Within nine strata (one, two, or three or more adults, by the youngest adult's age band), households without a stake are added by the gas connection walk's weight-fitting rule. The order is identity-keyed and drawn in proportion to a regularised donor propensity times the household's chance. The walk fills each stratum to its four-week share (regular players), then to its twelve-month share (occasional players).
  • Stakes. An added household's stake is a draw from the donor's positive stakes, scaled so the added regular players' mean equals the seen households'. An occasional player's stake is a tenth of that, an assumption until GSGB publishes how often people play. Households the diary saw keep their stakes.

The keys only re-share the bases, so every base still sums to its published figure, and the OBR rows below do not move.

Gate, export and diagnostics.

  • The new stage-health gate uk_stage_lcfs_consumption_excise_quantities recomputes every level from the vendored rows.
  • The new gate uk_stage_lcfs_consumption_gambling_participation recomputes the GSGB rates from the vendored rows and checks every stratum's shares, the stakes, and that the levelling read the topped-up keys.
  • The 24 inputs are on the export allow-list.
  • consumption_drift reports their totals at design and final weights.

Five measured OBR rows, held out of the objective. At prior weights, the engine's duty on the national levels against OBR's FY2024-25 receipts:

  • beer and cider £4.050bn against £3.746bn (+8.1%), wine £4.808bn against £4.686bn (+2.6%), spirits £4.205bn against £4.113bn (+2.2%), all accrued;
  • tobacco £8.167bn against £7.927bn (+3.0%), in cash;
  • betting and gaming £3.619bn against £3.616bn (+0.1%), in cash. This row is an identity, since the bases are receipts over the engine's rates, so it has its own exclusion reason and is measured only for drift.

An engine test pins each gap. Each row is measured at FY2024-25 (measurement_period: 2024, require_matching_fact_period, a matched_period hold), because policyengine-uk holds these inputs flat after 2024 (PolicyEngine/policyengine-uk#2596).

Two feed re-pins:

Read before reviewing

  • Net income falls by the three duties. household_tax rises by about £24.8bn at FY2024-25, so household_net_income, and every net-income, decile and inequality output read from it, falls by the same on Microcosm data. HBAI incomes and the VAT base don't move.

  • Alcohol and tobacco duty is a fixed multiple of recorded spending. The levels are national while spending and incidence stay at diary level (the ruling of 2026-10-23), so each household's duty is one multiple of its own spending: tobacco 2.22 times tobacco spending, alcohol 0.41 times alcohol spending.

  • Tobacco duty sits on fewer households than smoke. 8.2% of households carry it, against 11.9% of UK adults who smoked in 2023 (ONS). The parent totals keep the donor's incidence; the loss is in the share draws.

  • Gambling duty now sits on GSGB's participation. On a stage-only run on the real spine, every stratum lands on its target. 62.7% of households carry Lottery Duty and 29.8% the other gambling duties, against 10.7% and 2.0% on the diary's draws. At prior weights:

    • betting and gaming: £2.69bn over 29.8% of households, about £430 a year for each regular household and £43 for an occasional one, where before it was about £4,700 each on 2.0%;
    • Lottery Duty: £0.93bn over 62.7%, about £59 and £6, where before it was about £300 each on 10.7%.
  • The top-up carries the diary's income gradient. Carrying rises with equivalised income: the National Lottery from 52% of households in the bottom decile to 70% in the top, and betting and gaming from 24% to 36%. Age and sex come from GSGB; income, tenure, region and spending come from the donor propensity. If the diary misses online play more, and online play rises with income, the top-up leans low in income.

  • Regions, the out-of-sample check, match only partly. Region is not a target. Comparing each region with its own national level, London is the lowest in both keys, as in GSGB (frame 0.94 and 0.89, GSGB 0.81 and 0.83). Elsewhere the pattern follows GSGB weakly: the correlation across the 11 GB regions is 0.41 for the National Lottery and 0.11 for betting and gaming, whose propensity rests on about 190 donors. Northern Ireland takes GSGB's Great Britain rates.

  • Alcohol volume follows spending. No on-trade volume share can be archived (MESAS has no fixed file), so a pound buys the same volume in a pub as in a shop. Against a published on-trade share of about 27 percent, measured on the 2024-25 diary:

    • a pub pound gets about 1.6 times the volume it buys, and a shop pound about 0.78 times;
    • about 11 percent of the volume sits with other households;
    • the stage notes and receipts carry the decile effect.

    This follows the ruling of 2026-10-23.

  • Other known biases, in the stage notes:

    • other tobacco includes e-cigarette refills, so vapers carry tobacco duty;
    • the draught shares are national constants;
    • the remote UK horse-racing base includes non-UK racing, which understates the 2027 rise.
  • Gambling bases are receipts over rates, not the Commission's yield, a deviation from the plan. The yield covers Great Britain only and includes undutiable betting; here it only splits General Betting Duty.

  • The re-pin moves road fuel. Chronicle Remove the stale TANF degenerate-exclusion (P0) + cross-register and take-up preflights #384 replaced the June Consumer Trends edition with September's. Calendar-2024 household road fuel (07.2.2) falls from £37.025bn to £35.460bn (−4.2%); the stage levels petrol and diesel to that figure, and ons.household_road_fuel_expenditure binds it. The measured calendar-2025 electricity and gas rows move to £28.8bn and £19.0bn. No other compiled target moves, and the local surface is byte-identical.

Evidence

experiments/1359-excise-quantities-receipts.md and docs/evidence/uk-excise-quantities-1359/ (aggregates only) hold:

  • Stage-only runs on a real spine (78,440 households), all on 2.151.0:
  • The national reconciliation: the engine's duty on the levels against each OBR row.
  • Who carries the duty: the multiples, the incidence against ONS and the Gambling Commission, and where the frame loses it.
  • Theta (scripts/theta_calibration.py, outputs/theta_calibration.json): fitted on the donor with the committed declaration and vendored rows, 0.897 for the National Lottery and 0.081 for betting and gaming. The three-or-more-adult ratio is the check, and it fits less well (fitted 1.97 and 1.89 against the diary's 1.83 and 1.66).
  • The top-up on the real spine (78,440 households, stage-only, on 2.151.0): all nine LCFS gates pass, and every stratum lands on its four-week and twelve-month shares. On the final head, after the anchor fix (b2035eb30), the participation is unchanged, the added regular players' mean stake equals the seen households', and the occasional players' is about a tenth of it (outputs/stage_run_gsgb.json).
  • The tests, run locally and kept to the touched surfaces:
    • re-pin surface, 245;
    • declaration surface, 863;
    • LCFS stage and gate, 294;
    • engine on the locked 2.151.0, 65 of 65;
    • H2 parity, 3 of 3;
    • the integration smoke and ci_test_plan verify;
    • for the review round: the LCFS stage 132, the surfaces the split moves 732, the engine excise checks 10 and engine H2 and coverage 54, all passing;
    • after the rebase: the stage, spec and declaration 931, the re-pin and targets 353 (7 feed-gated skips), the withholding, gates, Orrery and build CLI 631, and the engine 64, all passing.
    • for the GSGB top-up and the 272c987 re-pin: the stage, gambling, stage-health, spec, contract, battery, gates and FRS spine files with the two pin tests, 1,161 passing, and ci_test_plan verify. Run alone afterwards: the H2 parity and excise engine tests, 11 passing; the pin and local census tests, 27 passing; and the high-value home stock's feed fingerprint check, passing. The full vendoring drift check, which loads the 7.5 GB feed twice, was not run, as at the 200bf6d re-pin; the vendored files come from that tool and feed.

Vahid's third review of #1383

#1383 merged with three items of that review open, so they land here (98a09827a):

  • 33: the withholding check refuses a local row that names no contract target, where it used to leave the row out of every family block. A contract test keeps the parentless local families free of a higher-grain row of their measurement.
  • 32: a build computes the withheld rows once. The national path's diagnostics take the national gates' set, and the Orrery result summary takes the set a gate on the same result recorded. A local surface's check logs how long it took.
  • 34: on the FRS 2024-25, fewer than ten code-6 amounts sit above Widowed Parent's Allowance's rate at no Bereavement Support Payment rate.

Open items

  • Decisions for María:
    • the gambling keys: settled 2026-10-25 as the split, brought to GSGB participation;
    • whether the top-up's income gradient and regional match are good enough, or whether it should also rake to GSGB's regional rates;
    • whether to level tobacco and gambling spending to Consumer Trends 02.2 and 09.4.3, which would move the VAT base.
  • Signatures. The five measure exclusions (expiring 2027-01-08) and the hold (expiring 2026-12-11) were approved on 2026-10-24 as drafted. The reasons restated for the review await María's approval.
  • Measurement arms. Two arms, a control on main and this head, each a spine plus a national calibration. They give the duty against OBR at final weights, each duty by equivalised decile beside ONS Effects of taxes and benefits, and the drift at final weights.
  • Scoring register. The uk-candidate-eval scoring register is re-frozen before the next national solve.
  • Follow-ups:
    • a prevalence redraw for tobacco (gambling is done here);
    • a key for Vaping Products Duty;
    • draught relief keyed to on-trade spending;
    • correcting the betting and gaming binding note at the next change to the targets contract.

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

@juaristi22 thanks, this is my review at e07424ddf.

Verdict. The engineering is careful. The three shares are appended to the chain, so 17 of main's 20 columns stay byte-identical. Every level is recomputed from the vendored rows, and every unit and divisor is checked against the engine. The disclosure handling is clean. I found one must, and it's a merge gate, not a method error: this has to land after #1383 and be regenerated on top of it. The shoulds are about what the levelled inputs do at household level. Duty is levelled to receipts, but tobacco and gambling spending stay at diary level and diary incidence is kept, so every tobacco household now pays about 2.1 times its recorded tobacco spending in duty, and every gambling household about 1.05 times its recorded stakes. That duty sits on a much smaller share of households than smoke or gamble. The national totals can be read off today without the measurement arms, and I give them below. Alcohol comes out 4% above OBR and tobacco 3% above. Gambling matches by construction, so that row validates nothing.

What I ran

  • Worktree at e07424ddf, uv sync --all-packages --locked --extra uk (policyengine-uk 2.147.2).
  • Engine-free tests of every touched area: the 13 touched engine-free files, about 890 tests, all pass.
  • test_uk_excise_quantity_inputs.py on the locked 2.147.2: 5 fail (the gambling checks), as the body says. On a --with policyengine-uk==2.151.0 overlay, all 9 pass.
  • In CI the engine-uk job runs with --maxfail=1. It stops at the first gambling failure, about a quarter of the way through the file list, so every engine-uk file after test_uk_excise_quantity_inputs.py has not run in CI at this head. That includes the coverage-manifest tests and H2 parity. Your local runs cover them, but CI won't until the lock moves.
  • tools/ci_test_plan.py verify: ok. Other CI jobs at the head: engine-free, engine-us, integration-uk, lint and wheels pass.
  • National reconciliation. I resolved the step's levels with the PR's own excise_levels() on the committed declaration. Then I ran them through policyengine-uk 2.151.0 as one household that holds the national totals. Every duty is linear in its inputs, so this gives the frame's duty at prior weights. Table in finding 2.
  • A trial merge with Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 at 74a2a378. Chronicle 200bf6d is the merge commit of chronicle#384 on main.
  • Checked against primary sources: the pe-uk 2.151.0 gambling, alcohol and tobacco variables and docs/engineering/gambling-duties.md; household_tax and household_net_income; ONS Adult smoking habits in the UK: 2023; the Gambling Commission's GSGB annual report 2025.
  • Not run: the stage on a spine, the measurement arms, and any decile split. These need the licensed LCFS tabs and a built frame, which I don't have for this branch. I also didn't run the feed-gated vendoring checks (they need the full feed).

What is generated and what I read

  • Read in full: the code and method. That is lcfs_consumption.py (+1,157), stage_health.py, the sources.yaml declaration, the engine test, test_support/.../uk_excise_facts.py, the exclusions and holds, the diagnostics, graph, terminal-gate and contract changes, the experiment note, the evidence README and outputs, and the changelog.
  • Spot-checked:
    • excise_facts.json (4,812 lines). The resolved levels agree with what we verified on chronicle#384. For example, beer plus cider come to 215.6m litres of alcohol, against OBR's 216.8m. Cigarettes are 13,577.9m sticks, and cigars plus other tobacco are 395.1 plus 301.5 tonnes.
    • ons_household_expenditure_facts.json, through the road-fuel row.
    • The re-pinned resources. In the ones I opened, only the feed identity changes.
    • The H2 fixture, which is synthetic (tools/graph_uk_spine_fixture.py).
  • Not read: sources.schema.json, target_reference_membership.json, the compile-parity receipt, the coverage manifest line by line, and most of the 820 new lines in test_uk_lcfs_consumption.py.

Findings

  1. must (merge gate): land Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 first, then regenerate this on top.
  2. should: the duty totals can be stated now, and alcohol and tobacco come out above OBR.
    • Levels are set at prior weights, so these are the frame's design-weight duties under 2.151.0 for FY2024-25:

      Row Engine on the levels OBR FY2024-25 Gap
      Beer and cider (accrued) £4.050bn £3.746bn +8.1%
      Wine and other fermented (accrued) £4.807bn £4.686bn +2.6%
      Spirits (accrued) £4.205bn £4.113bn +2.2%
      Alcohol total £13.062bn £12.545bn +4.1%
      Tobacco (cash) £8.167bn £7.927bn +3.0%
      Betting and gaming (cash) £3.619bn £3.616bn +0.1%
    • The beer gap is what the exclusion reason predicts: one strength per product, and no small producer relief or low-strength band.

    • Could you put these figures into the exclusion reasons and the experiment note now? Then the final-weight arms only add the drift.

    • Could you also add an engine test that runs the committed declaration's national levels through the engine and pins each gap within a tolerance? Today nothing would notice if a rate or strength change moved them.

  3. should: the betting and gaming row is an identity, not a check.
    • Each gambling base is receipts over the engine's own rate, so the engine gives back the receipts by construction (+0.1% above).
    • The same holds for the casino base. The engine's casino_effective_rate is itself HMRC Gaming Duty over the Commission's GB casino yield, so that base is just the Commission's £702m.
    • The shared exclusion reason says the gap comes from "the engine's rate schedule ... the reliefs and products the engine leaves out and the timing of receipts". That's true for alcohol and tobacco. For gambling the only thing that can open a gap is weight drift.
    • Please give that row its own reason, and don't count it as evidence that the gambling inputs are right. Their test is who carries them (finding 5).
  4. should: duty now exceeds recorded spending for every tobacco and gambling household.
    • Each input is proportional to one spending key, and duty is linear. So a household's duty is a fixed multiple of its own spending:
      • tobacco: £8.17bn of duty on £3.80bn of frame tobacco spending, 2.15 times for every household with any;
      • gambling: £3.62bn on £3.46bn of stakes, 1.05 times, with dutiable bases of about 6.4 times stakes;
      • alcohol: £13.06bn on £31.19bn, 0.42 times.
    • That's the ruling to level quantities but keep spending at survey level, applied to goods the diary under-records by a factor of two to seven. It is internally inconsistent at household level: no household can pay more duty than it spends.
    • It also means VAT on tobacco and alcohol stays on the survey base while the duties sit on receipts.
    • Could the stage notes and changelog state these multiples plainly? Please also reconsider levelling the tobacco and gambling spending to Consumer Trends 02.2 and 09.4.3, as road fuel is levelled to 07.2.2. That would move the VAT base, which is why I'd put it to a decision rather than ask for it here.
  5. should: the duty sits on far fewer households than smoke or gamble.
    • On the frame, 8.1% of households carry tobacco, a fifth below the donor's 10.3%. ONS counts 6.0 million adults (11.9%) who smoked in the UK in 2023, the latest bulletin I checked.
    • 15.0% of households carry gambling, against 47% of adults who gambled in the past four weeks (GSGB 2025).
    • The units differ: households against adults, and a two-week diary against a four-week recall. But the share of households with any smoker or gambler is higher than the adult rate, not lower. So these comparisons understate the gap.
    • With levels fixed, low incidence raises the duty per carrying household by the same factor. That drives the decile shares and winners and losers of any excise reform.
    • The ruling keeps diary incidence. Before merge, could the arms report:
      • each duty by equivalised income decile, beside ONS Effects of taxes and benefits;
      • why the share draw loses a fifth of tobacco incidence between donor and frame?
    • A prevalence redraw like redraw_zero_road_fuel is the natural follow-up if the deciles look off.
  6. should: every gambling base follows one stakes total.
    • Remote gaming (£5.54bn base, £1.16bn duty), Machine Games Duty and betting are all shared by total stakes. The lottery base alone (£7.77bn) is more than the diary's total stakes (£3.46bn), and the lottery is the most common activity in the GSGB, so lottery stakes very likely dominate that key. National Lottery players and online casino players are very different populations.
    • The diary keeps the lottery lines (c94316, c94319) apart from bookmaker betting and the rest.
    • Drawing two shares, lottery and non-lottery, and keying lottery duty to the first and everything else to the second would cost one more chain target. Please check that each still rests on 30 or more donors.
  7. should: vapers carry tobacco duty.
  8. should: household_net_income falls by about £24.8bn, not just household_tax rising.
    • policyengine-uk's household_net_income subtracts household_tax. So every net-income, decile and inequality output on Microcosm data drops by the three duties: £13.06bn + £8.17bn + £3.62bn at FY2024-25.
    • This is the pattern fuel_duty already follows, just at a larger scale, so it isn't a new design choice. VAT stays out of household_tax, so the duties are deducted and VAT isn't.
    • HBAI poverty is unaffected, as you say. The changelog should name household_net_income, since that's what the app's distributional charts read.
  9. nit: the draught shares are national constants. Every beer and cider household gets 39.7% and 23.3% draught relief, including off-trade-only households. Keying draught beer to on-trade alcohol spending would put the relief where it's used. This is a refinement for later, not this PR.
  10. nit: remote UK horse racing is overstated. The remote horse-racing share includes non-UK racing (documented). From 1 April 2027, when other remote betting goes to 25%, that puts too much of the base in the 15% bucket. It understates the 2027 rise, which only matters for projections or reforms that reach FY2027.

What looks right

  • Chain order. Appending the shares after the eighteen spend targets, with keyed uniforms, leaves every earlier draw unchanged. The stage run confirms it: 17 of 20 columns are identical, and petrol and diesel move by exactly the −4.227% Consumer Trends revision.
  • Strict declaration. The step refuses an undeclared input, a wrong unit, a strength without litres of alcohol, rows it doesn't take in full, and a rate outside (0, 1]. The engine test ties every strength and rate to the engine parameter at 30 April.
  • The gate recomputes every level from the vendored rows through the declaration, never from the receipt. It checks positivity follows spending exactly, and holds each share in [0, 1] on a 30+ donor band.
  • The General Betting Duty mapping. It matches pe-uk 2.151.0. Horse and dog pools sit in General Betting Duty, other pools in Pool Betting Duty, and on-course and spread betting are left at zero.
  • Calibration. The five OBR rows are held out of the objective at FY2024-25 with a matched-period hold, so calibration targets aren't used as validation.
  • Disclosure. It's fine in everything committed. Counts are banded ("30+", "<10"), there are no donor minima, maxima or records, and the only household count is the donor's total sample size, not a cell. The H2 fixture is synthetic.
  • The re-pin. It carries only feed identity outside the ONS file and the new excise facts. The road-fuel, electricity and gas moves match what we checked on chronicle#384.

🤖 Generated with Claude Code

@juaristi22
juaristi22 force-pushed the uk-excise-quantities-1359 branch from e07424d to 352af5e Compare October 24, 2026 10:47
@juaristi22
juaristi22 changed the base branch from main to uk-pe-uk-bump-2572 October 24, 2026 10:47
@vahid-ahmadi

Copy link
Copy Markdown
Contributor

@juaristi22 round 2, at 352af5e41. You've restacked onto #1383 at 74a2a378e and regenerated, so the merge gate is met. The restack also moves the household-level multiples in finding 4, and both of the ones that matter get worse. The shoulds are otherwise as they were: the exclusion reasons and the changelog are byte-identical to round 1.

What I checked

Status of round 1

  1. must, land on Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 and regenerate: resolved. The branch is stacked on Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383. The coverage manifest now lists all 13 gambling inputs under lcfs_consumption outputs, where Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383's head listed none, and the H2 fixture and the digests are regenerated. All that's left is merge order: Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 first, then a CI rerun once this is retargeted to main.
  2. should, state the national totals and pin the gaps: open. The reasons and the experiment note still give no figures (alcohol +4.1%, tobacco +3.0%, gambling +0.1% at prior weights). There's still no engine test that runs the declared levels through the engine.
  3. should, the gambling row holds by construction: open. The betting and gaming row still shares the alcohol and tobacco reason.
  4. should, duty against recorded spending: open, and the restack makes it worse (see new finding A).
  5. should, too few carrying households: open. On the restack, tobacco is 8.2% of households (donor 10.3%) and gambling 14.6% (donor 17.0%).
  6. should, one stakes key for all gambling: open. It weighs more now: the lottery base alone is 3.4 times the frame's total stakes.
  7. should, vapers carry tobacco duty: open. It's not yet in the stage notes.
  8. should, household_net_income falls about £24.8bn: open. The changelog still names only household_tax.
  9. nit, national draught shares: open. Fine to leave for later.
  10. nit, remote UK horse racing: open. Fine to leave for later.

New finding

A. should: the restack cuts the frame's gambling stakes by a third, so every gambling household now pays 1.6 times its stakes in duty.

  • Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383's division levelling brings recreation down from £183.3bn to £148.9bn at prior weights. Gambling stakes are drawn within that division, and they fall further, from £3.46bn to £2.27bn (−34%). Their share of the division goes from 1.9% to 1.5%, which now matches the donor.

  • The duty levels don't move, so the multiples do:

    Key Frame spending, round 1 Restack Duty Duty over spending, round 1 Restack
    Tobacco £3.80bn £3.68bn £8.17bn 2.15 times 2.22 times
    Gambling stakes £3.46bn £2.27bn £3.62bn 1.05 times 1.59 times
    Alcohol £31.19bn £31.97bn £13.06bn 0.42 times 0.41 times
  • The lottery base (£7.77bn) is now 3.4 times stakes, up from 2.2, and the remote gaming base (£5.54bn) is 2.4 times.

  • This is the right direction for recreation as a whole. But it shows that the gambling inputs follow whatever the chain does to the division, with no anchor of their own. So the case in round-1 finding 4 for levelling gambling spending to Consumer Trends 09.4.3 is stronger now. Please restate the multiples from the restacked run in the stage notes and changelog, wherever the round-1 figures would otherwise appear.

Merge order

#1383 first, and this PR after it. Nothing in #1383 since 74a2a378e conflicts.

🤖 Generated with Claude Code

@juaristi22

Copy link
Copy Markdown
Collaborator Author

@vahid-ahmadi thanks for the review. This round is three commits on the restacked branch: 9e143959b (the gambling keys and the stage notes), 303d1abf0 (the engine reconciliation and the exclusion reasons) and 03427bed2 (the receipts and changelog). Point by point:

  1. Land Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 first and regenerate on top. Done before this round. The branch is restacked on Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 (74a2a378e), and the manifests, digests, coverage manifest and H2 fixture were re-derived with the tools. The coverage manifest's lcfs_consumption family lists the 13 gambling inputs as stage outputs. With the lock at 2.151.0 the gambling checks pass, so engine-uk no longer stops at them. This PR is stacked, so its CI runs once Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 merges. Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 has since gained 3ce1fca6c (the cross-geography disclosure fix in the diagnostics). It touches no spec or derived file and merges cleanly with this branch; on the trial merge, the diagnostics, full-gate and graph-terminal tests pass (145 of 145).

  2. Duty totals. Your table reproduces on the restacked head; wine is £4.808bn. The figures are in the exclusion reasons and the experiment note. test_the_national_levels_sit_at_the_stated_gaps_from_obr runs the committed declaration's levels through the engine and pins each gap to half a percentage point. It reads each row's variables from its own binding, and OBR's value from the national compile-parity receipt.

  3. Betting and gaming is an identity. It has its own reason now, which says the row is measured for drift and is no evidence. Its £3m gap is Machine Games Duty's liabilities (£612.3m) against its cash receipts (£609.2m). HMRC's receipts for all the duties equal OBR's £3.616bn.

  4. Duty above recorded spending. The stage notes and the changelog state the multiples. On the restacked head they're larger than at e07424ddf, because Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383's division levelling takes recreation from £183bn to £149bn:

    • tobacco is 2.22 times frame tobacco spending (£8.17bn on £3.68bn);
    • alcohol is 0.41 times (£13.06bn on £31.97bn);
    • gambling depends on the keys, in 6.

    Levelling tobacco and gambling spending to Consumer Trends 02.2 and 09.4.3 is with María as a decision, since it moves the VAT base.

  5. Incidence. Decomposed on the stage run (outputs/excise_incidence.json). The parent totals keep almost the donor's incidence: alcohol and tobacco spending is positive for 52.7% of frame households against 54.6% of donors. The loss is in the share draws:

    • tobacco is drawn for 15.5% of the frame's households with alcohol and tobacco spending, against 18.9% of donors;
    • gambling stakes follow the same pattern: 14.8% against 17.5% of households with recreation spending.

    A share comes out zero more often on the frame than on the donor, so the regime gate of the share draw is where to look. The arms will report each duty by equivalised decile beside ETB. The prevalence redraw is the follow-up.

  6. Gambling keys. Done, with one change. The rest of the stakes includes society lotteries (c94313) and the Irish Lottery (c94315). Neither pays UK gambling duty, and more than half of the donors with non-lottery stakes play only those. So there are two appended shares:

    • the National Lottery's share of the stakes (c94316, c94319), which Lottery Duty follows;
    • the betting and gaming share of the rest (c94311, c94312, c94314), which the other twelve bases follow.

    Each rests on 30 or more donors. The catch is concentration. Betting and gaming stakes are positive for 3.6% of donors and 2.0% of frame households. So the £2.69bn of betting and gaming duty lands on 2% of households, about £4,700 a year each, 6.4 times their stakes. With one key, £3.62bn sat on the 14.6% with any stakes, about £850 each. Lottery Duty now sits on the 10.7% with National Lottery stakes, about £300 each. Revenue doesn't move, but any betting or gaming reform's distribution does, so María is deciding which keys this PR ships.

  7. Vapers. In the stage notes as a known bias.

  8. household_net_income. The changelog names it, with the £24.8bn at FY2024-25. The engine tests also check that net income loses the duties.

  9. Draught shares. In the stage notes; keying draught relief to on-trade spending is a follow-up.

  10. Remote UK horse racing. In the stage notes, with the understated 2027 rise.

Tests, on the touched surfaces:

  • the LCFS stage, 132 of 132;
  • the surfaces the split moves (stage health, the spec and its re-emission, diagnostics, coverage, battery bindings, the contract mirrors, engine-free H2), 732 of 732;
  • the engine excise checks with the reconciliation, 10 of 10;
  • H2 parity and coverage on the engine, 54 of 54;
  • tools/ci_test_plan.py verify, ok.

A stage-only run of the split on a real spine leaves 33 of the 46 columns byte-identical to the restacked head. Only the 13 gambling inputs move, and all eight LCFS gates pass.

🤖 Generated with Claude Code

juaristi22 and others added 16 commits October 24, 2026 17:39
microcosm#1359, step A1. The chain draws two shares after its twenty
targets (the eighteen spend targets and the two heating fuels): tobacco's share of the off-trade alcohol and tobacco that
make up P602, and on-trade alcohol's share of restaurants and hotels
(P611). The derive step declares them and computes them on the donor from
the adult diary lines (LCF 2024-25 Volume F), with refunds as zero, a zero
denominator as zero and the drinks past P611's one-decimal rounding held
at one. A household's tobacco, off-trade and on-trade alcohol spending is
its share of the total it drew, so the parts sum to the totals and no
total, the VAT base included, moves; appended last, the shares leave the
twenty draws bit-identical (tested against the chain without them).

The shares and the spending they give are scratch, on no table and outside
the support clip. A report-only excise_shares receipt sets each share on
the frame beside the donor's, in bands and withheld shares only. The spec,
schema and source_stages.json declare the shares; the coverage manifest's
source hash and the H2 fixture tool's synthetic donor follow. The H2
fixture itself is regenerated once, at the end of the PR.

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

microcosm#1359, step A2. The step gives each household its part of each
national excise quantity in proportion to its spending at prior weights:
alcohol volumes by its off- and on-trade alcohol spending (in proportion
to the two, or each channel's published share of the volume to that
channel's spenders), tobacco clearances by its tobacco spending. Each
policyengine-uk input it writes names its published row, unit, share of
the row and, for hectolitres of alcohol, the strength (with the engine
parameter it mirrors) that turns them into litres of the product. The
fiscal year is the one policyengine-uk charges excise over, so no bridge
is needed; weekly inputs are the annual quantity over the engine's 52
weeks. Outputs that share a row must take all of it.

The one-factor scaling level_road_fuel used is now _scale_to_level, shared
by both and unchanged for road fuel. The excise_quantities stage-health
check recomputes every level from the vendored rows through the stage's
own declaration and holds the frame totals, factors, incidence, basis,
share range, share support and the published total's residual beside
its lines to the receipt. Nothing declares the step yet: its facts wait
for PolicyEngine/chronicle#381, so its tests read synthetic rows
(test_support/microcosm_build/uk_excise_facts.py), never publisher ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microcosm#1359, step A3. The export-surface gate checks a column only
against the enhanced-FRS reference, so an engine test holds the excise
step to policyengine-uk 2.145.0: every input it can write is a household,
yearly, float input with no formula; the engine's 52 weeks are the step's;
each declared strength equals the engine parameter it names; and alcohol
and tobacco duty are positive, proportional to the quantities and in
household tax for 2024, 2025 and 2026.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microcosm#1359. Chronicle applies the publisher's scale, as its
hydrocarbon-oils package does: OBR's thousand hectolitres of alcohol,
million sticks and thousand kilograms arrive as litres of pure alcohol,
sticks and kilograms, with the published unit kept only as a dimension
(PolicyEngine/chronicle#381, branch uk-excise-facts-381). The step
declared the published units and scaled by 1e5 and 1e6, which would have
put the alcohol volumes 1e5 too high. It now declares the stored units
(litre_of_pure_alcohol, litre, stick, kilogram, scaled by one or a
thousand) and refuses a row whose own unit is not the declared one, so a
value can no longer be read in the wrong scale. The synthetic facts move
to the stored units with the same levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microcosm#1359. María ruled on 2026-10-23 to keep spending weights for the
alcohol volume: no on-trade share of volume can be archived, since MESAS
is a dashboard of NielsenIQ and Circana data with no fixed file. The rule
sets no level, but a pound buys the same volume in a pub as in a shop,
where a unit costs about three times more. The rule's comment records the
measured size on the LCFS 2024-25 diary against a quarter of the volume
sold on-trade: a pub pound carries about 1.6 times its volume and a shop
pound about 0.8 times, about a tenth of the duty sits with other
households, and the bottom three income deciles bear about 7 % too little
of it and the top decile about 7 % too much.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microcosm#1359, step C1. A third share follows the two excise shares in
the chain: gambling stakes over recreation (P609), from the seven COICOP
09.4.3 stake lines (football pools, bingo, other lotteries, bookmaker and
tote betting with gaming machines, the Irish Lottery, National Lottery
scratchcards and draws; LCF 2024-25 Volume F, Part 2 rows 1631-1649).
Like the others it is computed on the donor from the weekly lines, clipped
at one because the tab stores P609 to one decimal place, appended last so
no earlier draw moves, and scratch: a household's stakes are its share of
the recreation it drew, so the total and the VAT base keep their draw.
The synthetic donors and the H2 fixture tool carry stake lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m#1359)

level_excise_quantities now also writes:
- beer_draught_share and cider_draught_share, each the clearances under
  draught relief (alone or with small producer relief) over all of that
  product's clearances, in litres of pure alcohol;
- policyengine-uk's thirteen gambling duty bases
  (PolicyEngine/policyengine-uk#2590, from 2.151.0). A single-duty base is
  HMRC's receipts for the duty, or Machine Games Duty's liabilities for the
  band, over the engine's rate, so the engine charges back the published
  receipts. General Betting Duty's base, its receipts over the fixed-odds
  rate, is split over its five inputs by the Gambling Commission's gross
  gambling yield for Great Britain. Every base goes to households by their
  drawn gambling stakes at prior weights, the alcohol and tobacco rule.

A gambling series may pin dimensions of its own, such as the cash
accounting basis. The vendored resource is renamed excise_facts.json: it
carries HMRC's clearances, receipts and liabilities and the Commission's
yield. The stage-health gate checks the receipt's draught shares against
the declared rows. The engine test checks every new input and rate
against policyengine-uk, and that gambling duty is linear in the bases
and reaches household tax; it needs policyengine-uk 2.151.0 or later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…microcosm#1359)

The feed moves from abf0921 to 200bf6d55, Chronicle main after
PolicyEngine/chronicle#384 (1,502,291 rows, up 2,990; the schema digest
is unchanged). It brings HMRC's alcohol and tobacco clearances, betting
and gaming duty receipts and Machine Games Duty liabilities, the
Gambling Commission's industry statistics for Great Britain and OBR's
excise lines. It also replaces the June 2026 Consumer Trends edition
with September's.

- excise_facts.json vendors the 98 FY2024-25 rows the LCFS excise step
  reads. Every other vendored resource carries the same rows under the
  new feed identity, except ons_household_expenditure_facts.json, whose
  93 rows take the September values.
- Five OBR rows join the national contract, held out of the objective:
  beer and cider, wine and spirits duties accrued, and tobacco and
  betting and gaming duties cash. Each is measured at FY2024-25
  (measurement_period 2024, require_matching_fact_period, a
  matched_period hold), and diagnose_uk_consumption_taxes.py measures
  them at that period. The exclusions expire on 2027-01-08 and the hold
  on 2026-12-11.
- The September revisions move three compiled values. Calendar-2024
  household road fuel (07.2.2, bound) goes from GBP 37.025bn to
  GBP 35.460bn; the measured calendar-2025 electricity and gas rows also
  move.
- The local surface is byte-identical but for the membership's feed
  label.

The national references and parity were regenerated from a cut of the
pinned artifact without its constituency, MSOA and county rows. The
local ones came from a cut to the constituency, local-authority and
non-small-area statistical-scope rows. docs/uk-chronicle-feed-repin.md
records the entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…icrocosm#1359)

The lcfs_consumption stage now declares level_excise_quantities on the
vendored excise_facts.json. It reads HMRC's FY2024-25 clearances of beer,
cider, wine, spirits and other fermented products at the engine's
strengths, the beer and cider draught shares, the four tobacco products
and the thirteen gambling duty bases. Each is shared out by the drawn
spending at prior weights, with alcohol volume following spending.

- The stage writes 24 more household outputs, all nonnegative, after its
  existing ones and in declared order. The schema, the allowed operation
  kinds and the graph's typed cells know the step, and the export
  allow-list (code and gates.json) carries the inputs.
- The stage-health gate uk_stage_lcfs_consumption_excise_quantities
  recomputes every level from the vendored rows. It joins the spine gate
  scope and the microcosm-data contract mirrors, whose digests are
  re-pinned.
- consumption_drift reports each quantity and base at design and final
  weights.
- The stage notes document the spending rule's lean towards on-trade
  drinkers and the gambling mapping.

The engine checks of the gambling inputs need policyengine-uk 2.151.0 or
later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-pin (microcosm#1359)

lcfs_consumption declares 24 more outputs, which flow into the inputs of
the stages after it. Every stage that declares a re-vendored resource
moves its stage_contract_sha256. The synthetic LCFS donor carries the
alcohol, tobacco and gambling diary lines the shares read. The sources
are unchanged, and H2 parity passes on policyengine-uk 2.151.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
experiments/1359-excise-quantities-receipts.md gives:
- the national figures the step levels to;
- a stage-only run on a real spine against microcosm#1383's head;
- the re-pin's value moves;
- the tests.

docs/evidence/uk-excise-quantities-1359/ holds the scripts and the
sanitized outputs: aggregates only, with no column digests or donor
extremes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…crocosm#1359)

Vahid's review: every gambling base followed one stakes total, so National
Lottery players carried remote gaming and betting duty. The chain now draws
two more shares after the gambling stakes, appended so every earlier draw
stays bit-identical:
- the National Lottery's share of the stakes (c94316, c94319);
- the betting and gaming share of the rest (c94311, c94312, c94314).

Lottery Duty's base follows the National Lottery stakes and the other twelve
bases follow the betting and gaming stakes. Society lotteries (c94313) and
the Irish Lottery (c94315) pay no UK gambling duty and carry no base. Each
share rests on 30 or more donors.

The stage notes also state the known biases the review asked for:
- each duty as a multiple of the spending that shares it out;
- incidence against published prevalence, and where the frame loses it;
- vaping inside other tobacco;
- the national draught shares;
- the overstated remote UK horse-racing base.

A stage-only run on a real spine leaves 33 of the 46 columns byte-identical
to the restacked head. Only the 13 gambling inputs move, and all eight LCFS
gates pass. source_stages.json, the coverage manifest and the H2 fixture
re-derive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocosm#1359)

Vahid's review: nothing noticed if a rate or strength change moved the
duty the levels give. A new engine test runs the committed declaration's
national levels through policyengine-uk as one household. Every duty is
linear in its inputs, so this is the frame's duty at prior weights. The test
pins each OBR row's gap at FY2024-25:
- beer and cider +8.1%, wine +2.6%, spirits +2.2%;
- tobacco +3.0%;
- betting and gaming +0.1%.

The proportionality tests also check that household_net_income loses the
duties.

The five measure exclusions now state those figures. Betting and gaming
gets a reason of its own: its bases are receipts over the engine's rates,
so the row is an identity, measured for weight drift and not as evidence.
Its gap is Machine Games Duty's liabilities against its cash receipts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The receipts answer Vahid's review with aggregates only:
- the engine's duty on the national levels against OBR's FY2024-25 rows,
  with betting and gaming shown to be an identity;
- each duty as a multiple of the spending that shares it out, and the share
  of households carrying it, against ONS and Gambling Commission prevalence;
- where the frame loses incidence against the donor: in the share draws, not
  the parent totals;
- the gambling split's stage run, and how it concentrates the betting and
  gaming duties on the diary's incidence.

The changelog names household_net_income, which falls by the three duties
(about £24.8bn at FY2024-25), and states the multiples.

docs/evidence/uk-excise-quantities-1359/ gains the reconciliation and
incidence scripts and their outputs, and the split run with its comparison.
Every donor share in them rests on 30 or more donors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1383 merged with these open, so they land here.

Item 33: the withholding check refuses a local row that names no contract
target. Such a row used to join no family block, so nothing was withheld
beside a thin cell of its family. A contract test keeps each parentless
local family free of a higher-grain row of its measurement. Today those are
the two DESNZ kWh, three equivalised income and housing cost, and two
private rent families. The synthetic local rows in the gate, terminal and
Orrery tests now name a contract target that no family holds.

Item 32: a build computes the withheld rows once.
- The national path's diagnostics take the set that the national gates
  flagged in their calibration evidence.
- The Orrery result summary takes the set recorded by a gate that reads the
  same result, when that gate saw every summary row; otherwise it counts.
- A local surface's check logs its blocks, block checks and seconds to
  stderr, so the first real local surface times it.

Item 34: on the FRS 2024-25, fewer than ten code-6 amounts sit above Widowed
Parent's Allowance's rate at no Bereavement Support Payment rate. The stage
comment says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m#1359)

#1383's last commits level each COICOP division on the spend the chain drew
against the donor's same spend. After the rebase onto main, recreation comes
out at £151.0bn rather than £148.7bn. The gambling stakes are a share of it,
so they rise to £2.31bn.
- Lottery Duty is now about 0.55 times National Lottery stakes (was 0.56),
  with a base 4.6 times them (was 4.7).
- The other gambling duties are about 6.3 times betting and gaming stakes
  (was 6.4).

Incidence, duty per carrying household, and the alcohol and tobacco figures
do not move. The stage notes, changelog and receipts say so, and a stage run
on the rebased head is in the evidence. All eight LCFS gates pass, and 26 of
the 46 columns match the pre-rebase head. The others are the seven levelled
divisions and the 13 gambling inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juaristi22
juaristi22 force-pushed the uk-excise-quantities-1359 branch from 03427be to e80a424 Compare October 24, 2026 17:17
@juaristi22
juaristi22 changed the base branch from uk-pe-uk-bump-2572 to main October 24, 2026 17:18
@juaristi22 juaristi22 closed this Oct 24, 2026
@juaristi22 juaristi22 reopened this Oct 24, 2026
@vahid-ahmadi

Copy link
Copy Markdown
Contributor

@juaristi22 round 3, at e80a42478 (rebased onto main after #1383 merged). Almost everything from rounds 1 and 2 is resolved. One new should comes out of the gambling split we asked for: the new National Lottery share draw loses almost half the betting and gaming households. Nothing new blocks the merge. The one pre-merge ask left is the one from round 1: the arms' duty by income decile, still listed as open in the body.

First, a correction. Round 2 reviewed 352af5e41 and called items 2 to 8 open. Your fix commits (9e143959b, 303d1abf0, 03427bed2) are dated 11:36 to 11:38 UTC, before we posted at 12:04, and your reply a minute later already listed them. So most of that round was probably out of date when it went up. We should have fetched the head again before posting. Sorry for the noise.

What I ran

  • Worktree at e80a42478, uv sync --all-packages --locked --extra uk (policyengine-uk 2.151.0).
  • pytest on test_uk_excise_quantity_inputs.py (engine) and the engine-free test_uk_lcfs_consumption.py, test_uk_stage_health.py, test_uk_diagnostics.py, test_uk_orrery_contract.py, test_uk_full_gates.py and test_uk_graph_terminal.py: 379 passed.
  • tools/ci_test_plan.py verify: ok. CI at e80a42478: every job passes, engine-uk included.
  • git range-diff of 74a2a378e..03427bed2 against fe1e2ad2c..e80a42478. Commits 2 to 5, 7, 11, 13 and 14 are unchanged. The others differ by the rebase, and two are new: 98a09827a (Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 follow-ups) and e80a42478 (the restated figures).
  • I read the new code in full: the two new shares, the keys, the reconciliation test, the five exclusion reasons, 98a09827a, the stage notes, the changelog, the experiment note and excise_incidence.json. I checked the betting and gaming gap against the vendored facts. HMRC's receipts sum to £3,616.3m, and Machine Games Duty's liabilities are £612.3m against £609.2m of receipts.
  • Not run or read: the stage on a spine and the arms, which need the licensed tabs. In stage_dev_run.head-main-rebase.json I read only the excise_shares receipt, and I didn't open the rebase comparison. I didn't check the LCFS line labels for c94311 to c94319 against Volume D.

Status of earlier findings

  1. must, land after Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383: resolved. Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 merged at fe1e2ad2c, and this branch sits on it with green CI.
  2. should, state the totals and pin the gaps: resolved. The reasons carry the figures. test_the_national_levels_sit_at_the_stated_gaps_from_obr pins each gap within 0.5 points, reading OBR's values from the compile-parity receipt.
  3. should, the gambling row is an identity: resolved. It has its own reason, and the £3m gap is explained.
  4. should, duty above recorded spending: partly. The multiples are now in the stage notes, the changelog and the body. Levelling tobacco and gambling spending to Consumer Trends is still an open decision.
  5. should, too few carrying households: partly. The decomposition is useful: the parent totals keep the donor's incidence, and the loss is in the share draws. The decile comparison waits for the arms. New finding B makes this worse for betting and gaming.
  6. should, one stakes key: resolved, with a better split than we asked for. You were right to leave society and Irish lotteries out of the betting and gaming base.
  7. should, vapers: resolved (stage notes, with Vaping Products Duty).
  8. should, household_net_income: resolved, in the changelog and in the engine tests.
  9. and 10. nits: resolved as notes and follow-ups.
  • A (round 2), multiples after the restack: resolved. They are restated on main's levelling: tobacco 2.22 times, Lottery Duty 0.55, other gambling 6.25, alcohol 0.41.
  • Lock policyengine-uk 2.151.0 and write the UK inputs it reads (#1095, #1278) #1383 round 3, items 32 to 34: resolved. A local row without a contract target now refuses. The withheld set is computed once, with a timing line. Item 34 is now documented. One nit below.

New findings

B. should: the National Lottery share draw loses 45% of the betting and gaming households.

  • From excise_incidence.json: betting and gaming stakes are positive for 3.6% of donors and 2.0% of frame households.
  • Most of the loss is in one step. Among households with stakes, 45% of donors have non-lottery stakes, against 29% of the frame. The step before (stakes within recreation, 17.0% to 14.6%) loses less. The step after (betting and gaming within the rest, 46.5% against 46.6%) loses nothing.
  • A household has other stakes only when its National Lottery share is below one. So on the frame that share is exactly one far more often than on the donor. The money says the same: in the rebase run's excise_shares receipt, the National Lottery holds 73% of frame stakes against 51% of donor stakes.
  • That puts £2.69bn on about half the donor's share of households, about £4,700 each.
  • Revenue doesn't move, but the distribution of any betting or gaming reform does. Could you check the National Lottery share's regime gate before the arms run, the same check you plan for tobacco? I'd keep the split either way. Lottery players carrying remote gaming duty was the worse error.

C. nit: the national build trusts the gates' set without checking which rows it covers (full_build_cli.py:3163). The Orrery summary only uses a recorded set when that gate saw every summary row. The national path passes evidence["diagnostics"] straight through. Today both look to come from result.diagnostics (diagnostics_payload zips over it), so this is safe. A one-line subset check would keep it safe if they ever differ.

Decisions still open

  • The gambling keys. Our view: ship the split, and fix finding B before the arms.
  • Levelling tobacco and gambling spending to Consumer Trends (round 1, item 4).

Merge order

This PR, then my #1373. #1373 already conflicts with main in the same four files (the coverage manifest, contract.py and its test, and the H2 fixture). A trial merge onto this head adds no new conflicts.

🤖 Generated with Claude Code

@juaristi22

Copy link
Copy Markdown
Collaborator Author

Posting the completed local duty-by-income-decile measurements and isolated accounting check. These results describe the measured snapshot, not the latest PR head.

Measured feature: 03427bed280d54896f9d47c6d3a61cd86b5426c5, whose feature patches were rebased locally onto #1383 at d48652beb03dc3470da4bd68365e12773bb75936 (local base 58946a1a73ed68ff7271495a8595de65a3423864), plus local diagnostic changes. Runtime: PolicyEngine-UK 2.151.0 / Core 3.33.3. The PR has subsequently advanced to e80a424789222cdd12de8c5de84ea9031b9554b4; these measurements do not validate that newer head. This is a comment-only update, with no code push or requested CI rerun.

Measured accounting effect. On the actual post-stage frame, I compared populated excise inputs with an in-memory zero-excise control, holding demographics, recorded spending and weights fixed. This isolates the duty accounting effect; it is separate from a parent-branch comparison involving the Chronicle re-pin.

Policy year Duty total (£bn) Household tax change (£bn) Household net-income change (£bn) HBAI income change
2024 24.848 +24.848 -24.848 £0, exactly unchanged
2025 25.548 +25.548 -25.548 £0, exactly unchanged

At 2024 policy, the duty total comprises £13.062bn alcohol, £8.167bn tobacco and £3.619bn gambling. The roughly £25bn fall in household_net_income is now measured on this frame. The 2025 row applies 2025 policy to unchanged quantities; it is not a projected national forecast. Small tax/net-income residuals are consistent with float32 output precision (largest aggregate residual about £1,028 on £24.848bn).

Duty by income decile versus ONS ETB. Core specification: income-policy 2025, duty/quantity period 2024. Each cell is model / ONS, rounded to whole pounds of annual household duty. Whole-household amounts are attributed to each individual and averaged with person weights. Deciles rank equivalised disposable income after student-loan repayments and before indirect taxes, using the modified-OECD couple-normalised scale.

Income decile Alcohol (£/year) Tobacco (£/year) Gambling (£/year)
1 205 / 222 358 / 173 55 / 75
2 259 / 280 395 / 235 99 / 62
3 375 / 339 326 / 399 92 / 83
4 302 / 252 363 / 320 80 / 75
5 393 / 360 329 / 338 118 / 202
6 464 / 358 269 / 239 180 / 76
7 543 / 363 222 / 362 102 / 117
8 636 / 464 219 / 248 134 / 97
9 770 / 620 167 / 316 228 / 84
10 1,249 / 695 94 / 148 292 / 114

The comparison shows substantial incidence differences: alcohol in decile 10 is £1,249 versus £695 (about +80%); tobacco in decile 1 is £358 versus £173 (+107%), while decile 10 is £94 versus £148 (−36%); gambling in decile 9 is £228 versus £84 (+171%) and decile 10 is £292 versus £114 (+156%). These descriptive gaps do not isolate their causes.

A separate income-policy-2024 re-ranking sensitivity preserves the national duty totals and changes some decile means.

Validation and limits. One actual LCFS stage run; all eight stage gates passed. All 46 stored-column totals reproduce the saved split-stage totals within relative tolerance 1e-8 / £0.01. Historical row hashes were unavailable, so this does not establish identical household allocation. Independent recomputation from captured actual arrays verified both income-year specifications, weights, equivalence scales, student loans, tie/boundary allocation, row-order invariance, source support and accounting. Duty means agree within £4.3e-10 and decile totals within £0.0032. Each reported duty cell passes the declared minimum source-support check, with a 30+ distinct positive-duty source-household band. No household records, IDs or exact carrier counts are included here.

The source/result frame period is 2024 and its weight kind is IMPORTANCE. No fresh national calibration was performed. The ONS Table 2b benchmark covers April 2023–March 2024 (FYE 2024; fiscal-start 2023). Frame values are supplied at each requested policy period without inflation adjustment. These are nominal comparisons across unmatched years at existing stage weights. Final calibrated incidence and a matched-year ONS replication remain unmeasured. Alcohol combines beer/cider with wines/spirits; gambling uses the ONS betting-tax row, excluding the separately reported Allwyn National Lottery Fund contribution.

The previously missing stage-only decile measurement is now available for this snapshot. Final-weight national solves and measurements at subsequent heads remain separate.

juaristi22 added a commit to PolicyEngine/chronicle that referenced this pull request Oct 25, 2026
B.1 to B.4's measures now carry footnote i, which defines "excluding
lottery draw only" (only people whose sole gambling was lottery draws
are left out), and the non-response footnotes, and their rows guard
those cells. Every fact carries an age >= 18 constraint, and each age
band its bounds, as the ASHE packages do. The docs say what the tables
can and can't answer for PolicyEngine/microcosm#1381, with the
Commission's caveats, and the attribution names NatCen, the University
of Glasgow and the authors. Values and counts are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 and others added 3 commits October 25, 2026 16:21
…d (microcosm#1359)

The LCFS diary records a stake for about a fifth of the households that
GSGB's four-week participation implies, so the excise levelling shares
each gambling base over too few households at too high an amount. A new
top_up_gambling_participation step takes how many households play from
the Gambling Commission's adult rates by sex and age band, combined
within a household at a declared theta (0: one player per household,
1: adults who play independently), and which households and how much
from the diary:

- within nine strata (adults, youngest adult's age band), households
  without a stake are added by the gas connection walk's weight-fitting
  rule, in an identity-keyed order drawn in proportion to a regularised
  donor propensity times the household's published chance, first to the
  four-week share (regular players), then to the twelve-month share
  (occasional players);
- an added household's stake comes from a positive-only QRF of the
  donor's stakes at a quantile keyed on its id; an occasional player's is
  the declared share of it;
- households the diary saw keep their stakes, and level_excise_quantities
  shares each base by the topped-up keys, so every base still sums to its
  published figure.

The receipt gives shares only, and a stratum's figures only where each of
its cells rests on ten or more source households. The stage-health check
gambling_participation recomputes the adult rates from the vendored rows
and checks the settings, each stratum's shares, the stakes and that the
levelling read the topped-up keys.

No stage declares the step yet: it reads gambling_participation_facts.json,
which waits on the GSGB tables in Chronicle and a feed re-pin. The gate is
registered with the declaration. Synthetic facts and a synthetic
population in test_support exercise the step, the stage and the gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m#1359)

The stage-health check gambling_participation becomes the release-blocking
gate uk_stage_lcfs_consumption_gambling_participation: gates.json, the
spine gate scope, the microcosm-data contract mirrors and the battery's
parameter vocabulary (maximum_stratum_shortfall), with the gate digests
re-pinned. The kind joins the source manifest and the sources schema, and
the vendor selection names gambling_participation_facts.json: the 426
Great Britain rows of the Gambling Commission's GSGB 2025 participation
concept (PolicyEngine/chronicle#391). The step is declared once the feed
carries them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/microcosm-build/src/microcosm/build/uk/release_input_coverage_manifest.json
#	packages/microcosm-graph/tests/fixtures/parity/uk_spine/uk_spine.json
juaristi22 added a commit to PolicyEngine/chronicle that referenced this pull request Oct 25, 2026
GSGB 2025 gambling participation by sex, age, area and activity, for PolicyEngine/microcosm#1381
juaristi22 and others added 4 commits October 25, 2026 19:24
… (microcosm#1359)

The top-up scaled every added household's drawn stake by the frame's mean
stake over the donor's. The walk picks households whose predictors draw
larger stakes, so on the real spine the added regular players' mean came
out about 1.9 times the seen households' for betting and gaming (about 1.0
for the National Lottery). The scale now brings the added regular class's
mean to the seen households' (the donor's positive mean where the frame drew
none); occasional players keep a tenth of it. Who is added does not move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bles (microcosm#1359)

PolicyEngine/chronicle#391 adds the Gambling Commission's Gambling Survey
for Great Britain 2025 tables (1,024 facts; 1,503,315 rows). The merge
commit's tree is the pull request head's, merged onto an unmoved base, so
the artifact was built from that head's bundle and carries the merge
commit's digests.

The new vendored gambling_participation_facts.json holds the 426 Great
Britain participation rows the LCFS top-up reads, and the package declares
it. The other vendored resources keep their rows under the new feed
identity. The national and local reference files are byte-identical, the
membership moves only its feed label, the local census restates the pin,
and both compile-parity receipts are unchanged; both surfaces were
regenerated from the national and local cuts. The pin tests restate the
new identity, and the runbook records the re-pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m#1359)

The stage now brings its two gambling keys to the Gambling Commission's
GSGB 2025 participation before the excise levelling, as María ruled on
2026-10-25. The declaration reads the 426 vendored participation rows:
the population rates from tables B.3 (four weeks) and B.1 (twelve months),
each activity's own row from table B.9 (the all-channels row, or the online
row of the two online-only instant-win games), seven age bands, theta 0.9
for the National Lottery and 0.08 for betting and gaming, nine household
strata, an occasional stake share of a tenth, and the propensity's
settings.

Theta is fitted on the LCFS donor so that the declared rates, applied to
the donors' own adults, give the diary's ratio of the two-adult to the
one-adult household rate: 0.897 and 0.081, beside them in the evidence
(scripts/theta_calibration.py, aggregates only).

On a stage-only run on the 78,440-household spine, every LCFS gate passes,
and every stratum lands on its share: 62.7 percent of households carry
Lottery Duty and 29.8 percent the other gambling duties, against 10.7 and
2.0 percent on the diary's draws. The stage notes and the excise changelog
fragment describe the step and its known biases; the lockstep tests list
its operation and seed, and the stage tests expect its four fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… top-up (microcosm#1359)

The vendored headers restate the 272c987 feed identity, and the LCFS stage
declares top_up_gambling_participation and its resource, so the fixture's
LCFS declaration and its contract hashes move.

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

Copy link
Copy Markdown
Collaborator Author

@vahid-ahmadi this is ready for another look. Since your round 3 at e80a42478, the branch has seven new commits. María's ruling of 2026-10-25 also changed how the gambling keys are built, and that answers your finding B in a different way than you asked.

What changed

  1. The gambling keys are brought to GSGB participation (8a012e582, fdc61a85f, c3e8e27a4).
    • Why: the diary records a stake for about a fifth of the households that the Gambling Commission's GSGB 2025 four-week participation implies. Under any steady rate of play, a two-week diary sees at least half of the four-week players. So the gap is play the diary misses, and fixing the share draw's gate alone would not close it.
    • How: a new step, top_up_gambling_participation, runs before the excise levelling:
      • each adult's four-week and twelve-month chance is GSGB's rate for its sex and age band (B.1, B.3). A key's rate is the union of its activities (B.9), taken as independent within the population that plays any of them;
      • a household's chance combines its adults at theta, fitted on the donor's ratio of two-adult to one-adult household rates: 0.9 for the National Lottery, 0.08 for betting and gaming;
      • within nine household strata, households without a stake are added by the gas connection walk's weight-fitting rule. The order is drawn from a regularised donor propensity times the household's chance. The walk fills each stratum to its four-week share, then to its twelve-month share;
      • an added household's stake is drawn from the donor's positive stakes, scaled so the added regular players' mean equals the seen households'. Occasional players get a tenth.
    • What stays: households the diary saw keep their stakes, every base still sums to its published figure, and the OBR rows don't move.
    • New gate: uk_stage_lcfs_consumption_gambling_participation recomputes the rates from the vendored rows and checks every stratum.
  2. Re-pin to Chronicle 272c987 (GSGB 2025 gambling participation by sex, age, area and activity, for PolicyEngine/microcosm#1381 chronicle#391, 8fd289ec3) for the GSGB tables. No compiled target moves: the reference files are byte-identical, and both compile-parity receipts are unchanged.
  3. A stake fix found on the real spine (b2035eb30). Scaling the drawn stakes by the donor's mean gave the added betting and gaming households about 1.9 times the seen households' mean stake, because the walk picks households whose predictors draw larger stakes. The scale now sets the added regular players' mean equal to the seen households' mean.
  4. main merged in (44e9cb802) for UK spine: refuse malformed FRS claimant roles directly; check every supplied role reaches the engine #1205. Only the coverage manifest and the H2 fixture conflicted, and both are regenerated.

Results on the real spine (78,440 households, stage-only, policyengine-uk 2.151.0). All nine LCFS gates pass, and every stratum lands on its share. 62.7% of households now carry Lottery Duty and 29.8% the other gambling duties, against 10.7% and 2.0% before. Betting and gaming duty is now about £430 a year per regular household, where it was about £4,700 on 2.0% of households.

Where your review would help most

  • uk_runtime/gambling_participation.py: the reader's selectors, the household combination, the walk, and the receipt's withholding rule. On the selectors: B.9 prints online and in-person rows beside each activity's all-channels row, so each activity names its own row.
  • The declaration in uk/spec/sources.yaml and the new check in stage_health.py.
  • Two results that María will decide on:
    • Income gradient: carrying rises with income, from 52% to 70% of households across the deciles for the Lottery and from 24% to 36% for betting and gaming. The propensity carries the diary's income gradient.
    • Regions: London is the lowest region for both keys, as in GSGB. Elsewhere the regional pattern follows GSGB only weakly; the correlation across regions is 0.41 for the Lottery and 0.11 for betting and gaming.

Still open

  • Your nit C, the subset check in the national path.
  • Levelling spending to Consumer Trends.
  • The stake split, re-measured after the fix, and the feed-gated tests. Both run once the machine is free.
  • The duty-by-decile comparison posted on 2026-10-24 predates the top-up, so its gambling column will move.

Could you re-review?

🤖 Generated with Claude Code

…e-pin (microcosm#1359)

The evidence for the top-up, aggregates only:
- stage_run_gsgb.py runs the stage on microcosm#1333's spine twice in one
  process, as declared and without top_up_gambling_participation, on the
  final head. All nine LCFS gates pass; every stratum lands on its share;
  62.7 and 29.8 percent of households carry Lottery Duty and the other
  gambling duties, against 10.7 and 2.0 without the step. The added regular
  players' mean stake equals the seen households', and the occasional
  players' is about a tenth of it. The output also holds each
  equivalised-income decile's share of households and of the base, and the
  regional shares beside GSGB's regional rates (B.2, B.4), the out-of-sample
  check.
- repin_272c987.sh (with quiet_footprint.sh) is the re-pin as run.

The README names the two results left for review: carrying rises with
income, which moves the betting and gaming base towards the top decile,
and outside London the regional pattern follows GSGB only weakly.

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

Copy link
Copy Markdown
Contributor

@juaristi22 round 4, at 3a20860fb. The top-up is sourced, reproduces from what is committed as far as the adult rates go, and is gated. The re-pin is clean. Nothing below blocks the merge. The main new point is about the income gradient you flagged: part of it may come from the propensity's own features, and a cheap rerun would show how much.

What I ran

  • Fresh worktree at 3a20860fb, uv sync --all-packages --locked --extra uk.
  • pytest on test_uk_gambling_participation.py, test_uk_lcfs_consumption.py, test_uk_stage_health.py, test_uk_source_stages.py, test_uk_chronicle_feed.py, test_uk_full_targets.py, test_country_spec.py and test_contract.py: 711 passed. test_uk_orrery_contract.py: 15 passed. tools/ci_test_plan.py verify: ok. CI: all 10 checks pass.
  • The re-pin. For every JSON file 8fd289ec3 touches, I compared before and after with the feed identity fields removed. All 37 existing files are identical apart from those fields; the only real changes are the new gambling_participation_facts.json and its country_package.json entry. Chronicle#391 is merged and 272c987 is Chronicle's main tip.
  • The source. I downloaded the GSGB 2025 workbook from the manifest's URL. Its sha256 matches (ccee2269…), and the B.3 and B.9 cells I checked match the vendored rows (for example, any gambling at 18 to 24 is 0.3295, National Lottery draws at 18 to 24 is 0.1112).
  • The rates. I recomputed both keys' adult rates from the vendored rows with the declared selectors. They match the receipt in stage_run_gsgb.json (for example, NL regular, male 18 to 24, 0.2276; betting and gaming 0.2972). Nationally that gives 36.5% of adults for the National Lottery and 19.2% for betting and gaming over four weeks.
  • Your measurements. The 24 Oct duty total adds up (13.062 + 8.167 + 3.619 = 24.848). The £430 per regular betting and gaming household is about right from the receipt's shares (95.7% of the base on 20.6% of households).
  • Not run or read: the stage on a spine, theta_calibration.py and the arms (all need the licensed tabs); the engine tests. I didn't read the main merge (UK spine: refuse malformed FRS claimant roles directly; check every supplied role reaches the engine #1205, the NZ geography) or the H2 fixture line by line, and I didn't check the regional rows (B.2, B.4).

Status of earlier findings

  • B (round 3), the National Lottery share draw loses households: superseded, not fixed as asked. The diary-seen households now carry only 20% of NL stakes and 9% of betting and gaming stakes, so the share draw no longer sets who carries. It still sets the level: anchor at gambling_participation.py:738 is the seen households' mean, so every added stake scales to it. The regime-gate check has a smaller job now, not none.
  • C (round 3), subset check in the national path: open, as you say (full_build_cli.py:3165).
  • Round 1 item 4, level spending to Consumer Trends: open.
  • Round 1 item 5, too few carrying households: resolved for gambling by the top-up. Still open for tobacco (the prevalence redraw).
  • The duty-by-decile comparison: delivered on 24 Oct, thanks. The alcohol and tobacco columns stand, with your caveats. The gambling column predates the top-up, as you note.
  • Everything else from rounds 1 to 3 stays resolved.

New findings

  1. should: the propensity is fitted on a feature that contains the stake. lcfs_consumption.py:2891 gives the propensity every spend target, recreation_consumption among them, and the stakes are a share of recreation (P609, lcfs_consumption.py:683). So a donor with a recorded stake has a higher recreation total partly because of that stake. The walk then favours high-recreation households, which are higher-income households. This may also be why the raw draws came out about 1.9 times too high before b2035eb30.

    • It matters for the gradient you flagged. On the frame's own draws, betting and gaming carrying is almost flat across deciles (1.9% in the bottom, 2.1% in the top). After the top-up it runs from 24% to 36%, and the top decile's share of the base rises from 19% to 26%. So the top-up makes the gradient steeper; it doesn't just carry the diary's. Some of this is legitimate, since GSGB's age mix also varies by decile through the strata.
    • Two cheap reruns would separate the effects: recreation net of stakes as the feature, and the walk ordered by the published chance alone. Worth doing before the decision on the gradient. As a reference only (it uses the same diary): your ONS column from 24 Oct puts about 12% of gambling duty in the top decile.
    • The stage note at sources.yaml:4297 says the bias would lean low in income if the diary misses online play more. The measured result goes the other way, so the note should say what was measured.
  2. should: GSGB's own caveat on participation levels is missing from the bias list. The Commission's guidance on using GSGB statistics reports that mentioning gambling in the invitation "significantly increased estimates of gambling participation", and that the study "could not conclude whether estimates of gambling participation were more or less accurate". The step treats GSGB as the level and raises the number of carrying households 6 to 15 times on it. One sentence in the stage notes would cover it.

  3. should: the licence for the vendored GSGB rows. Chronicle's manifest says "license: None stated", and that the Commission's terms forbid copying its material without prior approval unless stated otherwise. That is why Chronicle keeps the workbook out of git. This PR copies 426 of its values into a public repo. Is that covered by the same decision of 25 Oct, or does it need a note to statistics@gamblingcommission.gov.uk? Either way, a line in the vendored file's policy or the PR body would record it.

  4. nit: the union's independence assumption is untested where it matters most. For betting and gaming at 18 to 24, the activities sum to 0.495 against a population rate of 0.295. Independence gives a union of 0.863, but anything from about 0.43 to 1.0 fits the published rows. GSGB B.13 (the number of activities among four-week gamblers) is in the 272c987 feed but not vendored, and would give a check. Theta for betting and gaming rests on the same diary: its two-adult to one-adult ratio, 1.40, sits near the theta 0 end of the 1.33 to 2.14 that theta 0 to 1 gives, so it lands at 0.08. A sensitivity at a higher theta would show how much the carrying share depends on it.

  5. nit: added and seen players get the same mean stake (gambling_participation.py:738). A two-week diary is more likely to catch frequent players, so the seen households are probably the heavier ones. Equal means spread the base more evenly than play probably is. The stage notes list the occasional share as an assumption; this one should be listed too.

  6. nit: the gate doesn't bound amount_scale. The receipt has 1.82 for NL and 0.63 for betting and gaming, so the raw draws missed the seen mean by large margins in opposite directions. A bound, say [0.5, 2], would catch a broken draw that the anchoring would otherwise hide.

  7. nit: gambling_participation.py:393 computes regular * union * annual / regular, which is union * annual. The guard also sets the twelve-month rate to zero whenever the four-week rate is zero. That's harmless with these rows, but union * annual is simpler and has no such edge.

Decisions still open

  • The income gradient. Our view: run the two reruns in finding 1 first. If removing the stake from the feature flattens most of it, take that version.
  • Regions. Our view: acceptable as it is. Region is not a target, London comes out right, and the betting and gaming propensity rests on about 190 donors.
  • Levelling spending to Consumer Trends (round 1, item 4).
  • The GSGB licence (finding 3).

Verdict

Ready to merge once findings 1 and 2 are reflected in the stage notes, whatever you decide on the gradient. Nothing here needs code changes before merge, unless the reruns in finding 1 change your mind about the feature. Merge order is unchanged: this PR, then #1373.

🤖 Generated with Claude Code

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.

UK: impute alcohol, tobacco and gambling consumption so policyengine-uk's excise duties stop scoring zero

2 participants