Skip to content

Pass is_tax through to detailed_budget - #972

Merged
PavelMakarchuk merged 1 commit into
mainfrom
detailed-budget-is-tax
Oct 28, 2026
Merged

PavelMakarchuk merged 1 commit into
mainfrom
detailed-budget-is-tax

Conversation

@PavelMakarchuk

Copy link
Copy Markdown
Collaborator

Fixes #971

Change

  • DetailedBudgetProgramOutput gains is_tax: bool | None = None. It is optional, so stored outputs without it still validate, and it is null when a row has no flag.
  • build_detailed_budget copies is_tax from each program-statistics row (a numpy scalar is unwrapped; anything that isn't a bool becomes null).
  • Golden OpenAPI regenerated: the only change is the new optional is_tax property on DetailedBudgetProgramOutput.

Why now

PolicyEngine/policyengine.py#724 adds a UK row for every tax and benefit in the budget totals that a reform changes. The v3 app chart decides which rows are benefits from its own BENEFIT_PROGRAMS set, so new benefit rows such as scottish_child_payment and pip would be drawn as taxes. On report 4738 (2027 UC freeze), Scottish Child Payment would show −£25m instead of +£25m. With this flag the app signs rows by is_tax (PolicyEngine/policyengine-app-v2#1451). This should deploy before a .py release with #724 reaches sim-api.

Testing

  • New tests in test_simulation_output_budget.py: each program carries its flag (Universal Credit and Scottish Child Payment as benefits, income tax as a tax), and the flag stays null when rows have none.
  • The builder's snapshot fixture now carries is_tax.
  • Executor: 1,891 tests pass. Gateway: 268 pass, including the OpenAPI golden. Contract: 322 pass.
  • ruff 0.15.22 format and check pass on the changed files. Pyright passes for every package; projects/policyengine-apis-integ could not be checked locally because it needs the generated clients, which CI builds.

🤖 Generated with Claude Code

build_detailed_budget dropped policyengine.py's is_tax flag, so consumers
signed each program from their own lists. Once policyengine.py adds a row
for every UK budget component a reform changes, a benefit missing from
those lists (Scottish Child Payment, PIP) would be drawn as a tax.

DetailedBudgetProgramOutput gains an optional is_tax (null in outputs
built before it), copied from each program-statistics row.

Fixes #971

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

@anth-volk anth-volk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Passing is_tax through detailed_budget lets the v3 chart sign each row from the result itself, and results stored before this still validate because the field is optional.

One minor, optional wording point: the DetailedBudgetProgramOutput description says is_tax is None only in outputs built before the flag was recorded, but _is_tax also returns None for any row without a boolean flag (as the build_detailed_budget docstring says).

As you noted, this and policyengine-app-v2#1451 need to merge and deploy before a .py release with PolicyEngine/policyengine.py#724 reaches sim-api.

@PavelMakarchuk
PavelMakarchuk merged commit 61ffde8 into main Oct 28, 2026
23 checks passed
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.

Pass is_tax through to detailed_budget so consumers can sign each program

2 participants