Conversation
policyengine-uk#2081 adds the person input uc_is_in_gainful_self_employment (UC Regs 2013 reg 64). Without it the model reads any self-employment income other than zero as gainful self-employment and none as none, so a trader who breaks even, or whose loss this build floors at zero, never gets the minimum income floor, while an employee's small side trade does. Set it from the FRS: true when the main job (EMPSTATI) is self-employment, whatever its profit (ADM H4013, H4054, H4503), and when a side trade's profit is above the person's employment income (ADM H4034). The SPI copy in impute_income re-derives it from its own imputed incomes. Releases of policyengine-uk without the variable skip the column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
- Test the rule against a per-person oracle written independently of the vectorised helper, over every employment status (catches dropping one status from the earnings test). - A built FRS or enhanced FRS without the column now fails instead of skipping, so a build that stops writing it is caught. - Check that the FRS-only imputation stage never rewrites the flag or its inputs. - Changelog states the earnings exception and that the flag is a survey proxy overriding policyengine-uk's default; the docstring says the flag is fixed at survey or imputation time and does not follow uprating. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 3, 2026 22:28
Contributor
Author
|
Hand-off to the UK hub (session 888dad4b → local_60f5f051)
Waiting on
Merge note. #528 rewrites the Follow-ups already split out: #526 (EMPSTATI 11), #527 (start-up period), #528 (trading losses), and the SPI-status work (#529 earnings groups). |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Model side: PolicyEngine/policyengine-uk#2081 (stacked on #1973), which adds the person input
uc_is_in_gainful_self_employment(UC Regs 2013 reg 64). Merge after #2081 merges, so the variable name is final. policyengine-uk releases without the variable skip the column, so outputs on today's lock (2.93.0) are unchanged.Summary
Without an input, #2081 decides gainful self-employment from income: any self-employment profit or loss counts, and none means none. This build floors losses at zero, so in the data "none" covers break-even traders and loss-makers, and the minimum income floor never applies to them. The formula also gives the floor to people whose main job is as an employee but who have a small side trade.
This PR sets the input from the FRS. It is a survey proxy for reg 64 that overrides #2081's income-based default:
uc_is_in_gainful_self_employmentThe SPI copy that
impute_incomestacks keeps its FRS donor's employment status but draws new incomes. The copy re-derives the flag from its own imputed incomes, so every row follows the same rule.What the FRS says
All figures are aggregates of the FRS 2024-25 end-user files (UKDS SN 9563) at survey weights, before uprating.
The variables
Adults whose main job is self-employment (EMPSTATI 3/4): 2,110 adults, 4.20m weighted
self_employment_income = max(0, SEINCAM2), so the 41 loss-makers become zero.Side trades. 227 adults (0.49m weighted) report a self-employment profit although their main job is not self-employment. For 65 of them (0.14m), all employees, that income is above their employment income.
Why these rules
From DWP's Advice for Decision Making (ADM), chapter H4:
What the survey cannot see. These limits are known:
Whether the floor then applies is decided by #2081, not by this input:
Not in this PR
self_employment_income.self_employment_incomeinto several incomes:total_income;in_work.A negative value would set a loss against other income in every one of them.
tests/test_non_negative_incomes.pykeeps incomes non-negative in this repo.uc_is_in_startup_period, never set by this build). Since 23 September 2020 (SI 2019/1152), reg 63(1)(a) asks whether the floor has previously applied to the trade, not whether the trade is new. The claimant must also be taking active steps to raise earnings (63(1)(b)), and a second start-up period needs a different trade and five years since the first (63(2)). Follow-up.Invariants (property-tested with Hypothesis)
impute_income, every row (FRS and SPI copy) carries the rule applied to its own status and incomes.A mutation check made seven deliberate defects in the rule and in the SPI step, including one from the independent review (dropping full-time employees from the earnings test). All seven fail the tests.
Impact
All figures come from real policyengine-uk microsimulations, one run per row.
Setup
uc_is_in_gainful_self_employmentwas checked to equal the dataset's value, for 2025-26 to 2030-31.Universal Credit spending change against the base, £bn
2026-27 in detail (this PR)
uc_is_in_gainful_self_employment)uc_mif_applies)Checklist
🤖 Generated with Claude Code