From 5e9490a3e56d8378d00237331424efd25357ac62 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:56:36 +0400 Subject: [PATCH 1/8] v3 household results: the country is required, never read as the US The household section already runs each household in the report's country, but the result code still fell back to the US: - compareHouseholds used the US result rows (its taxes and benefits) for any country without its own, and defaulted its country to "us". The country is now required, and a country without its own result rows is an error instead of a US reading. - HouseholdImpactResult defaulted its currency country to "us"; the section always passes it, and it is now required. - The unused simple-input run (FlagshipHouseholdInput, buildHouseholdPayload, runFlagshipHousehold, describeHouseholdInput and their helpers) built a US household with a US state whatever the report's country. Nothing calls it since the section moved to the household builder, so it is removed, and its run tests now cover runFlagshipHouseholdPayload. Tests first: compareHouseholds for a country without result rows expected an error and got a US reading; a HouseholdImpactResult props object without a country carries @ts-expect-error, which tsc reported as unused until the country became required. Co-Authored-By: Claude Opus 5.5 --- .../household/HouseholdImpactResult.tsx | 6 +- app/src/libs/flagship/householdRun.ts | 183 ++--------------- .../libs/flagship/householdRunMocks.ts | 17 +- .../report/HouseholdImpactResult.test.tsx | 33 +++ .../unit/libs/flagship/householdRun.test.ts | 190 +++++------------- 5 files changed, 115 insertions(+), 314 deletions(-) create mode 100644 app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx diff --git a/app/src/components/flagship/report/household/HouseholdImpactResult.tsx b/app/src/components/flagship/report/household/HouseholdImpactResult.tsx index 5fd648b0f..b506b02e1 100644 --- a/app/src/components/flagship/report/household/HouseholdImpactResult.tsx +++ b/app/src/components/flagship/report/household/HouseholdImpactResult.tsx @@ -192,15 +192,15 @@ export default function HouseholdImpactResult({ impact, summary, breakdowns, - countryId = 'us', + countryId, }: { impact: FlagshipHouseholdImpact; /** The household the result is for, e.g. "Single · no children · $50,000 earnings · Ohio · 2026". */ summary: string; /** The household run the v2 breakdowns read; without it, only the summary shows. */ breakdowns?: HouseholdBreakdownSource; - /** The report's country: its currency. */ - countryId?: CountryId; + /** The household's country: its currency. */ + countryId: CountryId; }) { const [view, setView] = useState<'summary' | HouseholdBreakdownView>('summary'); const { netIncome, components, programs } = impact; diff --git a/app/src/libs/flagship/householdRun.ts b/app/src/libs/flagship/householdRun.ts index f8e4a407f..04ea0dd74 100644 --- a/app/src/libs/flagship/householdRun.ts +++ b/app/src/libs/flagship/householdRun.ts @@ -1,127 +1,20 @@ -import { STATE_NAMES } from '@/api/billFeed'; import { createHousehold } from '@/api/household'; import { fetchHouseholdCalculation } from '@/api/householdCalculation'; import type { CountryId } from '@/libs/countries'; -import { Household } from '@/models/Household'; import type { V1HouseholdCreateEnvelope } from '@/models/household/v1Types'; import type { HouseholdCalculationData } from '@/types/calculation/household'; -import { formatCurrency } from '@/utils/formatters'; /** - * A household run for the flagship report: one family, described in a - * few fields, run under the report's baseline and reform policies for - * the report's year through the same v1 household endpoints the v2 - * household flow uses — create the household, then calculate it under - * each policy. + * A household run for the flagship report: a household built in the + * household builder, run under the report's baseline and reform policies + * for the report's year, in the household's own country, through the + * Public API's v1 household routes, as the older household flow uses + * them: create the household, then calculate it under each policy. */ -/** The household as the report's form describes it. US only for now. */ -export interface FlagshipHouseholdInput { - /** Two-letter state code, e.g. "CA". */ - state: string; - married: boolean; - /** One age per child, each 0–17. */ - childAges: number[]; - /** The head's annual employment income. */ - earnings: number; - /** The partner's annual employment income; read only when married. */ - partnerEarnings: number; -} - -export const MAX_CHILDREN = 6; -export const MAX_CHILD_AGE = 17; -export const DEFAULT_CHILD_AGE = 10; -const DEFAULT_STATE = 'CA'; -const DEFAULT_EARNINGS = 50000; -/** A change smaller than a dollar reads as no change. */ +/** A change smaller than a dollar (or a pound) reads as no change. */ const NEGLIGIBLE = 1; -/** - * The form's starting household: single, no children, $50,000 a year, in - * the report's state — a state or district report's own state, else - * California, the model's default. - */ -export function defaultHouseholdInput(region?: string): FlagshipHouseholdInput { - return { - state: stateFromRegion(region) ?? DEFAULT_STATE, - married: false, - childAges: [], - earnings: DEFAULT_EARNINGS, - partnerEarnings: 0, - }; -} - -/** "state/ny" → "NY", "congressional_district/TX-07" → "TX"; nothing for the country. */ -export function stateFromRegion(region?: string): string | undefined { - const match = region?.match(/^(?:state\/([a-z]{2})|congressional_district\/([a-z]{2})-)/i); - const code = (match?.[1] ?? match?.[2])?.toUpperCase(); - return code && STATE_NAMES[code] ? code : undefined; -} - -const isAmount = (value: number) => Number.isFinite(value) && value >= 0; -const isChildAge = (age: number) => Number.isInteger(age) && age >= 0 && age <= MAX_CHILD_AGE; - -/** Whether the household can run: a known state, amounts of zero or more, children's ages. */ -export function isValidHouseholdInput(input: FlagshipHouseholdInput): boolean { - return ( - !!STATE_NAMES[input.state] && - isAmount(input.earnings) && - (!input.married || isAmount(input.partnerEarnings)) && - input.childAges.length <= MAX_CHILDREN && - input.childAges.every(isChildAge) - ); -} - -/** - * The v1 household for the input, built with the v2 builder's own model - * so the API reads it as it reads a v2 household: the head is "you", and - * the API takes the year to calculate from "you"'s age. - */ -export function buildHouseholdPayload( - input: FlagshipHouseholdInput, - year: string -): V1HouseholdCreateEnvelope { - let household = Household.starter('us', year) - .withBuilderMaritalStatus(year, input.married ? 'married' : 'single') - .withBuilderChildCount(year, input.childAges.length); - const { primaryPersonKey, partnerKey, childKeys } = household.deriveBuilderComposition(year); - if (primaryPersonKey) { - household = household.setPersonVariableAtYear( - primaryPersonKey, - 'employment_income', - year, - input.earnings - ); - } - if (partnerKey) { - household = household.setPersonVariableAtYear( - partnerKey, - 'employment_income', - year, - input.partnerEarnings - ); - } - childKeys.forEach((childKey, index) => { - household = household.setPersonVariableAtYear( - childKey, - 'age', - year, - input.childAges[index] ?? DEFAULT_CHILD_AGE - ); - }); - const householdKey = Object.keys(household.householdData.households ?? {})[0]; - if (householdKey) { - household = household.setGroupVariableAtYear( - 'households', - householdKey, - 'state_name', - year, - input.state - ); - } - return household.toV1CreationPayload(); -} - type EntityPlural = 'people' | 'households' | 'tax_units' | 'spm_units' | 'benunits'; /** The app's camel-case keys, for results already in the app's shape. */ @@ -306,7 +199,7 @@ function benefitLine(variable: string, label: string, entity: EntityPlural): Lin return { id: variable, label, sign: 1, sources: [{ entity, variable }] }; } -/** Each country's parts of net income and programs; a country without its own reads the US's. */ +/** Each country's parts of net income and programs: the countries v3 runs households for. */ const LINES: Partial< Record > = { @@ -314,8 +207,17 @@ const LINES: Partial< uk: { components: UK_COMPONENTS, programs: UK_PROGRAMS }, }; +/** + * The rows a country's result lists (its `LINES` entry: parts of net + * income and programs). A country without an entry has none, and is never + * read as the US. + */ function linesFor(countryId: string) { - return LINES[countryId as CountryId] ?? LINES.us!; + const lines = LINES[countryId as CountryId]; + if (!lines) { + throw new Error(`Household results are not available for ${countryId}.`); + } + return lines; } export interface HouseholdLineChange { @@ -382,7 +284,7 @@ export function compareHouseholds( baseline: HouseholdCalculationData, reform: HouseholdCalculationData, year: string, - countryId: string = 'us' + countryId: string ): FlagshipHouseholdImpact { const lines = linesFor(countryId); const before = readHouseholdTotal(baseline, 'households', 'household_net_income', year); @@ -414,13 +316,6 @@ export function compareHouseholds( }; } -export interface RunFlagshipHouseholdArgs { - year: string; - input: FlagshipHouseholdInput; - baselinePolicyId: string; - reformPolicyId: string; -} - export interface FlagshipHouseholdRun extends FlagshipHouseholdImpact { householdId: string; /** @@ -486,27 +381,10 @@ async function calculateHousehold( } /** - * Creates the household and calculates it under the baseline and the - * reform at once. The API stores a new household on every call, even for - * identical inputs, so each run gets its own household ID. - */ -export async function runFlagshipHousehold({ - year, - input, - baselinePolicyId, - reformPolicyId, -}: RunFlagshipHouseholdArgs): Promise { - return runFlagshipHouseholdPayload({ - year, - payload: buildHouseholdPayload(input, year), - baselinePolicyId, - reformPolicyId, - }); -} - -/** - * The same run for a household built in full (the household builder's - * own model, any of its inputs), sent as the builder sends it. + * Creates the household, as the household builder sends it, and + * calculates it under the baseline and the reform at once, in the + * payload's own country. The API stores a new household on every call, + * even for identical inputs, so each run gets its own household ID. */ export async function runFlagshipHouseholdPayload({ year, @@ -531,20 +409,3 @@ export async function runFlagshipHouseholdPayload({ ...compareHouseholds(baseline, reform, year, payload.country_id), }; } - -/** "Single · 2 children · $50,000 · California": the household a result is for. */ -export function describeHouseholdInput( - input: FlagshipHouseholdInput, - countryId: CountryId = 'us' -): string { - const money = (value: number) => formatCurrency(value, countryId, { maximumFractionDigits: 0 }); - const children = input.childAges.length; - return [ - input.married ? 'Married' : 'Single', - children === 0 ? 'no children' : children === 1 ? '1 child' : `${children} children`, - input.married - ? `${money(input.earnings)} and ${money(input.partnerEarnings)} earnings` - : `${money(input.earnings)} earnings`, - STATE_NAMES[input.state] ?? input.state, - ].join(' · '); -} diff --git a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts index 8be39f781..456c44981 100644 --- a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts +++ b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts @@ -1,4 +1,4 @@ -import type { FlagshipHouseholdInput } from '@/libs/flagship/householdRun'; +import { Household } from '@/models/Household'; import type { HouseholdCalculationData } from '@/types/calculation/household'; export const HOUSEHOLD_YEAR = '2026'; @@ -8,13 +8,14 @@ export const CUSTOM_BASELINE_POLICY_ID = '45678'; export const REFORM_POLICY_ID = '98765'; export const CREATED_HOUSEHOLD_ID = 54321; -export const MARRIED_TWO_CHILDREN: FlagshipHouseholdInput = { - state: 'NY', - married: true, - childAges: [4, 12], - earnings: 60000, - partnerEarnings: 25000, -}; +/** A country whose household results v3 does not list (no rows of taxes and benefits). */ +export const COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS = 'ca'; + +/** A married couple with two children, as the household builder sends it to the Public API. */ +export const US_COUPLE_WITH_CHILDREN_PAYLOAD = Household.starter('us', HOUSEHOLD_YEAR) + .withBuilderMaritalStatus(HOUSEHOLD_YEAR, 'married') + .withBuilderChildCount(HOUSEHOLD_YEAR, 2) + .toV1CreationPayload(); interface ResultValues { netIncome: number; diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx new file mode 100644 index 000000000..c2ae2d275 --- /dev/null +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx @@ -0,0 +1,33 @@ +import type { ComponentProps } from 'react'; +import { render, screen } from '@test-utils'; +import { describe, expect, test } from 'vitest'; +import HouseholdImpactResult from '@/components/flagship/report/household/HouseholdImpactResult'; +import { compareHouseholds } from '@/libs/flagship/householdRun'; +import { + HOUSEHOLD_YEAR, + UK_BASELINE_RESULT, + UK_REFORM_RESULT, +} from '@/tests/fixtures/libs/flagship/householdRunMocks'; + +const UK_IMPACT = compareHouseholds(UK_BASELINE_RESULT, UK_REFORM_RESULT, HOUSEHOLD_YEAR, 'uk'); +const UK_SUMMARY = 'Single · no children · £30,000 earnings · 2026'; + +describe('HouseholdImpactResult', () => { + test('given no country then the props do not type-check, so no result reads as the US', () => { + // @ts-expect-error The household's country is required: it sets the currency. + const propsWithoutCountry: ComponentProps = { + impact: UK_IMPACT, + summary: UK_SUMMARY, + }; + + expect(propsWithoutCountry).not.toHaveProperty('countryId'); + }); + + test('given a UK household then its change in net income reads in pounds', () => { + render(); + + expect(screen.getByRole('group', { name: 'Change in annual net income' })).toHaveTextContent( + '£' + ); + }); +}); diff --git a/app/src/tests/unit/libs/flagship/householdRun.test.ts b/app/src/tests/unit/libs/flagship/householdRun.test.ts index 95898cdaf..f440193aa 100644 --- a/app/src/tests/unit/libs/flagship/householdRun.test.ts +++ b/app/src/tests/unit/libs/flagship/householdRun.test.ts @@ -1,26 +1,20 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import { - buildHouseholdPayload, compareHouseholds, - defaultHouseholdInput, - describeHouseholdInput, - isValidHouseholdInput, plainHouseholdError, readHouseholdTotal, - runFlagshipHousehold, runFlagshipHouseholdPayload, - stateFromRegion, } from '@/libs/flagship/householdRun'; import { Household } from '@/models/Household'; import { BASELINE_POLICY_ID, BASELINE_RESULT, + COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS, CREATED_HOUSEHOLD_ID, HOUSEHOLD_YEAR, householdResult, LOW_EARNER_BASELINE_RESULT, LOW_EARNER_REFORM_RESULT, - MARRIED_TWO_CHILDREN, REFORM_POLICY_ID, REFORM_RESULT, REFORM_RESULT_LOSS, @@ -29,6 +23,7 @@ import { UK_FAMILY_BASELINE_RESULT, UK_FAMILY_REFORM_RESULT, UK_REFORM_RESULT, + US_COUPLE_WITH_CHILDREN_PAYLOAD, } from '@/tests/fixtures/libs/flagship/householdRunMocks'; const mockCreateHousehold = vi.fn(); @@ -41,85 +36,6 @@ vi.mock('@/api/householdCalculation', () => ({ fetchHouseholdCalculation: (...args: unknown[]) => mockFetchHouseholdCalculation(...args), })); -describe('defaultHouseholdInput', () => { - test('given a nationwide report then the household is single, childless, in California', () => { - expect(defaultHouseholdInput()).toEqual({ - state: 'CA', - married: false, - childAges: [], - earnings: 50000, - partnerEarnings: 0, - }); - }); - - test("given a state or district report then the household starts in that report's state", () => { - expect(defaultHouseholdInput('state/ny').state).toBe('NY'); - expect(defaultHouseholdInput('congressional_district/TX-07').state).toBe('TX'); - }); - - test('given a region that is not a state then no state is read from it', () => { - expect(stateFromRegion('us')).toBeUndefined(); - expect(stateFromRegion('state/zz')).toBeUndefined(); - }); -}); - -describe('isValidHouseholdInput', () => { - test('given the default household then it can run', () => { - expect(isValidHouseholdInput(defaultHouseholdInput())).toBe(true); - }); - - test('given negative or missing earnings or an adult child then it cannot run', () => { - const base = defaultHouseholdInput(); - expect(isValidHouseholdInput({ ...base, earnings: -1 })).toBe(false); - expect(isValidHouseholdInput({ ...base, earnings: Number.NaN })).toBe(false); - expect(isValidHouseholdInput({ ...base, childAges: [18] })).toBe(false); - expect(isValidHouseholdInput({ ...base, childAges: [2.5] })).toBe(false); - }); - - test("given a single household then the partner's earnings are not read", () => { - expect(isValidHouseholdInput({ ...defaultHouseholdInput(), partnerEarnings: Number.NaN })).toBe( - true - ); - }); -}); - -describe('buildHouseholdPayload', () => { - test('given a married couple with two children then every person carries the year', () => { - const payload = buildHouseholdPayload(MARRIED_TWO_CHILDREN, HOUSEHOLD_YEAR); - const people = payload.data.people; - - expect(payload.country_id).toBe('us'); - // The API reads the year to calculate from "you"'s age. - expect(people.you.age).toEqual({ [HOUSEHOLD_YEAR]: 30 }); - expect(people.you.employment_income).toEqual({ [HOUSEHOLD_YEAR]: 60000 }); - expect(people['your partner'].employment_income).toEqual({ [HOUSEHOLD_YEAR]: 25000 }); - expect(people['your first dependent'].age).toEqual({ [HOUSEHOLD_YEAR]: 4 }); - expect(people['your second dependent'].age).toEqual({ [HOUSEHOLD_YEAR]: 12 }); - expect(people['your second dependent'].is_tax_unit_dependent).toEqual({ - [HOUSEHOLD_YEAR]: true, - }); - }); - - test('given a state then the household lives there', () => { - const payload = buildHouseholdPayload(MARRIED_TWO_CHILDREN, HOUSEHOLD_YEAR); - - expect(payload.data.households?.['your household'].state_name).toEqual({ - [HOUSEHOLD_YEAR]: 'NY', - }); - expect(payload.data.marital_units?.['your marital unit'].members).toEqual([ - 'you', - 'your partner', - ]); - }); - - test('given a single adult then the household has one person', () => { - const payload = buildHouseholdPayload(defaultHouseholdInput(), HOUSEHOLD_YEAR); - - expect(Object.keys(payload.data.people)).toEqual(['you']); - expect(payload.data.tax_units?.['your tax unit'].members).toEqual(['you']); - }); -}); - describe('readHouseholdTotal', () => { test('given a variable in the API result then its year value is read', () => { expect(readHouseholdTotal(BASELINE_RESULT, 'tax_units', 'income_tax', HOUSEHOLD_YEAR)).toBe( @@ -138,7 +54,7 @@ describe('readHouseholdTotal', () => { describe('compareHouseholds', () => { test('given a federal tax cut then net income and the tax line both show it', () => { - const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT, HOUSEHOLD_YEAR); + const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT, HOUSEHOLD_YEAR, 'us'); expect(impact.netIncome).toEqual({ baseline: 72000, reform: 73000, change: 1000 }); expect(impact.components).toEqual([ @@ -154,7 +70,7 @@ describe('compareHouseholds', () => { }); test('given a bigger Child Tax Credit then the credit is listed and unchanged ones are not', () => { - const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT, HOUSEHOLD_YEAR); + const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT, HOUSEHOLD_YEAR, 'us'); expect(impact.programs.map((line) => [line.label, line.change])).toEqual([ ['Child Tax Credit', 1000], @@ -165,7 +81,8 @@ describe('compareHouseholds', () => { const impact = compareHouseholds( LOW_EARNER_BASELINE_RESULT, LOW_EARNER_REFORM_RESULT, - HOUSEHOLD_YEAR + HOUSEHOLD_YEAR, + 'us' ); expect(impact.netIncome.change).toBe(0); @@ -182,7 +99,8 @@ describe('compareHouseholds', () => { ctcValue: 1500, grossCtc: 6000, }), - HOUSEHOLD_YEAR + HOUSEHOLD_YEAR, + 'us' ); expect(impact.programs).toEqual([ @@ -198,7 +116,12 @@ describe('compareHouseholds', () => { }); test('given net income moving beyond the listed parts then the rest is "Other"', () => { - const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT_WITH_REMAINDER, HOUSEHOLD_YEAR); + const impact = compareHouseholds( + BASELINE_RESULT, + REFORM_RESULT_WITH_REMAINDER, + HOUSEHOLD_YEAR, + 'us' + ); expect(impact.components.at(-1)).toEqual({ id: 'other', @@ -211,7 +134,7 @@ describe('compareHouseholds', () => { }); test('given a tax rise and a benefit cut then both read as losses', () => { - const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT_LOSS, HOUSEHOLD_YEAR); + const impact = compareHouseholds(BASELINE_RESULT, REFORM_RESULT_LOSS, HOUSEHOLD_YEAR, 'us'); expect(impact.netIncome.change).toBe(-900); expect(impact.components.map((line) => [line.id, line.effect])).toEqual([ @@ -222,7 +145,7 @@ describe('compareHouseholds', () => { }); test('given the same result twice then nothing changes', () => { - const impact = compareHouseholds(BASELINE_RESULT, BASELINE_RESULT, HOUSEHOLD_YEAR); + const impact = compareHouseholds(BASELINE_RESULT, BASELINE_RESULT, HOUSEHOLD_YEAR, 'us'); expect(impact.netIncome.change).toBe(0); expect(impact.components).toEqual([]); @@ -234,10 +157,21 @@ describe('compareHouseholds', () => { delete (missing.households as Record>)['your household'] .household_net_income; - expect(() => compareHouseholds(BASELINE_RESULT, missing, HOUSEHOLD_YEAR)).toThrow( + expect(() => compareHouseholds(BASELINE_RESULT, missing, HOUSEHOLD_YEAR, 'us')).toThrow( `The result has no net income for ${HOUSEHOLD_YEAR}.` ); - expect(() => compareHouseholds(missing, BASELINE_RESULT, HOUSEHOLD_YEAR)).toThrow(); + expect(() => compareHouseholds(missing, BASELINE_RESULT, HOUSEHOLD_YEAR, 'us')).toThrow(); + }); + + test('given a country without household result rows then the comparison fails, not reads as the US', () => { + expect(() => + compareHouseholds( + BASELINE_RESULT, + REFORM_RESULT, + HOUSEHOLD_YEAR, + COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS + ) + ).toThrow(`Household results are not available for ${COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS}.`); }); }); @@ -321,7 +255,14 @@ describe('runFlagshipHouseholdPayload for the UK', () => { }); }); -describe('runFlagshipHousehold', () => { +describe('runFlagshipHouseholdPayload', () => { + const runArgs = { + year: HOUSEHOLD_YEAR, + payload: US_COUPLE_WITH_CHILDREN_PAYLOAD, + baselinePolicyId: BASELINE_POLICY_ID, + reformPolicyId: REFORM_POLICY_ID, + }; + beforeEach(() => { vi.clearAllMocks(); mockCreateHousehold.mockResolvedValue({ result: { household_id: CREATED_HOUSEHOLD_ID } }); @@ -332,16 +273,9 @@ describe('runFlagshipHousehold', () => { }); test('given a household then it is created once and calculated under both policies', async () => { - const run = await runFlagshipHousehold({ - year: HOUSEHOLD_YEAR, - input: MARRIED_TWO_CHILDREN, - baselinePolicyId: BASELINE_POLICY_ID, - reformPolicyId: REFORM_POLICY_ID, - }); + const run = await runFlagshipHouseholdPayload(runArgs); - expect(mockCreateHousehold).toHaveBeenCalledWith( - buildHouseholdPayload(MARRIED_TWO_CHILDREN, HOUSEHOLD_YEAR) - ); + expect(mockCreateHousehold).toHaveBeenCalledWith(US_COUPLE_WITH_CHILDREN_PAYLOAD); expect(mockFetchHouseholdCalculation.mock.calls).toEqual([ ['us', String(CREATED_HOUSEHOLD_ID), BASELINE_POLICY_ID], ['us', String(CREATED_HOUSEHOLD_ID), REFORM_POLICY_ID], @@ -354,10 +288,8 @@ describe('runFlagshipHousehold', () => { }); test('given the same policy as baseline and reform then it is calculated once', async () => { - const run = await runFlagshipHousehold({ - year: HOUSEHOLD_YEAR, - input: MARRIED_TWO_CHILDREN, - baselinePolicyId: BASELINE_POLICY_ID, + const run = await runFlagshipHouseholdPayload({ + ...runArgs, reformPolicyId: BASELINE_POLICY_ID, }); @@ -368,30 +300,21 @@ describe('runFlagshipHousehold', () => { test('given a calculation failure then the run fails with it', async () => { mockFetchHouseholdCalculation.mockRejectedValue(new Error('Household calculation failed')); - await expect( - runFlagshipHousehold({ - year: HOUSEHOLD_YEAR, - input: MARRIED_TWO_CHILDREN, - baselinePolicyId: BASELINE_POLICY_ID, - reformPolicyId: REFORM_POLICY_ID, - }) - ).rejects.toThrow('Household calculation failed'); + await expect(runFlagshipHouseholdPayload(runArgs)).rejects.toThrow( + 'Household calculation failed' + ); }); test('given a timeout or a network failure then the run fails in plain words', async () => { - const args = { - year: HOUSEHOLD_YEAR, - input: MARRIED_TWO_CHILDREN, - baselinePolicyId: BASELINE_POLICY_ID, - reformPolicyId: REFORM_POLICY_ID, - }; mockFetchHouseholdCalculation.mockRejectedValue( new Error('Household calculation timed out after 50 seconds (client-side timeout)') ); - await expect(runFlagshipHousehold(args)).rejects.toThrow('The calculation took too long.'); + await expect(runFlagshipHouseholdPayload(runArgs)).rejects.toThrow( + 'The calculation took too long.' + ); mockCreateHousehold.mockRejectedValue(new TypeError('Failed to fetch')); - await expect(runFlagshipHousehold(args)).rejects.toThrow( + await expect(runFlagshipHouseholdPayload(runArgs)).rejects.toThrow( 'Could not reach PolicyEngine. Check your connection.' ); }); @@ -410,20 +333,3 @@ describe('plainHouseholdError', () => { expect(plainHouseholdError(error).cause).toBe(error); }); }); - -describe('describeHouseholdInput', () => { - test('given a household then it reads as one short line', () => { - expect(describeHouseholdInput(MARRIED_TWO_CHILDREN)).toBe( - 'Married · 2 children · $60,000 and $25,000 earnings · New York' - ); - expect(describeHouseholdInput(defaultHouseholdInput())).toBe( - 'Single · no children · $50,000 earnings · California' - ); - }); - - test('given the UK then amounts read in pounds', () => { - expect(describeHouseholdInput(MARRIED_TWO_CHILDREN, 'uk')).toContain( - '£60,000 and £25,000 earnings' - ); - }); -}); From f10f98b0aa45f9153ebee1359f062100762e6346 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:58:19 +0400 Subject: [PATCH 2/8] v3 household runs keep each calculation's .py bundle and SPM provenance The section's run read only the result of each household calculation on the Public API's v1 route (fetchHouseholdCalculation), so the response's policyengine_bundle, spm_config and spm_provenance were dropped. It now calls fetchHouseholdCalculationWithBundle and keeps them per policy on the run (FlagshipHouseholdRun.provenance), for the SPM methodology footnote and for Reproduce in Python. A run that calculates once for both policies gives the reform the baseline's provenance; a response without a bundle records null rather than a guessed version. Tests first: the section's and the run's tests mock only the call that keeps provenance, and three new run tests expect it per policy; all 29 failed while the run still called fetchHouseholdCalculation. Co-Authored-By: Claude Opus 5.5 --- app/src/libs/flagship/householdRun.ts | 47 ++++++++++++--- .../libs/flagship/householdRunMocks.ts | 25 ++++++++ .../report/HouseholdImpactAnalytics.test.tsx | 4 +- .../report/HouseholdImpactSection.test.tsx | 4 +- .../report/ReportViewHousehold.test.tsx | 4 +- .../unit/libs/flagship/householdRun.test.ts | 59 ++++++++++++++++++- 6 files changed, 132 insertions(+), 11 deletions(-) diff --git a/app/src/libs/flagship/householdRun.ts b/app/src/libs/flagship/householdRun.ts index 04ea0dd74..369233c66 100644 --- a/app/src/libs/flagship/householdRun.ts +++ b/app/src/libs/flagship/householdRun.ts @@ -1,8 +1,13 @@ import { createHousehold } from '@/api/household'; -import { fetchHouseholdCalculation } from '@/api/householdCalculation'; +import { + fetchHouseholdCalculationWithBundle, + type HouseholdCalculationResult, +} from '@/api/householdCalculation'; +import type { PolicyEngineBundle } from '@/api/societyWideCalculation'; import type { CountryId } from '@/libs/countries'; import type { V1HouseholdCreateEnvelope } from '@/models/household/v1Types'; import type { HouseholdCalculationData } from '@/types/calculation/household'; +import type { SPMProvenance, SPMSelection } from '@/types/spm'; /** * A household run for the flagship report: a household built in the @@ -324,6 +329,29 @@ export interface FlagshipHouseholdRun extends FlagshipHouseholdImpact { */ baseline: HouseholdCalculationData; reform: HouseholdCalculationData; + /** + * How each policy's calculation was made, as its response says: the + * methodology footnote and Reproduce in Python read it. + */ + provenance: { baseline: HouseholdCalculationProvenance; reform: HouseholdCalculationProvenance }; +} + +/** What one calculation response says about how it was made. */ +export interface HouseholdCalculationProvenance { + /** The .py bundle that calculated it; null when the response names none. */ + policyengineBundle: PolicyEngineBundle | null; + /** The SPM settings the calculation used (US households only). */ + spmConfig?: SPMSelection; + /** The SPM forecast and thresholds the calculation read (US households only). */ + spmProvenance?: SPMProvenance; +} + +function provenanceOf(response: HouseholdCalculationResult): HouseholdCalculationProvenance { + return { + policyengineBundle: response.policyengine_bundle ?? null, + ...(response.spm_config ? { spmConfig: response.spm_config } : {}), + ...(response.spm_provenance ? { spmProvenance: response.spm_provenance } : {}), + }; } /** @@ -347,12 +375,16 @@ export function plainHouseholdError(error: unknown): Error { * the API keeps calculating and caches the result, so the second ask * usually returns it at once. */ -async function calculateOnce(countryId: string, householdId: string, policyId: string) { +async function calculateOnce( + countryId: string, + householdId: string, + policyId: string +): Promise { try { - return await fetchHouseholdCalculation(countryId, householdId, policyId); + return await fetchHouseholdCalculationWithBundle(countryId, householdId, policyId); } catch (error) { if (error instanceof Error && /timed out/i.test(error.message)) { - return fetchHouseholdCalculation(countryId, householdId, policyId); + return fetchHouseholdCalculationWithBundle(countryId, householdId, policyId); } throw error; } @@ -404,8 +436,9 @@ export async function runFlagshipHouseholdPayload({ ); return { householdId, - baseline, - reform, - ...compareHouseholds(baseline, reform, year, payload.country_id), + baseline: baseline.result, + reform: reform.result, + provenance: { baseline: provenanceOf(baseline), reform: provenanceOf(reform) }, + ...compareHouseholds(baseline.result, reform.result, year, payload.country_id), }; } diff --git a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts index 456c44981..59eb50603 100644 --- a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts +++ b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts @@ -1,5 +1,8 @@ +import type { HouseholdCalculationResult } from '@/api/householdCalculation'; +import type { PolicyEngineBundle } from '@/api/societyWideCalculation'; import { Household } from '@/models/Household'; import type { HouseholdCalculationData } from '@/types/calculation/household'; +import type { SPMSelection } from '@/types/spm'; export const HOUSEHOLD_YEAR = '2026'; export const BASELINE_POLICY_ID = '2'; @@ -8,6 +11,28 @@ export const CUSTOM_BASELINE_POLICY_ID = '45678'; export const REFORM_POLICY_ID = '98765'; export const CREATED_HOUSEHOLD_ID = 54321; +/** The .py bundle a calculation response names (synthetic versions, never published pins). */ +export const BASELINE_RUN_BUNDLE: PolicyEngineBundle = { + policyengine_version: '9.1.0', + model_version: '8.1.0', + data_version: null, + dataset: null, +}; +export const REFORM_RUN_BUNDLE: PolicyEngineBundle = { + ...BASELINE_RUN_BUNDLE, + model_version: '8.2.0', +}; +/** The SPM settings a US calculation response echoes. */ +export const NATIONAL_SPM_CONFIG: SPMSelection = { geography_kind: 'national' }; + +/** One policy's calculation as the Public API's v1 household route answers it: the result and how it was made. */ +export function calculationResponse( + result: HouseholdCalculationData, + provenance: Omit = {} +): HouseholdCalculationResult { + return { result, policyengine_bundle: null, ...provenance }; +} + /** A country whose household results v3 does not list (no rows of taxes and benefits). */ export const COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS = 'ca'; diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactAnalytics.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactAnalytics.test.tsx index ff313b57c..16799eed6 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactAnalytics.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactAnalytics.test.tsx @@ -9,6 +9,7 @@ import { Household } from '@/models/Household'; import { BASELINE_POLICY_ID, BASELINE_RESULT, + calculationResponse, CREATED_HOUSEHOLD_ID, HOUSEHOLD_YEAR, REFORM_POLICY_ID, @@ -22,7 +23,8 @@ vi.mock('@/api/household', () => ({ createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), })); vi.mock('@/api/householdCalculation', () => ({ - fetchHouseholdCalculation: (...args: unknown[]) => mockFetchHouseholdCalculation(...args), + fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => + calculationResponse(await mockFetchHouseholdCalculation(...args)), })); /** An input value no event may carry. */ diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx index a38c05f03..e3f0c6071 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx @@ -10,6 +10,7 @@ import HouseholdImpactSection from '@/components/flagship/report/household/House import { BASELINE_POLICY_ID, BASELINE_RESULT, + calculationResponse, CREATED_HOUSEHOLD_ID, HOUSEHOLD_YEAR, LOW_EARNER_BASELINE_RESULT, @@ -28,7 +29,8 @@ vi.mock('@/api/household', () => ({ createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), })); vi.mock('@/api/householdCalculation', () => ({ - fetchHouseholdCalculation: (...args: unknown[]) => mockFetchHouseholdCalculation(...args), + fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => + calculationResponse(await mockFetchHouseholdCalculation(...args)), })); // Saving a calculated household to the user is tested on its own: here only diff --git a/app/src/tests/unit/components/flagship/report/ReportViewHousehold.test.tsx b/app/src/tests/unit/components/flagship/report/ReportViewHousehold.test.tsx index 3bbf9ab9a..3fbf087fe 100644 --- a/app/src/tests/unit/components/flagship/report/ReportViewHousehold.test.tsx +++ b/app/src/tests/unit/components/flagship/report/ReportViewHousehold.test.tsx @@ -4,6 +4,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; import ReportView from '@/components/flagship/report/ReportView'; import { BASELINE_RESULT, + calculationResponse, CREATED_HOUSEHOLD_ID, CUSTOM_BASELINE_POLICY_ID, HOUSEHOLD_YEAR, @@ -21,7 +22,8 @@ vi.mock('@/api/household', () => ({ createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), })); vi.mock('@/api/householdCalculation', () => ({ - fetchHouseholdCalculation: (...args: unknown[]) => mockFetchHouseholdCalculation(...args), + fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => + calculationResponse(await mockFetchHouseholdCalculation(...args)), })); // The v2 household builder, tested on its own, stands in by its state. vi.mock('@/components/household/HouseholdBuilderForm', () => ({ diff --git a/app/src/tests/unit/libs/flagship/householdRun.test.ts b/app/src/tests/unit/libs/flagship/householdRun.test.ts index f440193aa..1161627ad 100644 --- a/app/src/tests/unit/libs/flagship/householdRun.test.ts +++ b/app/src/tests/unit/libs/flagship/householdRun.test.ts @@ -9,22 +9,27 @@ import { Household } from '@/models/Household'; import { BASELINE_POLICY_ID, BASELINE_RESULT, + BASELINE_RUN_BUNDLE, + calculationResponse, COUNTRY_WITHOUT_HOUSEHOLD_RESULT_ROWS, CREATED_HOUSEHOLD_ID, HOUSEHOLD_YEAR, householdResult, LOW_EARNER_BASELINE_RESULT, LOW_EARNER_REFORM_RESULT, + NATIONAL_SPM_CONFIG, REFORM_POLICY_ID, REFORM_RESULT, REFORM_RESULT_LOSS, REFORM_RESULT_WITH_REMAINDER, + REFORM_RUN_BUNDLE, UK_BASELINE_RESULT, UK_FAMILY_BASELINE_RESULT, UK_FAMILY_REFORM_RESULT, UK_REFORM_RESULT, US_COUPLE_WITH_CHILDREN_PAYLOAD, } from '@/tests/fixtures/libs/flagship/householdRunMocks'; +import { SPM_RECEIPT } from '@/tests/fixtures/spm/spmMocks'; const mockCreateHousehold = vi.fn(); const mockFetchHouseholdCalculation = vi.fn(); @@ -32,8 +37,14 @@ const mockFetchHouseholdCalculation = vi.fn(); vi.mock('@/api/household', () => ({ createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), })); +/** How each policy's calculation was made, as its response says it: nothing unless a test sets it. */ +const mockCalculationProvenance = vi.fn(); vi.mock('@/api/householdCalculation', () => ({ - fetchHouseholdCalculation: (...args: unknown[]) => mockFetchHouseholdCalculation(...args), + fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => + calculationResponse( + await mockFetchHouseholdCalculation(...args), + mockCalculationProvenance(...args) + ), })); describe('readHouseholdTotal', () => { @@ -265,6 +276,7 @@ describe('runFlagshipHouseholdPayload', () => { beforeEach(() => { vi.clearAllMocks(); + mockCalculationProvenance.mockReset(); mockCreateHousehold.mockResolvedValue({ result: { household_id: CREATED_HOUSEHOLD_ID } }); mockFetchHouseholdCalculation.mockImplementation( async (_country: string, _household: string, policyId: string) => @@ -297,6 +309,51 @@ describe('runFlagshipHouseholdPayload', () => { expect(run.netIncome.change).toBe(0); }); + test('given each response names its .py bundle and SPM provenance then the run keeps them per policy', async () => { + mockCalculationProvenance.mockImplementation( + (_country: string, _household: string, policyId: string) => ({ + policyengine_bundle: + policyId === BASELINE_POLICY_ID ? BASELINE_RUN_BUNDLE : REFORM_RUN_BUNDLE, + spm_config: NATIONAL_SPM_CONFIG, + spm_provenance: SPM_RECEIPT, + }) + ); + + const run = await runFlagshipHouseholdPayload(runArgs); + + expect(run.provenance).toEqual({ + baseline: { + policyengineBundle: BASELINE_RUN_BUNDLE, + spmConfig: NATIONAL_SPM_CONFIG, + spmProvenance: SPM_RECEIPT, + }, + reform: { + policyengineBundle: REFORM_RUN_BUNDLE, + spmConfig: NATIONAL_SPM_CONFIG, + spmProvenance: SPM_RECEIPT, + }, + }); + }); + + test('given one calculation for both policies then the reform reads its provenance', async () => { + mockCalculationProvenance.mockReturnValue({ policyengine_bundle: BASELINE_RUN_BUNDLE }); + + const run = await runFlagshipHouseholdPayload({ + ...runArgs, + reformPolicyId: BASELINE_POLICY_ID, + }); + + expect(run.provenance.reform).toEqual(run.provenance.baseline); + expect(run.provenance.reform.policyengineBundle).toEqual(BASELINE_RUN_BUNDLE); + }); + + test('given responses without a bundle or SPM provenance then the run says none, not a guess', async () => { + const run = await runFlagshipHouseholdPayload(runArgs); + + expect(run.provenance.baseline).toEqual({ policyengineBundle: null }); + expect(run.provenance.reform).toEqual({ policyengineBundle: null }); + }); + test('given a calculation failure then the run fails with it', async () => { mockFetchHouseholdCalculation.mockRejectedValue(new Error('Household calculation failed')); From 439af61d5c06d4c475b2da516e6cb6b6c2db7e50 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:00:18 +0400 Subject: [PATCH 3/8] v3 households: US SPM geography choice and its methodology footnote The section hid the household builder's Supplemental Poverty Measure geography choice (hideSpmGeography) and ran every US household on national thresholds. It now shows the choice: a US household still starts on national thresholds, and the reader can pick local thresholds by county, with a county FIPS code. The household's spm setting and county_fips are sent with it, as in the household builder. With a real choice, the methodology footnote says something again: - Under the summary and the net income breakdown, the result shows the calculation's SPM settings and receipt (SPMMethodologyFootnote), read from each policy's response. One calculation for both policies shows once. A response without a receipt (any UK household) shows nothing. - The earnings variation and marginal tax rate views no longer hide their own variation footnote. SPMMethodologyFootnote now also takes just a calculation's SPM settings and receipt, so the v3 run's provenance can be shown without a full household output. Tests first: the builder's stand-in renders the real SPMGeographyChoice unless hidden. The choice on national thresholds, a county choice sent with its FIPS code, the methodology under a result, and both variation views keeping their footnote failed before the change (5 tests). Co-Authored-By: Claude Opus 5.5 --- .../report/household/HouseholdBreakdowns.tsx | 2 - .../household/HouseholdImpactResult.tsx | 23 ++- .../household/HouseholdImpactSection.tsx | 12 +- .../household/SPMMethodologyFootnote.tsx | 6 +- .../report/HouseholdBreakdownsSPM.test.tsx | 78 ++++++++++ .../report/HouseholdImpactSection.test.tsx | 135 +++++++++++++++--- 6 files changed, 232 insertions(+), 24 deletions(-) create mode 100644 app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx diff --git a/app/src/components/flagship/report/household/HouseholdBreakdowns.tsx b/app/src/components/flagship/report/household/HouseholdBreakdowns.tsx index 24689e0ef..93ae4e0d0 100644 --- a/app/src/components/flagship/report/household/HouseholdBreakdowns.tsx +++ b/app/src/components/flagship/report/household/HouseholdBreakdowns.tsx @@ -169,8 +169,6 @@ export default function HouseholdBreakdowns({ reform, simulations, policies: policyQuery.policies, - // v3 households use national thresholds, so the footnote says nothing new. - hideSpmFootnote: true, renderLoading: () => ( ('summary'); const { netIncome, components, programs } = impact; @@ -393,6 +409,11 @@ export default function HouseholdImpactResult({ ) : ( )} + + {/* The variation views show their own calculation's methodology. */} + {methodology && (view === 'summary' || view === 'net-income') && ( + + )} ); } diff --git a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx index a2ae81250..bf59d7275 100644 --- a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx +++ b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx @@ -75,8 +75,9 @@ export function ukNationPlace(region: string | undefined): Record; + /** Display calculation receipts in the results, without claiming certification. */ export default function SPMMethodologyFootnote({ output, context = 'point', }: { - output: (HouseholdCalculationOutput | null)[]; + /** The baseline's calculation first, then the reform's (null when there is none). */ + output: (HouseholdCalculationOutput | SPMMethodologySource | null)[]; context?: 'point' | 'variation'; }) { const receipts = output diff --git a/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx new file mode 100644 index 000000000..c2d26de33 --- /dev/null +++ b/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx @@ -0,0 +1,78 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { render, screen } from '@test-utils'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; +import HouseholdBreakdowns, { + type HouseholdBreakdownSource, + type HouseholdBreakdownView, +} from '@/components/flagship/report/household/HouseholdBreakdowns'; +import { + BASELINE_POLICY_ID, + BASELINE_RESULT, + CREATED_HOUSEHOLD_ID, + HOUSEHOLD_YEAR, + REFORM_POLICY_ID, + REFORM_RESULT, +} from '@/tests/fixtures/libs/flagship/householdRunMocks'; + +vi.mock('@/api/policy', () => ({ + fetchPolicyById: async (_country: string, policyId: string) => ({ + id: policyId, + country_id: 'us', + api_version: '1', + policy_json: {}, + label: null, + }), +})); +vi.mock('@/hooks/useParameterMetadata', () => ({ + useParameterMetadata: () => ({ parameters: {}, isReady: true }), +})); +// The v2 variation pages, tested on their own: stand-ins show whether the +// SPM methodology footnote under their chart is left out. +vi.mock('@/pages/report-output/earnings-variation/EarningsVariationSubPage', () => ({ + default: ({ hideSpmFootnote }: { hideSpmFootnote?: boolean }) => ( +
{hideSpmFootnote ? 'footnote hidden' : 'footnote'}
+ ), +})); +vi.mock('@/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage', () => ({ + default: ({ hideSpmFootnote }: { hideSpmFootnote?: boolean }) => ( +
{hideSpmFootnote ? 'footnote hidden' : 'footnote'}
+ ), +})); + +const SOURCE: HouseholdBreakdownSource = { + countryId: 'us', + year: HOUSEHOLD_YEAR, + householdId: String(CREATED_HOUSEHOLD_ID), + baselinePolicyId: BASELINE_POLICY_ID, + reformPolicyId: REFORM_POLICY_ID, + baseline: BASELINE_RESULT, + reform: REFORM_RESULT, + gains: true, +}; + +function renderBreakdowns(view: HouseholdBreakdownView) { + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return render( + + + + ); +} + +describe('HouseholdBreakdowns SPM methodology', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test.each([ + ['earnings-variation', 'Earnings variation'], + ['marginal-tax-rates', 'Marginal tax rates'], + ] as const)( + 'given %s then the page keeps its SPM methodology footnote', + async (view, pageName) => { + renderBreakdowns(view); + + expect(await screen.findByLabelText(pageName)).toHaveTextContent(/^footnote$/); + } + ); +}); diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx index e3f0c6071..4608be956 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx @@ -15,12 +15,14 @@ import { HOUSEHOLD_YEAR, LOW_EARNER_BASELINE_RESULT, LOW_EARNER_REFORM_RESULT, + NATIONAL_SPM_CONFIG, REFORM_POLICY_ID, REFORM_RESULT, REFORM_RESULT_LOSS, UK_BASELINE_RESULT, UK_REFORM_RESULT, } from '@/tests/fixtures/libs/flagship/householdRunMocks'; +import { SPM_RECEIPT } from '@/tests/fixtures/spm/spmMocks'; const mockCreateHousehold = vi.fn(); const mockFetchHouseholdCalculation = vi.fn(); @@ -28,9 +30,14 @@ const mockFetchHouseholdCalculation = vi.fn(); vi.mock('@/api/household', () => ({ createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), })); +/** How each policy's calculation was made, as its response says it: nothing unless a test sets it. */ +const mockCalculationProvenance = vi.fn(); vi.mock('@/api/householdCalculation', () => ({ fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => - calculationResponse(await mockFetchHouseholdCalculation(...args)), + calculationResponse( + await mockFetchHouseholdCalculation(...args), + mockCalculationProvenance(...args) + ), })); // Saving a calculated household to the user is tested on its own: here only @@ -43,19 +50,41 @@ vi.mock('@/hooks/useFlagshipHousehold', async (importOriginal) => ({ // The household builder is the v2 builder, tested on its own: here a stand-in // makes the same calls it makes, so the section's own work is what is tested. -vi.mock('@/components/household/HouseholdBuilderForm', () => ({ - default: ({ - household, - year, - maritalStatus, - numChildren, - onChange, - onMaritalStatusChange, - onNumChildrenChange, - variablePickerCommon, - personDisplayNameOf, - labelInputs, - }: any) => ( +vi.mock('@/components/household/HouseholdBuilderForm', async () => { + const { default: SPMGeographyChoice } = await import('@/components/household/SPMGeographyChoice'); + const { RESOLVED_CANONICAL_METADATA } = await import('@/tests/fixtures/spm/spmMocks'); + return { + default: (props: any) => ( + + ), + }; +}); + +/** The builder's stand-in: the real SPM choice (unless hidden), and the builder's own calls. */ +function HouseholdBuilderStandIn({ + household, + year, + maritalStatus, + numChildren, + onChange, + onMaritalStatusChange, + onNumChildrenChange, + variablePickerCommon, + personDisplayNameOf, + labelInputs, + hideSpmGeography, + SPMGeographyChoice, + spmMetadata, +}: any) { + return (
input.name).join(' ')} @@ -69,6 +98,14 @@ vi.mock('@/components/household/HouseholdBuilderForm', () => ({ ) .join(' ')} > + {!hideSpmGeography && ( + + )} {`${maritalStatus} · ${numChildren}`} {String( @@ -92,8 +129,8 @@ vi.mock('@/components/household/HouseholdBuilderForm', () => ({ Partner earns 20000
- ), -})); + ); +} // The v2 breakdowns, tested on their own: a stand-in shows what each is given. vi.mock('@/components/flagship/report/household/HouseholdBreakdowns', async (importOriginal) => ({ ...(await importOriginal< @@ -152,6 +189,7 @@ function calculateWith(reformResult: typeof REFORM_RESULT) { describe('HouseholdImpactSection', () => { beforeEach(() => { vi.clearAllMocks(); + mockCalculationProvenance.mockReset(); mockCreateHousehold.mockResolvedValue({ result: { household_id: CREATED_HOUSEHOLD_ID } }); calculateWith(REFORM_RESULT); // Radix Select scrolls its options and captures the pointer, which jsdom lacks. @@ -249,6 +287,63 @@ describe('HouseholdImpactSection', () => { expect(JSON.stringify(sent)).toContain('national'); }); + test('given a US household then the SPM geography choice shows, on national thresholds', () => { + renderSection(); + + expect( + screen.getByRole('combobox', { name: 'Supplemental Poverty Measure geography' }) + ).toHaveValue('national'); + }); + + test('given local thresholds and a county then the household is sent with them', async () => { + const user = userEvent.setup(); + renderSection(); + + await user.selectOptions( + screen.getByRole('combobox', { name: 'Supplemental Poverty Measure geography' }), + 'county' + ); + await user.type(screen.getByRole('textbox', { name: /County FIPS code/ }), '06037'); + await user.click(screen.getByRole('button', { name: 'Calculate' })); + + await waitFor(() => expect(mockCreateHousehold).toHaveBeenCalledTimes(1)); + const sent = mockCreateHousehold.mock.calls[0][0]; + expect(sent.spm).toEqual({ geography_kind: 'county' }); + expect(sent.data.households['your household'].county_fips).toEqual({ + [HOUSEHOLD_YEAR]: '06037', + }); + }); + + test('given a calculation with SPM provenance then its methodology shows under the result', async () => { + const user = userEvent.setup(); + mockCalculationProvenance.mockReturnValue({ + spm_config: NATIONAL_SPM_CONFIG, + spm_provenance: SPM_RECEIPT, + }); + renderSection(); + + await user.click(screen.getByRole('button', { name: 'Calculate' })); + + const result = await screen.findByRole('region', { name: 'Household impact' }); + const methodology = within(result).getByRole('region', { + name: 'SPM point calculation methodology', + }); + expect(methodology).toHaveTextContent('Baseline · Point calculation · National thresholds'); + expect(methodology).toHaveTextContent('Reform · Point calculation · National thresholds'); + }); + + test('given a calculation without SPM provenance then no methodology shows', async () => { + const user = userEvent.setup(); + renderSection(); + + await user.click(screen.getByRole('button', { name: 'Calculate' })); + + const result = await screen.findByRole('region', { name: 'Household impact' }); + expect( + within(result).queryByRole('region', { name: 'SPM point calculation methodology' }) + ).not.toBeInTheDocument(); + }); + test('given Calculate then the run shows its progress, then the change in net income', async () => { const user = userEvent.setup(); let finish!: () => void; @@ -592,6 +687,14 @@ describe('HouseholdImpactSection', () => { expect(result).not.toHaveTextContent('$'); }); + test('then no SPM geography choice shows', () => { + renderSection({ countryId: 'uk' }); + + expect( + screen.queryByRole('combobox', { name: 'Supplemental Poverty Measure geography' }) + ).not.toBeInTheDocument(); + }); + test("then the picker features the UK's inputs", () => { renderSection({ countryId: 'uk' }); const featured = screen From aa692760ccd26c23f683c333dabd247ab031069b Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:03:40 +0400 Subject: [PATCH 4/8] v3 UK households: suggest the inputs a reform reads, from a traced UK map The household section leads its variable search with the inputs a reform's parameters read, from a dependency map traced from the model. Only policyengine-us had a map, so UK reports suggested nothing. - build-parameter-dependency-map.py takes --country. The UK map traces policyengine-uk 2.130.1 with policyengine-core 3.33.0 (the versions the Public API calculates with) over 400 generated households: the UK microdata is not public, and which parameters a formula reads does not depend on the households, only on which formulas run, so the population varies ages, couples, children, pensioners, disability, every region, tenure and council tax band, and each income source. All 1,163 variables calculate; the map has 688 parameter paths and 989 consumed variables (195 KB), loaded only by UK reports' household section. - loadParameterDependencies(countryId) loads each country's map once, and every caller names its country: the validation surfaces (scorecard matches, calibration matches, reform readers and scorecard comparisons) ask for the US map by name, and a call without a country loads none. A name every object inherits, such as "constructor", is not a country with a map. - The section loads the report's country's map, and suggests only inputs a reader describes a household with, so inputs only the microdata sets (such as a public-spending imputation) no longer use up the list. Tests first: the UK map's package and readers, UK suggestions in the library and in the section, a call without a country rejecting (with @ts-expect-error, unused until the country became required), and the inherited-name case all failed before the change. Co-Authored-By: Claude Opus 5.5 --- app/scripts/build-parameter-dependency-map.py | 157 ++++++++++++++---- .../flagship/ScorecardComparisons.tsx | 5 +- .../components/flagship/ValidationPanel.tsx | 12 +- .../household/HouseholdImpactSection.tsx | 19 ++- .../flagship/parameterDependencies.uk.json | 1 + app/src/libs/flagship/calibrationMatching.ts | 2 +- app/src/libs/flagship/modelValidation.ts | 2 +- .../libs/flagship/parameterDependencies.ts | 63 ++++--- .../report/householdSuggestionMocks.ts | 46 +++++ .../HouseholdImpactSuggestions.test.tsx | 76 +++++++++ .../flagship/householdSuggestions.test.ts | 27 ++- .../flagship/parameterDependencies.test.ts | 39 +++++ 12 files changed, 379 insertions(+), 70 deletions(-) create mode 100644 app/src/data/flagship/parameterDependencies.uk.json create mode 100644 app/src/tests/fixtures/components/flagship/report/householdSuggestionMocks.ts create mode 100644 app/src/tests/unit/components/flagship/report/HouseholdImpactSuggestions.test.tsx diff --git a/app/scripts/build-parameter-dependency-map.py b/app/scripts/build-parameter-dependency-map.py index ddf72ccb4..470e54843 100644 --- a/app/scripts/build-parameter-dependency-map.py +++ b/app/scripts/build-parameter-dependency-map.py @@ -2,21 +2,30 @@ The flagship validation layer needs to know which model variables a reform touches, so it can look those variables up in the calibration dashboard and -the scorecard. Grepping formula source for parameter paths misses bracket, -scale, and vectorised accesses; the only exact record is what the model -actually reads at run time. This script runs policyengine-us with -policyengine-core's FullTracer over a subsample of the default microdata, -calculates every variable, and writes two edge sets: +the scorecard, and the report's household section uses the same map to +suggest the household inputs a reform reads. Grepping formula source for +parameter paths misses bracket, scale, and vectorised accesses; the only +exact record is what the model actually reads at run time. This script runs +a country model with policyengine-core's FullTracer, calculates every +variable, and writes two edge sets: readers[parameter_path] -> variables whose formula read that parameter consumers[variable] -> variables whose formula read that variable -Output: app/src/data/flagship/parameterDependencies.json (consumed by -app/src/libs/flagship/parameterDependencies.ts). +The US map traces policyengine-us over a subsample of its default microdata. +The UK map traces policyengine-uk over a generated population of households +(see uk_situation): the UK microdata is not public, and the formula reads do +not depend on which households run, only on which formulas run, so a +population varied enough to reach every program's eligible cases serves. -Run from a Python environment with policyengine-us installed: +Output (consumed by app/src/libs/flagship/parameterDependencies.ts): - python app/scripts/build-parameter-dependency-map.py [--households N] [--year YYYY] + us: app/src/data/flagship/parameterDependencies.json + uk: app/src/data/flagship/parameterDependencies.uk.json + +Run from a Python environment with the country's package installed: + + python app/scripts/build-parameter-dependency-map.py [--country us|uk] [--households N] [--year YYYY] Two core quirks are handled here rather than upstream: * core caches the yearly ParameterNodeAtInstant before its own tracing @@ -35,6 +44,7 @@ import argparse import json +import random import sys import time from collections import defaultdict @@ -42,13 +52,12 @@ from importlib.metadata import version from pathlib import Path -OUTPUT = ( - Path(__file__).resolve().parent.parent - / "src" - / "data" - / "flagship" - / "parameterDependencies.json" -) +DATA_DIRECTORY = Path(__file__).resolve().parent.parent / "src" / "data" / "flagship" +OUTPUTS = { + "us": DATA_DIRECTORY / "parameterDependencies.json", + "uk": DATA_DIRECTORY / "parameterDependencies.uk.json", +} +PACKAGES = {"us": "policyengine-us", "uk": "policyengine-uk"} # The simulation's tracer, once tracing is on (see patch_tracer). TRACING: dict = {} @@ -172,24 +181,110 @@ def walk(node) -> None: return readers, consumers +# The UK inputs the generated population varies, by how each is drawn. Enum +# inputs draw any of the model's own values, so the list never names a value +# the model does not have. +UK_PERSON_AMOUNTS = { + "employment_income": (0.6, 150_000), + "self_employment_income": (0.15, 80_000), + "private_pension_income": (0.2, 40_000), + "savings_interest_income": (0.4, 5_000), + "dividend_income": (0.15, 60_000), + "property_income": (0.1, 30_000), + "childcare_expenses": (0.15, 12_000), + "hours_worked": (0.6, 2_400), +} +UK_PERSON_FLAGS = {"is_disabled_for_benefits": 0.15, "is_blind": 0.03} +UK_PERSON_ENUMS = ("pip_dl_category", "pip_m_category", "dla_sc_category", "dla_m_category") +UK_HOUSEHOLD_AMOUNTS = {"rent": (0.4, 18_000)} +UK_HOUSEHOLD_ENUMS = ("region", "tenure_type", "council_tax_band") + + +def enum_names(system, variable_name: str) -> list[str]: + variable = system.variables[variable_name] + return [member.name for member in variable.possible_values] + + +def uk_situation(system, households: int, year: int, seed: int) -> dict: + """A varied UK population: singles, couples, children, pensioners, carers' cases, + every region, tenure and council tax band, and each income source, drawn with a + fixed seed so the map is reproducible.""" + draw = random.Random(seed) + period = str(year) + people: dict = {} + benunits: dict = {} + household_records: dict = {} + for household_index in range(households): + adult_count = draw.choice((1, 1, 2, 2, 2)) + child_count = draw.choice((0, 0, 1, 2, 3, 4)) + # Some households are of pension age, some of working age. + adult_age_range = draw.choice(((18, 30), (25, 64), (40, 66), (66, 95))) + members = [] + for adult_index in range(adult_count): + person_name = f"household {household_index} adult {adult_index}" + person = {"age": {period: draw.randint(*adult_age_range)}} + for variable_name, (share, ceiling) in UK_PERSON_AMOUNTS.items(): + if draw.random() < share: + person[variable_name] = {period: round(draw.uniform(0, ceiling))} + for variable_name, share in UK_PERSON_FLAGS.items(): + person[variable_name] = {period: draw.random() < share} + for variable_name in UK_PERSON_ENUMS: + if draw.random() < 0.15: + person[variable_name] = { + period: draw.choice(enum_names(system, variable_name)) + } + people[person_name] = person + members.append(person_name) + for child_index in range(child_count): + person_name = f"household {household_index} child {child_index}" + people[person_name] = {"age": {period: draw.randint(0, 19)}} + if draw.random() < 0.05: + people[person_name]["is_disabled_for_benefits"] = {period: True} + members.append(person_name) + benunits[f"household {household_index} benefit unit"] = {"members": members} + household = {"members": list(members)} + for variable_name, (share, ceiling) in UK_HOUSEHOLD_AMOUNTS.items(): + if draw.random() < share: + household[variable_name] = {period: round(draw.uniform(0, ceiling))} + for variable_name in UK_HOUSEHOLD_ENUMS: + household[variable_name] = {period: draw.choice(enum_names(system, variable_name))} + household_records[f"household {household_index}"] = household + return {"people": people, "benunits": benunits, "households": household_records} + + +def load_simulation(country: str, households: int, year: int): + """The traced simulation and the name of the data it runs on.""" + if country == "us": + from policyengine_us import Microsimulation + + simulation = Microsimulation() + # Subsampling rewraps the data as a plain dataframe; keep the real name. + dataset_name = getattr(simulation.dataset, "name", None) + simulation = simulation.subsample(n=households, seed=0) or simulation + return simulation, dataset_name + from policyengine_uk import CountryTaxBenefitSystem, Simulation + + situation = uk_situation(CountryTaxBenefitSystem(), households, year, seed=0) + simulation = Simulation(situation=situation, trace=True) + return simulation, "generated households (scripts/build-parameter-dependency-map.py)" + + def main() -> None: parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) - parser.add_argument("--households", type=int, default=2000) + parser.add_argument("--country", choices=sorted(OUTPUTS), default="us") + parser.add_argument("--households", type=int, default=None) parser.add_argument("--year", type=int, default=2026) - parser.add_argument("--output", type=Path, default=OUTPUT) + parser.add_argument("--output", type=Path, default=None) args = parser.parse_args() + households = args.households or (2000 if args.country == "us" else 400) + output = args.output or OUTPUTS[args.country] patch_tracer() - from policyengine_us import Microsimulation - started = time.time() - simulation = Microsimulation() - # Subsampling rewraps the data as a plain dataframe; keep the real name. - dataset_name = getattr(simulation.dataset, "name", None) - simulation = simulation.subsample(n=args.households, seed=0) or simulation + simulation, dataset_name = load_simulation(args.country, households, args.year) simulation.trace = True enable_parameter_tracing(simulation) - print(f"loaded microdata subsample in {time.time() - started:.0f}s", file=sys.stderr) + print(f"loaded {households} households in {time.time() - started:.0f}s", file=sys.stderr) started = time.time() outcomes = calculate_everything(simulation, args.year) @@ -202,11 +297,11 @@ def main() -> None: payload = { "generatedAt": datetime.now(timezone.utc).isoformat(timespec="seconds"), "model": { - "package": "policyengine-us", - "version": version("policyengine-us"), + "package": PACKAGES[args.country], + "version": version(PACKAGES[args.country]), "coreVersion": version("policyengine-core"), "dataset": dataset_name, - "households": args.households, + "households": households, "year": args.year, }, "variablesCalculated": outcomes["ok"], @@ -214,10 +309,10 @@ def main() -> None: "readers": {path: sorted(names) for path, names in sorted(readers.items())}, "consumers": {name: sorted(users) for name, users in sorted(consumers.items())}, } - args.output.parent.mkdir(parents=True, exist_ok=True) - args.output.write_text(json.dumps(payload, separators=(",", ":")) + "\n") + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(payload, separators=(",", ":")) + "\n") print( - f"wrote {args.output} — {len(readers)} parameter paths, {len(consumers)} consumed variables", + f"wrote {output} — {len(readers)} parameter paths, {len(consumers)} consumed variables", file=sys.stderr, ) diff --git a/app/src/components/flagship/ScorecardComparisons.tsx b/app/src/components/flagship/ScorecardComparisons.tsx index 8d805388f..b174de3ba 100644 --- a/app/src/components/flagship/ScorecardComparisons.tsx +++ b/app/src/components/flagship/ScorecardComparisons.tsx @@ -782,10 +782,11 @@ async function fetchScorecardComparisons( signal: AbortSignal ): Promise { let variables: string[] = []; - // The committed dependency map is US-only. Never use it for another country. + // The scorecard is the US model's: only the US dependency map is read, and + // never for another country. if (countryId === 'us') { try { - variables = variablesReachedByPaths(paths, await loadParameterDependencies()).map( + variables = variablesReachedByPaths(paths, await loadParameterDependencies('us')).map( (entry) => entry.variable ); } catch { diff --git a/app/src/components/flagship/ValidationPanel.tsx b/app/src/components/flagship/ValidationPanel.tsx index 46352562b..299744e41 100644 --- a/app/src/components/flagship/ValidationPanel.tsx +++ b/app/src/components/flagship/ValidationPanel.tsx @@ -258,8 +258,9 @@ export function trackRecordPrograms(trackRecord: ModelTrackRecord): string[] | n * at page level, not inside a tab panel, so the request runs in * parallel with the rest of the report instead of starting on tab * click (inactive tab panels are unmounted). The track record is the - * US model's (its dependency map and comparison table are US-only), so - * another country gets an empty one, and an unknown country waits. + * US model's (it reads the US dependency map, and its comparison table + * is US-only), so another country gets an empty one, and an unknown + * country waits. */ export function useModelTrackRecord( paths: string[], @@ -352,8 +353,9 @@ const NO_READERS: Record = {}; /** * The variables that read each reform path directly, from the traced - * dependency map: `undefined` while it loads. The map is US-only, so - * another country, or a map that fails to load, matches on the paths alone. + * dependency map: `undefined` while it loads. Only the US map is read here, + * as the validation data is the US model's, so another country, or a map + * that fails to load, matches on the paths alone. */ export function useReformReaders( countryId: string, @@ -368,7 +370,7 @@ export function useReformReaders( } const request = JSON.parse(requestKey) as { paths: string[] }; let cancelled = false; - loadParameterDependencies() + loadParameterDependencies('us') .then((map) => Object.fromEntries(request.paths.map((path) => [path, readersOfPath(path, map)])) ) diff --git a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx index bf59d7275..6f366092c 100644 --- a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx +++ b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx @@ -188,15 +188,22 @@ interface HouseholdImpactSectionProps { } /** - * The household inputs the reform's parameters read, nearest first; none - * until the dependency map loads, or when it traces none of them, or for - * a country without a traced map. + * The household inputs the reform's parameters read, nearest first, from + * the country's own traced map; none until the map loads, or when it + * traces none of them, or for a country without a traced map. Only inputs + * a reader describes a household with are suggested, as the builder's + * search lists only those. */ function useSuggestedInputs(paths: string[], countryId: string): string[] { const metadata = useSelector((state: RootState) => state.metadata); const inputs = useMemo( - () => new Set(getInputVariables(metadata).map((variable) => variable.name)), - [metadata] + () => + new Set( + getInputVariables(metadata) + .filter((variable) => describesAHousehold(variable, countryId)) + .map((variable) => variable.name) + ), + [metadata, countryId] ); const preferred = useMemo( () => new Set(commonHouseholdInputs(countryId).map((input) => input.name)), @@ -210,7 +217,7 @@ function useSuggestedInputs(paths: string[], countryId: string): string[] { setSuggested([]); return; } - loadParameterDependencies() + loadParameterDependencies(countryId) .then((map) => { if (!cancelled) { setSuggested( diff --git a/app/src/data/flagship/parameterDependencies.uk.json b/app/src/data/flagship/parameterDependencies.uk.json new file mode 100644 index 000000000..a92ecf70b --- /dev/null +++ b/app/src/data/flagship/parameterDependencies.uk.json @@ -0,0 +1 @@ +{"generatedAt":"2026-10-21T22:01:00+00:00","model":{"package":"policyengine-uk","version":"2.130.1","coreVersion":"3.33.0","dataset":"generated households (scripts/build-parameter-dependency-map.py)","households":400,"year":2026},"variablesCalculated":1163,"variablesFailed":0,"readers":{"gov.boe.base_rate":["plan_1_interest_rate"],"gov.contrib.abolish_council_tax":["household_benefits","pre_budget_change_household_benefits"],"gov.contrib.benefit_uprating.all":["household_benefits","pre_budget_change_household_benefits"],"gov.contrib.benefit_uprating.non_sp":["household_benefits","pre_budget_change_household_benefits"],"gov.contrib.cec.non_primary_residence_wealth_tax":["non_primary_residence_wealth_tax"],"gov.contrib.disability_basic_income.amount":["disability_basic_income"],"gov.contrib.freeze_pension_credit":["in_receipt_of_guarantee_credit","in_receipt_of_savings_credit_only","pension_credit"],"gov.contrib.labour.private_school_vat":["private_school_vat"],"gov.contrib.policyengine.budget.consumer_incident_tax_change":["consumer_incident_tax_revenue_change"],"gov.contrib.policyengine.budget.corporate_incident_tax_change":["corporate_incident_tax_revenue_change"],"gov.contrib.policyengine.budget.education":["education_budget_change"],"gov.contrib.policyengine.budget.nhs":["nhs_budget_change"],"gov.contrib.policyengine.budget.other_public_spending":["other_public_spending_budget_change"],"gov.contrib.policyengine.economy.gdp_per_capita":["household_market_income"],"gov.contrib.policyengine.employer_ni.capital_incidence":["employer_ni_response_capital_incidence"],"gov.contrib.policyengine.employer_ni.consumer_incidence":["employer_ni_response_consumer_incidence"],"gov.contrib.policyengine.employer_ni.employee_incidence":["employer_ni_fixed_employer_cost_change","employer_ni_response_capital_incidence","employer_ni_response_consumer_incidence"],"gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions":["adjusted_employer_cost","employer_ni_fixed_employer_cost_change","ni_class_1_employer"],"gov.contrib.ubi_center.basic_income.amount.adult_age":["bi_maximum"],"gov.contrib.ubi_center.basic_income.amount.by_age.child":["bi_maximum"],"gov.contrib.ubi_center.basic_income.amount.by_age.senior":["bi_maximum"],"gov.contrib.ubi_center.basic_income.amount.by_age.working_age":["bi_maximum"],"gov.contrib.ubi_center.basic_income.amount.child_min_age":["bi_maximum"],"gov.contrib.ubi_center.basic_income.amount.flat":["bi_maximum"],"gov.contrib.ubi_center.basic_income.interactions.include_in_means_tests":["council_tax_reduction_applicable_income","housing_benefit_applicable_income","income_support_applicable_income","pension_credit_income","tax_credits_current_year_income","uc_income_tax_on_earnings","uc_income_tax_on_trade","uc_individual_earned_income_before_mif"],"gov.contrib.ubi_center.basic_income.interactions.include_in_taxable_income":["net_income"],"gov.contrib.ubi_center.basic_income.interactions.withdraw_cb":["child_benefit_respective_amount"],"gov.contrib.ubi_center.basic_income.phase_out.household.rate":["bi_household_phaseout"],"gov.contrib.ubi_center.basic_income.phase_out.household.threshold":["bi_household_phaseout"],"gov.contrib.ubi_center.basic_income.phase_out.individual.rate":["bi_individual_phaseout"],"gov.contrib.ubi_center.basic_income.phase_out.individual.threshold":["bi_individual_phaseout"],"gov.contrib.ubi_center.carbon_tax.consumer_incidence":["carbon_tax"],"gov.contrib.ubi_center.carbon_tax.rate":["carbon_tax"],"gov.contrib.ubi_center.land_value_tax.corporate_rate":["LVT"],"gov.contrib.ubi_center.land_value_tax.household_rate":["LVT"],"gov.contrib.ubi_center.land_value_tax.rate":["LVT"],"gov.contrib.ubi_center.wealth_tax":["wealth_tax"],"gov.dcms.bbc.tv_licence.colour":["free_tv_licence_value","tv_licence"],"gov.dcms.bbc.tv_licence.discount.blind.discount":["tv_licence_discount"],"gov.dfe.adult_dependants_grant.income_limit":["adult_dependants_grant_eligible"],"gov.dfe.adult_dependants_grant.other_adult_income_limit":["adult_dependants_grant_adult_eligible"],"gov.dfe.bursary_fund_16_to_19.age.minimum":["bursary_fund_16_to_19_age_eligible"],"gov.dfe.bursary_fund_16_to_19.age.standard_upper":["bursary_fund_16_to_19_age_eligible"],"gov.dfe.care_to_learn.age_limit":["care_to_learn_eligible"],"gov.dfe.childcare_funding_rate":["extended_childcare_entitlement_total_per_child","targeted_childcare_entitlement","universal_childcare_entitlement"],"gov.dfe.childcare_grant.eligible_child.max_age.special_educational_needs":["childcare_grant_child_eligible"],"gov.dfe.childcare_grant.eligible_child.max_age.standard":["childcare_grant_child_eligible"],"gov.dfe.childcare_grant.income_limit.one_child":["childcare_grant_eligible"],"gov.dfe.childcare_grant.income_limit.two_or_more_children":["childcare_grant_eligible"],"gov.dfe.compulsory_school_age":["extended_childcare_entitlement_qualifying_child","is_of_compulsory_school_age","uc_expected_hours"],"gov.dfe.extended_childcare_entitlement.disability_criteria":["extended_childcare_entitlement_limited_capability_or_specified_benefit"],"gov.dfe.extended_childcare_entitlement.exemption.income_limit_applies":["extended_childcare_entitlement_work_condition"],"gov.dfe.extended_childcare_entitlement.exemption.partner_exclusion_criteria":["extended_childcare_entitlement_work_condition"],"gov.dfe.extended_childcare_entitlement.hours":["extended_childcare_entitlement_total_per_child","is_child_receiving_extended_childcare"],"gov.dfe.extended_childcare_entitlement.income.countable_sources":["extended_childcare_entitlement_meets_income_requirements"],"gov.dfe.extended_childcare_entitlement.income.limit":["extended_childcare_entitlement_meets_income_requirements","extended_childcare_entitlement_work_condition"],"gov.dfe.extended_childcare_entitlement.minimum_weekly_hours":["extended_childcare_entitlement_meets_income_requirements"],"gov.dfe.extended_childcare_entitlement.young_child_minimum_age":["extended_childcare_entitlement_qualifying_child"],"gov.dfe.maintenance_loans.full_time.maximum.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.maximum.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.maximum.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.maximum_entitled_to_benefits.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.maximum_entitled_to_benefits.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.maximum_entitled_to_benefits.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.entitled_to_benefits.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.entitled_to_benefits.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.entitled_to_benefits.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.not_entitled.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.not_entitled.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.minimum_rate.not_entitled.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.first_stage.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.first_stage.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.first_stage.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.second_stage.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.second_stage.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.entitled_to_benefits.second_stage.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.not_entitled.away_in_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.not_entitled.away_outside_london":["maintenance_loan"],"gov.dfe.maintenance_loans.full_time.taper.not_entitled.living_with_parents":["maintenance_loan"],"gov.dfe.maintenance_loans.household_income.higher_threshold":["maintenance_loan"],"gov.dfe.maintenance_loans.household_income.threshold":["maintenance_loan"],"gov.dfe.maintenance_loans.independent_student_age":["maintenance_loan_household_income"],"gov.dfe.maintenance_loans.over_60.max_amount":["maintenance_loan"],"gov.dfe.maintenance_loans.over_60.maximum_income":["maintenance_loan"],"gov.dfe.maintenance_loans.over_60.minimum_amount":["maintenance_loan"],"gov.dfe.maintenance_loans.over_60.reduction_per_pound":["maintenance_loan"],"gov.dfe.parents_learning_allowance.income_limit":["parents_learning_allowance_eligible"],"gov.dfe.targeted_childcare_entitlement.age_eligibility":["is_child_receiving_targeted_childcare","targeted_childcare_entitlement"],"gov.dfe.targeted_childcare_entitlement.hours_entitlement":["targeted_childcare_entitlement"],"gov.dfe.targeted_childcare_entitlement.income_limit.tax_credits":["meets_child_tax_credit_criteria_for_targeted_childcare_entitlement","meets_working_tax_credit_criteria_for_targeted_childcare_entitlement"],"gov.dfe.targeted_childcare_entitlement.income_limit.universal_credit":["meets_universal_credit_criteria_for_targeted_childcare_entitlement"],"gov.dfe.targeted_childcare_entitlement.qualifying_benefits":["targeted_childcare_entitlement_eligible"],"gov.dfe.targeted_childcare_entitlement.qualifying_criteria":["meets_tax_credit_criteria_for_targeted_childcare_entitlement","targeted_childcare_entitlement_eligible"],"gov.dfe.universal_childcare_entitlement.age.max":["is_child_receiving_universal_childcare","universal_childcare_entitlement_eligible"],"gov.dfe.universal_childcare_entitlement.age.min":["is_child_receiving_universal_childcare","universal_childcare_entitlement_eligible"],"gov.dfe.universal_childcare_entitlement.hours":["universal_childcare_entitlement"],"gov.dfe.weeks_per_year":["extended_childcare_entitlement_total_per_child","targeted_childcare_entitlement","universal_childcare_entitlement"],"gov.dft.bus.default_single_fare_share":["local_bus_single_fare_share"],"gov.dft.bus.fare_allocation_weight_by_age":["household_bus_fare_age_weight","person_bus_fare_spending"],"gov.dft.bus.fare_index":["person_bus_fare_spending"],"gov.dft.bus.prior_law_fare_index":["person_bus_fare_spending"],"gov.dft.bus.uncapped_single_fare":["local_bus_uncapped_single_fare"],"gov.dft.rail.fare_index":["rail_subsidy_spending"],"gov.dft.vehicle_excise_duty.alternative_fuel_discount":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.band_k_max_emissions":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.band_k_registration_cutoff":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.engine_size_threshold":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.expensive_car_supplement":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.expensive_car_threshold":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.first_year":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.first_year_diesel_non_rde2":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.graduated":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.graduated_registration_start":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.large_engine_rate":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.modern_registration_start":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.small_engine_rate":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.standard_rate":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.supplement_years":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.zero_emission_exempt":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.zero_emission_expensive_car_threshold":["car_vehicle_excise_duty"],"gov.dft.vehicle_excise_duty.zero_emission_supplement_registration_start":["car_vehicle_excise_duty"],"gov.dwp.ESA.exempt_work.higher_earnings_limit":["esa_income_claimant_remunerative_work"],"gov.dwp.ESA.exempt_work.hours_limit":["esa_income_claimant_remunerative_work"],"gov.dwp.ESA.exempt_work.lower_earnings_limit":["esa_income_claimant_remunerative_work"],"gov.dwp.ESA.income.capital.limit":["esa_income_eligible","is_on_income_related_esa"],"gov.dwp.ESA.income.capital.person_sources":["esa_income_assessable_capital"],"gov.dwp.ESA.income.capital.sources":["esa_income_assessable_capital"],"gov.dwp.ESA.income.capital.tariff_income.amount":["esa_income_tariff_income"],"gov.dwp.ESA.income.capital.tariff_income.step":["esa_income_tariff_income"],"gov.dwp.ESA.income.capital.tariff_income.threshold":["esa_income_tariff_income"],"gov.dwp.ESA.income.remunerative_work.partner_hours":["esa_income_eligible"],"gov.dwp.ESA.income.self_employment_class_2.above_lower_profits_threshold":["esa_exempt_work_earnings"],"gov.dwp.ESA.income.self_employment_class_2.deducted":["esa_exempt_work_earnings"],"gov.dwp.JSA.income.active":["benefit_cap_reduction","claimant_or_partner_jsa_income","income_support_eligible","is_on_income_based_jsa","jsa_income","legacy_benefits_closed"],"gov.dwp.JSA.income.capital.limit":["is_on_income_based_jsa","jsa_income_eligible"],"gov.dwp.JSA.income.capital.person_sources":["jsa_income_assessable_capital"],"gov.dwp.JSA.income.capital.sources":["jsa_income_assessable_capital"],"gov.dwp.JSA.income.capital.tariff_income.amount":["jsa_income_tariff_income"],"gov.dwp.JSA.income.capital.tariff_income.step":["jsa_income_tariff_income"],"gov.dwp.JSA.income.capital.tariff_income.threshold":["jsa_income_tariff_income"],"gov.dwp.JSA.remunerative_work.claimant_hours":["is_on_income_based_jsa","jsa_income_eligible"],"gov.dwp.JSA.remunerative_work.partner_hours":["jsa_income_eligible"],"gov.dwp.LHA.shared_accommodation_age_threshold":["is_housing_benefit_young_individual","is_lha_shared_accommodation_rate_specified_renter"],"gov.dwp.LHA.shared_accommodation_exception_benefits":["is_lha_shared_accommodation_rate_specified_renter"],"gov.dwp.attendance_allowance.higher":["attendance_allowance","is_severely_disabled_for_benefits"],"gov.dwp.attendance_allowance.lower":["attendance_allowance"],"gov.dwp.benefit_cap.non_single.in_london":["benefit_cap"],"gov.dwp.benefit_cap.non_single.outside_london":["benefit_cap"],"gov.dwp.benefit_cap.single.in_london":["benefit_cap"],"gov.dwp.benefit_cap.single.outside_london":["benefit_cap"],"gov.dwp.carer_premium.single":["carer_premium"],"gov.dwp.carers_allowance.earnings_limit":["is_gainfully_employed_for_carers_allowance"],"gov.dwp.carers_allowance.min_hours":["carers_allowance_pre_overlap","is_carer_for_benefits","partners_care_for_same_severely_disabled_person"],"gov.dwp.carers_allowance.qualifying_disability_benefits":["is_severely_disabled_for_carers_allowance"],"gov.dwp.carers_allowance.rate":["carers_allowance_pre_overlap","uc_unearned_carer_support_payment"],"gov.dwp.disability_premia.disability_couple":["disability_premium"],"gov.dwp.disability_premia.disability_single":["disability_premium"],"gov.dwp.disability_premia.enhanced_couple":["enhanced_disability_premium"],"gov.dwp.disability_premia.enhanced_single":["enhanced_disability_premium"],"gov.dwp.disability_premia.severe_couple":["severe_disability_premium"],"gov.dwp.disability_premia.severe_non_dependant_age":["has_non_dependant_for_severe_disability_premium"],"gov.dwp.disability_premia.severe_qualifying_benefits":["receives_severe_disability_premium_qualifying_benefit"],"gov.dwp.disability_premia.severe_qualifying_benefits_northern_ireland":["receives_severe_disability_premium_qualifying_benefit"],"gov.dwp.disability_premia.severe_single":["severe_disability_premium"],"gov.dwp.dla.mobility.higher":["dla_m"],"gov.dwp.dla.mobility.lower":["dla_m"],"gov.dwp.dla.self_care.higher":["dla_sc","is_enhanced_disabled_for_benefits","is_severely_disabled_for_benefits"],"gov.dwp.dla.self_care.lower":["dla_sc"],"gov.dwp.dla.self_care.middle":["dla_sc"],"gov.dwp.housing_benefit.allowances.age_threshold.older":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.age_threshold.younger":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.couple.aged":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.couple.older":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.couple.younger":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.lone_parent.aged":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.lone_parent.older":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.lone_parent.younger":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.single.aged":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.single.older":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.allowances.single.younger":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount"],"gov.dwp.housing_benefit.ineligible_charges.meals.at_least_three_a_day.aged_16_or_over":["housing_benefit_meals_deduction"],"gov.dwp.housing_benefit.ineligible_charges.meals.at_least_three_a_day.under_16":["housing_benefit_meals_deduction"],"gov.dwp.housing_benefit.ineligible_charges.meals.breakfast_only":["housing_benefit_meals_deduction"],"gov.dwp.housing_benefit.ineligible_charges.meals.fewer_than_three_a_day.aged_16_or_over":["housing_benefit_meals_deduction"],"gov.dwp.housing_benefit.ineligible_charges.meals.fewer_than_three_a_day.under_16":["housing_benefit_meals_deduction"],"gov.dwp.housing_benefit.means_test.capital.pension_age.limit":["housing_benefit_eligible"],"gov.dwp.housing_benefit.means_test.capital.pension_age.tariff_income.amount":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.capital.pension_age.tariff_income.step":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.capital.pension_age.tariff_income.threshold":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.capital.person_sources":["housing_benefit_assessable_capital"],"gov.dwp.housing_benefit.means_test.capital.sources":["housing_benefit_assessable_capital"],"gov.dwp.housing_benefit.means_test.capital.working_age.limit":["housing_benefit_eligible"],"gov.dwp.housing_benefit.means_test.capital.working_age.tariff_income.amount":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.capital.working_age.tariff_income.step":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.capital.working_age.tariff_income.threshold":["housing_benefit_tariff_income"],"gov.dwp.housing_benefit.means_test.childcare.child_age_limit.disabled":["num_relevant_children_for_housing_benefit_childcare"],"gov.dwp.housing_benefit.means_test.childcare.child_age_limit.standard":["num_relevant_children_for_housing_benefit_childcare"],"gov.dwp.housing_benefit.means_test.income_disregard.couple":["housing_benefit_applicable_income_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.lone_parent":["housing_benefit_applicable_income_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.single":["housing_benefit_applicable_income_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.age_threshold.older":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.age_threshold.younger":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.couple.minors":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.couple.older":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.couple.younger":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.lone_parent.older":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.lone_parent.younger":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.single.older":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.specified_or_temporary_accommodation.single.younger":["housing_benefit_specified_or_temporary_accommodation_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.worker":["housing_benefit_applicable_income_disregard"],"gov.dwp.housing_benefit.means_test.income_disregard.worker_age":["meets_housing_benefit_additional_earnings_disregard_conditions"],"gov.dwp.housing_benefit.means_test.income_disregard.worker_hours":["meets_housing_benefit_additional_earnings_disregard_conditions"],"gov.dwp.housing_benefit.means_test.income_disregard.worker_hours_lower":["meets_housing_benefit_additional_earnings_disregard_conditions"],"gov.dwp.housing_benefit.means_test.pension_contribution_deduction_rate":["housing_benefit_net_earnings"],"gov.dwp.housing_benefit.means_test.universal_credit_passport.great_britain":["housing_benefit_on_passporting_benefit"],"gov.dwp.housing_benefit.means_test.universal_credit_passport.northern_ireland":["housing_benefit_on_passporting_benefit"],"gov.dwp.housing_benefit.means_test.withdrawal_rate":["housing_benefit_entitlement"],"gov.dwp.housing_benefit.non_dep_deduction.age_threshold":["housing_benefit_individual_non_dep_deduction_eligible"],"gov.dwp.housing_benefit.non_dep_deduction.amount":["household_benefits_individual_non_dep_deduction"],"gov.dwp.housing_benefit.severe_disability_premium.qualifying_benefits":["housing_benefit_severe_disability_premium_applies"],"gov.dwp.income_support.active":["income_support","is_on_income_support","legacy_benefits_closed"],"gov.dwp.income_support.amounts.age_threshold.older":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.age_threshold.younger":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_16_24":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_couples_16_17":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_couples_age_gap":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_couples_over_18":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_lone_16_17":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_lone_over_18":["income_support_applicable_amount"],"gov.dwp.income_support.amounts.amount_over_25":["income_support_applicable_amount"],"gov.dwp.income_support.eligibility.lone_parent_youngest_child_age_limit":["income_support_eligible"],"gov.dwp.income_support.eligibility.remunerative_work.claimant_hours":["income_support_eligible"],"gov.dwp.income_support.eligibility.remunerative_work.partner_hours":["income_support_eligible"],"gov.dwp.income_support.means_test.capital.limit":["income_support_eligible"],"gov.dwp.income_support.means_test.capital.person_sources":["income_support_assessable_capital"],"gov.dwp.income_support.means_test.capital.sources":["income_support_assessable_capital"],"gov.dwp.income_support.means_test.capital.tariff_income.amount":["income_support_tariff_income"],"gov.dwp.income_support.means_test.capital.tariff_income.step":["income_support_tariff_income"],"gov.dwp.income_support.means_test.capital.tariff_income.threshold":["income_support_tariff_income"],"gov.dwp.income_support.means_test.income_disregard_couple":["income_support_applicable_income"],"gov.dwp.income_support.means_test.income_disregard_lone_parent":["income_support_applicable_income"],"gov.dwp.income_support.means_test.income_disregard_single":["income_support_applicable_income"],"gov.dwp.pension_credit.guarantee_credit.carer.addition":["carer_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.addition":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.disability.addition":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.disability.eligibility":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.disability.severe.addition":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.disability.severe.eligibility":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.eligibility.child_age_limit":["is_child_for_pension_credit","meets_qualifying_young_person_age_condition_for_pension_credit"],"gov.dwp.pension_credit.guarantee_credit.child.eligibility.education_or_training_start_age_limit":["meets_qualifying_young_person_entry_condition_for_pension_credit"],"gov.dwp.pension_credit.guarantee_credit.child.eligibility.qualifying_young_person_age_limit":["meets_qualifying_young_person_age_condition_for_pension_credit"],"gov.dwp.pension_credit.guarantee_credit.child.first.addition":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.child.first.born_before":["child_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.earnings_sources":["pension_credit_earnings"],"gov.dwp.pension_credit.guarantee_credit.income":["pension_credit_income"],"gov.dwp.pension_credit.guarantee_credit.minimum_guarantee":["standard_minimum_guarantee"],"gov.dwp.pension_credit.guarantee_credit.severe_disability.addition":["severe_disability_minimum_guarantee_addition"],"gov.dwp.pension_credit.guarantee_credit.severe_disability.other_resident_age_limit":["is_counted_resident_for_severe_disability_addition"],"gov.dwp.pension_credit.guarantee_credit.severe_disability.relevant_benefits":["receives_severe_disability_addition_qualifying_benefit"],"gov.dwp.pension_credit.income.capital.lower_threshold":["pension_credit_deemed_income"],"gov.dwp.pension_credit.income.capital.person_sources":["pension_credit_assessable_capital"],"gov.dwp.pension_credit.income.capital.sources":["pension_credit_assessable_capital"],"gov.dwp.pension_credit.income.capital.tariff_income.amount":["pension_credit_deemed_income"],"gov.dwp.pension_credit.income.capital.tariff_income.step":["pension_credit_deemed_income"],"gov.dwp.pension_credit.income.pension_contributions_deduction":["pension_credit_income"],"gov.dwp.pension_credit.mixed_age_couples.excluded":["meets_pension_credit_age_conditions"],"gov.dwp.pension_credit.mixed_age_couples.saving_latest_birth_date":["has_mixed_age_couple_pension_credit_saving"],"gov.dwp.pension_credit.savings_credit.cutoff_year":["meets_savings_credit_age_requirement"],"gov.dwp.pension_credit.savings_credit.excluded_income":["savings_credit_income"],"gov.dwp.pension_credit.savings_credit.minimum_age":["meets_savings_credit_age_requirement"],"gov.dwp.pension_credit.savings_credit.rate.phase_in":["savings_credit"],"gov.dwp.pension_credit.savings_credit.rate.phase_out":["savings_credit"],"gov.dwp.pension_credit.savings_credit.threshold":["is_savings_credit_eligible","savings_credit"],"gov.dwp.pip.daily_living.enhanced":["is_severely_disabled_for_benefits","pip_dl"],"gov.dwp.pip.daily_living.standard":["pip_dl"],"gov.dwp.pip.mobility.enhanced":["pip_m"],"gov.dwp.pip.mobility.standard":["pip_m"],"gov.dwp.sda.maximum":["sda"],"gov.dwp.ssmg.rate":["ssmg"],"gov.dwp.state_pension.age.age_by_birth_date":["state_pension_age","state_pension_credit_qualifying_age"],"gov.dwp.state_pension.age.day_by_birth_date":["state_pension_age","state_pension_credit_qualifying_age"],"gov.dwp.state_pension.age.male.age":["state_pension_age","state_pension_credit_qualifying_age"],"gov.dwp.state_pension.age.male.born_before":["state_pension_age"],"gov.dwp.tax_credits.active":["legacy_benefits_closed","tax_credit_award","tax_credits","tax_credits_applicable_income"],"gov.dwp.tax_credits.child_tax_credit.elements.child_element":["CTC_child_element"],"gov.dwp.tax_credits.child_tax_credit.elements.dis_child_element":["CTC_disabled_child_element"],"gov.dwp.tax_credits.child_tax_credit.eligibility.child_age_limit":["is_child_for_child_tax_credit","meets_qualifying_young_person_age_condition_for_child_tax_credit"],"gov.dwp.tax_credits.child_tax_credit.eligibility.education_or_training_start_age_limit":["meets_qualifying_young_person_entry_condition_for_child_tax_credit"],"gov.dwp.tax_credits.child_tax_credit.eligibility.qualifying_young_person_age_limit":["meets_qualifying_young_person_age_condition_for_child_tax_credit"],"gov.dwp.tax_credits.child_tax_credit.limit.born_before":["is_CTC_child_limit_exempt"],"gov.dwp.tax_credits.child_tax_credit.limit.child_count":["CTC_child_element","ctc_child_limit_affected"],"gov.dwp.tax_credits.means_test.income_reduction_rate":["tax_credits_reduction"],"gov.dwp.tax_credits.means_test.income_threshold":["tax_credits_reduction"],"gov.dwp.tax_credits.means_test.income_threshold_CTC_only":["tax_credits_reduction"],"gov.dwp.tax_credits.means_test.non_earned_disregard":["tax_credits_current_year_income"],"gov.dwp.tax_credits.means_test.pension_credit_passport":["tax_credits_applicable_income"],"gov.dwp.tax_credits.working_tax_credit.childcare_child_age_limit.disabled":["is_child_for_working_tax_credit_childcare_element"],"gov.dwp.tax_credits.working_tax_credit.childcare_child_age_limit.standard":["is_child_for_working_tax_credit_childcare_element"],"gov.dwp.tax_credits.working_tax_credit.elements.childcare_1":["housing_benefit_applicable_income_childcare_element"],"gov.dwp.tax_credits.working_tax_credit.elements.childcare_2":["housing_benefit_applicable_income_childcare_element"],"gov.dwp.tax_credits.working_tax_credit.elements.severely_disabled":["WTC_severely_disabled_element"],"gov.dwp.tax_credits.working_tax_credit.min_hours.couple_with_children":["is_WTC_eligible"],"gov.dwp.tax_credits.working_tax_credit.min_hours.default":["is_WTC_eligible"],"gov.dwp.tax_credits.working_tax_credit.min_hours.lower":["is_WTC_eligible"],"gov.dwp.tax_credits.working_tax_credit.min_hours.old_age":["is_WTC_eligible"],"gov.dwp.universal_credit.company_owner.intermediary_exclusion.chapters":["uc_company_intermediary_exclusion_applies"],"gov.dwp.universal_credit.company_owner.intermediary_exclusion.requires_main_employment":["uc_company_intermediary_exclusion_applies"],"gov.dwp.universal_credit.deductions.abolish.advance":["uc_deduction_rate"],"gov.dwp.universal_credit.deductions.abolish.government":["uc_deduction_rate"],"gov.dwp.universal_credit.deductions.abolish.third_party":["uc_deduction_rate"],"gov.dwp.universal_credit.deductions.cap":["uc_deduction_rate"],"gov.dwp.universal_credit.deductions.minimum_payable":["uc_deductions"],"gov.dwp.universal_credit.deductions.protected_floor":["universal_credit"],"gov.dwp.universal_credit.elements.child.amount":["two_child_limit_payment","uc_individual_child_element"],"gov.dwp.universal_credit.elements.child.disabled.amount":["uc_individual_disabled_child_element"],"gov.dwp.universal_credit.elements.child.eligibility.child_age_limit":["is_child_for_universal_credit","meets_qualifying_young_person_age_condition_for_universal_credit"],"gov.dwp.universal_credit.elements.child.eligibility.education_or_training_start_age_limit":["meets_qualifying_young_person_entry_condition_for_universal_credit"],"gov.dwp.universal_credit.elements.child.eligibility.qualifying_young_person_age_limit":["meets_qualifying_young_person_age_condition_for_universal_credit"],"gov.dwp.universal_credit.elements.child.eligibility.terminal_date_age_limit":["meets_qualifying_young_person_terminal_date_condition_for_universal_credit"],"gov.dwp.universal_credit.elements.child.first.higher_amount":["uc_individual_child_element"],"gov.dwp.universal_credit.elements.child.limit.born_before":["uc_is_child_born_before_child_limit"],"gov.dwp.universal_credit.elements.child.limit.child_count":["uc_individual_child_element"],"gov.dwp.universal_credit.elements.child.severely_disabled.amount":["uc_individual_severely_disabled_child_element"],"gov.dwp.universal_credit.elements.childcare.cap":["uc_maximum_childcare_element_amount"],"gov.dwp.universal_credit.elements.childcare.coverage_rate":["uc_childcare_element"],"gov.dwp.universal_credit.elements.housing.non_dep_deduction.age_threshold":["uc_individual_non_dep_deduction_eligible"],"gov.dwp.universal_credit.elements.housing.non_dep_deduction.amount":["uc_individual_non_dep_deduction"],"gov.dwp.universal_credit.eligibility.min_age":["meets_uc_minimum_age_condition"],"gov.dwp.universal_credit.eligibility.reduced_min_age":["meets_uc_minimum_age_condition"],"gov.dwp.universal_credit.means_test.capital.limit":["is_uc_eligible"],"gov.dwp.universal_credit.means_test.capital.person_sources":["uc_assessable_capital"],"gov.dwp.universal_credit.means_test.capital.sources":["uc_assessable_capital"],"gov.dwp.universal_credit.means_test.capital.tariff_income.amount":["uc_tariff_income"],"gov.dwp.universal_credit.means_test.capital.tariff_income.step":["uc_tariff_income"],"gov.dwp.universal_credit.means_test.capital.tariff_income.threshold":["uc_tariff_income"],"gov.dwp.universal_credit.means_test.income_definitions.unearned":["uc_unearned_income"],"gov.dwp.universal_credit.means_test.minimum_income_floor.ineligible_partner_hours":["uc_minimum_income_floor_gross"],"gov.dwp.universal_credit.means_test.minimum_income_floor.no_requirements_partner_hours":["uc_minimum_income_floor_gross"],"gov.dwp.universal_credit.means_test.minimum_income_floor.self_employed_national_insurance":["uc_minimum_income_floor_national_insurance"],"gov.dwp.universal_credit.means_test.reduction_rate":["uc_income_reduction"],"gov.dwp.universal_credit.means_test.work_allowance.with_housing":["uc_work_allowance"],"gov.dwp.universal_credit.means_test.work_allowance.without_housing":["uc_work_allowance"],"gov.dwp.universal_credit.standard_allowance.amount":["uc_standard_allowance"],"gov.dwp.universal_credit.standard_allowance.claimant_type.age_threshold":["uc_standard_allowance_claimant_type"],"gov.dwp.universal_credit.work_requirements.default_expected_hours":["uc_expected_hours"],"gov.dwp.universal_credit.work_requirements.interview_or_preparation_threshold_hours":["uc_minimum_income_floor_gross"],"gov.dwp.universal_credit.work_requirements.responsible_carer.child_age.interview_only":["uc_work_related_group_apart_from_earnings"],"gov.dwp.universal_credit.work_requirements.responsible_carer.child_age.no_requirements":["uc_work_related_group_apart_from_earnings"],"gov.dwp.universal_credit.work_requirements.responsible_carer.child_age.work_preparation":["uc_work_related_group_apart_from_earnings"],"gov.dwp.universal_credit.work_requirements.responsible_carer.expected_hours.below_compulsory_school_age":["uc_expected_hours"],"gov.dwp.universal_credit.work_requirements.responsible_carer.expected_hours.child_age_limit":["uc_expected_hours"],"gov.dwp.universal_credit.work_requirements.responsible_carer.expected_hours.compulsory_school_age":["uc_expected_hours"],"gov.dwp.winter_fuel_payment.amount.higher":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.amount.higher_shared_with_80_or_over":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.amount.higher_shared_with_under_80":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.amount.lower":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.amount.lower_shared":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.eligibility.countries":["winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.higher_age_requirement":["winter_fuel_payment"],"gov.dwp.winter_fuel_payment.eligibility.minimum_tax_credit_award":["is_on_winter_fuel_payment_relevant_benefit","winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.partner_receipt_counts":["is_on_winter_fuel_payment_relevant_benefit"],"gov.dwp.winter_fuel_payment.eligibility.relevant_benefits":["is_on_winter_fuel_payment_relevant_benefit","winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.require_benefits":["winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.state_pension_age_requirement":["winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.maximum_taxable_income":["winter_fuel_payment_eligible"],"gov.dwp.winter_fuel_payment.eligibility.taxable_income_test.use_maximum_taxable_income":["winter_fuel_payment_eligible"],"gov.economic_assumptions.yoy_growth.obr.rpi":["plan_1_interest_rate","plan_2_interest_rate","plan_5_interest_rate","postgraduate_interest_rate"],"gov.hmrc.alcohol_duty.high_abv":["beer_duty","cider_duty","other_fermented_duty","sparkling_cider_duty","spirits_duty","wine_duty"],"gov.hmrc.alcohol_duty.sparkling_cider_abv":["sparkling_cider_duty"],"gov.hmrc.alcohol_duty.spirits_abv":["beer_duty","cider_duty","other_fermented_duty","sparkling_cider_duty","spirits_duty","wine_duty"],"gov.hmrc.alcohol_duty.standard_abv":["beer_duty","cider_duty","other_fermented_duty","sparkling_cider_duty","spirits_duty","wine_duty"],"gov.hmrc.alcohol_duty.taxable_abv":["beer_duty","cider_duty","other_fermented_duty","sparkling_cider_duty","spirits_duty","wine_duty"],"gov.hmrc.alcohol_duty.wine_easement_abv":["wine_duty"],"gov.hmrc.alcohol_duty.wine_easement_max_abv":["wine_duty"],"gov.hmrc.alcohol_duty.wine_easement_min_abv":["wine_duty"],"gov.hmrc.business_rates.statistics.revenue.ENGLAND":["baseline_business_rates","business_rates"],"gov.hmrc.business_rates.statistics.revenue.NORTHERN_IRELAND":["baseline_business_rates","business_rates"],"gov.hmrc.business_rates.statistics.revenue.SCOTLAND":["baseline_business_rates","business_rates"],"gov.hmrc.business_rates.statistics.revenue.WALES":["baseline_business_rates","business_rates"],"gov.hmrc.cgt.additional_rate":["capital_gains_tax"],"gov.hmrc.cgt.annual_exempt_amount":["capital_gains_tax"],"gov.hmrc.cgt.badr.lifetime_limit":["capital_gains_tax"],"gov.hmrc.cgt.badr.rate":["capital_gains_tax"],"gov.hmrc.cgt.basic_rate":["capital_gains_tax"],"gov.hmrc.cgt.carried_interest.additional_rate":["capital_gains_tax"],"gov.hmrc.cgt.carried_interest.basic_rate":["capital_gains_tax"],"gov.hmrc.cgt.carried_interest.higher_rate":["capital_gains_tax"],"gov.hmrc.cgt.higher_rate":["capital_gains_tax"],"gov.hmrc.cgt.residential_property.additional_rate":["capital_gains_tax"],"gov.hmrc.cgt.residential_property.basic_rate":["capital_gains_tax"],"gov.hmrc.cgt.residential_property.higher_rate":["capital_gains_tax"],"gov.hmrc.child_benefit.amount.additional":["child_benefit_respective_amount"],"gov.hmrc.child_benefit.amount.eldest":["child_benefit_respective_amount"],"gov.hmrc.child_benefit.eligibility.child_age_limit":["is_child_for_child_benefit","meets_qualifying_young_person_age_condition_for_child_benefit"],"gov.hmrc.child_benefit.eligibility.education_or_training_start_age_limit":["meets_qualifying_young_person_entry_condition_for_child_benefit"],"gov.hmrc.child_benefit.eligibility.qualifying_young_person_age_limit":["meets_qualifying_young_person_age_condition_for_child_benefit"],"gov.hmrc.child_benefit.opt_out_charge_share":["child_benefit"],"gov.hmrc.fuel_duty.lpg":["fuel_duty"],"gov.hmrc.fuel_duty.natural_gas":["fuel_duty"],"gov.hmrc.fuel_duty.petrol_and_diesel":["fuel_duty"],"gov.hmrc.fuel_duty.rural_fuel_duty_relief":["fuel_duty"],"gov.hmrc.income_tax.adjusted_net_income_components":["net_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"gov.hmrc.income_tax.allowances.annual_allowance.default":["pension_annual_allowance"],"gov.hmrc.income_tax.allowances.annual_allowance.minimum":["pension_annual_allowance"],"gov.hmrc.income_tax.allowances.annual_allowance.reduction_rate":["pension_annual_allowance"],"gov.hmrc.income_tax.allowances.annual_allowance.taper":["pension_annual_allowance"],"gov.hmrc.income_tax.allowances.dividend_allowance":["dividend_income_tax","taxed_dividend_income"],"gov.hmrc.income_tax.allowances.marriage_allowance.eligible_bands":["meets_marriage_allowance_income_conditions"],"gov.hmrc.income_tax.allowances.marriage_allowance.max":["marriage_allowance"],"gov.hmrc.income_tax.allowances.marriage_allowance.rounding_increment":["marriage_allowance"],"gov.hmrc.income_tax.allowances.married_couples_allowance.deduction_rate":["married_couples_allowance_deduction"],"gov.hmrc.income_tax.allowances.personal_allowance.amount":["carers_allowance_earnings","marriage_allowance","personal_allowance","savings_starter_rate_income","uc_minimum_income_floor_income_tax"],"gov.hmrc.income_tax.allowances.personal_allowance.maximum_ANI":["personal_allowance"],"gov.hmrc.income_tax.allowances.personal_allowance.reduction_rate":["personal_allowance"],"gov.hmrc.income_tax.allowances.personal_savings_allowance.additional":["savings_allowance"],"gov.hmrc.income_tax.allowances.personal_savings_allowance.basic":["savings_allowance"],"gov.hmrc.income_tax.allowances.personal_savings_allowance.higher":["savings_allowance"],"gov.hmrc.income_tax.allowances.property_allowance":["property_allowance_deduction_if_used","property_receipts_within_allowance"],"gov.hmrc.income_tax.allowances.trading_allowance":["trading_allowance_deduction"],"gov.hmrc.income_tax.bases.taxable_self_employment_income_deductions":["taxable_self_employment_income"],"gov.hmrc.income_tax.charges.CB_HITC.phase_out_end":["CB_HITC","child_benefit"],"gov.hmrc.income_tax.charges.CB_HITC.phase_out_start":["CB_HITC","child_benefit"],"gov.hmrc.income_tax.earned_taxable_income_exclusions":["earned_taxable_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"gov.hmrc.income_tax.income_tax_additions":["income_tax"],"gov.hmrc.income_tax.income_tax_subtractions":["income_tax","property_finance_cost_relief","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"gov.hmrc.income_tax.rates.dividends":["dividend_income_tax"],"gov.hmrc.income_tax.rates.property.additional":["property_income_tax"],"gov.hmrc.income_tax.rates.property.basic":["property_finance_cost_relief","property_income_tax"],"gov.hmrc.income_tax.rates.property.higher":["property_income_tax"],"gov.hmrc.income_tax.rates.savings.additional":["savings_income_tax"],"gov.hmrc.income_tax.rates.savings.basic":["savings_income_tax"],"gov.hmrc.income_tax.rates.savings.higher":["savings_income_tax"],"gov.hmrc.income_tax.rates.savings_starter_rate.allowance":["savings_starter_rate_income"],"gov.hmrc.income_tax.rates.savings_starter_rate.phase_out":["savings_starter_rate_income"],"gov.hmrc.income_tax.rates.scotland.rates":["carers_allowance_earnings","earned_income_tax","esa_exempt_work_earnings","property_income_tax","tax_band","uc_income_tax_on_earnings","uc_income_tax_on_trade","uc_minimum_income_floor_income_tax"],"gov.hmrc.income_tax.rates.uk":["add_rate_earned_income","add_rate_earned_income_tax","add_rate_savings_income","basic_rate_earned_income","basic_rate_earned_income_tax","basic_rate_savings_income","basic_rate_savings_income_pre_starter","capital_gains_tax","carers_allowance_earnings","earned_income_tax","esa_exempt_work_earnings","gift_aid_grossed_up","higher_rate_earned_income","higher_rate_earned_income_tax","higher_rate_savings_income","personal_pension_contributions_tax","property_income_tax","savings_allowance","tax_band","uc_income_tax_on_earnings","uc_income_tax_on_trade","uc_minimum_income_floor_income_tax"],"gov.hmrc.income_tax.reliefs.pension_contribution.basic_amount":["pension_contributions_relief"],"gov.hmrc.income_tax.reliefs.property_finance_costs.disallowed_share":["deductible_property_finance_costs","property_finance_costs_relievable"],"gov.hmrc.isa.lifetime.charge_free_age":["lifetime_isa_countable_capital"],"gov.hmrc.isa.lifetime.withdrawal_charge":["lifetime_isa_countable_capital"],"gov.hmrc.minimum_wage.apprentice":["minimum_wage"],"gov.hmrc.minimum_wage.non_apprentice":["minimum_wage","uc_minimum_income_floor_gross"],"gov.hmrc.national_insurance.class_1.rates.employee.additional":["esa_exempt_work_earnings","ni_class_1_employee_additional","salary_sacrifice_pension_ni_employee","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_1.rates.employee.main":["esa_exempt_work_earnings","ni_class_1_employee_primary","salary_sacrifice_pension_ni_employee","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_1.rates.employer":["adjusted_employer_cost","employer_ni_fixed_employer_cost_change","ni_class_1_employer","salary_sacrifice_pension_ni_employer"],"gov.hmrc.national_insurance.class_1.thresholds.primary_threshold":["esa_exempt_work_earnings","ni_class_1_employee_primary","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_1.thresholds.secondary_threshold":["adjusted_employer_cost","employer_ni_fixed_employer_cost_change","ni_class_1_employer"],"gov.hmrc.national_insurance.class_1.thresholds.upper_earnings_limit":["esa_exempt_work_earnings","ni_class_1_employee_additional","ni_class_1_employee_primary","salary_sacrifice_pension_ni_employee","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_2.flat_rate":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_2","ni_class_4_maximum","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_2.lower_profits_threshold":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_2","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_2.lower_profits_threshold_applies":["carers_allowance_earnings","ni_class_2","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_2.small_profits_threshold":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_2","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_4.annual_maximum.includes_class_2":["ni_class_4","ni_class_4_maximum"],"gov.hmrc.national_insurance.class_4.rates.additional":["ni_class_4","ni_class_4_maximum","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_4.rates.main":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_4","ni_class_4_main","ni_class_4_maximum","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_4.thresholds.lower_profits_limit":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_4","ni_class_4_main","ni_class_4_maximum","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.class_4.thresholds.upper_profits_limit":["carers_allowance_earnings","esa_exempt_work_earnings","ni_class_4","ni_class_4_main","ni_class_4_maximum","uc_minimum_income_floor_national_insurance"],"gov.hmrc.national_insurance.salary_sacrifice_pension_cap":["pension_contributions_via_salary_sacrifice_adjusted","salary_sacrifice_broad_base_haircut","salary_sacrifice_returned_to_income"],"gov.hmrc.pensions.pension_contributions_relief_age_limit":["pension_contributions_relief","uc_individual_earned_income_before_mif"],"gov.hmrc.stamp_duty.abolish":["expected_sdlt"],"gov.hmrc.stamp_duty.non_residential.purchase":["sdlt_on_non_residential_property_transactions"],"gov.hmrc.stamp_duty.non_residential.rent":["sdlt_on_non_residential_property_rent"],"gov.hmrc.stamp_duty.property_sale_rate":["expected_lbtt","expected_ltt","expected_sdlt"],"gov.hmrc.stamp_duty.residential.purchase.additional.min":["sdlt_on_residential_property_transactions"],"gov.hmrc.stamp_duty.residential.purchase.additional.rate":["sdlt_on_residential_property_transactions"],"gov.hmrc.stamp_duty.residential.purchase.main.first.max":["sdlt_on_residential_property_transactions"],"gov.hmrc.stamp_duty.residential.purchase.main.first.rate":["sdlt_on_residential_property_transactions"],"gov.hmrc.stamp_duty.residential.purchase.main.subsequent":["sdlt_on_residential_property_transactions"],"gov.hmrc.stamp_duty.residential.rent":["sdlt_on_residential_property_rent"],"gov.hmrc.stamp_duty.statistics.non_residential.corporate.revenue":["corporate_sdlt"],"gov.hmrc.stamp_duty.statistics.residential.corporate.revenue":["corporate_sdlt"],"gov.hmrc.student_loans.interest_rates.plan_1.boe_margin":["plan_1_interest_rate"],"gov.hmrc.student_loans.interest_rates.plan_2.additional_rate":["plan_2_interest_rate"],"gov.hmrc.student_loans.interest_rates.plan_2.upper_threshold":["plan_2_interest_rate"],"gov.hmrc.student_loans.interest_rates.postgraduate_additional_rate":["postgraduate_interest_rate"],"gov.hmrc.student_loans.postgraduate_repayment_rate":["student_loan_repayment_rate"],"gov.hmrc.student_loans.repayment_rate":["student_loan_repayment_rate"],"gov.hmrc.student_loans.thresholds.plan_1":["student_loan_repayment"],"gov.hmrc.student_loans.thresholds.plan_2":["plan_2_interest_rate","student_loan_repayment"],"gov.hmrc.student_loans.thresholds.plan_4":["student_loan_repayment"],"gov.hmrc.student_loans.thresholds.plan_5":["student_loan_repayment"],"gov.hmrc.student_loans.thresholds.postgraduate":["student_loan_repayment"],"gov.hmrc.tax_free_childcare.age.disability":["tax_free_childcare_child_age_eligible"],"gov.hmrc.tax_free_childcare.age.standard":["tax_free_childcare_child_age_eligible"],"gov.hmrc.tax_free_childcare.carers_leave_qualifies":["tax_free_childcare_regarded_as_in_paid_work"],"gov.hmrc.tax_free_childcare.caring_or_incapacity_benefits":["tax_free_childcare_caring_or_incapacity_benefit"],"gov.hmrc.tax_free_childcare.caring_or_incapacity_benefits_in_payment":["tax_free_childcare_regarded_as_in_paid_work"],"gov.hmrc.tax_free_childcare.contribution.disabled_child":["tax_free_childcare"],"gov.hmrc.tax_free_childcare.contribution.rate":["tax_free_childcare"],"gov.hmrc.tax_free_childcare.contribution.standard_child":["tax_free_childcare"],"gov.hmrc.tax_free_childcare.declaration_periods_per_year":["tax_free_childcare","tax_free_childcare_eligible_declaration_periods"],"gov.hmrc.tax_free_childcare.disqualifying_benefits":["tax_free_childcare_program_eligible"],"gov.hmrc.tax_free_childcare.income.countable_sources":["tax_free_childcare_expected_declaration_period_income"],"gov.hmrc.tax_free_childcare.income.declaration_period_weeks":["tax_free_childcare_expected_declaration_period_income","tax_free_childcare_meets_income_requirements"],"gov.hmrc.tax_free_childcare.income.income_limit":["tax_free_childcare_meets_income_requirements"],"gov.hmrc.tax_free_childcare.minimum_weekly_hours":["tax_free_childcare_meets_income_requirements"],"gov.hmrc.vat.reduced_rate":["baseline_vat","vat"],"gov.hmrc.vat.reduced_rate_share":["reduced_rate_vat_consumption"],"gov.hmrc.vat.standard_rate":["baseline_vat","vat"],"gov.local_authorities.england.council_tax_reduction.pensioners.earnings_disregard.additional":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.england.council_tax_reduction.pensioners.earnings_disregard.couple":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.england.council_tax_reduction.pensioners.earnings_disregard.lone_parent":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.england.council_tax_reduction.pensioners.earnings_disregard.single":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.england.council_tax_reduction.pensioners.maximum_support_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.england.council_tax_reduction.pensioners.means_test.capital_limit":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.england.council_tax_reduction.pensioners.means_test.withdrawal_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.kingston_upon_thames.council_tax_reduction.maximum_support_rate":["kingston_upon_thames_council_tax_reduction"],"gov.local_authorities.kingston_upon_thames.council_tax_reduction.means_test.capital_limit":["kingston_upon_thames_council_tax_reduction"],"gov.local_authorities.kingston_upon_thames.council_tax_reduction.means_test.tariff_income_step":["kingston_upon_thames_council_tax_reduction"],"gov.local_authorities.kingston_upon_thames.council_tax_reduction.means_test.tariff_income_threshold":["kingston_upon_thames_council_tax_reduction"],"gov.local_authorities.kingston_upon_thames.council_tax_reduction.means_test.withdrawal_rate":["kingston_upon_thames_council_tax_reduction"],"gov.local_authorities.merton.council_tax_reduction.maximum_support_rate":["merton_council_tax_reduction"],"gov.local_authorities.merton.council_tax_reduction.means_test.capital_limit":["merton_council_tax_reduction"],"gov.local_authorities.merton.council_tax_reduction.means_test.withdrawal_rate":["merton_council_tax_reduction"],"gov.local_authorities.newham.council_tax_reduction.maximum_support_rate":["newham_council_tax_reduction"],"gov.local_authorities.newham.council_tax_reduction.means_test.capital_limit":["newham_council_tax_reduction"],"gov.local_authorities.newham.council_tax_reduction.means_test.withdrawal_rate":["newham_council_tax_reduction"],"gov.local_authorities.oxford.council_tax_reduction.income_band.maximum_support_rate":["oxford_council_tax_reduction"],"gov.local_authorities.oxford.council_tax_reduction.means_test.capital_limit":["oxford_council_tax_reduction"],"gov.local_authorities.oxford.council_tax_reduction.means_test.tariff_income_step":["oxford_council_tax_reduction"],"gov.local_authorities.oxford.council_tax_reduction.means_test.tariff_income_threshold":["oxford_council_tax_reduction"],"gov.local_authorities.scotland.council_tax_reduction.maximum_support_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.scotland.council_tax_reduction.means_test.capital_limit":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.scotland.council_tax_reduction.means_test.withdrawal_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.scotland.council_tax_reduction.pensioners.earnings_disregard.additional":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.scotland.council_tax_reduction.pensioners.earnings_disregard.couple":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.scotland.council_tax_reduction.pensioners.earnings_disregard.lone_parent":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.scotland.council_tax_reduction.pensioners.earnings_disregard.single":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.scotland.council_tax_reduction.working_age.young_person_follows_universal_credit":["is_child_or_young_person_for_council_tax_reduction"],"gov.local_authorities.wales.council_tax_reduction.maximum_support_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.wales.council_tax_reduction.means_test.capital_limit":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.wales.council_tax_reduction.means_test.withdrawal_rate":["simulated_council_tax_reduction_benunit"],"gov.local_authorities.wales.council_tax_reduction.pensioners.earnings_disregard.additional":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.wales.council_tax_reduction.pensioners.earnings_disregard.couple":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.wales.council_tax_reduction.pensioners.earnings_disregard.lone_parent":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.wales.council_tax_reduction.pensioners.earnings_disregard.single":["council_tax_reduction_pensioner_earnings_disregard"],"gov.local_authorities.westminster.council_tax_reduction.maximum_support_rate":["westminster_council_tax_reduction"],"gov.local_authorities.westminster.council_tax_reduction.means_test.capital_limit":["westminster_council_tax_reduction"],"gov.local_authorities.westminster.council_tax_reduction.means_test.tariff_income_step":["westminster_council_tax_reduction"],"gov.local_authorities.westminster.council_tax_reduction.means_test.tariff_income_threshold":["westminster_council_tax_reduction"],"gov.local_authorities.westminster.council_tax_reduction.means_test.withdrawal_rate":["westminster_council_tax_reduction"],"gov.revenue_scotland.lbtt.non_residential":["lbtt_on_non_residential_property_transactions"],"gov.revenue_scotland.lbtt.rent":["lbtt_on_non_residential_property_rent","lbtt_on_residential_property_rent"],"gov.revenue_scotland.lbtt.residential.additional_residence_surcharge":["lbtt_on_residential_property_transactions"],"gov.revenue_scotland.lbtt.residential.first_time_buyer_rate":["lbtt_on_residential_property_transactions"],"gov.revenue_scotland.lbtt.residential.rate":["lbtt_on_residential_property_transactions"],"gov.scottish_government.funded_early_learning_childcare.age.max":["scottish_funded_early_learning_childcare_eligible"],"gov.scottish_government.funded_early_learning_childcare.age.min":["scottish_funded_early_learning_childcare_eligible"],"gov.simulation.capital_gains_responses.badr_elasticity":["capital_gains_badr_elasticity"],"gov.simulation.capital_gains_responses.elasticity":["capital_gains_badr_behavioural_response","capital_gains_behavioural_response","capital_gains_elasticity"],"gov.simulation.capital_gains_responses.mtr_elasticity":["capital_gains_badr_behavioural_response","capital_gains_behavioural_response"],"gov.simulation.capital_gains_responses.separate_badr_elasticity":["capital_gains_badr_behavioural_response","capital_gains_badr_elasticity","capital_gains_behavioural_response"],"gov.simulation.labour_supply_responses.bounds.effective_wage_rate_change":["relative_wage_change"],"gov.simulation.labour_supply_responses.bounds.income_change":["relative_income_change"],"gov.simulation.labour_supply_responses.income_elasticity":["employment_income_behavioral_response","income_elasticity_lsr"],"gov.simulation.labour_supply_responses.substitution_elasticity":["employment_income_behavioral_response","substitution_elasticity_lsr"],"gov.simulation.marginal_tax_rate_adults":["cliff_evaluated","marginal_tax_rate","marginal_tax_rate_wrt_employer_cost"],"gov.simulation.marginal_tax_rate_delta":["cliff_gap","marginal_tax_rate","marginal_tax_rate_wrt_employer_cost"],"gov.simulation.microdata_vat_coverage":["baseline_vat","vat"],"gov.simulation.private_school_vat.private_school_fees":["private_school_vat"],"gov.simulation.private_school_vat.private_school_vat_basis":["private_school_vat"],"gov.simulation.uc_deductions.calibration_cap":["uc_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.AT_10":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.AT_15":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.AT_20":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.AT_25":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.AT_5":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.FIFTEEN_TO_20":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.FIVE_TO_10":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.OVER_25":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.TEN_TO_15":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.TWENTY_TO_25":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.latent_rate_distribution.UNDER_5":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.mean_monthly_amount_by_type.ADVANCE":["uc_deduction_rate"],"gov.simulation.uc_deductions.mean_monthly_amount_by_type.GOVERNMENT":["uc_deduction_rate"],"gov.simulation.uc_deductions.mean_monthly_amount_by_type.THIRD_PARTY":["uc_deduction_rate"],"gov.simulation.uc_deductions.region_incidence_factor":["uc_has_deduction","uc_latent_deduction_rate"],"gov.simulation.uc_deductions.type_combination.ADVANCE_AND_GOVERNMENT":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.ADVANCE_AND_THIRD_PARTY":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.ADVANCE_ONLY":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.ALL_THREE":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.GOVERNMENT_ONLY":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.THIRD_PARTY_AND_GOVERNMENT":["uc_deduction_combination"],"gov.simulation.uc_deductions.type_combination.THIRD_PARTY_ONLY":["uc_deduction_combination"],"gov.social_security_scotland.carer_support_payment.min_hours":["carer_support_payment_pre_overlap","partners_care_for_same_severely_disabled_person"],"gov.social_security_scotland.carer_support_payment.rate":["carer_support_payment_pre_overlap"],"gov.social_security_scotland.carer_support_payment.supplement":["scottish_carer_supplement"],"gov.social_security_scotland.pawhp.active":["pawhp_eligible"],"gov.social_security_scotland.pawhp.amount.higher":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.amount.higher_shared_with_80_or_over":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.amount.higher_shared_with_under_80":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.amount.lower":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.amount.lower_shared":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.eligibility.higher_age_requirement":["pension_age_winter_heating_payment"],"gov.social_security_scotland.pawhp.eligibility.minimum_tax_credit_award":["is_on_pawhp_relevant_benefit","pawhp_eligible"],"gov.social_security_scotland.pawhp.eligibility.partner_receipt_counts":["is_on_pawhp_relevant_benefit"],"gov.social_security_scotland.pawhp.eligibility.relevant_benefits":["is_on_pawhp_relevant_benefit","pawhp_eligible"],"gov.social_security_scotland.pawhp.eligibility.require_benefits":["pawhp_eligible"],"gov.social_security_scotland.pawhp.eligibility.state_pension_age_requirement":["pawhp_eligible"],"gov.social_security_scotland.scottish_child_payment.amount":["is_scp_eligible","scottish_child_payment"],"gov.social_security_scotland.scottish_child_payment.max_age":["is_scp_eligible_child"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.child_tax_credit":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.esa_income":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.income_support":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.jsa_income":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.pension_credit":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.universal_credit":["is_scp_eligible"],"gov.social_security_scotland.scottish_child_payment.qualifying_benefits.working_tax_credit":["is_scp_eligible"],"gov.social_security_scotland.two_child_limit_payment.in_effect":["two_child_limit_payment"],"gov.treasury.cost_of_living_support.disabled.amount":["cost_of_living_support_payment"],"gov.treasury.cost_of_living_support.disabled.qualifying_benefits":["cost_of_living_support_payment"],"gov.treasury.cost_of_living_support.means_tested_households.amount":["cost_of_living_support_payment"],"gov.treasury.cost_of_living_support.means_tested_households.qualifying_benefits":["cost_of_living_support_payment"],"gov.treasury.cost_of_living_support.pensioners.amount":["cost_of_living_support_payment"],"gov.treasury.cost_of_living_support.pensioners.qualifying_benefits":["cost_of_living_support_payment"],"gov.treasury.energy_bills_rebate.council_tax_rebate.amount":["ebr_council_tax_rebate"],"gov.treasury.energy_bills_rebate.council_tax_rebate.bands":["ebr_council_tax_rebate"],"gov.treasury.energy_bills_rebate.energy_bills_credit":["ebr_energy_bills_credit"],"gov.treasury.energy_bills_rebate.energy_bills_credit_monthly":["monthly_ebr_energy_bills_credit"],"gov.treasury.energy_bills_rebate.energy_bills_credit_northern_ireland":["monthly_ebr_energy_bills_credit"],"gov.wra.land_transaction_tax.non_residential":["ltt_on_non_residential_property_transactions"],"gov.wra.land_transaction_tax.rent":["ltt_on_non_residential_property_rent","ltt_on_residential_property_rent"],"gov.wra.land_transaction_tax.residential.higher_rate":["ltt_on_residential_property_transactions"],"gov.wra.land_transaction_tax.residential.primary":["ltt_on_residential_property_transactions"],"household.consumption.alcohol.beer_abv":["beer_abv"],"household.consumption.alcohol.cider_abv":["cider_abv"],"household.consumption.alcohol.other_fermented_abv":["other_fermented_abv"],"household.consumption.alcohol.sparkling_cider_abv":["sparkling_cider_abv"],"household.consumption.alcohol.spirits_abv":["spirits_abv"],"household.consumption.alcohol.wine_abv":["wine_abv"],"household.consumption.carbon.emissions.alcohol_and_tobacco":["carbon_consumption"],"household.consumption.carbon.emissions.clothing_and_footwear":["carbon_consumption"],"household.consumption.carbon.emissions.communication":["carbon_consumption"],"household.consumption.carbon.emissions.education":["carbon_consumption"],"household.consumption.carbon.emissions.food_and_non_alcoholic_beverages":["carbon_consumption"],"household.consumption.carbon.emissions.health":["carbon_consumption"],"household.consumption.carbon.emissions.household_furnishings":["carbon_consumption"],"household.consumption.carbon.emissions.housing_water_and_electricity":["carbon_consumption"],"household.consumption.carbon.emissions.miscellaneous":["carbon_consumption"],"household.consumption.carbon.emissions.recreation":["carbon_consumption"],"household.consumption.carbon.emissions.restaurants_and_hotels":["carbon_consumption"],"household.consumption.carbon.emissions.transport":["carbon_consumption"],"household.consumption.fuel.prices.diesel":["diesel_price"],"household.consumption.fuel.prices.petrol":["petrol_price"],"household.consumption.tobacco.cigarettes_per_pack":["tobacco_duty"],"household.consumption.tobacco.price_per_pack":["cigarette_price_per_pack"],"household.consumption.total_by_category.alcohol_and_tobacco_consumption":["carbon_consumption"],"household.consumption.total_by_category.clothing_and_footwear_consumption":["carbon_consumption"],"household.consumption.total_by_category.communication_consumption":["carbon_consumption"],"household.consumption.total_by_category.education_consumption":["carbon_consumption"],"household.consumption.total_by_category.food_and_non_alcoholic_beverages_consumption":["carbon_consumption"],"household.consumption.total_by_category.health_consumption":["carbon_consumption"],"household.consumption.total_by_category.household_furnishings_consumption":["carbon_consumption"],"household.consumption.total_by_category.housing_water_and_electricity_consumption":["carbon_consumption"],"household.consumption.total_by_category.miscellaneous_consumption":["carbon_consumption"],"household.consumption.total_by_category.recreation_consumption":["carbon_consumption"],"household.consumption.total_by_category.restaurants_and_hotels_consumption":["carbon_consumption"],"household.consumption.total_by_category.transport_consumption":["carbon_consumption"],"household.demographic.benefit_unit.presumed_child.age_limit":["is_claimant_or_partner"],"household.demographic.benefit_unit.presumed_child.minimum_age_gap":["is_claimant_or_partner"],"household.demographic.equiv.ahc.child_over_14":["household_equivalisation_ahc"],"household.demographic.equiv.ahc.child_under_14":["household_equivalisation_ahc"],"household.demographic.equiv.ahc.first_adult":["household_equivalisation_ahc"],"household.demographic.equiv.ahc.second_adult":["household_equivalisation_ahc"],"household.demographic.equiv.bhc.child_over_14":["household_equivalisation_bhc"],"household.demographic.equiv.bhc.child_under_14":["household_equivalisation_bhc"],"household.demographic.equiv.bhc.first_adult":["household_equivalisation_bhc"],"household.demographic.equiv.bhc.second_adult":["household_equivalisation_bhc"],"household.demographic.equiv.child_age_threshold":["is_hbai_child_aged_14_or_over","is_hbai_child_under_14"],"household.demographic.hbai.dependent_child.age_limit":["is_hbai_dependent_child"],"household.demographic.hbai.dependent_child.young_person_age_limit":["is_hbai_dependent_child"],"household.poverty.absolute_poverty_threshold_ahc":["in_deep_poverty_ahc","poverty_line_ahc","poverty_threshold_ahc"],"household.poverty.absolute_poverty_threshold_bhc":["in_deep_poverty_bhc","poverty_line","poverty_line_bhc","poverty_threshold_bhc"],"household.poverty.exclude_non_hbai_income":["baseline_hbai_excluded_income"],"household.wealth.land.intensity.property_wealth_by_region.EAST_MIDLANDS":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.EAST_OF_ENGLAND":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.LONDON":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.NORTHERN_IRELAND":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.NORTH_EAST":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.NORTH_WEST":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.SCOTLAND":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.SOUTH_EAST":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.SOUTH_WEST":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.UNKNOWN":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.WALES":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.WEST_MIDLANDS":["household_land_value"],"household.wealth.land.intensity.property_wealth_by_region.YORKSHIRE":["household_land_value"],"household.wealth.national_balance_sheet.household.financial_net_worth":["shareholding"]},"consumers":{"CB_HITC":["capped_mcad","child_benefit_less_tax_charge","income_tax"],"CTC_child_element":["CTC_maximum_rate"],"CTC_disabled_child_element":["CTC_maximum_rate"],"CTC_family_element":["CTC_maximum_rate"],"CTC_maximum_rate":["child_tax_credit_pre_minimum","tax_credits_reduction"],"CTC_severely_disabled_child_element":["CTC_maximum_rate"],"LHA_allowed_bedrooms":["LHA_category"],"LHA_cap":["housing_benefit_entitlement"],"LHA_category":["BRMA_LHA_rate","uc_LHA_cap","uncapped_BRMA_LHA_rate"],"LHA_eligible":["housing_benefit_eligible","housing_benefit_entitlement"],"LVT":["gov_tax","hbai_household_net_income","household_tax","pre_budget_change_household_tax"],"WTC_basic_element":["WTC_maximum_rate"],"WTC_childcare_element":["WTC_maximum_rate","childcare_grant_eligible"],"WTC_couple_element":["WTC_maximum_rate"],"WTC_disabled_element":["WTC_maximum_rate"],"WTC_lone_parent_element":["WTC_maximum_rate"],"WTC_maximum_rate":["child_tax_credit_pre_minimum","tax_credits_reduction","working_tax_credit_pre_minimum"],"WTC_severely_disabled_element":["WTC_maximum_rate"],"WTC_worker_element":["WTC_maximum_rate"],"aa_category":["attendance_allowance","is_severely_disabled_for_benefits"],"access_fund":["student_payments"],"add_rate_earned_income":["add_rate_earned_income_tax"],"add_rate_savings_income":["savings_income_tax","taxed_savings_income"],"additional_minimum_guarantee":["minimum_guarantee"],"additional_residential_property_purchased":["lbtt_on_residential_property_transactions","ltt_on_residential_property_transactions","sdlt_on_residential_property_transactions"],"additional_state_pension":["overlapping_state_pension","state_pension"],"adjusted_net_income":["CB_HITC","child_benefit","extended_childcare_entitlement_meets_income_requirements","extended_childcare_entitlement_work_condition","is_higher_earner","personal_allowance","tax_free_childcare_meets_income_requirements"],"adult_dependants_grant":["gov_spending","hbai_benefits","household_benefits"],"adult_dependants_grant_adult_eligible":["adult_dependants_grant_eligible"],"adult_dependants_grant_adult_receives_student_finance":["adult_dependants_grant_eligible"],"adult_dependants_grant_course_eligible":["adult_dependants_grant_eligible"],"adult_dependants_grant_eligible":["adult_dependants_grant"],"adult_dependants_grant_has_other_adult_dependant":["adult_dependants_grant_adult_eligible"],"adult_dependants_grant_household_income":["adult_dependants_grant_eligible"],"adult_dependants_grant_other_adult_income":["adult_dependants_grant_adult_eligible"],"adult_dependants_grant_receives_postgraduate_loan":["adult_dependants_grant_eligible"],"adult_ema":["student_payments"],"adult_index":["cliff_evaluated","maintenance_loan_has_sponsor","maintenance_loan_sponsor_income","marginal_tax_rate","marginal_tax_rate_wrt_employer_cost"],"adult_index_cg":["marginal_tax_rate_on_capital_gains"],"afcs":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_benefit_cap_exempt_health_disability","pre_budget_change_household_benefits"],"afcs_reported":["afcs"],"age":["LHA_allowed_bedrooms","adult_dependants_grant_adult_eligible","adult_index","age_18_64","age_over_64","age_under_18","bi_maximum","birth_year","bursary_fund_16_to_19_age_eligible","care_to_learn_eligible","child_benefit_child_index","child_index","child_minimum_guarantee_addition","childcare_grant_child_eligible","council_tax_reduction_individual_non_dep_deduction_eligible","council_tax_reduction_liable_person","current_education","date_of_birth","eldest_adult_age","eldest_child_age","eldest_claimant_or_partner_age","extended_childcare_entitlement_qualifying_child","extended_childcare_entitlement_total_per_child","has_attained_state_pension_credit_qualifying_age","has_mixed_age_couple_pension_credit_saving","has_non_dependant_for_severe_disability_premium","has_own_esa_award","household_bus_fare_age_weight","housing_benefit_LHA_allowed_bedrooms","housing_benefit_has_non_dependant","housing_benefit_individual_non_dep_deduction_eligible","housing_benefit_meals_deduction","housing_benefit_severe_disability_premium_applies","is_CTC_child_limit_exempt","is_WTC_eligible","is_adult","is_benefit_cap_exempt_health_disability","is_benunit_eldest_child","is_benunit_head","is_cared_for_by_carer_benefit_recipient","is_child","is_child_for_child_benefit","is_child_for_child_tax_credit","is_child_for_pension_credit","is_child_for_universal_credit","is_child_for_working_tax_credit_childcare_element","is_child_receiving_extended_childcare","is_child_receiving_targeted_childcare","is_child_receiving_universal_childcare","is_claimant_or_partner","is_counted_resident_for_severe_disability_addition","is_hbai_child_aged_14_or_over","is_hbai_child_under_14","is_hbai_dependent_child","is_higher_earner","is_household_head","is_lha_shared_accommodation_rate_specified_renter","is_of_compulsory_school_age","is_older_child","is_on_income_based_jsa","is_on_income_related_esa","is_on_income_support","is_on_pension_credit","is_resolved_household_head","is_responsible_for_child_or_young_person_for_uc_or_housing_benefit","is_scp_eligible_child","is_uc_assessed_claimant","is_young_child","lha_renter_has_non_dependant","lifetime_isa_countable_capital","maintenance_loan","maintenance_loan_eligible","maintenance_loan_has_sponsor","maintenance_loan_household_income","maintenance_loan_in_higher_education","maintenance_loan_sponsor_income","meets_housing_benefit_additional_earnings_disregard_conditions","meets_qualifying_young_person_age_condition_for_child_benefit","meets_qualifying_young_person_age_condition_for_child_tax_credit","meets_qualifying_young_person_age_condition_for_pension_credit","meets_qualifying_young_person_age_condition_for_universal_credit","meets_qualifying_young_person_entry_condition_for_child_benefit","meets_qualifying_young_person_entry_condition_for_child_tax_credit","meets_qualifying_young_person_entry_condition_for_pension_credit","meets_qualifying_young_person_entry_condition_for_universal_credit","meets_qualifying_young_person_terminal_date_condition_for_universal_credit","meets_savings_credit_age_requirement","meets_uc_minimum_age_condition","minimum_wage","minimum_wage_category","months_since_last_birthday","months_since_state_pension_age","num_relevant_children_for_housing_benefit_childcare","over_16","pawhp_eligible","pension_age_winter_heating_payment","pension_contributions_relief","person_bus_fare_spending","scottish_funded_early_learning_childcare_eligible","state_pension_age","state_pension_credit_qualifying_age","targeted_childcare_entitlement","tax_free_childcare_child_age_eligible","tv_licence_discount","uc_child_index","uc_individual_earned_income_before_mif","uc_individual_non_dep_deduction_eligible","uc_is_child_born_before_child_limit","uc_is_responsible_carer","uc_minimum_income_floor_gross","uc_standard_allowance_claimant_type","uc_youngest_child_age","universal_childcare_entitlement","universal_childcare_entitlement_eligible","winter_fuel_payment","winter_fuel_payment_eligible","youngest_adult_age","youngest_child_age","youngest_child_age_for_legacy_benefits","youngest_claimant_or_partner_age"],"age_started_or_accepted_current_education_or_training":["meets_qualifying_young_person_entry_condition_for_child_benefit","meets_qualifying_young_person_entry_condition_for_child_tax_credit","meets_qualifying_young_person_entry_condition_for_pension_credit","meets_qualifying_young_person_entry_condition_for_universal_credit"],"age_under_18":["adult_index","adult_index_cg","is_hbai_dependent_child"],"alcohol_and_tobacco_consumption":["carbon_consumption","consumption"],"alcohol_duty":["gov_tax","household_tax"],"allowances":["capital_gains_tax","non_savings_income_after_allowances","received_allowances_dividend_income","received_allowances_earned_income","received_allowances_savings_income","savings_allowance","tax_band"],"armed_forces_independence_payment":["cost_of_living_support_payment","council_tax_reduction_applicant_has_non_dep_exemption","housing_benefit_severe_disability_premium_applies","is_benefit_cap_exempt_health_disability","is_severely_disabled_for_benefits","is_severely_disabled_for_carers_allowance","maintenance_loan_entitled_to_benefits","receives_severe_disability_addition_qualifying_benefit","receives_severe_disability_premium_qualifying_benefit"],"attendance_allowance":["cost_of_living_support_payment","council_tax_reduction_applicant_has_non_dep_exemption","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_severe_disability_premium_applies","is_benefit_cap_exempt_health_disability","is_lha_shared_accommodation_rate_specified_renter","is_severely_disabled_for_benefits","is_severely_disabled_for_carers_allowance","pre_budget_change_household_benefits","receives_severe_disability_addition_qualifying_benefit","receives_severe_disability_premium_qualifying_benefit","uc_non_dep_deduction_exempt"],"attends_private_school":["private_school_vat"],"baseline_hbai_excluded_income":["hbai_excluded_income_change"],"baseline_vat":["vat_change"],"basic_income":["council_tax_reduction_applicable_income","gov_spending","household_benefits","housing_benefit_applicable_income","pre_budget_change_household_benefits"],"basic_rate_earned_income":["basic_rate_earned_income_tax"],"basic_rate_savings_income":["savings_income_tax","taxed_savings_income"],"basic_rate_savings_income_pre_starter":["savings_starter_rate_income"],"basic_state_pension":["overlapping_state_pension","state_pension"],"beer_abv":["beer_duty"],"beer_draught_share":["beer_duty"],"beer_duty":["alcohol_duty"],"beer_litres":["beer_duty"],"benefit_cap":["benefit_cap_reduction"],"benefit_cap_reduction":["housing_benefit","uc_deductions","uc_has_deduction","universal_credit"],"benefits_premiums":["housing_benefit_applicable_amount"],"benunit_contains_household_head":["is_liable_for_household_rent","is_non_dependant_of_household_head","pays_rent_to_householder","personal_rent","share_of_household_rent","uc_non_dependants_counted"],"benunit_has_carer":["uc_carer_element"],"benunit_is_rent_liable":["housing_benefit_individual_non_dep_deduction_eligible","housing_benefit_shares_accommodation","is_non_dependant_of_household_head","uc_individual_non_dep_deduction_eligible","uc_non_dep_receives_pension_credit"],"benunit_is_renting":["LHA_eligible","maintenance_loan_household_income","maintenance_loan_living_arrangement"],"benunit_rent":["LHA_cap","benunit_is_rent_liable","housing_benefit_entitlement","uc_LHA_cap","uc_housing_costs_element"],"benunit_reported_capital":["pension_credit_reported_capital","uc_reported_capital"],"benunit_tenure_type":["benunit_is_renting","in_social_housing","uc_housing_costs_element"],"bi_household_phaseout":["bi_phaseout"],"bi_individual_phaseout":["bi_household_phaseout","bi_phaseout"],"bi_maximum":["basic_income","bi_household_phaseout","bi_individual_phaseout"],"bi_phaseout":["basic_income"],"blind_persons_allowance":["allowances","esa_exempt_work_earnings","property_finance_costs_relieved"],"brma":["BRMA_LHA_rate","housing_benefit_LHA_rate","uc_LHA_cap","uncapped_BRMA_LHA_rate"],"bsp":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits"],"bsp_reported":["bsp"],"bursary_fund_16_to_19":["gov_spending","hbai_benefits","household_benefits"],"bursary_fund_16_to_19_age_eligible":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_in_care_or_care_leaver":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_in_qualifying_education":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_receives_uc_or_esa_in_own_right":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_receives_uc_or_is_in_own_right":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_self_supporting":["bursary_fund_16_to_19_vulnerable_group_eligible"],"bursary_fund_16_to_19_vulnerable_group_eligible":["bursary_fund_16_to_19"],"bus_fare_spending_reported":["person_bus_fare_spending"],"bus_in_london_trips":["local_bus_trips"],"bus_subsidy_spending":["dft_subsidy_spending"],"business_rates":["corporate_tax_incidence","gov_tax","household_tax","pre_budget_change_household_tax"],"capital_allowances":["ni_class_4_profits_before_losses","taxable_self_employment_income","trading_allowance_deduction"],"capital_gains":["capital_gains_tax","household_market_income","marginal_tax_rate_on_capital_gains"],"capital_gains_badr":["capital_gains_tax"],"capital_gains_badr_behavioural_response":["capital_gains_tax","marginal_tax_rate_on_capital_gains"],"capital_gains_before_response":["adult_index_cg","capital_gains","capital_gains_tax","relative_capital_gains_mtr_change","relative_capital_gains_retention_rate_change","relative_income_change","relative_wage_change"],"capital_gains_behavioural_response":["capital_gains","capital_gains_tax","marginal_tax_rate_on_capital_gains"],"capital_gains_carried_interest":["capital_gains_tax"],"capital_gains_elasticity":["capital_gains_badr_elasticity"],"capital_gains_residential_property":["capital_gains_tax"],"capital_gains_tax":["gov_tax","household_tax","pre_budget_change_household_tax"],"capped_mcad":["income_tax","property_finance_cost_relief","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"car_co2_emissions":["car_vehicle_excise_duty"],"car_engine_size":["car_vehicle_excise_duty"],"car_first_registration_date":["car_vehicle_excise_duty"],"car_fuel_type":["car_vehicle_excise_duty"],"car_is_ved_exempt":["car_vehicle_excise_duty"],"car_list_price":["car_vehicle_excise_duty"],"car_vehicle_excise_duty":["vehicle_excise_duty"],"carbon_consumption":["carbon_tax"],"carbon_tax":["gov_tax","household_tax","pre_budget_change_household_tax"],"care_hours":["carer_support_payment_pre_overlap","carers_allowance_pre_overlap","is_carer_for_benefits","partners_care_for_same_severely_disabled_person"],"care_to_learn":["gov_spending","household_benefits"],"care_to_learn_eligible":["care_to_learn"],"care_to_learn_qualifying_child":["care_to_learn_eligible"],"carer_minimum_guarantee_addition":["additional_minimum_guarantee"],"carer_premium":["pension_age_benefits_premiums","working_age_benefits_premiums"],"carer_support_payment":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","pension_credit_income","pre_budget_change_household_benefits","receives_carer_support_payment","scottish_carer_supplement","social_security_income","tax_free_childcare_regarded_as_in_paid_work","uc_unearned_carer_support_payment"],"carer_support_payment_overlapping_benefits":["carer_support_payment"],"carer_support_payment_pre_overlap":["carer_support_payment","council_tax_reduction_applicable_income","is_entitled_to_carer_benefit"],"carers_allowance":["council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","pension_credit_income","pre_budget_change_household_benefits","receives_carers_allowance","social_security_income","tax_free_childcare_regarded_as_in_paid_work","uc_unearned_income"],"carers_allowance_earnings":["is_gainfully_employed_for_carers_allowance"],"carers_allowance_overlapping_benefits":["carers_allowance"],"carers_allowance_pre_overlap":["carers_allowance","is_entitled_to_carer_benefit"],"carers_allowance_reported":["carer_support_payment_pre_overlap","carers_allowance_pre_overlap","is_carer_for_benefits","partners_care_for_same_severely_disabled_person"],"charitable_investment_gifts":["allowances","property_finance_costs_relieved"],"child_benefit":["CB_HITC","benefit_cap_reduction","child_benefit_less_tax_charge","council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","oxford_council_tax_reduction","pre_budget_change_household_benefits"],"child_benefit_child_index":["child_benefit_respective_amount"],"child_benefit_entitlement":["child_benefit"],"child_benefit_opts_out":["child_benefit"],"child_benefit_respective_amount":["child_benefit_entitlement"],"child_ema":["student_payments"],"child_index":["is_eldest_child"],"child_minimum_guarantee_addition":["additional_minimum_guarantee"],"child_tax_credit":["benefit_cap_reduction","ctc_child_limit_affected","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_scp_eligible","meets_child_tax_credit_criteria_for_targeted_childcare_entitlement","pre_budget_change_household_benefits","tax_free_childcare_program_eligible"],"child_tax_credit_pre_minimum":["ctc_entitlement"],"child_tax_credit_reported":["claims_all_entitled_benefits","claims_legacy_benefits","is_CTC_eligible","legacy_benefits_closed","pawhp_eligible","tax_credit_award","winter_fuel_payment_eligible","would_claim_CTC"],"childcare_expenses":["housing_benefit_applicable_income_childcare_element","tax_free_childcare","uc_childcare_element","weekly_childcare_expenses"],"childcare_grant":["gov_spending","hbai_benefits","household_benefits"],"childcare_grant_child_eligible":["childcare_grant_eligible_children"],"childcare_grant_child_has_special_educational_needs":["childcare_grant_child_eligible"],"childcare_grant_eligible":["childcare_grant"],"childcare_grant_eligible_children":["childcare_grant_eligible"],"childcare_grant_full_time_student":["childcare_grant_eligible"],"childcare_grant_household_income":["childcare_grant_eligible"],"childcare_grant_provider_is_nanny":["childcare_grant_uses_qualifying_provider"],"childcare_grant_receives_nhs_childcare_support":["childcare_grant_eligible"],"childcare_grant_receives_postgraduate_loan":["childcare_grant_eligible"],"childcare_grant_student_finance_eligible":["childcare_grant_eligible"],"childcare_grant_uses_qualifying_provider":["childcare_grant_eligible"],"cider_abv":["cider_duty"],"cider_draught_share":["cider_duty"],"cider_duty":["alcohol_duty"],"cider_litres":["cider_duty"],"cigarette_price_per_pack":["tobacco_duty"],"cigarettes_per_week":["tobacco_duty"],"cigars_grams_per_week":["tobacco_duty"],"claimant_or_partner_esa_income":["council_tax_reduction_applicable_income","council_tax_reduction_relevant_income_based_benefit","extended_childcare_entitlement_limited_capability_or_specified_benefit","housing_benefit_applicable_income","in_receipt_of_income_support_jsa_ib_or_esa_ir","is_on_income_related_esa","is_scp_eligible","targeted_childcare_entitlement_eligible","tax_credits_applicable_income"],"claimant_or_partner_jsa_income":["council_tax_reduction_applicable_income","council_tax_reduction_relevant_income_based_benefit","housing_benefit_applicable_income","in_receipt_of_income_support_jsa_ib_or_esa_ir","is_on_income_based_jsa","is_scp_eligible","targeted_childcare_entitlement_eligible","tax_credits_applicable_income"],"claims_all_entitled_benefits":["would_claim_CTC","would_claim_IS","would_claim_WTC","would_claim_council_tax_reduction","would_claim_housing_benefit"],"claims_legacy_benefits":["is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_lha_shared_accommodation_rate_specified_renter"],"claims_uc_at_legacy_closure":["claims_universal_credit","council_tax_reduction_pensioner","is_uc_eligible","left_pension_route_at_legacy_closure"],"claims_universal_credit":["housing_benefit_eligible","is_CTC_eligible","is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_lha_shared_accommodation_rate_specified_renter","uc_has_deduction","uc_non_dependants_counted","universal_credit","universal_credit_pre_benefit_cap"],"cliff_gap":["is_on_cliff"],"clothing_and_footwear_consumption":["carbon_consumption","consumption"],"communication_consumption":["carbon_consumption","consumption"],"consumer_debt":["net_wealth"],"consumer_incident_tax_revenue_change":["gov_tax","household_tax"],"consumption":["consumer_incident_tax_revenue_change","consumption_shareholding","full_rate_vat_consumption","reduced_rate_vat_consumption"],"corporate_incident_tax_revenue_change":["gov_tax","household_tax"],"corporate_land_value":["LVT","land_value"],"corporate_sdlt":["corporate_tax_incidence","expected_sdlt","gov_tax","household_tax","pre_budget_change_household_tax"],"corporate_sector_wealth":["corporate_land_value","shareholding"],"corporate_tax_incidence":["hbai_excluded_income"],"corporate_wealth":["corporate_sector_wealth","esa_income_assessable_capital","household_uc_pool_company_holdings","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","pension_credit_assessable_capital","total_wealth","uc_assessable_capital"],"cost_of_living_support_payment":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits"],"council_tax":["council_tax_less_benefit","council_tax_reduction_maximum_eligible_liability","gov_tax","hbai_household_net_income","household_tax","pre_budget_change_household_tax"],"council_tax_band":["ebr_council_tax_rebate"],"council_tax_benefit":["council_tax_reduction","gov_spending","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits"],"council_tax_benefit_reported":["council_tax_benefit","would_claim_council_tax_reduction"],"council_tax_reduction":["council_tax_less_benefit"],"council_tax_reduction_applicable_amount":["kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"council_tax_reduction_applicable_income":["kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"council_tax_reduction_applicant_has_non_dep_exemption":["kingston_upon_thames_council_tax_reduction_non_dep_deductions","merton_council_tax_reduction_non_dep_deductions","newham_council_tax_reduction_non_dep_deductions","oxford_council_tax_reduction_non_dep_deductions","westminster_council_tax_reduction_non_dep_deductions"],"council_tax_reduction_assessable_capital":["simulated_council_tax_reduction_benunit"],"council_tax_reduction_claimant_benunit":["council_tax_benefit","council_tax_reduction_household_has_simulated_claim","council_tax_reduction_individual_non_dep_deduction_eligible","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"council_tax_reduction_claims_are_joint":["council_tax_reduction_claimant_benunit"],"council_tax_reduction_head_applies_alone":["council_tax_reduction_pensioner","is_council_tax_reduction_applicant_or_partner"],"council_tax_reduction_household_has_simulated_claim":["council_tax_benefit"],"council_tax_reduction_individual_non_dep_deduction":["council_tax_reduction_non_dep_deductions"],"council_tax_reduction_individual_non_dep_deduction_eligible":["council_tax_reduction_individual_non_dep_deduction","kingston_upon_thames_council_tax_reduction_individual_non_dep_deduction","merton_council_tax_reduction_individual_non_dep_deduction","newham_council_tax_reduction_individual_non_dep_deduction","oxford_council_tax_reduction_individual_non_dep_deduction","westminster_council_tax_reduction_individual_non_dep_deduction"],"council_tax_reduction_joint_liability_share":["council_tax_benefit","council_tax_reduction_non_dep_deductions","kingston_upon_thames_council_tax_reduction","kingston_upon_thames_council_tax_reduction_non_dep_deductions","merton_council_tax_reduction","merton_council_tax_reduction_non_dep_deductions","newham_council_tax_reduction","newham_council_tax_reduction_non_dep_deductions","oxford_council_tax_reduction","oxford_council_tax_reduction_non_dep_deductions","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction","westminster_council_tax_reduction_non_dep_deductions"],"council_tax_reduction_liable_person":["council_tax_benefit","council_tax_reduction_claimant_benunit","council_tax_reduction_head_applies_alone","council_tax_reduction_joint_liability_share","is_council_tax_reduction_applicant_or_partner"],"council_tax_reduction_maximum_eligible_liability":["council_tax_benefit","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"council_tax_reduction_non_dep_deductions":["simulated_council_tax_reduction_benunit"],"council_tax_reduction_pensioner":["council_tax_reduction_applicable_amount","council_tax_reduction_pensioner_earnings_disregard","council_tax_reduction_scheme_supported","is_child_or_young_person_for_council_tax_reduction","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"council_tax_reduction_pensioner_earnings_disregard":["council_tax_reduction_applicable_income"],"council_tax_reduction_relevant_income_based_benefit":["council_tax_reduction_pensioner","housing_benefit_pension_age_regulations_apply","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","westminster_council_tax_reduction"],"council_tax_reduction_scheme_supported":["council_tax_benefit","council_tax_reduction_household_has_simulated_claim"],"country":["adult_dependants_grant_eligible","bursary_fund_16_to_19_vulnerable_group_eligible","care_to_learn_eligible","carer_support_payment_pre_overlap","carers_allowance_pre_overlap","childcare_grant_eligible","council_tax_reduction_pensioner","council_tax_reduction_pensioner_earnings_disregard","council_tax_reduction_scheme_supported","extended_childcare_entitlement_qualifying_child","housing_benefit_on_passporting_benefit","is_child_or_young_person_for_council_tax_reduction","is_scp_eligible","kingston_upon_thames_council_tax_reduction","lbtt_liable","ltt_liable","maintenance_loan_in_england_system","merton_council_tax_reduction","monthly_ebr_energy_bills_credit","newham_council_tax_reduction","oxford_council_tax_reduction","parents_learning_allowance_eligible","partners_care_for_same_severely_disabled_person","pawhp_eligible","pays_scottish_income_tax","receives_severe_disability_premium_qualifying_benefit","scottish_funded_early_learning_childcare_eligible","sdlt_liable","simulated_council_tax_reduction_benunit","targeted_childcare_entitlement_eligible","travel_grant_eligible","two_child_limit_payment","universal_childcare_entitlement_eligible","westminster_council_tax_reduction","winter_fuel_payment_eligible"],"covenanted_payments":["allowances"],"ctc_entitlement":["child_tax_credit"],"cumulative_non_residential_rent":["lbtt_on_non_residential_property_rent","ltt_on_non_residential_property_rent","sdlt_on_non_residential_property_rent"],"cumulative_residential_rent":["lbtt_on_residential_property_rent","ltt_on_residential_property_rent","sdlt_on_residential_property_rent"],"current_education":["bursary_fund_16_to_19_in_qualifying_education","care_to_learn_eligible","council_tax_benefit","is_in_non_advanced_education","maintenance_loan_in_higher_education"],"date_of_birth":["birth_year","child_minimum_guarantee_addition","has_attained_state_pension_credit_qualifying_age","has_mixed_age_couple_pension_credit_saving","is_CTC_child_limit_exempt","meets_savings_credit_age_requirement","months_since_state_pension_age","state_pension_age","state_pension_credit_qualifying_age","uc_child_index","uc_is_child_born_before_child_limit"],"deductible_property_finance_costs":["property_allowance_deduction_if_used","taxable_property_income","uses_property_allowance"],"deep_poverty_gap":["in_deep_poverty"],"deep_poverty_line":["deep_poverty_gap"],"deficiency_relief":["taxable_dividend_income"],"dfe_education_spending":["gov_spending","household_benefits"],"dft_subsidy_spending":["gov_spending","household_benefits"],"diesel_litres":["fuel_duty"],"diesel_price":["diesel_litres"],"diesel_spending":["diesel_litres"],"disability_basic_income":["gov_spending","household_benefits","pre_budget_change_household_benefits"],"disability_premium":["maintenance_loan_entitled_to_benefits","working_age_benefits_premiums"],"disabled_students_allowance":["gov_spending","hbai_benefits","household_benefits"],"disabled_students_allowance_course_eligible":["disabled_students_allowance_eligible"],"disabled_students_allowance_eligible":["disabled_students_allowance"],"disabled_students_allowance_eligible_expenses":["disabled_students_allowance_eligible"],"disabled_students_allowance_has_qualifying_condition":["disabled_students_allowance_eligible"],"disabled_students_allowance_receives_equivalent_support":["disabled_students_allowance_eligible"],"dividend_income":["capital_income","hbai_household_net_income","household_market_income","housing_benefit_net_earnings","market_income","tax_credits_current_year_income","taxable_dividend_income","total_income"],"dividend_income_tax":["income_tax","income_tax_pre_charges"],"dla":["bursary_fund_16_to_19_vulnerable_group_eligible","child_minimum_guarantee_addition","cost_of_living_support_payment","disability_basic_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_benefit_cap_exempt_health_disability","pre_budget_change_household_benefits"],"dla_m":["dla","maintenance_loan_entitled_to_benefits"],"dla_m_category":["dla_m"],"dla_sc":["council_tax_reduction_applicant_has_non_dep_exemption","dla","is_enhanced_disabled_for_benefits","is_severely_disabled_for_benefits","maintenance_loan_entitled_to_benefits"],"dla_sc_category":["dla_sc","dla_sc_middle_plus","receives_highest_dla_sc"],"dla_sc_middle_plus":["housing_benefit_severe_disability_premium_applies","is_lha_shared_accommodation_rate_specified_renter","is_severely_disabled_for_carers_allowance","receives_severe_disability_addition_qualifying_benefit","receives_severe_disability_premium_qualifying_benefit","uc_non_dep_deduction_exempt"],"domestic_energy_consumption":["monthly_domestic_energy_consumption"],"domestic_rates":["gov_tax","hbai_household_net_income","household_tax","pre_budget_change_household_tax"],"earned_income_tax":["income_tax","income_tax_pre_charges"],"earned_taxable_income":["add_rate_earned_income","basic_rate_earned_income","earned_income_tax","higher_rate_earned_income","non_savings_taxable_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"ebr_council_tax_rebate":["energy_bills_rebate"],"ebr_energy_bills_credit":["energy_bills_rebate"],"ebr_energy_bills_credit_eligible":["monthly_ebr_energy_bills_credit"],"education_consumption":["carbon_consumption","consumption"],"education_grants":["student_payments"],"eldest_child_age":["is_benunit_eldest_child"],"eldest_claimant_or_partner_age":["council_tax_reduction_applicable_amount","housing_benefit_applicable_amount","housing_benefit_specified_or_temporary_accommodation_disregard","income_support_applicable_amount","is_WTC_eligible","is_housing_benefit_young_individual"],"employee_pension_contributions":["carers_allowance_earnings","esa_exempt_work_earnings","hbai_household_net_income","pension_contributions","pension_contributions_for_annual_allowance"],"employee_pension_contributions_reported":["employee_pension_contributions"],"employer_cost":["marginal_tax_rate_wrt_employer_cost"],"employer_ni_fixed_employer_cost_change":["employment_income"],"employer_ni_response_capital_incidence":["household_tax"],"employer_ni_response_consumer_incidence":["household_tax"],"employer_pension_contributions":["adjusted_employer_cost","baseline_employer_cost","employer_cost","employer_ni_fixed_employer_cost_change","pension_contributions_for_annual_allowance"],"employment_benefits":["housing_benefit_net_earnings","housing_benefit_specified_or_temporary_accommodation_disregard","taxable_employment_income"],"employment_deductions":["esa_exempt_work_earnings","taxable_employment_income"],"employment_expenses":["employment_deductions"],"employment_income":["adjusted_employer_cost","carers_allowance_earnings","council_tax_reduction_applicable_income","earned_income","employer_cost","esa_exempt_work_earnings","extended_childcare_entitlement_meets_income_requirements","hbai_household_net_income","household_market_income","housing_benefit_applicable_income","housing_benefit_net_earnings","housing_benefit_specified_or_temporary_accommodation_disregard","in_work","income_support_applicable_income","marginal_tax_rate","marginal_tax_rate_wrt_employer_cost","market_income","ni_class_1_income","pension_contributions_relief","pension_credit_earnings","salary_sacrifice_pension_ni_employee","tax_credits_current_year_income","tax_free_childcare_expected_declaration_period_income","taxable_employment_income","total_income","uc_individual_earned_income_before_mif"],"employment_income_before_lsr":["baseline_employer_cost","employer_ni_fixed_employer_cost_change","employment_income","income_elasticity_lsr","pension_contributions_via_salary_sacrifice_from_pay","substitution_elasticity_lsr"],"employment_income_behavioral_response":["baseline_employer_cost","employer_ni_fixed_employer_cost_change","employment_income"],"energy_bills_rebate":["gov_spending","household_benefits","pre_budget_change_household_benefits"],"enhanced_disability_premium":["working_age_benefits_premiums"],"epg_subsidy":["gov_spending","household_benefits","pre_budget_change_household_benefits"],"equiv_hbai_household_net_income":["household_income_decile","in_deep_poverty_bhc","in_poverty_bhc","in_relative_poverty_bhc"],"equiv_hbai_household_net_income_ahc":["in_deep_poverty_ahc","in_poverty_ahc","in_relative_poverty_ahc"],"esa_contrib":["benefit_cap_reduction","carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits","council_tax_reduction_applicable_income","esa","extended_childcare_entitlement_limited_capability_or_specified_benefit","gov_spending","has_own_esa_award","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","income_support_eligible","is_benefit_cap_exempt_health_disability","pension_credit_income","pre_budget_change_household_benefits","savings_credit_income","tax_free_childcare_caring_or_incapacity_benefit","tax_free_childcare_regarded_as_in_paid_work","uc_unearned_income"],"esa_contrib_reported":["bursary_fund_16_to_19_receives_uc_or_esa_in_own_right","esa_contrib","social_security_income"],"esa_exempt_work_earnings":["esa_income_claimant_remunerative_work"],"esa_includes_support_component":["is_benefit_cap_exempt_health_disability","uc_limited_capability_for_WRA"],"esa_income":["benefit_cap_reduction","claimant_or_partner_esa_income","cost_of_living_support_payment","esa","gov_spending","has_own_esa_award","hbai_benefits","hbai_household_net_income","household_benefits","income_support_eligible","is_benefit_cap_exempt_health_disability","is_on_income_related_esa","pre_budget_change_household_benefits"],"esa_income_assessable_capital":["esa_income_eligible","esa_income_tariff_income","is_on_income_related_esa"],"esa_income_claimant_remunerative_work":["esa_income_eligible","is_on_income_related_esa"],"esa_income_eligible":["benefit_cap_reduction","claimant_or_partner_esa_income","esa_income","income_support_eligible","is_benefit_cap_exempt_health_disability","is_on_income_related_esa"],"esa_income_remunerative_work_hours":["esa_income_claimant_remunerative_work","esa_income_eligible"],"esa_income_reported":["benefit_cap_reduction","bursary_fund_16_to_19_receives_uc_or_esa_in_own_right","claimant_or_partner_esa_income","claims_all_entitled_benefits","claims_legacy_benefits","esa_income","esa_income_assessable_capital","esa_income_eligible","has_mixed_age_couple_pension_credit_saving","has_own_esa_award","income_support_eligible","is_benefit_cap_exempt_health_disability","is_on_income_related_esa","pawhp_eligible","winter_fuel_payment_eligible"],"esa_income_tariff_income":["benefit_cap_reduction","claimant_or_partner_esa_income","esa_income","income_support_eligible","is_benefit_cap_exempt_health_disability","is_on_income_related_esa"],"expected_lbtt":["gov_tax","household_tax","pre_budget_change_household_tax"],"expected_ltt":["gov_tax","household_tax","pre_budget_change_household_tax"],"expected_sdlt":["gov_tax","household_tax","pre_budget_change_household_tax"],"extended_childcare_entitlement":["gov_spending","household_benefits","is_child_receiving_extended_childcare"],"extended_childcare_entitlement_eligible":["extended_childcare_entitlement","extended_childcare_entitlement_per_child","extended_childcare_entitlement_total_per_child"],"extended_childcare_entitlement_limited_capability_or_specified_benefit":["extended_childcare_entitlement_work_condition"],"extended_childcare_entitlement_meets_income_requirements":["extended_childcare_entitlement_work_condition"],"extended_childcare_entitlement_per_child":["extended_childcare_entitlement","is_child_receiving_extended_childcare"],"extended_childcare_entitlement_qualifying_child":["extended_childcare_entitlement_eligible","extended_childcare_entitlement_total_per_child","is_child_receiving_extended_childcare"],"extended_childcare_entitlement_total_per_child":["extended_childcare_entitlement_per_child","targeted_childcare_entitlement","universal_childcare_entitlement"],"extended_childcare_entitlement_work_condition":["extended_childcare_entitlement_eligible"],"external_child_payments":["hbai_household_net_income"],"food_and_non_alcoholic_beverages_consumption":["carbon_consumption","consumption"],"free_school_fruit_veg":["hbai_household_net_income"],"free_school_meals":["hbai_household_net_income"],"free_school_milk":["hbai_household_net_income"],"free_tv_licence_value":["hbai_household_net_income"],"fuel_duty":["gov_tax","household_tax","pre_budget_change_household_tax"],"full_rate_vat_consumption":["baseline_vat","vat"],"full_rate_vat_expenditure_rate":["full_rate_vat_consumption"],"gender":["is_female","is_male"],"gift_aid":["gift_aid_grossed_up"],"gift_aid_grossed_up":["add_rate_earned_income","add_rate_savings_income","adjusted_net_income","basic_rate_earned_income","basic_rate_savings_income","basic_rate_savings_income_pre_starter","capital_gains_tax","dividend_income_tax","earned_income_tax","higher_rate_earned_income","higher_rate_savings_income","property_income_tax","savings_allowance","tax_band","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"gov_spending":["gov_balance"],"gov_tax":["gov_balance"],"guarantee_credit":["in_receipt_of_guarantee_credit","in_receipt_of_savings_credit_only","pension_credit_entitlement","targeted_childcare_entitlement_eligible"],"hand_rolling_tobacco_grams_per_week":["tobacco_duty"],"has_attained_state_pension_credit_qualifying_age":["council_tax_reduction_pensioner","has_mixed_age_couple_pension_credit_saving","housing_benefit_pension_age_regulations_apply","income_support_eligible","is_mixed_age_couple","is_on_pension_credit","is_uc_eligible","meets_pension_credit_age_conditions","pension_age_severe_disability_premium","pension_credit_assessable_capital","uc_member_of_couple_claims_as_single_person","uc_work_related_group_apart_from_earnings","winter_fuel_payment_eligible"],"has_mixed_age_couple_pension_credit_saving":["meets_pension_credit_age_conditions"],"has_non_dependant_for_severe_disability_premium":["severe_disability_premium"],"has_own_esa_award":["esa_includes_support_component","uc_limited_capability_for_WRA"],"hbai_excluded_income":["baseline_hbai_excluded_income","hbai_excluded_income_change"],"hbai_household_net_income":["deep_poverty_gap","equiv_hbai_household_net_income","hbai_household_net_income_ahc","poverty_gap","poverty_gap_bhc","real_hbai_household_net_income"],"hbai_household_net_income_ahc":["equiv_hbai_household_net_income_ahc","poverty_gap_ahc","real_hbai_household_net_income_ahc"],"health_consumption":["carbon_consumption","consumption"],"healthy_start_vouchers":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits"],"heated_tobacco_grams_per_week":["tobacco_duty"],"high_value_council_tax_surcharge":["gov_tax","household_tax"],"higher_rate_earned_income":["higher_rate_earned_income_tax"],"higher_rate_savings_income":["savings_income_tax","taxed_savings_income"],"hours_worked":["in_work","uc_is_responsible_carer","weekly_hours"],"household_benefits":["household_gross_income","household_net_income","household_net_income_ahc"],"household_benefits_individual_non_dep_deduction":["housing_benefit_non_dep_deductions"],"household_bus_fare_age_weight":["person_bus_fare_spending"],"household_count_people":["household_income_decile","household_wealth_decile","in_relative_poverty_ahc","in_relative_poverty_bhc"],"household_equivalisation_ahc":["equiv_hbai_household_net_income_ahc","poverty_line_ahc"],"household_equivalisation_bhc":["equiv_hbai_household_net_income","equiv_household_net_income","poverty_line","poverty_line_bhc","pre_budget_change_ons_household_income_decile"],"household_furnishings_consumption":["carbon_consumption","consumption"],"household_income_decile":["income_decile"],"household_land_value":["LVT","land_value"],"household_market_income":["household_gross_income","household_net_income","household_net_income_ahc","pre_budget_change_household_net_income"],"household_net_income":["equiv_household_net_income","marginal_tax_rate","marginal_tax_rate_on_capital_gains","marginal_tax_rate_wrt_employer_cost","real_household_net_income","relative_income_change"],"household_net_income_ahc":["real_household_net_income_ahc"],"household_owns_tv":["free_tv_licence_value","tv_licence"],"household_tax":["household_net_income","household_net_income_ahc"],"household_uc_pool_company_holdings":["uc_assessable_capital"],"household_uc_reported_capital":["uc_assessable_capital"],"household_uc_unreported_claimants":["uc_assessable_capital"],"household_weight":["carbon_tax","corporate_land_value","household_income_decile","household_wealth_decile","in_relative_poverty_ahc","in_relative_poverty_bhc","person_weight","pre_budget_change_ons_household_income_decile"],"housing_benefit":["cost_of_living_support_payment","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits"],"housing_benefit_LHA_allowed_bedrooms":["housing_benefit_LHA_category"],"housing_benefit_LHA_category":["housing_benefit_LHA_rate"],"housing_benefit_LHA_rate":["LHA_cap"],"housing_benefit_applicable_amount":["housing_benefit_entitlement"],"housing_benefit_applicable_income":["housing_benefit_entitlement"],"housing_benefit_applicable_income_childcare_element":["housing_benefit_applicable_income","housing_benefit_applicable_income_disregard","housing_benefit_savings_credit_only_income"],"housing_benefit_applicable_income_disregard":["housing_benefit_applicable_income","housing_benefit_savings_credit_only_income"],"housing_benefit_assessable_capital":["housing_benefit_eligible","housing_benefit_tariff_income"],"housing_benefit_board_and_attendance_determination":["LHA_cap"],"housing_benefit_eligible":["housing_benefit_applicable_amount","housing_benefit_pre_benefit_cap"],"housing_benefit_entitlement":["housing_benefit_pre_benefit_cap"],"housing_benefit_has_non_dependant":["housing_benefit_LHA_category"],"housing_benefit_individual_non_dep_deduction_eligible":["household_benefits_individual_non_dep_deduction"],"housing_benefit_meals_deduction":["LHA_cap","housing_benefit_entitlement"],"housing_benefit_net_earnings":["council_tax_reduction_pensioner_earnings_disregard","housing_benefit_applicable_income_disregard"],"housing_benefit_non_dep_deductions":["housing_benefit_entitlement"],"housing_benefit_on_passporting_benefit":["housing_benefit_applicable_income","housing_benefit_applicable_income_disregard","housing_benefit_assessable_capital"],"housing_benefit_pension_age_regulations_apply":["benefits_premiums","housing_benefit_applicable_amount","housing_benefit_applicable_income","housing_benefit_assessable_capital","housing_benefit_eligible","housing_benefit_specified_or_temporary_accommodation_disregard","housing_benefit_tariff_income","is_benefit_cap_exempt_other"],"housing_benefit_pre_benefit_cap":["benefit_cap_reduction","housing_benefit"],"housing_benefit_reported":["claims_all_entitled_benefits","claims_legacy_benefits","has_mixed_age_couple_pension_credit_saving","housing_benefit_eligible","would_claim_housing_benefit"],"housing_benefit_savings_credit_only_income":["housing_benefit_applicable_income"],"housing_benefit_severe_disability_premium_applies":["housing_benefit_LHA_category"],"housing_benefit_shares_accommodation":["housing_benefit_LHA_category"],"housing_benefit_specified_or_temporary_accommodation_disregard":["housing_benefit_applicable_income_disregard"],"housing_benefit_tariff_income":["housing_benefit_applicable_income"],"housing_costs":["household_net_income_ahc"],"housing_water_and_electricity_consumption":["carbon_consumption","consumption"],"iidb":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_benefit_cap_exempt_health_disability","pre_budget_change_household_benefits","uc_unearned_income"],"iidb_reported":["iidb"],"in_FE":["council_tax_benefit"],"in_HE":["council_tax_benefit","council_tax_reduction_claimant_benunit","council_tax_reduction_joint_liability_share","maintenance_loan_has_sponsor","maintenance_loan_in_higher_education","maintenance_loan_sponsor_income"],"in_receipt_of_guarantee_credit":["council_tax_reduction_applicable_income","council_tax_reduction_assessable_capital","housing_benefit_applicable_income","housing_benefit_assessable_capital","housing_benefit_tariff_income"],"in_receipt_of_income_support_jsa_ib_or_esa_ir":["housing_benefit_on_passporting_benefit"],"in_receipt_of_savings_credit_only":["council_tax_reduction_applicable_income","council_tax_reduction_assessable_capital","housing_benefit_applicable_income","housing_benefit_assessable_capital","housing_benefit_tariff_income"],"in_rural_fuel_duty_relief_area":["fuel_duty"],"in_social_housing":["LHA_eligible","housing_benefit_eligible"],"in_specified_or_temporary_accommodation":["housing_benefit_specified_or_temporary_accommodation_disregard"],"in_work":["extended_childcare_entitlement_work_condition","tax_free_childcare_treated_as_in_work","uc_childcare_work_condition"],"incapacity_benefit":["benefit_cap_reduction","carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits","extended_childcare_entitlement_limited_capability_or_specified_benefit","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits","social_security_income","tax_free_childcare_caring_or_incapacity_benefit","tax_free_childcare_regarded_as_in_paid_work"],"incapacity_benefit_reported":["incapacity_benefit"],"income_support":["benefit_cap_reduction","cost_of_living_support_payment","council_tax_reduction_applicable_income","council_tax_reduction_relevant_income_based_benefit","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","in_receipt_of_income_support_jsa_ib_or_esa_ir","is_on_income_support","is_scp_eligible","pre_budget_change_household_benefits","targeted_childcare_entitlement_eligible","tax_credits_applicable_income"],"income_support_applicable_amount":["income_support_entitlement"],"income_support_applicable_income":["income_support_entitlement"],"income_support_assessable_capital":["income_support_eligible","income_support_tariff_income"],"income_support_eligible":["income_support_entitlement"],"income_support_remunerative_work_hours":["income_support_eligible"],"income_support_reported":["bursary_fund_16_to_19_receives_uc_or_is_in_own_right","claims_all_entitled_benefits","claims_legacy_benefits","has_mixed_age_couple_pension_credit_saving","income_support_eligible","is_on_income_support","legacy_benefits_closed","pawhp_eligible","winter_fuel_payment_eligible","would_claim_IS"],"income_support_tariff_income":["income_support_applicable_income"],"income_tax":["council_tax_reduction_applicable_income","gov_tax","hbai_household_net_income","household_tax","housing_benefit_applicable_income","housing_benefit_net_earnings","income_support_applicable_income","pension_credit_income","post_tax_income","pre_budget_change_household_tax","tax"],"income_tax_pre_charges":["capped_mcad","property_finance_cost_relief"],"individual_savings_account_interest_income":["tax_free_savings_income"],"inflation_adjustment":["real_hbai_household_net_income","real_hbai_household_net_income_ahc","real_household_net_income","real_household_net_income_ahc"],"is_CTC_child_limit_exempt":["CTC_child_element","ctc_child_limit_affected"],"is_CTC_eligible":["CTC_disabled_child_element","CTC_family_element","CTC_severely_disabled_child_element","ctc_entitlement"],"is_SP_age":["additional_state_pension","bi_maximum","is_WA_adult","is_hbai_pensioner","is_hbai_working_age_adult","ni_liable","pawhp_eligible","state_pension_type","winter_fuel_payment_eligible"],"is_WTC_eligible":["WTC_basic_element","WTC_childcare_element","WTC_couple_element","WTC_disabled_element","WTC_lone_parent_element","WTC_severely_disabled_element","WTC_worker_element","wtc_entitlement"],"is_adult":["benunit_count_adults","eldest_adult_age","family_type","is_WA_adult","num_adults","num_disabled_adults","num_enhanced_disabled_adults","num_severely_disabled_adults","youngest_adult_age"],"is_apprentice":["bursary_fund_16_to_19_in_qualifying_education","care_to_learn_eligible","is_in_non_advanced_education","minimum_wage","minimum_wage_category"],"is_before_universal_credit_qualifying_young_person_terminal_date":["meets_qualifying_young_person_terminal_date_condition_for_universal_credit"],"is_benefit_cap_exempt":["benefit_cap"],"is_benefit_cap_exempt_earnings":["is_benefit_cap_exempt"],"is_benefit_cap_exempt_health_disability":["is_benefit_cap_exempt"],"is_benefit_cap_exempt_other":["is_benefit_cap_exempt"],"is_benefit_cap_single_claimant_rate":["benefit_cap"],"is_benunit_head":["council_tax_benefit","council_tax_reduction","has_own_esa_award","household_num_benunits","household_uc_reported_capital","is_benefit_cap_exempt_health_disability","is_claimant_or_partner","is_hbai_dependent_child","is_on_income_based_jsa","is_on_income_related_esa","is_on_income_support","is_on_pension_credit","kingston_upon_thames_council_tax_reduction_non_dep_deductions","merton_council_tax_reduction_non_dep_deductions","newham_council_tax_reduction_non_dep_deductions","oxford_council_tax_reduction_non_dep_deductions","pawhp_eligible","personal_rent","westminster_council_tax_reduction_non_dep_deductions","winter_fuel_payment_eligible"],"is_blind":["council_tax_reduction_applicant_has_non_dep_exemption","disabled_students_allowance_has_qualifying_condition","has_non_dependant_for_severe_disability_premium","housing_benefit_severe_disability_premium_applies","is_child_for_working_tax_credit_childcare_element","is_counted_resident_for_severe_disability_addition","num_relevant_children_for_housing_benefit_childcare","severe_disability_minimum_guarantee_addition","severe_disability_premium","tax_free_childcare","tax_free_childcare_child_age_eligible","tv_licence_discount"],"is_cared_for_by_carer_benefit_recipient":["severe_disability_minimum_guarantee_addition","severe_disability_premium"],"is_carer_for_benefits":["esa_income_eligible","income_support_eligible","is_benefit_cap_exempt_health_disability","meets_uc_minimum_age_condition","num_carers","receives_uc_carer_element","uc_unable_to_provide_childcare","uc_work_related_group_apart_from_earnings"],"is_child":["benunit_count_children","child_index","eldest_child_age","family_type","is_benunit_eldest_child","num_children","num_disabled_children","num_enhanced_disabled_children","num_severely_disabled_children","youngest_child_age"],"is_child_for_child_benefit":["income_support_eligible","is_child_or_qualifying_young_person_for_child_benefit","youngest_child_age_for_legacy_benefits"],"is_child_for_child_tax_credit":["is_child_or_qualifying_young_person_for_child_tax_credit"],"is_child_for_pension_credit":["is_child_or_qualifying_young_person_for_pension_credit"],"is_child_for_universal_credit":["is_child_or_qualifying_young_person_for_universal_credit","meets_uc_minimum_age_condition","uc_youngest_child_age"],"is_child_or_qualifying_young_person_for_child_benefit":["care_to_learn_qualifying_child","child_benefit_child_index","child_benefit_respective_amount","has_non_dependant_for_severe_disability_premium","is_child_or_young_person_for_legacy_benefits"],"is_child_or_qualifying_young_person_for_child_tax_credit":["CTC_child_element","CTC_disabled_child_element","ctc_child_limit_affected","is_CTC_eligible","is_responsible_for_child_or_qualifying_young_person_for_child_tax_credit","tax_credits_current_year_income"],"is_child_or_qualifying_young_person_for_pension_credit":["child_minimum_guarantee_addition"],"is_child_or_qualifying_young_person_for_universal_credit":["is_benefit_cap_exempt_health_disability","is_child_or_young_person_for_council_tax_reduction","is_responsible_for_child_or_qualifying_young_person_for_universal_credit","lha_renter_has_non_dependant","uc_child_index","uc_individual_disabled_child_element","uc_individual_severely_disabled_child_element","uc_is_child_born_before_child_limit","uc_is_child_limit_affected"],"is_child_or_young_person_for_council_tax_reduction":["council_tax_reduction_applicable_income","is_responsible_for_child_or_young_person_for_council_tax_reduction"],"is_child_or_young_person_for_legacy_benefits":["council_tax_reduction_applicable_income","housing_benefit_applicable_income","housing_benefit_has_non_dependant","housing_benefit_severe_disability_premium_applies","income_support_applicable_income","is_benefit_cap_exempt_health_disability","is_child_or_young_person_for_council_tax_reduction","is_responsible_for_child_or_young_person_for_legacy_benefits","lha_renter_has_non_dependant","meets_housing_benefit_additional_earnings_disregard_conditions","num_relevant_children_for_housing_benefit_childcare","youngest_child_age_for_legacy_benefits"],"is_claimant_or_partner":["WTC_severely_disabled_element","benefit_cap_reduction","carer_minimum_guarantee_addition","carer_premium","claimant_or_partner_esa_income","claimant_or_partner_jsa_income","council_tax_reduction_applicable_income","council_tax_reduction_head_applies_alone","council_tax_reduction_liable_person","eldest_claimant_or_partner_age","esa_income_assessable_capital","esa_income_eligible","extended_childcare_entitlement_eligible","extended_childcare_entitlement_work_condition","has_mixed_age_couple_pension_credit_saving","has_non_dependant_for_severe_disability_premium","has_own_esa_award","housing_benefit_LHA_allowed_bedrooms","housing_benefit_applicable_income","housing_benefit_assessable_capital","housing_benefit_has_non_dependant","housing_benefit_net_earnings","housing_benefit_pension_age_regulations_apply","housing_benefit_severe_disability_premium_applies","housing_benefit_specified_or_temporary_accommodation_disregard","income_support_applicable_income","income_support_assessable_capital","income_support_eligible","is_WTC_eligible","is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_cared_for_by_carer_benefit_recipient","is_child_for_working_tax_credit_childcare_element","is_child_or_qualifying_young_person_for_child_benefit","is_child_or_qualifying_young_person_for_child_tax_credit","is_child_or_qualifying_young_person_for_pension_credit","is_child_or_young_person_for_council_tax_reduction","is_child_or_young_person_for_legacy_benefits","is_council_tax_reduction_applicant_or_partner","is_dependent_child_for_student_support","is_lha_shared_accommodation_rate_specified_renter","is_liable_for_household_rent","is_mixed_age_couple","is_on_income_based_jsa","is_on_income_related_esa","is_on_income_support","is_on_pawhp_relevant_benefit","is_on_pension_credit","is_on_universal_credit","is_on_winter_fuel_payment_relevant_benefit","is_responsible_for_child_or_qualifying_young_person_for_child_tax_credit","is_responsible_for_child_or_young_person_for_uc_or_housing_benefit","is_savings_credit_eligible","is_uc_claimant","jsa_income_assessable_capital","jsa_income_eligible","lha_renter_has_non_dependant","maintenance_loan_entitled_to_benefits","marital_status","marriage_allowance","meets_housing_benefit_additional_earnings_disregard_conditions","meets_pension_credit_age_conditions","meets_severe_disability_addition_residence_condition","num_disabled_claimants_or_partners_for_legacy_benefits","num_enhanced_disabled_claimants_or_partners_for_legacy_benefits","partners_care_for_same_severely_disabled_person","partners_unused_personal_allowance","pawhp_eligible","pension_age_severe_disability_premium","pension_age_winter_heating_payment","pension_credit_assessable_capital","pension_credit_earnings","pension_credit_income","relation_type","savings_credit_income","severe_disability_minimum_guarantee_addition","severe_disability_premium","tax_credit_award","tax_credits_current_year_income","tax_free_childcare_regarded_as_in_paid_work","tax_free_childcare_work_condition","winter_fuel_payment","winter_fuel_payment_eligible","would_claim_IS","youngest_claimant_or_partner_age"],"is_council_tax_reduction_applicant_or_partner":["council_tax_reduction_applicable_income","council_tax_reduction_applicant_has_non_dep_exemption","council_tax_reduction_pensioner"],"is_council_tax_reduction_lone_parent":["council_tax_reduction_applicable_amount"],"is_counted_resident_for_severe_disability_addition":["meets_severe_disability_addition_residence_condition"],"is_couple":["adult_dependants_grant_adult_eligible","council_tax_reduction_applicable_amount","council_tax_reduction_pensioner_earnings_disregard","disability_premium","enhanced_disability_premium","housing_benefit_applicable_amount","housing_benefit_applicable_income_disregard","housing_benefit_specified_or_temporary_accommodation_disregard","income_support_applicable_income","is_WTC_eligible","is_benefit_cap_single_claimant_rate","is_lha_shared_accommodation_rate_specified_renter","is_married","maintenance_loan_household_income","maintenance_loan_living_arrangement","meets_housing_benefit_additional_earnings_disregard_conditions","severe_disability_minimum_guarantee_addition","severe_disability_premium"],"is_dependent_child_for_student_support":["childcare_grant_child_eligible","parents_learning_allowance_dependent_children"],"is_disabled_for_benefits":["CTC_disabled_child_element","disabled_students_allowance_has_qualifying_condition","is_WTC_eligible","is_child_for_working_tax_credit_childcare_element","meets_housing_benefit_additional_earnings_disregard_conditions","num_disabled_adults","num_disabled_children","num_disabled_claimants_or_partners_for_legacy_benefits","num_relevant_children_for_housing_benefit_childcare","tax_free_childcare","tax_free_childcare_child_age_eligible","uc_individual_disabled_child_element","uc_limited_capability_for_WRA","uc_limited_capability_for_work"],"is_engaged_in_owned_company_trade":["uc_company_capital"],"is_enhanced_disabled_for_benefits":["num_enhanced_disabled_adults","num_enhanced_disabled_children","num_enhanced_disabled_claimants_or_partners_for_legacy_benefits"],"is_entitled_to_carer_benefit":["carer_minimum_guarantee_addition","carer_premium","extended_childcare_entitlement_limited_capability_or_specified_benefit","is_benefit_cap_exempt_health_disability","is_carer_for_benefits","partners_care_for_same_severely_disabled_person","tax_free_childcare_caring_or_incapacity_benefit"],"is_gainfully_employed_for_carers_allowance":["carers_allowance_pre_overlap"],"is_guarantee_credit_eligible":["guarantee_credit","is_pension_credit_eligible"],"is_hbai_adult":["household_equivalisation_ahc","household_equivalisation_bhc","is_claimant_or_partner","is_hbai_pensioner","is_hbai_working_age_adult"],"is_hbai_child_aged_14_or_over":["household_equivalisation_ahc","household_equivalisation_bhc"],"is_hbai_child_under_14":["household_equivalisation_ahc","household_equivalisation_bhc"],"is_hbai_dependent_child":["hbai_person_type","is_hbai_adult","is_hbai_child_aged_14_or_over","is_hbai_child_under_14"],"is_hbai_working_age_adult":["hbai_person_type"],"is_higher_earner":["CB_HITC"],"is_household_head":["is_resolved_household_head"],"is_housing_benefit_young_individual":["housing_benefit_LHA_category"],"is_in_approved_training":["is_hbai_dependent_child","meets_qualifying_young_person_education_or_training_condition_for_child_benefit","meets_qualifying_young_person_education_or_training_condition_for_child_tax_credit","meets_qualifying_young_person_education_or_training_condition_for_pension_credit","meets_qualifying_young_person_education_or_training_condition_for_universal_credit"],"is_in_non_advanced_education":["is_hbai_dependent_child","meets_qualifying_young_person_education_or_training_condition_for_child_benefit","meets_qualifying_young_person_education_or_training_condition_for_child_tax_credit","meets_qualifying_young_person_education_or_training_condition_for_pension_credit","meets_qualifying_young_person_education_or_training_condition_for_universal_credit"],"is_lha_shared_accommodation_rate_specified_renter":["LHA_category"],"is_liable_for_household_rent":["housing_benefit_LHA_allowed_bedrooms","housing_benefit_has_non_dependant","housing_benefit_severe_disability_premium_applies","share_of_household_rent","uc_non_dependants_counted"],"is_lone_parent":["council_tax_reduction_pensioner_earnings_disregard","housing_benefit_applicable_amount","housing_benefit_applicable_income_disregard","housing_benefit_specified_or_temporary_accommodation_disregard","income_support_applicable_income","income_support_eligible","meets_housing_benefit_additional_earnings_disregard_conditions"],"is_looked_after_by_local_authority":["extended_childcare_entitlement_qualifying_child","income_support_eligible","is_benefit_cap_exempt_health_disability","is_child_for_working_tax_credit_childcare_element","is_child_or_qualifying_young_person_for_child_tax_credit","is_child_or_qualifying_young_person_for_pension_credit","is_child_or_qualifying_young_person_for_universal_credit","is_child_or_young_person_for_legacy_benefits","tax_free_childcare_qualifying_child","uc_youngest_child_age"],"is_male":["LHA_allowed_bedrooms","housing_benefit_LHA_allowed_bedrooms","state_pension_age"],"is_married":["marital_status"],"is_mixed_age_couple":["has_mixed_age_couple_pension_credit_saving","legacy_benefits_closed","meets_pension_credit_age_conditions"],"is_non_dependant_of_household_head":["LHA_allowed_bedrooms","housing_benefit_has_non_dependant","housing_benefit_non_dep_deductions","housing_benefit_severe_disability_premium_applies","lha_renter_has_non_dependant","uc_non_dep_deductions"],"is_of_compulsory_school_age":["universal_childcare_entitlement_eligible"],"is_on_income_based_jsa":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","pawhp_eligible","winter_fuel_payment_eligible"],"is_on_income_related_esa":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","maintenance_loan_entitled_to_benefits","pawhp_eligible","winter_fuel_payment_eligible"],"is_on_income_support":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","pawhp_eligible","winter_fuel_payment_eligible"],"is_on_pawhp_relevant_benefit":["pawhp_eligible","pension_age_winter_heating_payment"],"is_on_pension_credit":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","pawhp_eligible","winter_fuel_payment_eligible"],"is_on_universal_credit":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","pawhp_eligible","winter_fuel_payment_eligible"],"is_on_winter_fuel_payment_relevant_benefit":["winter_fuel_payment","winter_fuel_payment_eligible"],"is_parent":["care_to_learn_eligible","care_to_learn_qualifying_child","childcare_grant_eligible","is_claimant_or_partner","is_hbai_dependent_child","maintenance_loan_entitled_to_benefits","maintenance_loan_household_income","maintenance_loan_living_arrangement","parents_learning_allowance_eligible","tax_free_childcare_eligible","tax_free_childcare_qualifying_child"],"is_pension_credit_eligible":["guarantee_credit","in_receipt_of_guarantee_credit","in_receipt_of_savings_credit_only","pension_credit_entitlement"],"is_qualifying_young_person_for_child_benefit":["is_benefit_cap_exempt_health_disability","is_child_or_qualifying_young_person_for_child_benefit"],"is_qualifying_young_person_for_child_tax_credit":["is_child_or_qualifying_young_person_for_child_tax_credit"],"is_qualifying_young_person_for_pension_credit":["is_child_or_qualifying_young_person_for_pension_credit","is_counted_resident_for_severe_disability_addition"],"is_qualifying_young_person_for_universal_credit":["is_benefit_cap_exempt_health_disability","is_child_or_qualifying_young_person_for_universal_credit"],"is_resolved_household_head":["benunit_contains_household_head","council_tax_reduction_head_applies_alone","council_tax_reduction_liable_person","is_council_tax_reduction_applicant_or_partner","maintenance_loan_household_income","maintenance_loan_living_arrangement","personal_rent"],"is_responsible_for_child_or_qualifying_young_person_for_child_tax_credit":["is_WTC_eligible"],"is_responsible_for_child_or_qualifying_young_person_for_universal_credit":["is_responsible_for_child_or_young_person_for_uc_or_housing_benefit","is_uc_work_allowance_eligible"],"is_responsible_for_child_or_young_person_for_council_tax_reduction":["is_council_tax_reduction_lone_parent"],"is_responsible_for_child_or_young_person_for_legacy_benefits":["income_support_applicable_amount","is_lone_parent","is_responsible_for_child_or_young_person_for_uc_or_housing_benefit","is_single_person"],"is_responsible_for_child_or_young_person_for_uc_or_housing_benefit":["is_benefit_cap_single_claimant_rate","is_lha_shared_accommodation_rate_specified_renter"],"is_savings_credit_eligible":["is_pension_credit_eligible","savings_credit"],"is_scp_eligible":["scottish_child_payment"],"is_scp_eligible_child":["is_scp_eligible"],"is_severely_disabled_for_benefits":["WTC_severely_disabled_element","num_severely_disabled_adults","num_severely_disabled_children","uc_individual_severely_disabled_child_element"],"is_severely_disabled_for_carers_allowance":["is_cared_for_by_carer_benefit_recipient"],"is_shared_accommodation":["housing_benefit_shares_accommodation"],"is_single":["disability_premium","enhanced_disability_premium","income_support_applicable_amount","income_support_eligible","is_WTC_eligible","is_council_tax_reduction_lone_parent","is_lone_parent","is_single_person"],"is_single_person":["housing_benefit_applicable_amount","income_support_applicable_income","is_housing_benefit_young_individual"],"is_uc_assessed_claimant":["benefit_cap_reduction","is_benefit_cap_exempt_earnings","is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_uc_single_or_joint_claimant","uc_childcare_work_condition","uc_earned_income","uc_individual_earned_income","uc_is_ineligible_partner","uc_is_responsible_carer","uc_member_of_couple_claims_as_single_person","uc_minimum_income_floor_gross","uc_unearned_income","uc_work_related_group_apart_from_earnings","uc_youngest_child_age"],"is_uc_claimant":["household_uc_pool_company_holdings","household_uc_unreported_claimants","is_responsible_for_child_or_qualifying_young_person_for_universal_credit","is_uc_assessed_claimant","meets_uc_minimum_age_condition","uc_assessable_capital","uc_child_index","uc_individual_disabled_child_element","uc_individual_severely_disabled_child_element","uc_is_child_limit_affected"],"is_uc_eligible":["uc_income_reduction","uc_maximum_amount","uc_non_dependants_counted","uc_tariff_income"],"is_uc_entitled":["council_tax_reduction_pensioner","housing_benefit_on_passporting_benefit","housing_benefit_pension_age_regulations_apply","left_pension_route_at_legacy_closure"],"is_uc_single_or_joint_claimant":["is_uc_eligible","uc_childcare_work_condition","uc_member_of_couple_claims_as_single_person","uc_standard_allowance_claimant_type"],"is_uc_work_allowance_eligible":["uc_work_allowance"],"jsa_contrib":["benefit_cap_reduction","carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits","council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","income_support_eligible","jsa","pension_credit_income","pre_budget_change_household_benefits","savings_credit_income","uc_unearned_income"],"jsa_contrib_reported":["jsa_contrib","social_security_income"],"jsa_income":["benefit_cap_reduction","claimant_or_partner_jsa_income","cost_of_living_support_payment","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","income_support_eligible","is_on_income_based_jsa","jsa","pre_budget_change_household_benefits"],"jsa_income_assessable_capital":["is_on_income_based_jsa","jsa_income_eligible","jsa_income_tariff_income"],"jsa_income_eligible":["is_on_income_based_jsa"],"jsa_income_reported":["benefit_cap_reduction","claimant_or_partner_jsa_income","claims_all_entitled_benefits","claims_legacy_benefits","has_mixed_age_couple_pension_credit_saving","income_support_eligible","is_on_income_based_jsa","jsa_income_assessable_capital","jsa_income_eligible","legacy_benefits_closed","pawhp_eligible","winter_fuel_payment_eligible"],"jsa_income_tariff_income":["is_on_income_based_jsa"],"jsa_remunerative_work_hours":["is_on_income_based_jsa","jsa_income_eligible"],"kingston_upon_thames_council_tax_reduction":["simulated_council_tax_reduction_benunit"],"kingston_upon_thames_council_tax_reduction_individual_non_dep_deduction":["kingston_upon_thames_council_tax_reduction_non_dep_deductions"],"kingston_upon_thames_council_tax_reduction_non_dep_deductions":["kingston_upon_thames_council_tax_reduction"],"land_and_buildings_transaction_tax":["expected_lbtt"],"land_transaction_tax":["expected_ltt"],"land_value":["LVT"],"lbtt_liable":["land_and_buildings_transaction_tax"],"lbtt_on_non_residential_property_rent":["lbtt_on_rent"],"lbtt_on_non_residential_property_transactions":["lbtt_on_transactions"],"lbtt_on_rent":["land_and_buildings_transaction_tax"],"lbtt_on_residential_property_rent":["lbtt_on_rent"],"lbtt_on_residential_property_transactions":["lbtt_on_transactions"],"lbtt_on_transactions":["land_and_buildings_transaction_tax"],"left_pension_route_at_legacy_closure":["housing_benefit_eligible","is_pension_credit_eligible"],"legacy_benefits_closed":["claims_uc_at_legacy_closure","esa_income_eligible","housing_benefit_eligible","is_CTC_eligible","is_WTC_eligible","is_on_income_based_jsa","is_on_income_related_esa","jsa_income_eligible","left_pension_route_at_legacy_closure"],"lha_renter_has_non_dependant":["is_lha_shared_accommodation_rate_specified_renter"],"liable_for_share_of_household_rent":["council_tax_reduction_claims_are_joint","council_tax_reduction_individual_non_dep_deduction_eligible","council_tax_reduction_liable_person","housing_benefit_shares_accommodation","is_liable_for_household_rent"],"lifetime_isa_balance":["has_lifetime_isa","household_lifetime_isa_balance","lifetime_isa_countable_capital"],"lifetime_isa_countable_capital":["esa_income_assessable_capital","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","pension_credit_assessable_capital","uc_assessable_capital"],"local_authority":["council_tax_reduction_scheme_supported","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","westminster_council_tax_reduction"],"loss_relief":["taxable_self_employment_income"],"lpg_kg":["fuel_duty"],"ltt_liable":["land_transaction_tax","ltt_on_non_residential_property_transactions"],"ltt_on_non_residential_property_rent":["ltt_on_rent"],"ltt_on_non_residential_property_transactions":["ltt_on_transactions"],"ltt_on_rent":["land_transaction_tax"],"ltt_on_residential_property_rent":["ltt_on_rent"],"ltt_on_residential_property_transactions":["ltt_on_transactions"],"ltt_on_transactions":["land_transaction_tax"],"main_residence_value":["main_residential_property_purchased","non_primary_residence_wealth_tax","residential_property_value"],"main_residential_property_purchased":["lbtt_on_residential_property_transactions","ltt_on_residential_property_transactions","sdlt_on_residential_property_transactions"],"main_residential_property_purchased_is_first_home":["lbtt_on_residential_property_transactions","sdlt_on_residential_property_transactions"],"maintenance_expenses":["hbai_household_net_income","market_income"],"maintenance_income":["hbai_household_net_income","household_market_income","market_income"],"maintenance_loan_candidate_benunit_income":["maintenance_loan_sponsor_income"],"maintenance_loan_eligible":["childcare_grant_student_finance_eligible","maintenance_loan","travel_grant_income_assessed_support"],"maintenance_loan_entitled_to_benefits":["maintenance_loan"],"maintenance_loan_has_sponsor":["maintenance_loan_household_income","maintenance_loan_living_arrangement"],"maintenance_loan_household_income":["adult_dependants_grant_household_income","childcare_grant_household_income","maintenance_loan","parents_learning_allowance_household_income","travel_grant_household_income"],"maintenance_loan_in_england_system":["disabled_students_allowance_eligible","maintenance_loan_eligible"],"maintenance_loan_in_higher_education":["adult_dependants_grant_adult_receives_student_finance","adult_dependants_grant_course_eligible","disabled_students_allowance_course_eligible","maintenance_loan_eligible","maintenance_loan_has_sponsor","maintenance_loan_household_income","maintenance_loan_living_arrangement","maintenance_loan_sponsor_income","parents_learning_allowance_course_eligible"],"maintenance_loan_living_arrangement":["maintenance_loan"],"maintenance_loan_sponsor_income":["maintenance_loan_household_income"],"marginal_tax_rate":["cliff_gap","relative_wage_change"],"marginal_tax_rate_on_capital_gains":["relative_capital_gains_mtr_change","relative_capital_gains_retention_rate_change"],"marital_status":["marriage_allowance"],"marriage_allowance":["earned_taxable_income"],"married_couples_allowance":["married_couples_allowance_deduction"],"married_couples_allowance_deduction":["capped_mcad","esa_exempt_work_earnings"],"maternity_allowance":["carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits","council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","pre_budget_change_household_benefits","tax_free_childcare_treated_as_in_work","uc_childcare_treated_as_in_paid_work","uc_unearned_income"],"maternity_allowance_reported":["maternity_allowance"],"max_free_entitlement_hours_used":["extended_childcare_entitlement_total_per_child","targeted_childcare_entitlement","universal_childcare_entitlement"],"maximum_extended_childcare_hours_usage":["extended_childcare_entitlement_total_per_child"],"meals_included_in_rent":["housing_benefit_meals_deduction"],"meets_housing_benefit_additional_earnings_disregard_conditions":["council_tax_reduction_pensioner_earnings_disregard","housing_benefit_applicable_income_disregard"],"meets_marriage_allowance_income_conditions":["marriage_allowance"],"meets_pension_credit_age_conditions":["housing_benefit_eligible","income_support_eligible","is_pension_credit_eligible","is_uc_eligible","left_pension_route_at_legacy_closure","legacy_benefits_closed"],"meets_qualifying_young_person_age_condition_for_child_benefit":["is_qualifying_young_person_for_child_benefit"],"meets_qualifying_young_person_age_condition_for_child_tax_credit":["is_qualifying_young_person_for_child_tax_credit"],"meets_qualifying_young_person_age_condition_for_pension_credit":["is_qualifying_young_person_for_pension_credit"],"meets_qualifying_young_person_age_condition_for_universal_credit":["is_qualifying_young_person_for_universal_credit"],"meets_qualifying_young_person_education_or_training_condition_for_child_benefit":["is_qualifying_young_person_for_child_benefit"],"meets_qualifying_young_person_education_or_training_condition_for_child_tax_credit":["is_qualifying_young_person_for_child_tax_credit"],"meets_qualifying_young_person_education_or_training_condition_for_pension_credit":["is_qualifying_young_person_for_pension_credit"],"meets_qualifying_young_person_education_or_training_condition_for_universal_credit":["is_qualifying_young_person_for_universal_credit"],"meets_qualifying_young_person_entry_condition_for_child_benefit":["is_qualifying_young_person_for_child_benefit"],"meets_qualifying_young_person_entry_condition_for_child_tax_credit":["is_qualifying_young_person_for_child_tax_credit"],"meets_qualifying_young_person_entry_condition_for_pension_credit":["is_qualifying_young_person_for_pension_credit"],"meets_qualifying_young_person_entry_condition_for_universal_credit":["is_qualifying_young_person_for_universal_credit"],"meets_qualifying_young_person_terminal_date_condition_for_universal_credit":["is_qualifying_young_person_for_universal_credit"],"meets_savings_credit_age_requirement":["is_savings_credit_eligible"],"meets_severe_disability_addition_residence_condition":["severe_disability_minimum_guarantee_addition"],"meets_uc_minimum_age_condition":["is_uc_eligible","uc_is_ineligible_partner","uc_member_of_couple_claims_as_single_person"],"meets_universal_credit_criteria_for_targeted_childcare_entitlement":["targeted_childcare_entitlement_eligible"],"merton_council_tax_reduction":["simulated_council_tax_reduction_benunit"],"merton_council_tax_reduction_individual_non_dep_deduction":["merton_council_tax_reduction_non_dep_deductions"],"merton_council_tax_reduction_non_dep_deductions":["merton_council_tax_reduction"],"minimum_guarantee":["guarantee_credit","is_guarantee_credit_eligible","savings_credit"],"minimum_wage":["extended_childcare_entitlement_meets_income_requirements","tax_free_childcare_meets_income_requirements"],"miscellaneous_consumption":["carbon_consumption","consumption"],"miscellaneous_income":["hbai_household_net_income","household_market_income","market_income","tax_credits_current_year_income","taxable_miscellaneous_income"],"monthly_domestic_energy_consumption":["monthly_epg_consumption_level","monthly_epg_subsidy"],"monthly_ebr_energy_bills_credit":["ebr_energy_bills_credit"],"monthly_epg_consumption_level":["monthly_epg_subsidy"],"monthly_epg_subsidy":["epg_subsidy"],"months_since_last_birthday":["birth_year","child_minimum_guarantee_addition","date_of_birth","has_attained_state_pension_credit_qualifying_age","has_mixed_age_couple_pension_credit_saving","is_CTC_child_limit_exempt","meets_savings_credit_age_requirement","months_since_state_pension_age","state_pension_age","state_pension_credit_qualifying_age","uc_child_index","uc_is_child_born_before_child_limit"],"months_since_state_pension_age":["is_SP_age","meets_savings_credit_age_requirement","ni_class_4_liable","state_pension_type"],"mortgage":["housing_costs"],"mortgage_capital_repayment":["mortgage"],"mortgage_debt":["net_wealth"],"mortgage_interest_repayment":["hbai_household_net_income_ahc","mortgage"],"national_insurance":["council_tax_reduction_applicable_income","gov_tax","hbai_household_net_income","household_tax","housing_benefit_applicable_income","income_support_applicable_income","pension_credit_income","post_tax_income","pre_budget_change_household_tax","tax","total_national_insurance"],"natural_gas_kg":["fuel_duty"],"net_income":["adjusted_net_income","capital_gains_tax","earned_taxable_income","maintenance_loan_candidate_benunit_income","maintenance_loan_household_income","non_savings_income_after_allowances","pension_annual_allowance","plan_2_interest_rate","property_finance_costs_relieved","received_allowances_earned_income","savings_allowance","student_loan_repayment","tax_band","uc_income_tax_on_earnings","uc_income_tax_on_trade","unused_personal_allowance"],"new_state_pension":["overlapping_state_pension","state_pension"],"newham_council_tax_reduction":["simulated_council_tax_reduction_benunit"],"newham_council_tax_reduction_individual_non_dep_deduction":["newham_council_tax_reduction_non_dep_deductions"],"newham_council_tax_reduction_non_dep_deductions":["newham_council_tax_reduction"],"nhs_a_and_e_spending":["nhs_spending"],"nhs_admitted_patient_spending":["nhs_spending"],"nhs_outpatient_spending":["nhs_spending"],"nhs_spending":["gov_spending","household_benefits"],"ni_class_1_employee":["carers_allowance_earnings","housing_benefit_net_earnings","national_insurance","ni_class_4","ni_employee","uc_national_insurance_on_earnings"],"ni_class_1_employee_additional":["ni_class_1_employee"],"ni_class_1_employee_primary":["ni_class_1_employee","ni_class_4_maximum"],"ni_class_1_employer":["ni_employer","total_national_insurance"],"ni_class_1_income":["ni_class_1_employee_additional","ni_class_1_employee_primary","ni_class_1_employer"],"ni_class_1_secondary_liable":["ni_class_1_employer"],"ni_class_2":["housing_benefit_net_earnings","national_insurance","ni_class_4","ni_class_4_maximum","ni_self_employed","uc_national_insurance_on_earnings","uc_national_insurance_on_trade"],"ni_class_3":["national_insurance"],"ni_class_4":["housing_benefit_net_earnings","national_insurance","ni_self_employed","uc_national_insurance_on_earnings","uc_national_insurance_on_trade"],"ni_class_4_liable":["esa_exempt_work_earnings","ni_class_4","ni_class_4_main","uc_minimum_income_floor_national_insurance"],"ni_class_4_loss_relief":["ni_class_4_losses_carried_forward","ni_class_4_profits"],"ni_class_4_losses_brought_forward":["ni_class_4_loss_relief","ni_class_4_losses_carried_forward"],"ni_class_4_maximum":["ni_class_4"],"ni_class_4_profits":["ni_class_2","ni_class_4","ni_class_4_main","ni_class_4_maximum"],"ni_class_4_profits_before_losses":["ni_class_4_loss_relief","ni_class_4_profits"],"ni_class_4_trading_loss":["ni_class_4_loss_relief","ni_class_4_losses_carried_forward"],"ni_employer":["employer_cost","gov_tax"],"ni_liable":["esa_exempt_work_earnings","ni_class_1_employee","ni_class_2","uc_minimum_income_floor_national_insurance"],"non_primary_residence_wealth_tax":["gov_tax","household_tax","pre_budget_change_household_tax"],"non_residential_property_purchased":["lbtt_on_non_residential_property_transactions","ltt_on_non_residential_property_transactions","sdlt_on_non_residential_property_transactions"],"non_residential_property_value":["esa_income_assessable_capital","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","pension_credit_assessable_capital","property_wealth","uc_assessable_capital"],"non_residential_rent":["lbtt_on_non_residential_property_rent","ltt_on_non_residential_property_rent","sdlt_on_non_residential_property_rent"],"non_savings_income_after_allowances":["property_income_tax","taxable_property_income_after_allowances"],"non_savings_taxable_income":["add_rate_savings_income","basic_rate_savings_income","basic_rate_savings_income_pre_starter","dividend_income_tax","higher_rate_savings_income","savings_starter_rate_income","taxed_income"],"num_carers":["benunit_has_carer"],"num_disabled_claimants_or_partners_for_legacy_benefits":["disability_premium"],"num_enhanced_disabled_claimants_or_partners_for_legacy_benefits":["enhanced_disability_premium"],"num_relevant_children_for_housing_benefit_childcare":["housing_benefit_applicable_income_childcare_element"],"num_vehicles":["owns_vehicle"],"other_deductions":["allowances","property_finance_costs_relieved"],"other_fermented_abv":["other_fermented_duty"],"other_fermented_draught_share":["other_fermented_duty"],"other_fermented_duty":["alcohol_duty"],"other_fermented_litres":["other_fermented_duty"],"other_investment_income":["taxable_savings_interest_income"],"other_local_bus_trips":["local_bus_trips"],"other_public_spending_budget_change":["gov_spending"],"other_residential_property_value":["additional_residential_property_purchased","esa_income_assessable_capital","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","pension_credit_assessable_capital","residential_property_value","uc_assessable_capital"],"other_tax_credits":["income_tax","property_finance_cost_relief","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"other_tobacco_grams_per_week":["tobacco_duty"],"over_16":["ni_class_1_secondary_liable","ni_class_4_liable","ni_liable","tax_free_childcare_work_condition"],"overlapping_state_pension":["carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits"],"owned_company_capital":["uc_company_capital"],"owned_company_carries_on_property_business":["uc_company_owner_treatment_applies"],"owned_company_carries_on_trade":["uc_company_capital","uc_company_gainful_self_employment","uc_company_owner_treatment_applies","uc_company_self_employed_earnings"],"owned_company_holding_value":["uc_company_holding_disregard"],"owned_company_income_share":["uc_company_self_employed_earnings"],"owned_company_intermediary_earnings_chapter":["uc_company_intermediary_exclusion_applies"],"owned_company_intermediary_earnings_from_main_employment":["uc_company_intermediary_exclusion_applies"],"owned_company_is_main_employment":["uc_company_capital","uc_company_gainful_self_employment"],"owned_company_trade_assets":["uc_company_capital"],"owned_land":["esa_income_assessable_capital","household_land_value","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","pension_credit_assessable_capital"],"oxford_council_tax_reduction":["simulated_council_tax_reduction_benunit"],"oxford_council_tax_reduction_individual_non_dep_deduction":["oxford_council_tax_reduction_non_dep_deductions"],"oxford_council_tax_reduction_non_dep_deductions":["oxford_council_tax_reduction"],"parents_learning_allowance":["gov_spending","hbai_benefits","household_benefits"],"parents_learning_allowance_course_eligible":["parents_learning_allowance_eligible"],"parents_learning_allowance_dependent_children":["parents_learning_allowance_eligible"],"parents_learning_allowance_eligible":["parents_learning_allowance"],"parents_learning_allowance_household_income":["parents_learning_allowance_eligible"],"partners_care_for_same_severely_disabled_person":["carer_premium"],"partners_unused_personal_allowance":["marriage_allowance"],"pawhp":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits"],"pawhp_eligible":["pension_age_winter_heating_payment"],"pays_rent_to_householder":["benunit_tenure_type","council_tax_reduction_individual_non_dep_deduction_eligible","housing_benefit_shares_accommodation"],"pays_scottish_income_tax":["carers_allowance_earnings","earned_income_tax","esa_exempt_work_earnings","property_income_tax","tax_band","uc_income_tax_on_earnings","uc_income_tax_on_trade","uc_minimum_income_floor_income_tax"],"pension_age_benefits_premiums":["benefits_premiums","council_tax_reduction_applicable_amount"],"pension_age_severe_disability_premium":["pension_age_benefits_premiums"],"pension_age_winter_heating_payment":["pawhp"],"pension_annual_allowance":["pension_contributions_relief","personal_pension_contributions_tax"],"pension_contributions":["carers_allowance_earnings","council_tax_reduction_applicable_income","household_net_income","housing_benefit_applicable_income","housing_benefit_net_earnings","income_support_applicable_income","pension_contributions_relief","pension_credit_income","uc_individual_earned_income_before_mif"],"pension_contributions_for_annual_allowance":["personal_pension_contributions_tax"],"pension_contributions_relief":["allowances","capital_gains_tax"],"pension_contributions_via_salary_sacrifice":["pension_contributions_via_salary_sacrifice_from_pay"],"pension_contributions_via_salary_sacrifice_adjusted":["pension_contributions_for_annual_allowance"],"pension_contributions_via_salary_sacrifice_from_pay":["pension_contributions_via_salary_sacrifice_adjusted","salary_sacrifice_returned_to_income"],"pension_credit":["cost_of_living_support_payment","council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_savings_credit_only_income","in_receipt_of_savings_credit_only","is_on_pension_credit","is_scp_eligible","pre_budget_change_household_benefits","tax_credits_applicable_income","tv_licence_discount","uc_non_dep_receives_pension_credit"],"pension_credit_assessable_capital":["council_tax_reduction_assessable_capital","housing_benefit_assessable_capital","pension_credit_deemed_income"],"pension_credit_deemed_income":["pension_credit_income"],"pension_credit_earnings":["pension_credit_income"],"pension_credit_entitlement":["pension_credit"],"pension_credit_income":["council_tax_reduction_applicable_income","guarantee_credit","housing_benefit_savings_credit_only_income","is_guarantee_credit_eligible","savings_credit","savings_credit_income"],"pension_credit_reported":["has_mixed_age_couple_pension_credit_saving","is_on_pension_credit","legacy_benefits_closed","pawhp_eligible","winter_fuel_payment_eligible"],"pension_credit_reported_capital":["pension_credit_assessable_capital"],"person_bus_fare_spending":["bus_fare_spending"],"person_id":["is_benunit_eldest_child"],"person_weight":["benunit_weight"],"personal_allowance":["allowances","esa_exempt_work_earnings","property_finance_costs_relieved","unused_personal_allowance"],"personal_pension_contributions":["capital_gains_tax","esa_exempt_work_earnings","hbai_household_net_income","pension_contributions","pension_contributions_for_annual_allowance"],"personal_pension_contributions_tax":["income_tax"],"personal_rent":["benunit_rent","family_rent"],"petrol_litres":["fuel_duty"],"petrol_price":["petrol_litres"],"petrol_spending":["petrol_litres"],"pip":["bursary_fund_16_to_19_vulnerable_group_eligible","child_minimum_guarantee_addition","cost_of_living_support_payment","disability_basic_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_benefit_cap_exempt_health_disability","pre_budget_change_household_benefits"],"pip_dl":["council_tax_reduction_applicant_has_non_dep_exemption","housing_benefit_severe_disability_premium_applies","is_lha_shared_accommodation_rate_specified_renter","is_severely_disabled_for_benefits","is_severely_disabled_for_carers_allowance","maintenance_loan_entitled_to_benefits","pip","receives_severe_disability_addition_qualifying_benefit","receives_severe_disability_premium_qualifying_benefit","uc_non_dep_deduction_exempt"],"pip_dl_category":["pip_dl","receives_enhanced_pip_dl"],"pip_m":["maintenance_loan_entitled_to_benefits","pip"],"pip_m_category":["pip_m"],"plan_1_interest_rate":["plan_4_interest_rate","student_loan_interest_rate"],"plan_2_interest_rate":["student_loan_interest_rate"],"plan_4_interest_rate":["student_loan_interest_rate"],"plan_5_interest_rate":["student_loan_interest_rate"],"postgraduate_interest_rate":["student_loan_interest_rate"],"poverty_gap":["in_poverty"],"poverty_line":["deep_poverty_line","poverty_gap"],"poverty_line_ahc":["poverty_gap_ahc"],"poverty_line_bhc":["poverty_gap_bhc"],"poverty_threshold_ahc":["in_poverty_ahc"],"poverty_threshold_bhc":["in_poverty_bhc"],"pre_budget_change_household_benefits":["pre_budget_change_household_net_income"],"pre_budget_change_household_net_income":["pre_budget_change_ons_household_income_decile"],"pre_budget_change_household_tax":["pre_budget_change_household_net_income"],"private_pension_income":["council_tax_reduction_applicable_income","earned_income","hbai_household_net_income","household_market_income","housing_benefit_applicable_income","housing_benefit_net_earnings","income_support_applicable_income","market_income","pension_credit_income","tax_credits_current_year_income","taxable_pension_income","total_income","total_pension_income","uc_unearned_income"],"private_pension_wealth":["corporate_sector_wealth","total_wealth"],"private_school_vat":["gov_tax","household_tax"],"private_transfer_income":["hbai_household_net_income","household_market_income","market_income"],"property_allowable_expenses":["property_allowance_deduction_if_used"],"property_allowance_deduction":["taxable_property_income"],"property_allowance_deduction_if_used":["property_allowance_deduction","uses_property_allowance"],"property_finance_cost_relief":["income_tax","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"property_finance_costs":["deductible_property_finance_costs","property_finance_costs_relievable","property_income_after_finance_costs"],"property_finance_costs_brought_forward":["property_finance_costs_carried_forward","property_finance_costs_relievable"],"property_finance_costs_relievable":["property_finance_costs_carried_forward","property_finance_costs_relieved","uses_property_allowance"],"property_finance_costs_relieved":["property_finance_cost_relief","property_finance_costs_carried_forward"],"property_income":["hbai_household_net_income","household_market_income","housing_benefit_net_earnings","market_income","property_allowable_expenses","property_allowance_deduction_if_used","property_income_after_finance_costs","property_receipts_within_allowance","taxable_property_income","total_income","uses_property_allowance"],"property_income_after_finance_costs":["council_tax_reduction_applicable_income","housing_benefit_applicable_income","income_support_applicable_income","pension_credit_income","tax_credits_current_year_income"],"property_income_tax":["income_tax","income_tax_pre_charges","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"property_purchased":["additional_residential_property_purchased","main_residential_property_purchased","non_residential_property_purchased"],"property_receipts_within_allowance":["property_allowance_deduction_if_used"],"property_rental_income":["property_allowable_expenses","property_allowance_deduction_if_used","property_receipts_within_allowance"],"property_wealth":["household_land_value","total_wealth"],"rail_subsidy_spending":["dft_subsidy_spending"],"rail_usage":["rail_subsidy_spending"],"received_allowances_dividend_income":["dividend_income_tax","received_allowances","taxed_dividend_income"],"received_allowances_earned_income":["earned_taxable_income","received_allowances","received_allowances_dividend_income","received_allowances_savings_income"],"received_allowances_savings_income":["add_rate_savings_income","basic_rate_savings_income","basic_rate_savings_income_pre_starter","dividend_income_tax","higher_rate_savings_income","received_allowances","received_allowances_dividend_income","savings_starter_rate_income"],"receives_benefits_in_own_right":["is_benefit_cap_exempt_health_disability","is_qualifying_young_person_for_child_benefit","is_qualifying_young_person_for_child_tax_credit","is_qualifying_young_person_for_pension_credit","is_qualifying_young_person_for_universal_credit"],"receives_carer_benefit":["is_cared_for_by_carer_benefit_recipient","is_carer_for_benefits","is_entitled_to_carer_benefit","tax_free_childcare_regarded_as_in_paid_work","uc_non_dep_deduction_exempt"],"receives_carer_support_payment":["receives_carer_benefit"],"receives_carers_allowance":["receives_carer_benefit"],"receives_enhanced_pip_dl":["child_minimum_guarantee_addition"],"receives_highest_dla_sc":["child_minimum_guarantee_addition"],"receives_limited_capability_for_work_credits":["extended_childcare_entitlement_limited_capability_or_specified_benefit","tax_free_childcare_caring_or_incapacity_benefit","tax_free_childcare_regarded_as_in_paid_work"],"receives_severe_disability_addition_qualifying_benefit":["is_counted_resident_for_severe_disability_addition","severe_disability_minimum_guarantee_addition"],"receives_severe_disability_premium_qualifying_benefit":["has_non_dependant_for_severe_disability_premium","severe_disability_premium"],"receives_uc_carer_element":["extended_childcare_entitlement_limited_capability_or_specified_benefit"],"recreation_consumption":["carbon_consumption","consumption"],"reduced_rate_vat_consumption":["baseline_vat","vat"],"region":["benefit_cap","benunit_region","country","household_land_value","maintenance_loan_living_arrangement","uc_has_deduction","uc_latent_deduction_rate"],"relation_type":["is_couple","is_savings_credit_eligible","is_single","savings_credit","standard_minimum_guarantee"],"relative_income_change":["income_elasticity_lsr"],"relative_wage_change":["substitution_elasticity_lsr"],"rent":["hbai_household_net_income_ahc","housing_costs","lbtt_on_residential_property_rent","ltt_on_residential_property_rent","personal_rent","sdlt_on_residential_property_rent"],"rent_paid_as_boarder":["pays_rent_to_householder","personal_rent"],"rent_paid_as_lodger":["pays_rent_to_householder","personal_rent"],"residential_property_value":["property_wealth"],"restaurants_and_hotels_consumption":["carbon_consumption","consumption"],"salary_sacrifice_broad_base_haircut":["employment_income"],"salary_sacrifice_returned_to_income":["employee_pension_contributions","employment_income","salary_sacrifice_pension_ni_employee","salary_sacrifice_pension_ni_employer"],"savings":["council_tax_reduction_assessable_capital","esa_income_assessable_capital","housing_benefit_assessable_capital","income_support_assessable_capital","jsa_income_assessable_capital","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","pension_credit_assessable_capital","total_wealth","uc_assessable_capital","westminster_council_tax_reduction"],"savings_allowance":["add_rate_savings_income","basic_rate_savings_income","higher_rate_savings_income"],"savings_credit":["in_receipt_of_savings_credit_only","pension_credit_entitlement"],"savings_credit_income":["is_savings_credit_eligible","savings_credit"],"savings_income_tax":["income_tax","income_tax_pre_charges"],"savings_interest_income":["capital_income","hbai_household_net_income","household_market_income","housing_benefit_net_earnings","market_income","tax_credits_current_year_income","taxable_savings_interest_income","total_income"],"savings_starter_rate_income":["add_rate_savings_income","basic_rate_savings_income","higher_rate_savings_income"],"scottish_carer_supplement":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits","social_security_income"],"scottish_child_payment":["gov_spending","hbai_household_net_income","household_benefits"],"scottish_funded_early_learning_childcare_start_date_reached":["scottish_funded_early_learning_childcare_eligible"],"sda":["benefit_cap_reduction","carer_support_payment_overlapping_benefits","carers_allowance_overlapping_benefits","extended_childcare_entitlement_limited_capability_or_specified_benefit","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","pre_budget_change_household_benefits","tax_free_childcare_caring_or_incapacity_benefit","tax_free_childcare_regarded_as_in_paid_work"],"sda_reported":["sda"],"sdlt_liable":["stamp_duty_land_tax"],"sdlt_on_non_residential_property_rent":["sdlt_on_rent"],"sdlt_on_non_residential_property_transactions":["sdlt_on_transactions"],"sdlt_on_rent":["stamp_duty_land_tax"],"sdlt_on_residential_property_rent":["sdlt_on_rent"],"sdlt_on_residential_property_transactions":["sdlt_on_transactions"],"sdlt_on_transactions":["stamp_duty_land_tax"],"self_employment_gross_receipts":["trading_allowance_deduction"],"self_employment_income":["carers_allowance_earnings","council_tax_reduction_applicable_income","earned_income","esa_exempt_work_earnings","extended_childcare_entitlement_meets_income_requirements","hbai_household_net_income","household_market_income","housing_benefit_applicable_income","housing_benefit_net_earnings","housing_benefit_specified_or_temporary_accommodation_disregard","in_work","income_support_applicable_income","market_income","ni_class_4_profits_before_losses","pension_contributions_relief","pension_credit_earnings","tax_credits_current_year_income","tax_free_childcare_expected_declaration_period_income","taxable_self_employment_income","total_income","trading_allowance_deduction","uc_individual_earned_income_before_mif","uc_is_in_gainful_self_employment"],"severe_disability_minimum_guarantee_addition":["additional_minimum_guarantee"],"severe_disability_premium":["maintenance_loan_entitled_to_benefits","pension_age_severe_disability_premium","working_age_benefits_premiums"],"share_of_household_rent":["housing_benefit_non_dep_deductions","personal_rent"],"shareholding":["baseline_business_rates","business_rates","carbon_tax","corporate_incident_tax_revenue_change","corporate_sdlt"],"simulated_council_tax_reduction_benunit":["council_tax_benefit"],"social_security_income":["housing_benefit_net_earnings","income_support_applicable_income","tax_credits_current_year_income","taxable_social_security_income","total_income"],"sparkling_cider_abv":["sparkling_cider_duty"],"sparkling_cider_draught_share":["sparkling_cider_duty"],"sparkling_cider_duty":["alcohol_duty"],"sparkling_cider_litres":["sparkling_cider_duty"],"spirits_abv":["spirits_duty"],"spirits_draught_share":["spirits_duty"],"spirits_duty":["alcohol_duty"],"spirits_litres":["spirits_duty"],"ssmg":["council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","pre_budget_change_household_benefits"],"ssmg_reported":["ssmg"],"stamp_duty_land_tax":["expected_sdlt"],"standard_minimum_guarantee":["minimum_guarantee","savings_credit"],"stands_as_sole_owner_or_partner_of_company":["uc_company_owner_treatment_applies"],"state_pension":["council_tax_reduction_applicable_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","overlapping_state_pension","pension_credit_income","pre_budget_change_household_benefits","social_security_income","total_pension_income","uc_unearned_income"],"state_pension_age":["months_since_state_pension_age"],"state_pension_credit_qualifying_age":["has_attained_state_pension_credit_qualifying_age"],"state_pension_reported":["additional_state_pension","basic_state_pension","new_state_pension"],"state_pension_type":["additional_state_pension","basic_state_pension","new_state_pension","overlapping_state_pension"],"statutory_maternity_pay":["adjusted_employer_cost","baseline_employer_cost","council_tax_reduction_applicable_income","employer_cost","employer_ni_fixed_employer_cost_change","employment_benefits","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","ni_class_1_income","pre_budget_change_household_benefits","tax_free_childcare_treated_as_in_work","uc_childcare_treated_as_in_paid_work","uc_individual_earned_income_before_mif"],"statutory_paternity_pay":["adjusted_employer_cost","baseline_employer_cost","employer_cost","employer_ni_fixed_employer_cost_change","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","ni_class_1_income","tax_free_childcare_treated_as_in_work","uc_childcare_treated_as_in_paid_work","uc_individual_earned_income_before_mif"],"statutory_sick_pay":["adjusted_employer_cost","baseline_employer_cost","council_tax_reduction_applicable_income","employer_cost","employer_ni_fixed_employer_cost_change","employment_benefits","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","housing_benefit_applicable_income","ni_class_1_income","pre_budget_change_household_benefits","tax_free_childcare_treated_as_in_work","uc_childcare_treated_as_in_paid_work","uc_individual_earned_income_before_mif"],"structural_insurance_payments":["hbai_household_net_income_ahc"],"student_loan_balance":["net_wealth","student_loan_repayment"],"student_loan_plan":["has_student_loan","student_loan_interest_rate","student_loan_repayment","student_loan_repayment_rate"],"student_loan_repayment":["student_loan_repayments"],"student_loan_repayment_rate":["student_loan_repayment"],"student_loan_repayments":["gov_tax","hbai_household_net_income","household_tax"],"targeted_childcare_entitlement":["extended_childcare_entitlement_per_child","gov_spending","household_benefits","is_child_receiving_targeted_childcare"],"targeted_childcare_entitlement_eligible":["targeted_childcare_entitlement"],"tax":["benunit_tax","tax_modelling"],"tax_band":["meets_marriage_allowance_income_conditions"],"tax_credit_award":["is_on_pawhp_relevant_benefit","is_on_winter_fuel_payment_relevant_benefit","pawhp_eligible","winter_fuel_payment_eligible"],"tax_credits":["council_tax_reduction_applicable_income","ctc_entitlement","housing_benefit_applicable_income","tax_credit_award"],"tax_credits_applicable_income":["tax_credits_reduction"],"tax_credits_current_year_income":["meets_child_tax_credit_criteria_for_targeted_childcare_entitlement","meets_working_tax_credit_criteria_for_targeted_childcare_entitlement","tax_credits_applicable_income"],"tax_credits_reduction":["child_tax_credit_pre_minimum","working_tax_credit_pre_minimum"],"tax_free_childcare":["childcare_grant_eligible","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_child_receiving_tax_free_childcare"],"tax_free_childcare_caring_or_incapacity_benefit":["tax_free_childcare_regarded_as_in_paid_work"],"tax_free_childcare_child_age_eligible":["tax_free_childcare_qualifying_child"],"tax_free_childcare_eligible":["tax_free_childcare","tax_free_childcare_eligible_declaration_periods"],"tax_free_childcare_eligible_declaration_periods":["tax_free_childcare"],"tax_free_childcare_expected_declaration_period_income":["tax_free_childcare_meets_income_requirements"],"tax_free_childcare_meets_income_requirements":["tax_free_childcare_eligible"],"tax_free_childcare_meets_uk_connection":["tax_free_childcare_program_eligible"],"tax_free_childcare_on_adoption_leave":["tax_free_childcare_treated_as_in_work"],"tax_free_childcare_on_carers_leave":["tax_free_childcare_regarded_as_in_paid_work"],"tax_free_childcare_on_qualifying_leave":["tax_free_childcare_treated_as_in_work"],"tax_free_childcare_on_shared_parental_leave":["tax_free_childcare_treated_as_in_work"],"tax_free_childcare_program_eligible":["tax_free_childcare_eligible"],"tax_free_childcare_qualifying_child":["is_child_receiving_tax_free_childcare","tax_free_childcare","tax_free_childcare_eligible"],"tax_free_childcare_regarded_as_in_paid_work":["tax_free_childcare_meets_income_requirements","tax_free_childcare_work_condition"],"tax_free_childcare_self_employment_start_up_period":["tax_free_childcare_meets_income_requirements"],"tax_free_childcare_spend_routed_share":["tax_free_childcare"],"tax_free_childcare_treated_as_in_work":["tax_free_childcare_regarded_as_in_paid_work","tax_free_childcare_work_condition"],"tax_free_childcare_uses_qualifying_provider":["tax_free_childcare"],"tax_free_childcare_work_condition":["tax_free_childcare_eligible"],"tax_free_savings_income":["taxable_savings_interest_income"],"tax_reported":["tax_modelling"],"taxable_dividend_income":["dividend_income_tax","earned_taxable_income","net_income","non_savings_income_after_allowances","property_finance_costs_relieved","received_allowances_dividend_income","received_allowances_earned_income","taxed_dividend_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"taxable_employment_income":["net_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"taxable_miscellaneous_income":["housing_benefit_net_earnings","net_income","total_income","uc_income_tax_on_earnings","uc_income_tax_on_trade","uc_individual_earned_income_before_mif"],"taxable_pension_income":["net_income"],"taxable_property_income":["earned_taxable_income","net_income","property_finance_costs_relieved","taxable_property_income_after_allowances","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"taxable_property_income_after_allowances":["non_savings_taxable_income","property_income_tax"],"taxable_savings_interest_income":["add_rate_savings_income","basic_rate_savings_income","basic_rate_savings_income_pre_starter","dividend_income_tax","earned_taxable_income","higher_rate_savings_income","net_income","non_savings_income_after_allowances","property_finance_costs_relieved","received_allowances_earned_income","received_allowances_savings_income","savings_starter_rate_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"taxable_self_employment_income":["net_income","uc_income_tax_on_earnings","uc_income_tax_on_trade"],"taxable_social_security_income":["net_income"],"taxed_dividend_income":["taxed_income"],"taxed_income":["personal_pension_contributions_tax"],"taxed_savings_income":["taxed_income"],"tenure_type":["benunit_tenure_type","is_renting","ons_tenure_type"],"tobacco_duty":["gov_tax","household_tax"],"total_income":["bi_household_phaseout","bi_individual_phaseout","household_benefits_individual_non_dep_deduction","housing_benefit_net_earnings","post_tax_income","winter_fuel_payment_eligible"],"total_wealth":["household_wealth_decile","net_wealth","non_primary_residence_wealth_tax","wealth_tax"],"trading_allowance_deduction":["ni_class_4_profits_before_losses","taxable_self_employment_income"],"trading_loss":["loss_relief"],"transport_consumption":["carbon_consumption","consumption"],"travel_grant":["gov_spending","hbai_benefits","household_benefits"],"travel_grant_abroad_placement":["travel_grant_eligible"],"travel_grant_clinical_placement":["travel_grant_eligible"],"travel_grant_eligible":["travel_grant"],"travel_grant_eligible_expenses":["travel_grant_eligible"],"travel_grant_income_assessed_support":["travel_grant_eligible"],"travel_grant_receives_means_tested_nhs_bursary":["travel_grant_eligible"],"tv_licence":["gov_tax","household_tax","pre_budget_change_household_tax"],"tv_licence_discount":["free_tv_licence_value","tv_licence"],"two_child_limit_payment":["gov_spending","household_benefits"],"uc_LCWRA_element":["is_benefit_cap_exempt_health_disability","uc_disability_elements"],"uc_LHA_cap":["uc_housing_costs_element"],"uc_assessable_capital":["is_uc_eligible","kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","uc_tariff_income","westminster_council_tax_reduction"],"uc_carer_element":["is_benefit_cap_exempt_health_disability","receives_uc_carer_element","uc_maximum_amount"],"uc_child_element":["uc_maximum_amount"],"uc_child_index":["uc_individual_child_element"],"uc_childcare_element":["childcare_grant_eligible","uc_maximum_amount"],"uc_childcare_element_claimed":["uc_childcare_element"],"uc_childcare_element_eligible_children":["uc_maximum_childcare_element_amount"],"uc_childcare_treated_as_in_paid_work":["uc_childcare_work_condition"],"uc_childcare_work_condition":["uc_childcare_element_claimed"],"uc_company_capital":["uc_assessable_capital"],"uc_company_gainful_self_employment":["uc_is_in_gainful_self_employment"],"uc_company_holding_disregard":["household_uc_pool_company_holdings","uc_assessable_capital"],"uc_company_intermediary_exclusion_applies":["uc_company_owner_treatment_applies"],"uc_company_owner_treatment_applies":["uc_company_capital","uc_company_gainful_self_employment","uc_company_holding_disregard","uc_company_self_employed_earnings"],"uc_company_self_employed_earnings":["uc_individual_earned_income_before_mif"],"uc_deduction_combination":["uc_deduction_rate"],"uc_deduction_random_draw":["uc_has_deduction","uc_latent_deduction_rate"],"uc_deduction_rate":["uc_deductions"],"uc_deduction_type_random_draw":["uc_deduction_combination"],"uc_deductions":["universal_credit"],"uc_disability_elements":["uc_maximum_amount"],"uc_earned_income":["kingston_upon_thames_council_tax_reduction","meets_universal_credit_criteria_for_targeted_childcare_entitlement","merton_council_tax_reduction","newham_council_tax_reduction","uc_income_reduction","westminster_council_tax_reduction"],"uc_expected_hours":["uc_minimum_income_floor_gross"],"uc_has_deduction":["uc_deduction_combination","uc_latent_deduction_rate"],"uc_has_offer_of_paid_work_starting_by_end_of_next_assessment_period":["uc_childcare_work_condition"],"uc_housing_costs_element":["uc_maximum_amount","uc_work_allowance"],"uc_income_reduction":["universal_credit_pre_benefit_cap"],"uc_income_tax_on_earnings":["uc_income_tax_on_trade","uc_individual_earned_income_before_mif"],"uc_income_tax_on_trade":["uc_individual_earned_income_before_mif"],"uc_individual_child_element":["uc_child_element","uc_childcare_element_eligible_children","uc_is_child_limit_affected"],"uc_individual_disabled_child_element":["uc_disability_elements"],"uc_individual_earned_income":["uc_earned_income"],"uc_individual_earned_income_before_mif":["is_benefit_cap_exempt_earnings","uc_individual_earned_income"],"uc_individual_non_dep_deduction":["uc_non_dep_deductions"],"uc_individual_non_dep_deduction_eligible":["uc_individual_non_dep_deduction"],"uc_individual_severely_disabled_child_element":["uc_disability_elements"],"uc_is_adopter_in_first_year":["uc_work_related_group_apart_from_earnings"],"uc_is_child_born_before_child_limit":["uc_individual_child_element"],"uc_is_child_limit_affected":["two_child_limit_payment"],"uc_is_foster_parent_or_new_friend_or_family_carer":["uc_work_related_group_apart_from_earnings"],"uc_is_in_gainful_self_employment":["uc_mif_applies"],"uc_is_in_pregnancy_or_post_confinement_period":["uc_work_related_group_apart_from_earnings"],"uc_is_in_startup_period":["uc_mif_applies"],"uc_is_ineligible_partner":["is_benefit_cap_exempt_health_disability","is_lha_shared_accommodation_rate_specified_renter","is_on_universal_credit","is_uc_single_or_joint_claimant","uc_is_responsible_carer","uc_member_of_couple_claims_as_single_person","uc_minimum_income_floor_gross","uc_work_related_group_apart_from_earnings"],"uc_is_responsible_carer":["uc_expected_hours","uc_work_related_group_apart_from_earnings"],"uc_is_responsible_foster_parent_of_child_under_one":["uc_work_related_group_apart_from_earnings"],"uc_is_student_with_no_work_related_requirements":["uc_work_related_group_apart_from_earnings"],"uc_is_temporarily_absent_from_claimant_household":["uc_unable_to_provide_childcare"],"uc_latent_deduction_rate":["uc_deduction_rate"],"uc_limited_capability_for_WRA":["disability_basic_income","extended_childcare_entitlement_limited_capability_or_specified_benefit","is_benefit_cap_exempt_health_disability","uc_limited_capability_for_work","uc_work_related_group_apart_from_earnings"],"uc_limited_capability_for_work":["is_uc_work_allowance_eligible","meets_uc_minimum_age_condition","uc_unable_to_provide_childcare","uc_work_related_group_apart_from_earnings"],"uc_maximum_amount":["kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","uc_income_reduction","universal_credit_pre_benefit_cap","westminster_council_tax_reduction"],"uc_maximum_childcare_element_amount":["uc_childcare_element"],"uc_member_of_couple_claims_as_single_person":["is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_lha_shared_accommodation_rate_specified_renter"],"uc_mif_applies":["uc_individual_earned_income"],"uc_minimum_income_floor":["uc_individual_earned_income"],"uc_minimum_income_floor_gross":["uc_minimum_income_floor","uc_minimum_income_floor_income_tax","uc_minimum_income_floor_national_insurance"],"uc_minimum_income_floor_income_tax":["uc_minimum_income_floor"],"uc_minimum_income_floor_national_insurance":["uc_minimum_income_floor"],"uc_national_insurance_on_earnings":["uc_individual_earned_income_before_mif","uc_national_insurance_on_trade"],"uc_national_insurance_on_trade":["uc_individual_earned_income_before_mif"],"uc_non_dep_deduction_exempt":["uc_individual_non_dep_deduction_eligible"],"uc_non_dep_deductions":["uc_housing_costs_element"],"uc_non_dep_receives_pension_credit":["uc_non_dep_deduction_exempt"],"uc_non_dependants_counted":["LHA_allowed_bedrooms","lha_renter_has_non_dependant","uc_non_dep_deductions"],"uc_reported_capital":["household_uc_pool_company_holdings","household_uc_reported_capital","household_uc_unreported_claimants","uc_assessable_capital"],"uc_standard_allowance":["uc_deductions","uc_maximum_amount","universal_credit"],"uc_standard_allowance_claimant_type":["uc_standard_allowance"],"uc_tariff_income":["uc_unearned_income"],"uc_unable_to_provide_childcare":["uc_childcare_work_condition"],"uc_unearned_carer_support_payment":["uc_unearned_income"],"uc_unearned_income":["kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","uc_income_reduction","westminster_council_tax_reduction"],"uc_work_allowance":["uc_earned_income"],"uc_work_related_group_apart_from_earnings":["uc_mif_applies","uc_minimum_income_floor_gross"],"uc_youngest_child_age":["uc_expected_hours","uc_is_responsible_carer","uc_work_related_group_apart_from_earnings"],"universal_childcare_entitlement":["extended_childcare_entitlement_per_child","gov_spending","household_benefits","is_child_receiving_universal_childcare"],"universal_childcare_entitlement_eligible":["universal_childcare_entitlement"],"universal_credit":["cost_of_living_support_payment","council_tax_reduction_applicable_income","disability_basic_income","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_on_universal_credit","is_scp_eligible","kingston_upon_thames_council_tax_reduction","kingston_upon_thames_council_tax_reduction_non_dep_deductions","meets_universal_credit_criteria_for_targeted_childcare_entitlement","merton_council_tax_reduction","merton_council_tax_reduction_non_dep_deductions","newham_council_tax_reduction","oxford_council_tax_reduction","pre_budget_change_household_benefits","receives_uc_carer_element","uc_is_child_limit_affected","westminster_council_tax_reduction"],"universal_credit_pre_benefit_cap":["benefit_cap_reduction","is_benefit_cap_exempt_health_disability","is_benefit_cap_single_claimant_rate","is_uc_entitled","tax_free_childcare_program_eligible","uc_deductions","uc_has_deduction","universal_credit"],"universal_credit_reported":["bursary_fund_16_to_19_receives_uc_or_esa_in_own_right","bursary_fund_16_to_19_receives_uc_or_is_in_own_right","claims_all_entitled_benefits","has_mixed_age_couple_pension_credit_saving","is_on_universal_credit","meets_pension_credit_age_conditions","pawhp_eligible","winter_fuel_payment_eligible"],"unused_personal_allowance":["partners_unused_personal_allowance"],"uses_property_allowance":["property_allowance_deduction","property_finance_costs_carried_forward","property_finance_costs_relieved"],"vat":["gov_tax","vat_change"],"vat_change":["household_tax","pre_budget_change_household_tax"],"vehicle_excise_duty":["gov_tax","household_tax"],"water_and_sewerage_charges":["hbai_household_net_income_ahc","housing_costs"],"wealth_tax":["gov_tax","household_tax","pre_budget_change_household_tax"],"weekly_hours":["benunit_weekly_hours","esa_income_remunerative_work_hours","income_support_remunerative_work_hours","is_WTC_eligible","jsa_remunerative_work_hours","meets_housing_benefit_additional_earnings_disregard_conditions"],"westminster_council_tax_reduction":["simulated_council_tax_reduction_benunit"],"westminster_council_tax_reduction_individual_non_dep_deduction":["westminster_council_tax_reduction_non_dep_deductions"],"westminster_council_tax_reduction_non_dep_deductions":["westminster_council_tax_reduction"],"wine_abv":["wine_duty"],"wine_draught_share":["wine_duty"],"wine_duty":["alcohol_duty"],"wine_litres":["wine_duty"],"winter_fuel_allowance":["cost_of_living_support_payment","gov_spending","hbai_benefits","hbai_household_net_income","household_benefits"],"winter_fuel_payment":["winter_fuel_allowance"],"winter_fuel_payment_eligible":["winter_fuel_payment"],"working_age_benefits_premiums":["benefits_premiums","council_tax_reduction_applicable_amount","income_support_applicable_amount"],"working_tax_credit":["gov_spending","hbai_benefits","hbai_household_net_income","household_benefits","is_benefit_cap_exempt_health_disability","is_scp_eligible","meets_child_tax_credit_criteria_for_targeted_childcare_entitlement","meets_working_tax_credit_criteria_for_targeted_childcare_entitlement","pension_credit_income","pre_budget_change_household_benefits","tax_free_childcare_program_eligible"],"working_tax_credit_reported":["claims_all_entitled_benefits","claims_legacy_benefits","council_tax_reduction_pensioner","is_WTC_eligible","is_uc_eligible","legacy_benefits_closed","pawhp_eligible","tax_credit_award","winter_fuel_payment_eligible","would_claim_WTC"],"would_claim_CTC":["child_tax_credit","child_tax_credit_pre_minimum"],"would_claim_IS":["income_support","income_support_assessable_capital"],"would_claim_WTC":["working_tax_credit","working_tax_credit_pre_minimum"],"would_claim_adult_dependants_grant":["adult_dependants_grant_eligible"],"would_claim_bursary_fund_16_to_19":["bursary_fund_16_to_19_vulnerable_group_eligible"],"would_claim_care_to_learn":["care_to_learn_eligible"],"would_claim_carers_allowance":["carer_support_payment_pre_overlap","carers_allowance_pre_overlap","is_carer_for_benefits","partners_care_for_same_severely_disabled_person"],"would_claim_child_benefit":["child_benefit"],"would_claim_childcare_grant":["childcare_grant_eligible"],"would_claim_council_tax_reduction":["kingston_upon_thames_council_tax_reduction","merton_council_tax_reduction","newham_council_tax_reduction","oxford_council_tax_reduction","simulated_council_tax_reduction_benunit","westminster_council_tax_reduction"],"would_claim_disabled_students_allowance":["disabled_students_allowance_eligible"],"would_claim_extended_childcare":["extended_childcare_entitlement_eligible"],"would_claim_housing_benefit":["housing_benefit","housing_benefit_pre_benefit_cap"],"would_claim_marriage_allowance":["marriage_allowance"],"would_claim_parents_learning_allowance":["parents_learning_allowance_eligible"],"would_claim_pc":["in_receipt_of_guarantee_credit","in_receipt_of_savings_credit_only","income_support_eligible","pension_credit"],"would_claim_scp":["scottish_child_payment"],"would_claim_targeted_childcare":["targeted_childcare_entitlement"],"would_claim_tfc":["tax_free_childcare_eligible"],"would_claim_travel_grant":["travel_grant_eligible"],"would_claim_uc":["claims_universal_credit"],"would_claim_uc_at_legacy_closure":["claims_uc_at_legacy_closure"],"would_claim_uc_childcare":["uc_childcare_element_claimed"],"would_claim_universal_childcare":["universal_childcare_entitlement"],"would_evade_tv_licence_fee":["free_tv_licence_value","tv_licence"],"wtc_entitlement":["working_tax_credit"],"youngest_child_age_for_legacy_benefits":["income_support_eligible"],"youngest_claimant_or_partner_age":["income_support_applicable_amount","is_WTC_eligible"]}} diff --git a/app/src/libs/flagship/calibrationMatching.ts b/app/src/libs/flagship/calibrationMatching.ts index 128713617..de34d5969 100644 --- a/app/src/libs/flagship/calibrationMatching.ts +++ b/app/src/libs/flagship/calibrationMatching.ts @@ -555,7 +555,7 @@ export async function calibrationMatchesForPaths( ): Promise { let map; try { - map = await loadParameterDependencies(); + map = await loadParameterDependencies('us'); } catch { return null; } diff --git a/app/src/libs/flagship/modelValidation.ts b/app/src/libs/flagship/modelValidation.ts index 012ec5e2d..b7d5208f5 100644 --- a/app/src/libs/flagship/modelValidation.ts +++ b/app/src/libs/flagship/modelValidation.ts @@ -203,7 +203,7 @@ export async function scorecardMatchesForPaths( ): Promise { let map; try { - map = await loadParameterDependencies(); + map = await loadParameterDependencies('us'); } catch { return null; } diff --git a/app/src/libs/flagship/parameterDependencies.ts b/app/src/libs/flagship/parameterDependencies.ts index 2fed365bb..2217b835c 100644 --- a/app/src/libs/flagship/parameterDependencies.ts +++ b/app/src/libs/flagship/parameterDependencies.ts @@ -1,16 +1,18 @@ /** * Parameter → variable dependency map, traced from the model. * - * `app/scripts/build-parameter-dependency-map.py` runs policyengine-us with - * core's FullTracer and records which variables read each parameter path - * and which variables consume each variable. This module answers the - * question the validation layer needs: given a reform's parameter paths, - * which model variables does it move, and how far downstream? Depth is the - * validation "ring": 0–1 is the primary variable, 2–4 the mechanism it - * feeds, deeper is incidental. + * `app/scripts/build-parameter-dependency-map.py` runs a country model + * (policyengine-us, or policyengine-uk) with core's FullTracer and records + * which variables read each parameter path and which variables consume + * each variable. This module answers the question the validation layer + * needs: given a reform's parameter paths, which model variables does it + * move, and how far downstream? Depth is the validation "ring": 0–1 is the + * primary variable, 2–4 the mechanism it feeds, deeper is incidental. The + * household section reads the same map upstream, for the inputs a reform + * makes worth filling in. * - * The JSON is loaded lazily — it is a few hundred kilobytes and only the - * validation surfaces need it. + * Each country's JSON is loaded lazily: the US map is over a megabyte and + * only the validation and household surfaces need one. */ export interface ParameterDependencyMap { @@ -124,26 +126,41 @@ export function variablesReachedByPaths( ); } +/** The countries with a traced map, each loaded only when first asked for. */ +const MAP_LOADERS: Record Promise<{ default: unknown }>> = { + us: () => import('@/data/flagship/parameterDependencies.json'), + uk: () => import('@/data/flagship/parameterDependencies.uk.json'), +}; + /** - * Whether a country has a traced map. The map is traced from - * policyengine-us only, so another country's reform paths would match - * US variables: such a country gets no map at all. + * Whether a country has a traced map. Each map is traced from its own + * country's model, so another country's reform paths never match it: a + * country without its own map gets none at all. */ export function hasParameterDependencies(countryId: string): boolean { - return countryId === 'us'; + return Object.hasOwn(MAP_LOADERS, countryId); } -let cached: Promise | null = null; +const cached = new Map>(); -/** The traced map for policyengine-us, loaded once per session. */ -export function loadParameterDependencies(): Promise { - if (!cached) { - cached = import('@/data/flagship/parameterDependencies.json').then( - (module) => module.default as ParameterDependencyMap - ); - cached.catch(() => { - cached = null; +/** + * A country's traced map, loaded once per session. Every caller names its + * country, so none is given the US map by default: the validation surfaces + * (calibration, scorecard, track record) ask for the US map, as their data + * is the US model's. + */ +export function loadParameterDependencies(countryId: string): Promise { + if (!hasParameterDependencies(countryId)) { + return Promise.reject(new Error(`No traced parameter map for ${countryId}.`)); + } + const loader = MAP_LOADERS[countryId]; + let map = cached.get(countryId); + if (!map) { + map = loader().then((module) => module.default as ParameterDependencyMap); + cached.set(countryId, map); + map.catch(() => { + cached.delete(countryId); }); } - return cached; + return map; } diff --git a/app/src/tests/fixtures/components/flagship/report/householdSuggestionMocks.ts b/app/src/tests/fixtures/components/flagship/report/householdSuggestionMocks.ts new file mode 100644 index 000000000..871ef7f68 --- /dev/null +++ b/app/src/tests/fixtures/components/flagship/report/householdSuggestionMocks.ts @@ -0,0 +1,46 @@ +import type { VariableInfo } from '@/utils/VariableResolver'; + +/** Universal Credit's child element: a UK reform's parameter. */ +export const UK_CHILD_ELEMENT_PATH = 'gov.dwp.universal_credit.elements.child.amount'; + +/** The Child Tax Credit's base amount: a US reform's parameter. */ +export const US_CTC_BASE_PATH = 'gov.irs.credits.ctc.amount.base'; + +function ukInput(name: string, entity: string, documentation?: string): VariableInfo { + return { + name, + label: name.replaceAll('_', ' '), + entity, + valueType: 'float', + defaultValue: 0, + isInputVariable: true, + hiddenInput: false, + // The UK model files no input under a module. + moduleName: '', + documentation, + }; +} + +/** + * UK inputs as the model's metadata lists them: some a reader describes a + * household with, and one only the microdata sets (a public-spending + * imputation), which the household section never suggests. + */ +export const UK_INPUT_VARIABLES: VariableInfo[] = [ + ukInput('age', 'person'), + ukInput('employment_income', 'person'), + ukInput('childcare_expenses', 'person'), + ukInput('is_disabled_for_benefits', 'person'), + ukInput('rent', 'household'), + ukInput('region', 'household'), + ukInput('dfe_education_spending', 'person'), +]; + +export const UK_HOUSEHOLD_INPUT_NAMES = new Set( + UK_INPUT_VARIABLES.filter((variable) => variable.name !== 'dfe_education_spending').map( + (variable) => variable.name + ) +); + +/** An input only the microdata sets, which the UK map reaches from the child element. */ +export const UK_MICRODATA_ONLY_INPUT = 'dfe_education_spending'; diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactSuggestions.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactSuggestions.test.tsx new file mode 100644 index 000000000..f89ad425d --- /dev/null +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactSuggestions.test.tsx @@ -0,0 +1,76 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { render, screen, userEvent, waitFor } from '@test-utils'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; +import HouseholdImpactSection from '@/components/flagship/report/household/HouseholdImpactSection'; +import { + UK_CHILD_ELEMENT_PATH, + UK_INPUT_VARIABLES, + UK_MICRODATA_ONLY_INPUT, +} from '@/tests/fixtures/components/flagship/report/householdSuggestionMocks'; +import { + BASELINE_POLICY_ID, + HOUSEHOLD_YEAR, + REFORM_POLICY_ID, +} from '@/tests/fixtures/libs/flagship/householdRunMocks'; + +// The model's inputs, as its metadata lists them. +vi.mock('@/utils/VariableResolver', async (importOriginal) => ({ + ...(await importOriginal()), + getInputVariables: () => UK_INPUT_VARIABLES, +})); +// The household builder, tested on its own: a stand-in shows the inputs it is asked to lead with. +vi.mock('@/components/household/HouseholdBuilderForm', () => ({ + default: ({ suggestedVariables }: { suggestedVariables?: string[] }) => ( +
+ ), +})); +vi.mock('@/utils/HouseholdValidation', () => ({ + HouseholdValidation: { isReadyForSimulation: () => ({ isValid: true, errors: [] }) }, +})); + +function renderSection(countryId: string, reformPaths: string[]) { + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return render( + + + , + countryId + ); +} + +const suggestedInputs = () => + (screen.getByRole('generic', { name: 'Household builder' }).getAttribute('data-suggested') ?? '') + .split(' ') + .filter(Boolean); + +describe('HouseholdImpactSection suggestions', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test("given a UK reform to Universal Credit's child element then the inputs it reads are suggested", async () => { + const user = userEvent.setup(); + renderSection('uk', [UK_CHILD_ELEMENT_PATH]); + + await user.click(screen.getByRole('button', { name: 'Add a household' })); + + await waitFor(() => expect(suggestedInputs()).toContain('childcare_expenses')); + expect(suggestedInputs()).toContain('age'); + }); + + test('given a UK reform then an input only the microdata sets is never suggested', async () => { + const user = userEvent.setup(); + renderSection('uk', [UK_CHILD_ELEMENT_PATH]); + + await user.click(screen.getByRole('button', { name: 'Add a household' })); + + await waitFor(() => expect(suggestedInputs()).toContain('age')); + expect(suggestedInputs()).not.toContain(UK_MICRODATA_ONLY_INPUT); + }); +}); diff --git a/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts b/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts index 15cc1f033..511f11c79 100644 --- a/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts +++ b/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts @@ -1,6 +1,13 @@ import { describe, expect, test } from 'vitest'; import { suggestedHouseholdInputs } from '@/libs/flagship/householdSuggestions'; -import type { ParameterDependencyMap } from '@/libs/flagship/parameterDependencies'; +import { + loadParameterDependencies, + type ParameterDependencyMap, +} from '@/libs/flagship/parameterDependencies'; +import { + UK_CHILD_ELEMENT_PATH, + UK_HOUSEHOLD_INPUT_NAMES, +} from '@/tests/fixtures/components/flagship/report/householdSuggestionMocks'; const MAP = { generatedAt: '', @@ -83,3 +90,21 @@ describe('suggestedHouseholdInputs', () => { ).toEqual(['childcare_expenses']); }); }); + +describe('suggestedHouseholdInputs for the UK', () => { + test("given a change to Universal Credit's child element then a child's age and childcare lead", async () => { + const map = await loadParameterDependencies('uk'); + + const suggested = suggestedHouseholdInputs( + [UK_CHILD_ELEMENT_PATH], + map, + UK_HOUSEHOLD_INPUT_NAMES, + undefined, + new Set(), + 'uk' + ); + + expect(suggested).toContain('age'); + expect(suggested).toContain('childcare_expenses'); + }); +}); diff --git a/app/src/tests/unit/libs/flagship/parameterDependencies.test.ts b/app/src/tests/unit/libs/flagship/parameterDependencies.test.ts index a045746d4..700809ed2 100644 --- a/app/src/tests/unit/libs/flagship/parameterDependencies.test.ts +++ b/app/src/tests/unit/libs/flagship/parameterDependencies.test.ts @@ -1,5 +1,7 @@ import { describe, expect, test } from 'vitest'; import { + hasParameterDependencies, + loadParameterDependencies, normalizeParameterPath, ParameterDependencyMap, readersOfPath, @@ -86,3 +88,40 @@ describe('variablesReachedByPaths', () => { expect(variablesReachedByPaths([STANDARD_DEDUCTION_PATH], map)).toEqual([]); }); }); + +describe('each country has its own traced map', () => { + test.each(['us', 'uk'])('given %s then it has a traced map', (countryId) => { + expect(hasParameterDependencies(countryId)).toBe(true); + }); + + test('given a country without a traced model then it has none', () => { + expect(hasParameterDependencies('ca')).toBe(false); + }); + + test("given a name an object inherits then it is not a country's map", async () => { + expect(hasParameterDependencies('constructor')).toBe(false); + await expect(loadParameterDependencies('constructor')).rejects.toThrow( + 'No traced parameter map for constructor.' + ); + }); + + test("given the UK then the map is traced from policyengine-uk, with the UK's parameters", async () => { + const map = await loadParameterDependencies('uk'); + + expect(map.model.package).toBe('policyengine-uk'); + expect(readersOfPath('gov.dwp.universal_credit.elements.child.amount', map)).toContain( + 'uc_individual_child_element' + ); + }); + + test('given the US then the map is traced from policyengine-us, as the validation surfaces read it', async () => { + expect((await loadParameterDependencies('us')).model.package).toBe('policyengine-us'); + }); + + test('given no country then no map loads, so no caller is given the US map by default', async () => { + // @ts-expect-error Every caller names its country. + await expect(loadParameterDependencies()).rejects.toThrow( + 'No traced parameter map for undefined.' + ); + }); +}); From 66b86359319a702bd198085fdfa1218053003f2a Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:06:44 +0400 Subject: [PATCH 5/8] v3 UK households: acceptance checks from issues #358 and #562 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests only: the section already sends UK households as built and runs them in the UK, so these check that behavior against the two issues rather than drive a change. They pass on the code as it is; each was confirmed to fail under a temporary change: the section sending a new household instead of the built one failed three, and the run calling the US routes for a UK household failed the fourth. - #358: the issue's household (an adult earning £100, a dependant aged 10 earning £10, one benefit unit, a BRMA, region and local authority naming different places) is sent as described and its result shows. The Public API once failed on it; run locally, policyengine-uk 2.130.1 (the version the Public API pins) calculates it for 2025 (net income £10,159.73) and 2026 (£10,657.91). - #562: a couple and a child share one benefit unit and one household; an input set on the benefit unit is sent on the benefit unit; the household runs under the report's two policies and reads in pounds. Saving and reusing a household, #562's second item, belongs to the standalone household report. Co-Authored-By: Claude Opus 5.5 --- .../libs/flagship/householdRunMocks.ts | 41 +++++ .../report/UKHouseholdAcceptance.test.tsx | 141 ++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 app/src/tests/unit/components/flagship/report/UKHouseholdAcceptance.test.tsx diff --git a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts index 59eb50603..1a8c0d8cf 100644 --- a/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts +++ b/app/src/tests/fixtures/libs/flagship/householdRunMocks.ts @@ -258,3 +258,44 @@ export const UK_FAMILY_REFORM_RESULT = ukHouseholdResult({ universalCredit: 20511.06, childBenefit: 2337.4, }); + +/** + * Issue #358's UK household, in the report's year: an adult earning £100 + * and a dependant aged 10 earning £10, in one benefit unit, with a BRMA, + * region and local authority that name different places. The Public API + * once failed to calculate it; policyengine-uk 2.130.1 calculates it. + */ +export const ISSUE_358_HOUSEHOLD = Household.fromV1CreationPayload({ + country_id: 'uk', + data: { + people: { + you: { age: at(40), employment_income: at(100) }, + 'your first dependent': { age: at(10), employment_income: at(10) }, + }, + benunits: { 'your benefit unit': { members: ['you', 'your first dependent'] } }, + households: { + 'your household': { + members: ['you', 'your first dependent'], + brma: at('AYLESBURY'), + region: at('NORTH_EAST'), + local_authority: at('ABERDEENSHIRE'), + }, + }, + }, +}); + +/** + * Issue #562's UK household: a couple and a child, built with the + * household builder's own controls, with an input set on the benefit unit + * (it opts out of Child Benefit) rather than on a person or the household. + */ +export const UK_COUPLE_WITH_BENEFIT_UNIT_INPUT = Household.starter('uk', HOUSEHOLD_YEAR) + .withBuilderMaritalStatus(HOUSEHOLD_YEAR, 'married') + .withBuilderChildCount(HOUSEHOLD_YEAR, 1) + .setGroupVariableAtYear( + 'benunits', + 'your benefit unit', + 'child_benefit_opts_out', + HOUSEHOLD_YEAR, + true + ); diff --git a/app/src/tests/unit/components/flagship/report/UKHouseholdAcceptance.test.tsx b/app/src/tests/unit/components/flagship/report/UKHouseholdAcceptance.test.tsx new file mode 100644 index 000000000..025b66f39 --- /dev/null +++ b/app/src/tests/unit/components/flagship/report/UKHouseholdAcceptance.test.tsx @@ -0,0 +1,141 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { render, screen, userEvent, waitFor, within } from '@test-utils'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; +import HouseholdImpactSection from '@/components/flagship/report/household/HouseholdImpactSection'; +import type { Household } from '@/models/Household'; +import { + BASELINE_POLICY_ID, + calculationResponse, + CREATED_HOUSEHOLD_ID, + HOUSEHOLD_YEAR, + ISSUE_358_HOUSEHOLD, + REFORM_POLICY_ID, + UK_BASELINE_RESULT, + UK_COUPLE_WITH_BENEFIT_UNIT_INPUT, + UK_REFORM_RESULT, +} from '@/tests/fixtures/libs/flagship/householdRunMocks'; + +/** + * Acceptance checks for UK households in v3's report section, from issues + * #358 (a UK household the Public API once failed to calculate) and #562 (the + * UK household checklist: several people, an input on the benefit unit, + * the report's policies, the whole run). Saving and reusing a household + * (#562's second item) belongs to the standalone household report. + */ + +const mockCreateHousehold = vi.fn(); +const mockFetchHouseholdCalculation = vi.fn(); + +vi.mock('@/api/household', () => ({ + createHousehold: (...args: unknown[]) => mockCreateHousehold(...args), +})); +vi.mock('@/api/householdCalculation', () => ({ + fetchHouseholdCalculationWithBundle: async (...args: unknown[]) => + calculationResponse(await mockFetchHouseholdCalculation(...args)), +})); +// The household builder, tested on its own: a stand-in hands the section a +// household the builder can make, as its onChange does. +vi.mock('@/components/household/HouseholdBuilderForm', async () => { + const fixtures = await import('@/tests/fixtures/libs/flagship/householdRunMocks'); + return { + default: ({ onChange }: { onChange: (household: Household) => void }) => ( +
+ + +
+ ), + }; +}); +vi.mock('@/utils/HouseholdValidation', () => ({ + HouseholdValidation: { isReadyForSimulation: () => ({ isValid: true, errors: [] }) }, +})); + +async function calculateUKHousehold(describe: string) { + const user = userEvent.setup(); + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + render( + + + , + 'uk' + ); + await user.click(screen.getByRole('button', { name: 'Add a household' })); + await user.click(screen.getByRole('button', { name: describe })); + await user.click(screen.getByRole('button', { name: 'Calculate' })); + await waitFor(() => expect(mockCreateHousehold).toHaveBeenCalledTimes(1)); + return mockCreateHousehold.mock.calls[0][0]; +} + +describe('UK households in the report section (issues #358 and #562)', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockCreateHousehold.mockResolvedValue({ result: { household_id: CREATED_HOUSEHOLD_ID } }); + mockFetchHouseholdCalculation.mockImplementation( + async (_country: string, _household: string, policyId: string) => + policyId === BASELINE_POLICY_ID ? UK_BASELINE_RESULT : UK_REFORM_RESULT + ); + }); + + test("given issue 358's household then it is sent as described, and its result shows", async () => { + const sent = await calculateUKHousehold("Describe issue 358's household"); + + expect(sent).toEqual(ISSUE_358_HOUSEHOLD.toV1CreationPayload()); + expect(sent.data.benunits['your benefit unit'].members).toEqual([ + 'you', + 'your first dependent', + ]); + expect(sent.data.households['your household']).toMatchObject({ + brma: { [HOUSEHOLD_YEAR]: 'AYLESBURY' }, + region: { [HOUSEHOLD_YEAR]: 'NORTH_EAST' }, + local_authority: { [HOUSEHOLD_YEAR]: 'ABERDEENSHIRE' }, + }); + const result = await screen.findByRole('region', { name: 'Household impact' }); + expect( + within(result).getByRole('group', { name: 'Change in annual net income' }) + ).toHaveTextContent('+£500'); + }); + + test('given a couple and a child then all three share one benefit unit and one household', async () => { + const sent = await calculateUKHousehold('Describe a couple with a child'); + + const everyone = ['you', 'your partner', 'your first dependent']; + expect(Object.keys(sent.data.people)).toEqual(everyone); + expect(sent.data.benunits['your benefit unit'].members).toEqual(everyone); + expect(sent.data.households['your household'].members).toEqual(everyone); + }); + + test('given an input on the benefit unit then it is sent on the benefit unit', async () => { + const sent = await calculateUKHousehold('Describe a couple with a child'); + + expect(sent).toEqual(UK_COUPLE_WITH_BENEFIT_UNIT_INPUT.toV1CreationPayload()); + expect(sent.data.benunits['your benefit unit'].child_benefit_opts_out).toEqual({ + [HOUSEHOLD_YEAR]: true, + }); + expect(sent.data.households['your household'].child_benefit_opts_out).toBeUndefined(); + }); + + test("given a UK household then it runs under the report's two policies, in pounds", async () => { + await calculateUKHousehold('Describe a couple with a child'); + + await waitFor(() => + expect(mockFetchHouseholdCalculation.mock.calls).toEqual([ + ['uk', String(CREATED_HOUSEHOLD_ID), BASELINE_POLICY_ID], + ['uk', String(CREATED_HOUSEHOLD_ID), REFORM_POLICY_ID], + ]) + ); + const result = await screen.findByRole('region', { name: 'Household impact' }); + expect(within(result).getByRole('table', { name: 'Taxes and benefits' })).toHaveTextContent( + 'Income Tax' + ); + expect(result).not.toHaveTextContent('$'); + }); +}); From eb6fba31e5e8be0a090e524246f6dd0768f3480d Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:23:49 +0400 Subject: [PATCH 6/8] Remove the options that hid the SPM choice and the variation footnote HouseholdBuilderForm's hideSpmGeography and the earnings variation and marginal tax rate pages' hideSpmFootnote existed only so v3 could hide them; since v3 shows both, nothing passes them. They are removed, so every builder shows the SPM geography choice (for a US household) and every variation chart its methodology footnote. Tests first: the builder test that hid the SPM choice now passes the option under @ts-expect-error and expects the choice to show; it failed (the choice was hidden, and tsc reported the directive as unused). The breakdowns test that checked v3 did not pass hideSpmFootnote is removed with the option. Co-Authored-By: Claude Opus 5.5 --- .../household/HouseholdBuilderForm.tsx | 21 ++--- .../EarningsVariationSubPage.tsx | 13 +--- .../MarginalTaxRatesSubPage.tsx | 13 +--- .../report/HouseholdBreakdownsSPM.test.tsx | 78 ------------------- .../report/HouseholdImpactSection.test.tsx | 17 ++-- .../household/HouseholdBuilderForm.test.tsx | 6 +- 6 files changed, 26 insertions(+), 122 deletions(-) delete mode 100644 app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx diff --git a/app/src/components/household/HouseholdBuilderForm.tsx b/app/src/components/household/HouseholdBuilderForm.tsx index b394f1fb0..fe157948f 100644 --- a/app/src/components/household/HouseholdBuilderForm.tsx +++ b/app/src/components/household/HouseholdBuilderForm.tsx @@ -59,8 +59,6 @@ export interface HouseholdBuilderFormProps { onNumChildrenChange: (num: number) => void; disabled?: boolean; isReadOnly?: boolean; - /** Leave out the SPM geography choice; the household keeps its own. */ - hideSpmGeography?: boolean; /** "columns" sets the sections side by side on a wide screen; stacked by default. */ layout?: 'stacked' | 'columns'; /** Variables to list first in the variable search, in order, under their own heading. */ @@ -259,7 +257,6 @@ export default function HouseholdBuilderForm({ onChange, onMaritalStatusChange, onNumChildrenChange, - hideSpmGeography = false, layout = 'stacked', suggestedVariables, suggestedLabel = 'Suggested', @@ -609,16 +606,14 @@ export default function HouseholdBuilderForm({ : undefined } > - {!hideSpmGeography && ( - - )} + {/* Floating notification when variable added to different entity */} {warningMessage && (
ReactNode; - /** Leave out the SPM methodology footnote under the chart; shown by default. */ - hideSpmFootnote?: boolean; } /** @@ -59,7 +57,6 @@ export default function EarningsVariationSubPage({ baselineVariation: providedBaselineVariation = null, reformVariation: providedReformVariation = null, renderLoading, - hideSpmFootnote = false, }: Props) { const [selectedVariable, setSelectedVariable] = useState('household_net_income'); const [searchOpen, setSearchOpen] = useState(false); @@ -387,12 +384,10 @@ export default function EarningsVariationSubPage({ year={normalizedReportYear} /> )} - {!hideSpmFootnote && ( - - )} + ); } diff --git a/app/src/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage.tsx b/app/src/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage.tsx index 867b6ac30..626ed2d1e 100644 --- a/app/src/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage.tsx +++ b/app/src/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage.tsx @@ -57,8 +57,6 @@ interface Props { reformVariation?: HouseholdCalculationOutput | null; /** What shows while the variation calculates; a spinner and a line by default. */ renderLoading?: () => ReactNode; - /** Leave out the SPM methodology footnote under the chart; shown by default. */ - hideSpmFootnote?: boolean; /** Name the country's currency in the explainer ("next pound" in the UK); "dollar" by default. */ currencyAwareText?: boolean; } @@ -110,7 +108,6 @@ export default function MarginalTaxRatesSubPage({ baselineVariation: providedBaselineVariation = null, reformVariation: providedReformVariation = null, renderLoading, - hideSpmFootnote = false, currencyAwareText = false, }: Props) { const [viewMode, setViewMode] = useState('both'); @@ -514,12 +511,10 @@ export default function MarginalTaxRatesSubPage({ {renderChart()}
- {!hideSpmFootnote && ( - - )} + ); } diff --git a/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx deleted file mode 100644 index c2d26de33..000000000 --- a/app/src/tests/unit/components/flagship/report/HouseholdBreakdownsSPM.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { render, screen } from '@test-utils'; -import { beforeEach, describe, expect, test, vi } from 'vitest'; -import HouseholdBreakdowns, { - type HouseholdBreakdownSource, - type HouseholdBreakdownView, -} from '@/components/flagship/report/household/HouseholdBreakdowns'; -import { - BASELINE_POLICY_ID, - BASELINE_RESULT, - CREATED_HOUSEHOLD_ID, - HOUSEHOLD_YEAR, - REFORM_POLICY_ID, - REFORM_RESULT, -} from '@/tests/fixtures/libs/flagship/householdRunMocks'; - -vi.mock('@/api/policy', () => ({ - fetchPolicyById: async (_country: string, policyId: string) => ({ - id: policyId, - country_id: 'us', - api_version: '1', - policy_json: {}, - label: null, - }), -})); -vi.mock('@/hooks/useParameterMetadata', () => ({ - useParameterMetadata: () => ({ parameters: {}, isReady: true }), -})); -// The v2 variation pages, tested on their own: stand-ins show whether the -// SPM methodology footnote under their chart is left out. -vi.mock('@/pages/report-output/earnings-variation/EarningsVariationSubPage', () => ({ - default: ({ hideSpmFootnote }: { hideSpmFootnote?: boolean }) => ( -
{hideSpmFootnote ? 'footnote hidden' : 'footnote'}
- ), -})); -vi.mock('@/pages/report-output/marginal-tax-rates/MarginalTaxRatesSubPage', () => ({ - default: ({ hideSpmFootnote }: { hideSpmFootnote?: boolean }) => ( -
{hideSpmFootnote ? 'footnote hidden' : 'footnote'}
- ), -})); - -const SOURCE: HouseholdBreakdownSource = { - countryId: 'us', - year: HOUSEHOLD_YEAR, - householdId: String(CREATED_HOUSEHOLD_ID), - baselinePolicyId: BASELINE_POLICY_ID, - reformPolicyId: REFORM_POLICY_ID, - baseline: BASELINE_RESULT, - reform: REFORM_RESULT, - gains: true, -}; - -function renderBreakdowns(view: HouseholdBreakdownView) { - const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - return render( - - - - ); -} - -describe('HouseholdBreakdowns SPM methodology', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - test.each([ - ['earnings-variation', 'Earnings variation'], - ['marginal-tax-rates', 'Marginal tax rates'], - ] as const)( - 'given %s then the page keeps its SPM methodology footnote', - async (view, pageName) => { - renderBreakdowns(view); - - expect(await screen.findByLabelText(pageName)).toHaveTextContent(/^footnote$/); - } - ); -}); diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx index 4608be956..8115a3086 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx @@ -68,7 +68,7 @@ vi.mock('@/components/household/HouseholdBuilderForm', async () => { }; }); -/** The builder's stand-in: the real SPM choice (unless hidden), and the builder's own calls. */ +/** The builder's stand-in: the real SPM choice, as the builder shows it, and the builder's own calls. */ function HouseholdBuilderStandIn({ household, year, @@ -80,7 +80,6 @@ function HouseholdBuilderStandIn({ variablePickerCommon, personDisplayNameOf, labelInputs, - hideSpmGeography, SPMGeographyChoice, spmMetadata, }: any) { @@ -98,14 +97,12 @@ function HouseholdBuilderStandIn({ ) .join(' ')} > - {!hideSpmGeography && ( - - )} + {`${maritalStatus} · ${numChildren}`} {String( diff --git a/app/src/tests/unit/components/household/HouseholdBuilderForm.test.tsx b/app/src/tests/unit/components/household/HouseholdBuilderForm.test.tsx index 50d7a7a6a..4f75ea5f9 100644 --- a/app/src/tests/unit/components/household/HouseholdBuilderForm.test.tsx +++ b/app/src/tests/unit/components/household/HouseholdBuilderForm.test.tsx @@ -40,11 +40,11 @@ describe('HouseholdBuilderForm', () => { expect(screen.getByRole('separator', { hidden: true })).toBeInTheDocument(); }); - test('given hideSpmGeography then the SPM choice is left out', () => { + test('given a request to hide the SPM choice then there is no such option, and the choice shows', () => { + // @ts-expect-error Every builder shows the SPM choice; it cannot be hidden. renderForm({ hideSpmGeography: true }); - expect(screen.queryByText('Supplemental Poverty Measure geography')).not.toBeInTheDocument(); - expect(screen.getByText('Marital status')).toBeInTheDocument(); + expect(screen.getByText('Supplemental Poverty Measure geography')).toBeInTheDocument(); }); test('given the columns layout then the number of children is typed, any whole number to 10', () => { From 5d3752a33b33c0ecbf996e3fb69f3281c16a0b31 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:25:24 +0400 Subject: [PATCH 7/8] v3 household results: test where the SPM methodology shows, and say who reads the bundle Tests for two rules of the SPM methodology footnote that had none: - When the report's baseline and reform are the same policy, the section calculates once and shows that calculation's methodology once, with no reform entry. - HouseholdImpactResult shows the point calculation's methodology under the summary and the net income breakdown, and not under varying earnings or marginal tax rates, which show their own. The comment on FlagshipHouseholdRun.provenance said Reproduce in Python reads it; v3 does not offer that yet, so it now says the .py bundle is kept for it. The tests check behavior the code already has, so they could not fail first. Each was confirmed to fail under a temporary change: passing the reform's provenance when one calculation served both policies failed the first; showing the footnote under every view failed the two variation cases; showing it under the summary alone failed the net income case. Co-Authored-By: Claude Opus 5.5 --- app/src/libs/flagship/householdRun.ts | 6 +- .../report/HouseholdImpactResult.test.tsx | 73 ++++++++++++++++++- .../report/HouseholdImpactSection.test.tsx | 23 +++++- 3 files changed, 96 insertions(+), 6 deletions(-) diff --git a/app/src/libs/flagship/householdRun.ts b/app/src/libs/flagship/householdRun.ts index 369233c66..e68cc805e 100644 --- a/app/src/libs/flagship/householdRun.ts +++ b/app/src/libs/flagship/householdRun.ts @@ -330,8 +330,10 @@ export interface FlagshipHouseholdRun extends FlagshipHouseholdImpact { baseline: HouseholdCalculationData; reform: HouseholdCalculationData; /** - * How each policy's calculation was made, as its response says: the - * methodology footnote and Reproduce in Python read it. + * How each policy's calculation was made, as its response says. The + * SPM methodology footnote reads the SPM settings and receipt; the .py + * bundle is kept so Reproduce in Python can name the versions that + * calculated the result, which v3 does not offer yet. */ provenance: { baseline: HouseholdCalculationProvenance; reform: HouseholdCalculationProvenance }; } diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx index c2ae2d275..a5b626b75 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactResult.test.tsx @@ -1,16 +1,64 @@ import type { ComponentProps } from 'react'; -import { render, screen } from '@test-utils'; -import { describe, expect, test } from 'vitest'; +import { render, screen, userEvent } from '@test-utils'; +import { describe, expect, test, vi } from 'vitest'; import HouseholdImpactResult from '@/components/flagship/report/household/HouseholdImpactResult'; import { compareHouseholds } from '@/libs/flagship/householdRun'; import { + BASELINE_POLICY_ID, + BASELINE_RESULT, + CREATED_HOUSEHOLD_ID, HOUSEHOLD_YEAR, + NATIONAL_SPM_CONFIG, + REFORM_POLICY_ID, + REFORM_RESULT, UK_BASELINE_RESULT, UK_REFORM_RESULT, } from '@/tests/fixtures/libs/flagship/householdRunMocks'; +import { SPM_RECEIPT } from '@/tests/fixtures/spm/spmMocks'; + +// The v2 breakdowns, tested on their own: a stand-in names the one shown. +vi.mock('@/components/flagship/report/household/HouseholdBreakdowns', async (importOriginal) => ({ + ...(await importOriginal< + typeof import('@/components/flagship/report/household/HouseholdBreakdowns') + >()), + default: ({ view }: { view: string }) =>
{view}
, +})); const UK_IMPACT = compareHouseholds(UK_BASELINE_RESULT, UK_REFORM_RESULT, HOUSEHOLD_YEAR, 'uk'); const UK_SUMMARY = 'Single · no children · £30,000 earnings · 2026'; +const US_IMPACT = compareHouseholds(BASELINE_RESULT, REFORM_RESULT, HOUSEHOLD_YEAR, 'us'); +const NATIONAL_THRESHOLDS_PROVENANCE = { + policyengineBundle: null, + spmConfig: NATIONAL_SPM_CONFIG, + spmProvenance: SPM_RECEIPT, +}; + +function renderUSResultWithMethodology() { + render( + + ); +} + +const pointMethodology = () => + screen.queryByRole('region', { name: 'SPM point calculation methodology' }); describe('HouseholdImpactResult', () => { test('given no country then the props do not type-check, so no result reads as the US', () => { @@ -30,4 +78,25 @@ describe('HouseholdImpactResult', () => { '£' ); }); + + test('given SPM provenance then the methodology shows under the summary and the net income breakdown', async () => { + const user = userEvent.setup(); + renderUSResultWithMethodology(); + + expect(pointMethodology()).toBeInTheDocument(); + await user.click(screen.getByRole('tab', { name: 'Net income' })); + expect(pointMethodology()).toBeInTheDocument(); + }); + + test.each(['Varying earnings', 'Marginal tax rates'])( + 'given SPM provenance then %s shows its own variation methodology, not the point calculation', + async (tabName) => { + const user = userEvent.setup(); + renderUSResultWithMethodology(); + + await user.click(screen.getByRole('tab', { name: tabName })); + + expect(pointMethodology()).not.toBeInTheDocument(); + } + ); }); diff --git a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx index 8115a3086..b970d4ad3 100644 --- a/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx +++ b/app/src/tests/unit/components/flagship/report/HouseholdImpactSection.test.tsx @@ -155,7 +155,8 @@ function renderSection({ region, countryId = 'us', added = true, -}: { region?: string; countryId?: string; added?: boolean } = {}) { + reformPolicyId = REFORM_POLICY_ID, +}: { region?: string; countryId?: string; added?: boolean; reformPolicyId?: string } = {}) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); const view = render( @@ -164,7 +165,7 @@ function renderSection({ year={HOUSEHOLD_YEAR} region={region} baselinePolicyId={BASELINE_POLICY_ID} - reformPolicyId={REFORM_POLICY_ID} + reformPolicyId={reformPolicyId} /> ); @@ -329,6 +330,24 @@ describe('HouseholdImpactSection', () => { expect(methodology).toHaveTextContent('Reform · Point calculation · National thresholds'); }); + test('given the same policy as baseline and reform then its one calculation shows its methodology once', async () => { + const user = userEvent.setup(); + mockCalculationProvenance.mockReturnValue({ + spm_config: NATIONAL_SPM_CONFIG, + spm_provenance: SPM_RECEIPT, + }); + renderSection({ reformPolicyId: BASELINE_POLICY_ID }); + + await user.click(screen.getByRole('button', { name: 'Calculate' })); + + const result = await screen.findByRole('region', { name: 'Household impact' }); + const methodology = within(result).getByRole('region', { + name: 'SPM point calculation methodology', + }); + expect(methodology).toHaveTextContent('Baseline · Point calculation · National thresholds'); + expect(methodology).not.toHaveTextContent('Reform ·'); + }); + test('given a calculation without SPM provenance then no methodology shows', async () => { const user = userEvent.setup(); renderSection(); From 425a7ee74714830afc6283af174fae21199767f3 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:26:56 +0400 Subject: [PATCH 8/8] Household suggestion helpers: every caller names its country suggestedHouseholdInputs and describesAHousehold still defaulted their country to "us", the fallback this PR removes elsewhere. Both now take the household's country as a required argument; suggestedHouseholdInputs takes its limit and the inputs it lists first as named options instead of positional ones, so a caller never passes undefined to reach the country. The section, their only caller, already passed it. Tests first: a call of each without a country, under @ts-expect-error, failed the type check while the default existed; the existing tests now name the country, and four of them failed at runtime against the old argument order. Co-Authored-By: Claude Opus 5.5 --- .../household/HouseholdImpactSection.tsx | 9 +---- app/src/libs/flagship/householdSuggestions.ts | 14 +++++-- .../libs/flagship/householdVariableGroups.ts | 2 +- .../flagship/householdSuggestions.test.ts | 30 ++++++++------ .../flagship/householdVariableGroups.test.ts | 39 ++++++++++++------- 5 files changed, 57 insertions(+), 37 deletions(-) diff --git a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx index 6f366092c..127d7e251 100644 --- a/app/src/components/flagship/report/household/HouseholdImpactSection.tsx +++ b/app/src/components/flagship/report/household/HouseholdImpactSection.tsx @@ -221,14 +221,7 @@ function useSuggestedInputs(paths: string[], countryId: string): string[] { .then((map) => { if (!cancelled) { setSuggested( - suggestedHouseholdInputs( - pathsKey.split('\n'), - map, - inputs, - undefined, - preferred, - countryId - ) + suggestedHouseholdInputs(pathsKey.split('\n'), map, inputs, countryId, { preferred }) ); } }) diff --git a/app/src/libs/flagship/householdSuggestions.ts b/app/src/libs/flagship/householdSuggestions.ts index 8d5731e4a..184a482ef 100644 --- a/app/src/libs/flagship/householdSuggestions.ts +++ b/app/src/libs/flagship/householdSuggestions.ts @@ -37,10 +37,16 @@ export function suggestedHouseholdInputs( paths: string[], map: ParameterDependencyMap, inputs: Set, - limit: number = DEFAULT_LIMIT, - /** Inputs a household commonly has, listed ahead of the rest at any distance. */ - preferred: ReadonlySet = new Set(), - countryId: string = 'us' + /** The household's country: a US state program's own input is left out only in the US. */ + countryId: string, + { + limit = DEFAULT_LIMIT, + preferred = new Set(), + }: { + limit?: number; + /** Inputs a household commonly has, listed ahead of the rest at any distance. */ + preferred?: ReadonlySet; + } = {} ): string[] { // The variables the parameters feed, and the ones one step on. const start = variablesReachedByPaths(paths, map, 1).map((entry) => entry.variable); diff --git a/app/src/libs/flagship/householdVariableGroups.ts b/app/src/libs/flagship/householdVariableGroups.ts index 329b3705f..a875a2d43 100644 --- a/app/src/libs/flagship/householdVariableGroups.ts +++ b/app/src/libs/flagship/householdVariableGroups.ts @@ -181,7 +181,7 @@ const UK_DATASET_INPUTS = new RegExp( * microdata sets (ids, weights, take-up draws), inputs only the UK * dataset supplies, and inputs the model keeps only for old API callers. */ -export function describesAHousehold(variable: VariableInfo, countryId: string = 'us'): boolean { +export function describesAHousehold(variable: VariableInfo, countryId: string): boolean { const module = variable.moduleName ?? ''; return !( /^deprecated/i.test(variable.documentation ?? '') || diff --git a/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts b/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts index 511f11c79..ce2edb100 100644 --- a/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts +++ b/app/src/tests/unit/libs/flagship/householdSuggestions.test.ts @@ -45,13 +45,13 @@ const INPUTS = new Set([ describe('suggestedHouseholdInputs', () => { test('given a SNAP parameter then the inputs SNAP reads lead, nearest first', () => { expect( - suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS) + suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS, 'us') ).toEqual(['childcare_expenses', 'employment_income', 'rent']); }); test('given inputs only downstream of the change then they are not suggested', () => { expect( - suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS) + suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS, 'us') ).not.toContain('capital_gains'); }); @@ -59,7 +59,8 @@ describe('suggestedHouseholdInputs', () => { const suggested = suggestedHouseholdInputs( ['gov.usda.snap.emergency_allotment.allowed'], MAP, - INPUTS + INPUTS, + 'us' ); expect(suggested).not.toContain('household_weight'); expect(suggested).not.toContain('snap_reported'); @@ -67,7 +68,7 @@ describe('suggestedHouseholdInputs', () => { }); test('given a parameter the map does not trace then nothing is suggested', () => { - expect(suggestedHouseholdInputs(['gov.irs.unknown'], MAP, INPUTS)).toEqual([]); + expect(suggestedHouseholdInputs(['gov.irs.unknown'], MAP, INPUTS, 'us')).toEqual([]); }); test('given a state prefix then it is a US state program only in the US', () => { @@ -78,19 +79,28 @@ describe('suggestedHouseholdInputs', () => { const inputs = new Set([...INPUTS, 'in_FE']); const paths = ['gov.usda.snap.emergency_allotment.allowed']; - expect(suggestedHouseholdInputs(paths, map, inputs)).not.toContain('in_FE'); - expect(suggestedHouseholdInputs(paths, map, inputs, undefined, new Set(), 'uk')).toContain( - 'in_FE' - ); + expect(suggestedHouseholdInputs(paths, map, inputs, 'us')).not.toContain('in_FE'); + expect(suggestedHouseholdInputs(paths, map, inputs, 'uk')).toContain('in_FE'); }); test('given a limit then only that many are suggested', () => { expect( - suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS, 1) + suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS, 'us', { + limit: 1, + }) ).toEqual(['childcare_expenses']); }); }); +describe('suggestedHouseholdInputs without a country', () => { + test('given no country then the call does not type-check, so no country is read as the US', () => { + expect( + // @ts-expect-error Every caller names the household's country. + suggestedHouseholdInputs(['gov.usda.snap.emergency_allotment.allowed'], MAP, INPUTS) + ).toBeDefined(); + }); +}); + describe('suggestedHouseholdInputs for the UK', () => { test("given a change to Universal Credit's child element then a child's age and childcare lead", async () => { const map = await loadParameterDependencies('uk'); @@ -99,8 +109,6 @@ describe('suggestedHouseholdInputs for the UK', () => { [UK_CHILD_ELEMENT_PATH], map, UK_HOUSEHOLD_INPUT_NAMES, - undefined, - new Set(), 'uk' ); diff --git a/app/src/tests/unit/libs/flagship/householdVariableGroups.test.ts b/app/src/tests/unit/libs/flagship/householdVariableGroups.test.ts index 3429a1e8b..6f12d2cc5 100644 --- a/app/src/tests/unit/libs/flagship/householdVariableGroups.test.ts +++ b/app/src/tests/unit/libs/flagship/householdVariableGroups.test.ts @@ -78,37 +78,50 @@ describe('describesAHousehold', () => { const input = (name: string, moduleName: string, documentation?: string) => ({ name, label: name, moduleName, documentation }) as VariableInfo; + test('given no country then the call does not type-check, so no country is read as the US', () => { + // @ts-expect-error Every caller names the household's country. + expect(describesAHousehold(input('rent', 'household.expense.housing.rent'))).toBe(true); + }); + test('given survey totals, incidence inputs or deprecated inputs then they are left out', () => { expect( - describesAHousehold(input('spm_unit_snap', 'household.income.spm_unit.spm_unit_snap')) + describesAHousehold(input('spm_unit_snap', 'household.income.spm_unit.spm_unit_snap'), 'us') ).toBe(false); expect( - describesAHousehold(input('cbo_excise_tax', 'household.income.household.cbo_excise_tax')) + describesAHousehold( + input('cbo_excise_tax', 'household.income.household.cbo_excise_tax'), + 'us' + ) + ).toBe(false); + expect( + describesAHousehold(input('ssi_reported', 'household.income.person.ssi_reported'), 'us') ).toBe(false); - expect(describesAHousehold(input('ssi_reported', 'household.income.person.ssi_reported'))).toBe( - false - ); expect( describesAHousehold( - input('use_reported_ssi', 'household.income.person.use_reported_ssi', 'Deprecated: …') + input('use_reported_ssi', 'household.income.person.use_reported_ssi', 'Deprecated: …'), + 'us' ) ).toBe(false); }); test('given what a household is described by then it is kept', () => { - expect(describesAHousehold(input('rent', 'household.expense.housing.rent'))).toBe(true); - expect(describesAHousehold(input('tip_income', 'household.income.person.tip_income'))).toBe( - true - ); + expect(describesAHousehold(input('rent', 'household.expense.housing.rent'), 'us')).toBe(true); + expect( + describesAHousehold(input('tip_income', 'household.income.person.tip_income'), 'us') + ).toBe(true); }); test('given US ids and weights then they are left out, and the US inputs beside them kept', () => { expect( - describesAHousehold(input('tax_unit_id', 'household.demographic.identifiers.tax_unit_id')) + describesAHousehold( + input('tax_unit_id', 'household.demographic.identifiers.tax_unit_id'), + 'us' + ) ).toBe(false); expect( describesAHousehold( - input('household_weight', 'household.demographic.weights.household_weight') + input('household_weight', 'household.demographic.weights.household_weight'), + 'us' ) ).toBe(false); for (const [name, moduleName] of [ @@ -118,7 +131,7 @@ describe('describesAHousehold', () => { ['state_name', 'household.demographic.geographic.state.state_name'], ['pension_income', 'household.income.person.pension_income'], ]) { - expect(describesAHousehold(input(name, moduleName))).toBe(true); + expect(describesAHousehold(input(name, moduleName), 'us')).toBe(true); } });