From d93bb3c403092ebe0b812398590e93127371a06b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 05:25:35 -0500 Subject: [PATCH 01/11] Limit Alaska's dividend to the statuses the statute names, add a take-up input The microsimulation pays the dividend to every Alaska resident: 756,512 people for 2027, against 618,863 applications Alaska paid for 2025. AS 43.23.005(a)(5) limits the dividend to citizens, lawful permanent residents, refugees and asylees, so an eligibility variable now applies that test. The statute's other tests (an application, residence during all of the prior year, absences, felony bars) have no PolicyEngine inputs, and Core forbids formula-time draws. A boolean input that defaults to true, like the other programs' take-up flags, lets microdata or a user say Alaska did not pay a person. Household calculations keep paying each eligible resident. Energy relief shares the gate because Alaska pays it as part of the dividend. Co-Authored-By: Claude Opus 5.5 --- changelog.d/ak-pfd-receipt.added.md | 1 + docs/validation/alaska-pfd.md | 32 +++- ...ividend_eligible_immigration_statuses.yaml | 25 +++ .../gov/states/ak/dor/ak_energy_relief.yaml | 46 ++++++ .../ak/dor/ak_permanent_fund_dividend.yaml | 105 +++++++++++++ .../ak_permanent_fund_dividend_eligible.yaml | 81 ++++++++++ ...test_ak_permanent_fund_dividend_receipt.py | 148 ++++++++++++++++++ .../gov/states/ak/dor/ak_energy_relief.py | 16 +- .../ak/dor/ak_permanent_fund_dividend.py | 17 +- .../ak_permanent_fund_dividend_eligible.py | 27 ++++ ..._ak_permanent_fund_dividend_if_eligible.py | 18 +++ 11 files changed, 500 insertions(+), 16 deletions(-) create mode 100644 changelog.d/ak-pfd-receipt.added.md create mode 100644 policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml create mode 100644 policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py create mode 100644 policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py create mode 100644 policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py diff --git a/changelog.d/ak-pfd-receipt.added.md b/changelog.d/ak-pfd-receipt.added.md new file mode 100644 index 00000000000..04b8cfe1262 --- /dev/null +++ b/changelog.d/ak-pfd-receipt.added.md @@ -0,0 +1 @@ +Limit the Alaska Permanent Fund Dividend and energy relief to the immigration statuses AS 43.23.005(a)(5) names, and add a default-true take-up input for residents Alaska does not pay. diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index d2b8982deee..a6a0656203a 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -1,11 +1,13 @@ # Alaska Permanent Fund Dividend in model comparisons PolicyEngine automatically calculates `ak_permanent_fund_dividend` and -`ak_energy_relief` for each person whose household is in Alaska. These amounts -are calculated benefits, rather than imputed income or fixed default inputs. -Current Alaska residence is an eligibility proxy: the model does not check an -application, prior-year residence, or actual receipt. Supplying wages and other -known income does not disable either calculation. +`ak_energy_relief` for each person who lives in Alaska, holds an immigration +status the dividend statute names, and takes up the dividend. These amounts are +calculated benefits, rather than imputed income or fixed default inputs. The +model has no inputs for the statute's other tests: an application, residence +during all of the prior calendar year, allowable absences, or a felony sentence +or incarceration. Supplying wages and other known income does not disable either +calculation. The regular PFD is included in federal gross income for nondependent people. A dependent child's PFD is not automatically added to the parent's adjusted @@ -14,6 +16,26 @@ status; supply explicit roles or dependent status when constructing a household. The regular dividend appears on [2022 Schedule 1, line 8g](https://www.irs.gov/pub/irs-prior/f1040s1--2022.pdf#page=1). +## Who receives the dividend + +`ak_permanent_fund_dividend_eligible` applies the status test in [AS +43.23.005(a)(5)](https://www.akleg.gov/basis/statutes.asp#43.23.005): a citizen, a +lawful permanent resident, a refugee, or an asylee. [15 AAC +23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154) also counts conditional +permanent residents and people on indefinite parole as lawfully admitted for +permanent residence. PolicyEngine has no status for either, so code them as +`LEGAL_PERMANENT_RESIDENT`. `PAROLED_ONE_YEAR`, `CUBAN_HAITIAN_ENTRANT` and +`CONDITIONAL_ENTRANT` do not say whether a parole runs indefinitely or whether +the person may adopt the United States as their domicile, so they stay out. + +`takes_up_ak_permanent_fund_dividend_if_eligible` covers the statute's other +tests and anyone who does not apply. It defaults to true, so a household +calculation pays every eligible Alaska resident unless an input says otherwise. +Set it to false for a person Alaska did not pay. Both variables gate +`ak_energy_relief` as well, because Alaska pays energy relief as part of the +dividend ([sec. 13, ch. 34, SLA +2026](https://www.akleg.gov/PDF/34/Bills/HB0263Z.PDF#page=84)). + ## Override calculated payments Supply either variable as an input on the person to replace its calculated diff --git a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml new file mode 100644 index 00000000000..bee353fca15 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml @@ -0,0 +1,25 @@ +description: Alaska limits the Permanent Fund Dividend to people with these immigration statuses. + +values: + # 15 AAC 23.154(a) counts conditional permanent residents and aliens on + # indefinite parole as lawfully admitted for permanent residence. + # PolicyEngine has no status for either, so code them as + # LEGAL_PERMANENT_RESIDENT. PAROLED_ONE_YEAR stays out because it does not + # say whether a parole is indefinite. + 2015-01-01: + - CITIZEN + - LEGAL_PERMANENT_RESIDENT + - REFUGEE + - ASYLEE + +metadata: + unit: list + period: year + label: Alaska Permanent Fund Dividend eligible immigration statuses + reference: + - title: AS 43.23.005(a)(5), Eligibility + href: https://www.akleg.gov/basis/statutes.asp#43.23.005 + - title: 15 AAC 23.154, Eligibility of aliens + href: https://www.akleg.gov/basis/aac.asp#15.23.154 + - title: Alaska Permanent Fund Dividend Division, Immigration - Alien Status and Eligibility for the PFD + href: https://pfd.alaska.gov/eligibility/immigration diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml index 15b1baed973..92792d2c7b8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml @@ -82,3 +82,49 @@ state_code: AK output: ak_energy_relief: 0 + +- name: Case 13, a resident with no status input gets the 2026 energy relief, since the status defaults to citizen and take-up to true. + period: 2026 + input: + state_code: AK + output: + ak_energy_relief: 200 + +- name: Case 14, an undocumented Alaska resident gets no energy relief, because Alaska pays it as part of the dividend. + period: 2026 + input: + state_code: AK + immigration_status: UNDOCUMENTED + output: + ak_energy_relief: 0 + +- name: Case 15, a resident who does not take up the dividend gets no energy relief. + period: 2026 + input: + state_code: AK + takes_up_ak_permanent_fund_dividend_if_eligible: false + output: + ak_energy_relief: 0 + +- name: Case 16, each person in one household gets energy relief on their own status and take-up. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + citizen: + age: 40 + undocumented_parent: + age: 36 + immigration_status: UNDOCUMENTED + non_applicant: + age: 15 + takes_up_ak_permanent_fund_dividend_if_eligible: false + asylee: + age: 22 + immigration_status: ASYLEE + households: + household: + members: [citizen, undocumented_parent, non_applicant, asylee] + state_code: AK + output: + ak_energy_relief: [298.17, 0, 0, 298.17] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml index c27d49c6925..eb5b1096a1a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml @@ -122,3 +122,108 @@ ak_energy_relief: 0 irs_gross_income: 51_000 adjusted_gross_income: 51_000 + +- name: Case 15, a resident with no status input gets the dividend, since the status defaults to citizen and take-up to true. + period: 2026 + input: + state_code: AK + output: + ak_permanent_fund_dividend_eligible: true + takes_up_ak_permanent_fund_dividend_if_eligible: true + ak_permanent_fund_dividend: 1_000 + +- name: Case 16, a lawful permanent resident gets the dividend under AS 43.23.005(a)(5)(B). + period: 2026 + input: + state_code: AK + immigration_status: LEGAL_PERMANENT_RESIDENT + output: + ak_permanent_fund_dividend: 1_000 + +- name: Case 17, a refugee gets the dividend under AS 43.23.005(a)(5)(C). + period: 2026 + input: + state_code: AK + immigration_status: REFUGEE + output: + ak_permanent_fund_dividend: 1_000 + +- name: Case 18, an asylee gets the dividend under AS 43.23.005(a)(5)(D). + period: 2026 + input: + state_code: AK + immigration_status: ASYLEE + output: + ak_permanent_fund_dividend: 1_000 + +- name: Case 19, an undocumented Alaska resident gets no dividend. + period: 2026 + input: + state_code: AK + immigration_status: UNDOCUMENTED + output: + ak_permanent_fund_dividend_eligible: false + ak_permanent_fund_dividend: 0 + +- name: Case 20, a resident who does not take up the dividend gets none, though eligible. + period: 2026 + input: + state_code: AK + takes_up_ak_permanent_fund_dividend_if_eligible: false + output: + ak_permanent_fund_dividend_eligible: true + ak_permanent_fund_dividend: 0 + +- name: Case 21, each person in one household gets the dividend on their own status and take-up. + period: 2026 + input: + people: + citizen: + age: 40 + permanent_resident: + age: 38 + immigration_status: LEGAL_PERMANENT_RESIDENT + undocumented_parent: + age: 36 + immigration_status: UNDOCUMENTED + non_applicant: + age: 15 + takes_up_ak_permanent_fund_dividend_if_eligible: false + child: + age: 5 + households: + household: + members: [citizen, permanent_resident, undocumented_parent, non_applicant, child] + state_code: AK + output: + ak_permanent_fund_dividend: [1_000, 1_000, 0, 0, 1_000] + +- name: Case 22, a filer who does not take up the dividend has no dividend in federal gross income or AGI. + period: 2026 + input: + people: + person: + age: 40 + employment_income: 50_000 + takes_up_ak_permanent_fund_dividend_if_eligible: false + tax_units: + tax_unit: + members: [person] + households: + household: + members: [person] + state_code: AK + output: + ak_permanent_fund_dividend: 0 + ak_energy_relief: 0 + irs_gross_income: 50_000 + adjusted_gross_income: 50_000 + +- name: Case 23, a supplied dividend overrides the calculation for an ineligible person. + period: 2026 + input: + state_code: AK + immigration_status: UNDOCUMENTED + ak_permanent_fund_dividend: 1_000 + output: + ak_permanent_fund_dividend: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml new file mode 100644 index 00000000000..58492623da0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml @@ -0,0 +1,81 @@ +- name: Case 1, a citizen in Alaska is eligible under AS 43.23.005(a)(5)(A). + period: 2026 + input: + state_code: AK + immigration_status: CITIZEN + output: + ak_permanent_fund_dividend_eligible: true + +- name: Case 2, a lawful permanent resident in Alaska is eligible under AS 43.23.005(a)(5)(B). + period: 2026 + input: + state_code: AK + immigration_status: LEGAL_PERMANENT_RESIDENT + output: + ak_permanent_fund_dividend_eligible: true + +- name: Case 3, a refugee in Alaska is eligible under AS 43.23.005(a)(5)(C). + period: 2026 + input: + state_code: AK + immigration_status: REFUGEE + output: + ak_permanent_fund_dividend_eligible: true + +- name: Case 4, an asylee in Alaska is eligible under AS 43.23.005(a)(5)(D). + period: 2026 + input: + state_code: AK + immigration_status: ASYLEE + output: + ak_permanent_fund_dividend_eligible: true + +- name: Case 5, the statuses AS 43.23.005(a)(5) does not name are not eligible, including fixed-term parole. + period: 2026 + input: + people: + undocumented: + immigration_status: UNDOCUMENTED + daca: + immigration_status: DACA + tps: + immigration_status: TPS + paroled: + immigration_status: PAROLED_ONE_YEAR + deportation_withheld: + immigration_status: DEPORTATION_WITHHELD + cuban_haitian_entrant: + immigration_status: CUBAN_HAITIAN_ENTRANT + conditional_entrant: + immigration_status: CONDITIONAL_ENTRANT + households: + household: + members: + [undocumented, daca, tps, paroled, deportation_withheld, cuban_haitian_entrant, conditional_entrant] + state_code: AK + output: + ak_permanent_fund_dividend_eligible: [false, false, false, false, false, false, false] + +- name: Case 6, a citizen outside Alaska is not eligible. + period: 2026 + input: + state_code: WA + immigration_status: CITIZEN + output: + ak_permanent_fund_dividend_eligible: false + +- name: Case 7, the status test applies in 2015, PolicyEngine's first modeled dividend year. + period: 2015 + input: + people: + citizen: + immigration_status: CITIZEN + undocumented: + immigration_status: UNDOCUMENTED + households: + household: + members: [citizen, undocumented] + state_code: AK + output: + ak_permanent_fund_dividend_eligible: [true, false] + ak_permanent_fund_dividend: [2_072, 0] diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py new file mode 100644 index 00000000000..f0777e06c3d --- /dev/null +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -0,0 +1,148 @@ +"""Invariants for who receives the Alaska Permanent Fund Dividend and energy relief. + +AS 43.23.005(a)(5) limits the dividend to citizens, lawful permanent residents, +refugees and asylees, and Alaska pays energy relief as part of the dividend +(sec. 13, ch. 34, SLA 2026). PolicyEngine also lets an input say a person does +not take up the dividend, for the statute's tests it has no input for. These +properties hold for every year and every mix of people in a household: + +1. A person's dividend and energy relief each equal zero or the full amount + for the year. +2. A person gets the dividend exactly when they live in Alaska, hold a status + the statute names, and take it up. A status or take-up flag left unset + counts as citizen and true, so a household calculation pays a resident + unless an input says otherwise. +3. Energy relief passes through the same gate as the dividend. +4. Nobody outside Alaska gets either payment. +5. Turning one person's take-up off lowers that person's payment to zero and + changes nobody else's. + +ELIGIBLE_STATUSES restates AS 43.23.005(a)(5) here on purpose, so a change to +the parameter file that the statute does not support fails these tests. The +YAML tests pin examples. These tests run the real model on every status and +take-up combination, and on drawn years and household mixes, through one shared +tax-benefit system, so they build no extra model and load no dataset. +""" + +from itertools import product + +import numpy as np +import pytest +from hypothesis import given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system +from policyengine_us.tools.parameters import FIRST_MODELED_YEAR +from policyengine_us.variables.household.demographic.person.immigration_status import ( + ImmigrationStatus, +) + +# AS 43.23.005(a)(5)(A) to (D). +ELIGIBLE_STATUSES = {"CITIZEN", "LEGAL_PERMANENT_RESIDENT", "REFUGEE", "ASYLEE"} +TAKE_UP = "takes_up_ak_permanent_fund_dividend_if_eligible" +STATUSES = tuple(status.name for status in ImmigrationStatus) +# None leaves the input unset, so the model's default applies. +STATUS_INPUTS = (None, *STATUSES) +TAKE_UP_INPUTS = (None, True, False) +STATES = ("AK", "WA") +LAST_YEAR = 2100 +# 2015 starts the modeled years, 2022 and 2026 pay energy relief, and 2027 pays +# none. +EXHAUSTIVE_YEARS = (2015, 2022, 2026, 2027) + +PERSON = st.tuples( + st.sampled_from(STATES), + st.sampled_from(STATUS_INPUTS), + st.sampled_from(TAKE_UP_INPUTS), +) +PEOPLE = st.lists(PERSON, min_size=1, max_size=10) +YEARS = st.integers(min_value=FIRST_MODELED_YEAR, max_value=LAST_YEAR) + + +def amounts(year): + dor = system.parameters.gov.states.ak.dor + instant = f"{year}-01-01" + return float(dor.permanent_fund_dividend(instant)), float( + dor.energy_relief(instant) + ) + + +def calculate(year, people): + """Run the model with each state's people in one household.""" + situation_people = {} + members = {state: [] for state in STATES} + for index, (state, status, take_up) in enumerate(people): + name = f"person_{index}" + inputs = {} + if status is not None: + inputs["immigration_status"] = {year: status} + if take_up is not None: + inputs[TAKE_UP] = {year: take_up} + situation_people[name] = inputs + members[state].append(name) + situation = { + "people": situation_people, + "households": { + state: {"members": names, "state_code": {year: state}} + for state, names in members.items() + if names + }, + } + simulation = Simulation(situation=situation) + # The results follow the order of situation_people, whatever the households. + assert list(simulation.persons.ids) == list(situation_people) + return ( + np.asarray(simulation.calculate("ak_permanent_fund_dividend", year)), + np.asarray(simulation.calculate("ak_energy_relief", year)), + ) + + +def receives(state, status, take_up): + return ( + state == "AK" + and (status is None or status in ELIGIBLE_STATUSES) + and take_up is not False + ) + + +def assert_gate_holds(year, people, dividend, energy_relief): + full_dividend, full_energy_relief = amounts(year) + for person, paid, relief in zip(people, dividend, energy_relief): + expected = receives(*person) + assert paid == pytest.approx(full_dividend if expected else 0, abs=0.005) + assert relief == pytest.approx(full_energy_relief if expected else 0, abs=0.005) + + +def test_statuses_named_in_the_statute_exist_in_the_model(): + assert ELIGIBLE_STATUSES <= set(STATUSES) + + +@pytest.mark.parametrize("year", EXHAUSTIVE_YEARS) +def test_every_combination_of_state_status_and_take_up(year): + people = list(product(STATES, STATUS_INPUTS, TAKE_UP_INPUTS)) + assert_gate_holds(year, people, *calculate(year, people)) + + +@settings(max_examples=25, deadline=None) +@given(YEARS, PEOPLE) +def test_payments_follow_the_gate_for_any_year_and_household_mix(year, people): + assert_gate_holds(year, people, *calculate(year, people)) + + +@settings(max_examples=15, deadline=None) +@given(YEARS, PEOPLE, st.integers(min_value=0)) +def test_turning_take_up_off_only_removes_that_persons_payment(year, people, draw): + target = draw % len(people) + declined = list(people) + declined[target] = (*people[target][:2], False) + dividend, energy_relief = calculate(year, people) + new_dividend, new_energy_relief = calculate(year, declined) + + assert new_dividend[target] == 0 + assert new_energy_relief[target] == 0 + others = np.arange(len(people)) != target + np.testing.assert_allclose(new_dividend[others], dividend[others], atol=0.005) + np.testing.assert_allclose( + new_energy_relief[others], energy_relief[others], atol=0.005 + ) diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py index 845c9a3efc3..dce34a52847 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py @@ -6,9 +6,12 @@ class ak_energy_relief(Variable): entity = Person label = "Alaska One Time Energy Relief" documentation = ( - "Energy relief calculated automatically for each Alaska resident, " - "using current residence as an eligibility proxy without checking an " - "application, prior-year residence, or actual receipt. Supplying " + "Energy relief calculated automatically for each Alaska resident who " + "passes ak_permanent_fund_dividend_eligible and takes up the dividend " + "(takes_up_ak_permanent_fund_dividend_if_eligible, true by default), " + "since Alaska pays it as part of the dividend. The model does not " + "otherwise check an application, prior-year residence, or actual " + "receipt. Supplying " "other income does not disable the calculation. Override this " "person-level variable directly; excluding both Alaska transfers " "requires also overriding ak_permanent_fund_dividend for each person. " @@ -21,6 +24,9 @@ class ak_energy_relief(Variable): unit = USD definition_period = YEAR reference = "https://pfd.alaska.gov" - defined_for = StateCode.AK + defined_for = "ak_permanent_fund_dividend_eligible" - adds = ["gov.states.ak.dor.energy_relief"] + def formula(person, period, parameters): + p = parameters(period).gov.states.ak.dor + takes_up = person("takes_up_ak_permanent_fund_dividend_if_eligible", period) + return p.energy_relief * takes_up diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py index b07a5fa303d..8ccd4a8b254 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py @@ -6,10 +6,12 @@ class ak_permanent_fund_dividend(Variable): entity = Person label = "Alaska Permanent Fund Dividend" documentation = ( - "Regular PFD calculated automatically for each Alaska resident, using " - "current residence as an eligibility proxy without checking an " - "application, prior-year residence, or actual receipt. Supplying wages " - "or other income does not disable this calculation. Supply this " + "Regular PFD calculated automatically for each Alaska resident who " + "passes ak_permanent_fund_dividend_eligible and takes up the dividend " + "(takes_up_ak_permanent_fund_dividend_if_eligible, true by default). " + "The model does not otherwise check an application, prior-year " + "residence, or actual receipt. Supplying wages or other income does " + "not disable this calculation. Supply this " "person-level variable to override it; to exclude both Alaska " "transfers, also supply ak_energy_relief as zero for every person. " "A dependent child's PFD is not automatically included in a parent's " @@ -20,6 +22,9 @@ class ak_permanent_fund_dividend(Variable): unit = USD definition_period = YEAR reference = "https://pfd.alaska.gov" - defined_for = StateCode.AK + defined_for = "ak_permanent_fund_dividend_eligible" - adds = ["gov.states.ak.dor.permanent_fund_dividend"] + def formula(person, period, parameters): + p = parameters(period).gov.states.ak.dor + takes_up = person("takes_up_ak_permanent_fund_dividend_if_eligible", period) + return p.permanent_fund_dividend * takes_up diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py new file mode 100644 index 00000000000..fdb91cbc247 --- /dev/null +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class ak_permanent_fund_dividend_eligible(Variable): + value_type = bool + entity = Person + label = "Alaska Permanent Fund Dividend eligible" + documentation = ( + "Applies the immigration status test in AS 43.23.005(a)(5). PolicyEngine " + "has no inputs for the statute's other tests: an application, residence " + "during all of the qualifying year, allowable absences, or a felony " + "sentence or incarceration during that year. To model a person who " + "fails one of those, set " + "takes_up_ak_permanent_fund_dividend_if_eligible to false. See " + "docs/validation/alaska-pfd.md." + ) + definition_period = YEAR + reference = "https://www.akleg.gov/basis/statutes.asp#43.23.005" + defined_for = StateCode.AK + + def formula(person, period, parameters): + p = parameters(period).gov.states.ak.dor + immigration_status = person("immigration_status", period) + return np.isin( + immigration_status.decode_to_str(), + p.permanent_fund_dividend_eligible_immigration_statuses, + ) diff --git a/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py new file mode 100644 index 00000000000..dc2035c16b6 --- /dev/null +++ b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class takes_up_ak_permanent_fund_dividend_if_eligible(Variable): + value_type = bool + entity = Person + label = "Takes up the Alaska Permanent Fund Dividend if eligible" + documentation = ( + "Whether Alaska pays this person the Permanent Fund Dividend and any " + "energy relief when they pass ak_permanent_fund_dividend_eligible. Set " + "this to false for someone who did not apply or who fails a test " + "PolicyEngine has no input for, such as residence during all of the " + "prior calendar year. It defaults to true, so a household calculation " + "pays every eligible Alaska resident unless an input says otherwise. " + "See docs/validation/alaska-pfd.md." + ) + definition_period = YEAR + default_value = True From 7ce082b12c2c775194983564b361d33565cf6900 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 06:01:40 -0500 Subject: [PATCH 02/11] Cover every state in the dividend properties and link the data issue Draw any state in the Hypothesis properties, so invariant 4 (nobody outside Alaska gets paid) holds for more than Washington. Point the validation doc at microcosm#1551 and say why is_incarcerated does not feed the dividend. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 16 +++++++++++++++- ...test_ak_permanent_fund_dividend_receipt.py | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index a6a0656203a..78803919cf6 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -6,7 +6,9 @@ status the dividend statute names, and takes up the dividend. These amounts are calculated benefits, rather than imputed income or fixed default inputs. The model has no inputs for the statute's other tests: an application, residence during all of the prior calendar year, allowable absences, or a felony sentence -or incarceration. Supplying wages and other known income does not disable either +or conviction-based incarceration during that year. `is_incarcerated` does not +say why a person is incarcerated or tie to that year, so it does not affect the +dividend. Supplying wages and other known income does not disable either calculation. The regular PFD is included in federal gross income for nondependent people. @@ -36,6 +38,18 @@ Set it to false for a person Alaska did not pay. Both variables gate dividend ([sec. 13, ch. 34, SLA 2026](https://www.akleg.gov/PDF/34/Bills/HB0263Z.PDF#page=84)). +### Recipients in microsimulation + +Populace does not set `takes_up_ak_permanent_fund_dividend_if_eligible` yet, so +the default microsimulation pays everyone who passes the status test: 730,150 +people for 2024, against the [624,489 applications Alaska +paid](https://pfd.alaska.gov/division-info/summary-of-dividend-applications-payments) +for that dividend year. The Microcosm data pipeline can close that gap by setting +the flag from reported receipt and Alaska's paid count; see +[microcosm issue 1551](https://github.com/PolicyEngine/microcosm/issues/1551). +The microsimulation has no inputs before its 2024 base year, so it cannot report +recipients for earlier years. + ## Override calculated payments Supply either variable as an input on the person to replace its calculated diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py index f0777e06c3d..ba39084e499 100644 --- a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -20,10 +20,11 @@ ELIGIBLE_STATUSES restates AS 43.23.005(a)(5) here on purpose, so a change to the parameter file that the statute does not support fails these tests. The YAML tests pin examples. These tests run the real model on every status and -take-up combination, and on drawn years and household mixes, through one shared -tax-benefit system, so they build no extra model and load no dataset. +take-up combination, and on drawn years, states and household mixes, through one +shared tax-benefit system, so they build no extra model and load no dataset. """ +from collections import defaultdict from itertools import product import numpy as np @@ -34,6 +35,9 @@ from policyengine_us import Simulation from policyengine_us.system import system from policyengine_us.tools.parameters import FIRST_MODELED_YEAR +from policyengine_us.variables.household.demographic.geographic.state_code import ( + StateCode, +) from policyengine_us.variables.household.demographic.person.immigration_status import ( ImmigrationStatus, ) @@ -45,14 +49,16 @@ # None leaves the input unset, so the model's default applies. STATUS_INPUTS = (None, *STATUSES) TAKE_UP_INPUTS = (None, True, False) -STATES = ("AK", "WA") +EXHAUSTIVE_STATES = ("AK", "WA") +ALL_STATES = tuple(state.name for state in StateCode) LAST_YEAR = 2100 # 2015 starts the modeled years, 2022 and 2026 pay energy relief, and 2027 pays # none. EXHAUSTIVE_YEARS = (2015, 2022, 2026, 2027) PERSON = st.tuples( - st.sampled_from(STATES), + # Half the draws land in Alaska, so the gate's positive cases stay common. + st.one_of(st.just("AK"), st.sampled_from(ALL_STATES)), st.sampled_from(STATUS_INPUTS), st.sampled_from(TAKE_UP_INPUTS), ) @@ -71,7 +77,7 @@ def amounts(year): def calculate(year, people): """Run the model with each state's people in one household.""" situation_people = {} - members = {state: [] for state in STATES} + members = defaultdict(list) for index, (state, status, take_up) in enumerate(people): name = f"person_{index}" inputs = {} @@ -86,7 +92,6 @@ def calculate(year, people): "households": { state: {"members": names, "state_code": {year: state}} for state, names in members.items() - if names }, } simulation = Simulation(situation=situation) @@ -120,7 +125,7 @@ def test_statuses_named_in_the_statute_exist_in_the_model(): @pytest.mark.parametrize("year", EXHAUSTIVE_YEARS) def test_every_combination_of_state_status_and_take_up(year): - people = list(product(STATES, STATUS_INPUTS, TAKE_UP_INPUTS)) + people = list(product(EXHAUSTIVE_STATES, STATUS_INPUTS, TAKE_UP_INPUTS)) assert_gate_holds(year, people, *calculate(year, people)) From 7ebe2de8ddb97020a3e51e8fde1ae0888d5ea953 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 06:02:30 -0500 Subject: [PATCH 03/11] Say which incarceration the dividend's statute asks about The eligibility variable's documentation now matches the validation doc: the model has no input for conviction-based incarceration during the qualifying year, even though is_incarcerated exists. Co-Authored-By: Claude Opus 5.5 --- .../gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py index fdb91cbc247..5fd1ed87c23 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -9,8 +9,8 @@ class ak_permanent_fund_dividend_eligible(Variable): "Applies the immigration status test in AS 43.23.005(a)(5). PolicyEngine " "has no inputs for the statute's other tests: an application, residence " "during all of the qualifying year, allowable absences, or a felony " - "sentence or incarceration during that year. To model a person who " - "fails one of those, set " + "sentence or conviction-based incarceration during that year. To model " + "a person who fails one of those, set " "takes_up_ak_permanent_fund_dividend_if_eligible to false. See " "docs/validation/alaska-pfd.md." ) From afbea17edf364e9256768d04cd518b826ff5d9db Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 07:29:37 -0500 Subject: [PATCH 04/11] Explain the dividend status test's basis, and keep age out of the gate State v. Andrade and 15 AAC 23.154 read "lawfully admitted for permanent residence" as legal presence plus the ability to intend to remain, so the parameter comment, docs and variable documentation now say that and give the reason each excluded status stays out. OAH 25-3180-PFD covers fixed-term parole. A child born during the dividend year cannot qualify, but age 0 does not show that: Populace's age is ASEC A_AGE, measured as of a February to April survey date, so most records aged 0 were born the year before. The tests now draw ages and pin that a resident aged 0 stays paid unless an input says otherwise. Add YAML cases for an undocumented child and for a supplied zero dividend that leaves energy relief in place. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 83 +++++++++++++++---- ...ividend_eligible_immigration_statuses.yaml | 24 ++++-- .../gov/states/ak/dor/ak_energy_relief.yaml | 24 ++++++ .../ak/dor/ak_permanent_fund_dividend.yaml | 34 ++++++++ .../ak_permanent_fund_dividend_eligible.yaml | 2 +- ...test_ak_permanent_fund_dividend_receipt.py | 60 +++++++++----- .../gov/states/ak/dor/ak_energy_relief.py | 5 +- .../ak/dor/ak_permanent_fund_dividend.py | 5 +- .../ak_permanent_fund_dividend_eligible.py | 13 +-- ..._ak_permanent_fund_dividend_if_eligible.py | 7 +- 10 files changed, 199 insertions(+), 58 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 78803919cf6..3bffe931eb2 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -4,12 +4,13 @@ PolicyEngine automatically calculates `ak_permanent_fund_dividend` and `ak_energy_relief` for each person who lives in Alaska, holds an immigration status the dividend statute names, and takes up the dividend. These amounts are calculated benefits, rather than imputed income or fixed default inputs. The -model has no inputs for the statute's other tests: an application, residence -during all of the prior calendar year, allowable absences, or a felony sentence -or conviction-based incarceration during that year. `is_incarcerated` does not -say why a person is incarcerated or tie to that year, so it does not affect the -dividend. Supplying wages and other known income does not disable either -calculation. +model's inputs cannot resolve the statute's other tests: an application, +residence on the application date and during all of the prior calendar year, 72 +consecutive hours of presence in the prior two years, allowable absences, +Selective Service compliance, or a felony sentence or conviction-based +incarceration during that year. `is_incarcerated` does not say why a person is +incarcerated or tie to that year, so it does not affect the dividend. Supplying +wages and other known income does not disable either calculation. The regular PFD is included in federal gross income for nondependent people. A dependent child's PFD is not automatically added to the parent's adjusted @@ -21,14 +22,40 @@ The regular dividend appears on [2022 Schedule 1, line ## Who receives the dividend `ak_permanent_fund_dividend_eligible` applies the status test in [AS -43.23.005(a)(5)](https://www.akleg.gov/basis/statutes.asp#43.23.005): a citizen, a -lawful permanent resident, a refugee, or an asylee. [15 AAC -23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154) also counts conditional -permanent residents and people on indefinite parole as lawfully admitted for -permanent residence. PolicyEngine has no status for either, so code them as -`LEGAL_PERMANENT_RESIDENT`. `PAROLED_ONE_YEAR`, `CUBAN_HAITIAN_ENTRANT` and -`CONDITIONAL_ENTRANT` do not say whether a parole runs indefinitely or whether -the person may adopt the United States as their domicile, so they stay out. +43.23.005(a)(5)](https://www.akleg.gov/basis/statutes.asp#43.23.005): a citizen, +an alien lawfully admitted for permanent residence, a refugee, or an asylee. + +*State v. Andrade*, 23 P.3d 58 (Alaska 2001), holds that the state phrase does +not incorporate the federal definition: an alien must be legally present and able +to form the intent to remain in Alaska. [15 AAC +23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154) applies that to a status +under 8 U.S.C. 1101 to 1189 that lets the alien adopt the United States as their +domicile. It names immigrants, nonimmigrants whose status requires no foreign +residence, people on indefinite parole, and conditional permanent residents. +PolicyEngine has no status for the last three, so code them as +`LEGAL_PERMANENT_RESIDENT`. Under 15 AAC 23.154(b) and (d), a status that requires +a foreign residence fails, and a limited-stay nonimmigrant fails the residence +test until they take a significant step toward a permanent or indefinite status. +The Division's [immigration +page](https://pfd.alaska.gov/eligibility/immigration) also describes paths for +US nationals and Compact of Free Association migrants. PolicyEngine has no status +for those either, so code a person the Division accepts as +`LEGAL_PERMANENT_RESIDENT`. + +The other values of `immigration_status` stay out: + +- `PAROLED_ONE_YEAR` does not say a parole runs indefinitely. [OAH No. + 25-3180-PFD](https://aws.state.ak.us/OAH/Decision/Display?rec=7350) denied a + fixed-term parolee a 2025 dividend. +- `TPS` (8 U.S.C. 1254a) and `DEPORTATION_WITHHELD` (8 U.S.C. 1231(b)(3)) fall + outside 8 U.S.C. 1101 to 1189, and `DACA` is not a status under those sections. +- `UNDOCUMENTED` does not meet *Andrade*'s legal presence requirement. +- `CUBAN_HAITIAN_ENTRANT` and `CONDITIONAL_ENTRANT` do not say whether the person + may adopt the United States as their domicile. + +Alaska tests the status held during the qualifying year, and 15 AAC 23.154(c) +starts an alien's qualifying year on January 1 after the person gains the status. +PolicyEngine has no status history, so it reads the status for the dividend year. `takes_up_ak_permanent_fund_dividend_if_eligible` covers the statute's other tests and anyone who does not apply. It defaults to true, so a household @@ -38,6 +65,25 @@ Set it to false for a person Alaska did not pay. Both variables gate dividend ([sec. 13, ch. 34, SLA 2026](https://www.akleg.gov/PDF/34/Bills/HB0263Z.PDF#page=84)). +### Children under one + +A child born during the dividend year cannot receive that year's dividend. The +child fails AS 43.23.005(a)(3), and (c) reaches only children born in the two +calendar years before the dividend year. The Division [takes +applications](https://pfd.alaska.gov/application/applying-for-a-child) for a child +"born on or before December 31st of the qualifying year." + +`age` cannot identify these children, because a child aged 0 may have been born in +either calendar year. Populace takes `age` from the CPS ASEC, which the Census +Bureau [conducts in February, March and +April](https://www.census.gov/topics/income-poverty/poverty/guidance/surveys-programs.html) +and which records age [as of the survey +date](https://www.census.gov/programs-surveys/cps/technical-documentation/subject-definitions.html). +Most children under one on those dates were born the year before, and those +children can qualify under (c). So the model pays a resident aged 0 unless the +take-up input says otherwise. Set that input to false for a child born during the +dividend year. + ### Recipients in microsimulation Populace does not set `takes_up_ak_permanent_fund_dividend_if_eligible` yet, so @@ -53,10 +99,11 @@ recipients for earlier years. ## Override calculated payments Supply either variable as an input on the person to replace its calculated -amount. To exclude both transfers from a controlled comparison, set both to zero -for **every person**. Overriding only `ak_permanent_fund_dividend` leaves energy -relief in place. Overriding only tax-unit AGI leaves the underlying person income -and benefit amounts unchanged. +amount. Overriding only `ak_permanent_fund_dividend` leaves energy relief in +place. To exclude both transfers from a controlled comparison, set +`takes_up_ak_permanent_fund_dividend_if_eligible` to false or set both payments +to zero, for **every person**. Overriding only tax-unit AGI leaves the underlying +person income and benefit amounts unchanged. This complete situation retains Alaska residence while excluding both payments: diff --git a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml index bee353fca15..e0f9a4a884b 100644 --- a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml +++ b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml @@ -1,11 +1,23 @@ description: Alaska limits the Permanent Fund Dividend to people with these immigration statuses. values: - # 15 AAC 23.154(a) counts conditional permanent residents and aliens on - # indefinite parole as lawfully admitted for permanent residence. - # PolicyEngine has no status for either, so code them as - # LEGAL_PERMANENT_RESIDENT. PAROLED_ONE_YEAR stays out because it does not - # say whether a parole is indefinite. + # State v. Andrade, 23 P.3d 58 (Alaska 2001), holds that "lawfully admitted + # for permanent residence" in AS 43.23.005(a)(5)(B) does not incorporate the + # federal definition: an alien must be legally present and able to form the + # intent to remain in Alaska. 15 AAC 23.154(a) applies that to a status under + # 8 U.S.C. 1101 to 1189 that lets the alien adopt the United States as their + # domicile: an immigrant, a nonimmigrant whose status requires no foreign + # residence, a person on indefinite parole, or a conditional permanent + # resident. PolicyEngine has no status for the last three, so code them as + # LEGAL_PERMANENT_RESIDENT. + # + # The other statuses stay out. PAROLED_ONE_YEAR does not say a parole runs + # indefinitely, and OAH No. 25-3180-PFD denied a fixed-term parolee a + # dividend. TPS (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. + # 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and DACA is not a status + # under those sections. UNDOCUMENTED does not meet Andrade's legal presence + # requirement. CUBAN_HAITIAN_ENTRANT and CONDITIONAL_ENTRANT do not say + # whether the person may adopt the United States as their domicile. 2015-01-01: - CITIZEN - LEGAL_PERMANENT_RESIDENT @@ -23,3 +35,5 @@ metadata: href: https://www.akleg.gov/basis/aac.asp#15.23.154 - title: Alaska Permanent Fund Dividend Division, Immigration - Alien Status and Eligibility for the PFD href: https://pfd.alaska.gov/eligibility/immigration + - title: Alaska Office of Administrative Hearings, OAH No. 25-3180-PFD (adopted March 10, 2026) + href: https://aws.state.ak.us/OAH/Decision/Display?rec=7350 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml index 92792d2c7b8..e372c665507 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_energy_relief.yaml @@ -128,3 +128,27 @@ state_code: AK output: ak_energy_relief: [298.17, 0, 0, 298.17] + +- name: Case 17, a supplied zero dividend leaves energy relief in place, since each payment has its own override. + period: 2026 + input: + state_code: AK + ak_permanent_fund_dividend: 0 + output: + ak_energy_relief: 200 + +- name: Case 18, an undocumented child gets no energy relief though a citizen parent does. + period: 2026 + input: + people: + parent: + age: 35 + child: + age: 6 + immigration_status: UNDOCUMENTED + households: + household: + members: [parent, child] + state_code: AK + output: + ak_energy_relief: [200, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml index eb5b1096a1a..48e799fbf71 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml @@ -227,3 +227,37 @@ ak_permanent_fund_dividend: 1_000 output: ak_permanent_fund_dividend: 1_000 + +- name: Case 24, an undocumented child gets no dividend though a citizen parent does, since AS 43.23.005(c) waives (a)(2) to (4) but not the status test in (a)(5). + period: 2026 + input: + people: + parent: + age: 35 + child: + age: 6 + immigration_status: UNDOCUMENTED + households: + household: + members: [parent, child] + state_code: AK + output: + ak_permanent_fund_dividend: [1_000, 0] + +- name: Case 25, a resident child under one gets the dividend unless an input says otherwise, since age 0 does not show a birth during the dividend year. + period: 2026 + input: + people: + infant: + age: 0 + infant_born_during_the_dividend_year: + age: 0 + takes_up_ak_permanent_fund_dividend_if_eligible: false + one_year_old: + age: 1 + households: + household: + members: [infant, infant_born_during_the_dividend_year, one_year_old] + state_code: AK + output: + ak_permanent_fund_dividend: [1_000, 0, 1_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml index 58492623da0..43be6995589 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml @@ -30,7 +30,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 5, the statuses AS 43.23.005(a)(5) does not name are not eligible, including fixed-term parole. +- name: Case 5, the statuses AS 43.23.005(a)(5) and 15 AAC 23.154(a) do not reach are not eligible, including parole that does not say it runs indefinitely. period: 2026 input: people: diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py index ba39084e499..402cfbc103b 100644 --- a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -3,15 +3,17 @@ AS 43.23.005(a)(5) limits the dividend to citizens, lawful permanent residents, refugees and asylees, and Alaska pays energy relief as part of the dividend (sec. 13, ch. 34, SLA 2026). PolicyEngine also lets an input say a person does -not take up the dividend, for the statute's tests it has no input for. These -properties hold for every year and every mix of people in a household: +not take up the dividend, for the statute's tests its inputs cannot resolve. These +properties hold for every modeled year, every mix of people in a household and +every age, unless an input overrides the eligibility variable or a payment: 1. A person's dividend and energy relief each equal zero or the full amount for the year. 2. A person gets the dividend exactly when they live in Alaska, hold a status the statute names, and take it up. A status or take-up flag left unset counts as citizen and true, so a household calculation pays a resident - unless an input says otherwise. + unless an input says otherwise. Age plays no part: age 0 does not show a + birth during the dividend year. 3. Energy relief passes through the same gate as the dividend. 4. Nobody outside Alaska gets either payment. 5. Turning one person's take-up off lowers that person's payment to zero and @@ -19,12 +21,13 @@ ELIGIBLE_STATUSES restates AS 43.23.005(a)(5) here on purpose, so a change to the parameter file that the statute does not support fails these tests. The -YAML tests pin examples. These tests run the real model on every status and -take-up combination, and on drawn years, states and household mixes, through one -shared tax-benefit system, so they build no extra model and load no dataset. +YAML tests pin examples. These tests run the real model on every combination of +state, status, take-up and representative age, and on drawn years, states, ages +and household mixes, through one shared tax-benefit system, so they build no +extra model and load no dataset. """ -from collections import defaultdict +from collections import defaultdict, namedtuple from itertools import product import numpy as np @@ -49,6 +52,9 @@ # None leaves the input unset, so the model's default applies. STATUS_INPUTS = (None, *STATUSES) TAKE_UP_INPUTS = (None, True, False) +# None leaves age at the model's default of 40. The rest straddle the ages where +# a mistaken child or newborn rule would change a payment. +AGE_INPUTS = (None, 0, 1, 17, 18, 65) EXHAUSTIVE_STATES = ("AK", "WA") ALL_STATES = tuple(state.name for state in StateCode) LAST_YEAR = 2100 @@ -56,11 +62,14 @@ # none. EXHAUSTIVE_YEARS = (2015, 2022, 2026, 2027) -PERSON = st.tuples( +Person = namedtuple("Person", ["state", "status", "take_up", "age"]) +PERSON = st.builds( + Person, # Half the draws land in Alaska, so the gate's positive cases stay common. st.one_of(st.just("AK"), st.sampled_from(ALL_STATES)), st.sampled_from(STATUS_INPUTS), st.sampled_from(TAKE_UP_INPUTS), + st.one_of(st.sampled_from(AGE_INPUTS), st.integers(min_value=0, max_value=120)), ) PEOPLE = st.lists(PERSON, min_size=1, max_size=10) YEARS = st.integers(min_value=FIRST_MODELED_YEAR, max_value=LAST_YEAR) @@ -78,15 +87,17 @@ def calculate(year, people): """Run the model with each state's people in one household.""" situation_people = {} members = defaultdict(list) - for index, (state, status, take_up) in enumerate(people): + for index, person in enumerate(people): name = f"person_{index}" inputs = {} - if status is not None: - inputs["immigration_status"] = {year: status} - if take_up is not None: - inputs[TAKE_UP] = {year: take_up} + if person.status is not None: + inputs["immigration_status"] = {year: person.status} + if person.take_up is not None: + inputs[TAKE_UP] = {year: person.take_up} + if person.age is not None: + inputs["age"] = {year: person.age} situation_people[name] = inputs - members[state].append(name) + members[person.state].append(name) situation = { "people": situation_people, "households": { @@ -103,18 +114,18 @@ def calculate(year, people): ) -def receives(state, status, take_up): +def receives(person): return ( - state == "AK" - and (status is None or status in ELIGIBLE_STATUSES) - and take_up is not False + person.state == "AK" + and (person.status is None or person.status in ELIGIBLE_STATUSES) + and person.take_up is not False ) def assert_gate_holds(year, people, dividend, energy_relief): full_dividend, full_energy_relief = amounts(year) for person, paid, relief in zip(people, dividend, energy_relief): - expected = receives(*person) + expected = receives(person) assert paid == pytest.approx(full_dividend if expected else 0, abs=0.005) assert relief == pytest.approx(full_energy_relief if expected else 0, abs=0.005) @@ -124,8 +135,13 @@ def test_statuses_named_in_the_statute_exist_in_the_model(): @pytest.mark.parametrize("year", EXHAUSTIVE_YEARS) -def test_every_combination_of_state_status_and_take_up(year): - people = list(product(EXHAUSTIVE_STATES, STATUS_INPUTS, TAKE_UP_INPUTS)) +def test_every_combination_of_state_status_take_up_and_age(year): + people = [ + Person(*combination) + for combination in product( + EXHAUSTIVE_STATES, STATUS_INPUTS, TAKE_UP_INPUTS, AGE_INPUTS + ) + ] assert_gate_holds(year, people, *calculate(year, people)) @@ -140,7 +156,7 @@ def test_payments_follow_the_gate_for_any_year_and_household_mix(year, people): def test_turning_take_up_off_only_removes_that_persons_payment(year, people, draw): target = draw % len(people) declined = list(people) - declined[target] = (*people[target][:2], False) + declined[target] = people[target]._replace(take_up=False) dividend, energy_relief = calculate(year, people) new_dividend, new_energy_relief = calculate(year, declined) diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py index dce34a52847..e4e2da66d1f 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py @@ -13,8 +13,9 @@ class ak_energy_relief(Variable): "otherwise check an application, prior-year residence, or actual " "receipt. Supplying " "other income does not disable the calculation. Override this " - "person-level variable directly; excluding both Alaska transfers " - "requires also overriding ak_permanent_fund_dividend for each person. " + "person-level variable directly, which leaves " + "ak_permanent_fund_dividend in place. To remove both Alaska payments " + "for a person, set the take-up input to false. " "The rounded 2022 energy payment is $662, separate from the $2,622 " "regular PFD. Its exclusion from federal gross income follows the " "IRS's 2022 nonchallenge convention, which does not establish a " diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py index 8ccd4a8b254..7b0f014cd50 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py @@ -12,8 +12,9 @@ class ak_permanent_fund_dividend(Variable): "The model does not otherwise check an application, prior-year " "residence, or actual receipt. Supplying wages or other income does " "not disable this calculation. Supply this " - "person-level variable to override it; to exclude both Alaska " - "transfers, also supply ak_energy_relief as zero for every person. " + "person-level variable to override it, which leaves ak_energy_relief " + "in place. To remove both Alaska payments for a person, set the " + "take-up input to false. " "A dependent child's PFD is not automatically included in a parent's " "AGI. In 2022 this variable is the rounded $2,622 taxable regular " "dividend; the separate $662 energy payment follows the IRS's 2022 " diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py index 5fd1ed87c23..5dd1b75abcc 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -6,11 +6,14 @@ class ak_permanent_fund_dividend_eligible(Variable): entity = Person label = "Alaska Permanent Fund Dividend eligible" documentation = ( - "Applies the immigration status test in AS 43.23.005(a)(5). PolicyEngine " - "has no inputs for the statute's other tests: an application, residence " - "during all of the qualifying year, allowable absences, or a felony " - "sentence or conviction-based incarceration during that year. To model " - "a person who fails one of those, set " + "Applies the immigration status test in AS 43.23.005(a)(5). " + "PolicyEngine's inputs cannot resolve the statute's other tests: an " + "application, residence on the application date and during all of " + "the qualifying year, 72 consecutive hours of presence in the prior " + "two years, allowable absences, Selective Service compliance, a " + "felony sentence or conviction-based incarceration during that " + "year, or whether a child was born during the dividend year. To " + "model a person who fails one of those, set " "takes_up_ak_permanent_fund_dividend_if_eligible to false. See " "docs/validation/alaska-pfd.md." ) diff --git a/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py index dc2035c16b6..55355085650 100644 --- a/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py @@ -9,9 +9,10 @@ class takes_up_ak_permanent_fund_dividend_if_eligible(Variable): "Whether Alaska pays this person the Permanent Fund Dividend and any " "energy relief when they pass ak_permanent_fund_dividend_eligible. Set " "this to false for someone who did not apply or who fails a test " - "PolicyEngine has no input for, such as residence during all of the " - "prior calendar year. It defaults to true, so a household calculation " - "pays every eligible Alaska resident unless an input says otherwise. " + "PolicyEngine's inputs cannot resolve, such as residence during all " + "of the prior calendar year or a child's birth during the dividend " + "year. It defaults to true, so a household calculation pays every " + "eligible Alaska resident unless an input says otherwise. " "See docs/validation/alaska-pfd.md." ) definition_period = YEAR From 059bf7c64a4636095e56898c300b81c1e38309a2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 07:33:09 -0500 Subject: [PATCH 05/11] Say the dividend status test applies on the application date too 15 AAC 23.154(e) tests status on the application date, alongside the qualifying year that OAH 25-3180-PFD applies. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 3bffe931eb2..5ded32ed93d 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -53,9 +53,10 @@ The other values of `immigration_status` stay out: - `CUBAN_HAITIAN_ENTRANT` and `CONDITIONAL_ENTRANT` do not say whether the person may adopt the United States as their domicile. -Alaska tests the status held during the qualifying year, and 15 AAC 23.154(c) -starts an alien's qualifying year on January 1 after the person gains the status. -PolicyEngine has no status history, so it reads the status for the dividend year. +Alaska tests the status held during the qualifying year and on the application +date (15 AAC 23.154(e)), and 15 AAC 23.154(c) starts an alien's qualifying year on +January 1 after the person gains the status. PolicyEngine has no status history, +so it reads the status for the dividend year. `takes_up_ak_permanent_fund_dividend_if_eligible` covers the statute's other tests and anyone who does not apply. It defaults to true, so a household From 5684790593325fb2102d91bca35df41a279f4e08 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 12:13:33 -0500 Subject: [PATCH 06/11] Say what is_incarcerated records, and give children's birth dates The dividend docs and the eligibility variable's documentation said why a person "is incarcerated" and that a child "was born" in a year. They now say that `is_incarcerated` records no conviction, and they give children's birth dates instead. No formula, parameter or test changes. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 18 +++++++++--------- .../dor/ak_permanent_fund_dividend_eligible.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 5ded32ed93d..62229ec724d 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -8,9 +8,9 @@ model's inputs cannot resolve the statute's other tests: an application, residence on the application date and during all of the prior calendar year, 72 consecutive hours of presence in the prior two years, allowable absences, Selective Service compliance, or a felony sentence or conviction-based -incarceration during that year. `is_incarcerated` does not say why a person is -incarcerated or tie to that year, so it does not affect the dividend. Supplying -wages and other known income does not disable either calculation. +incarceration during that year. `is_incarcerated` records no conviction and does +not tie to that year, so it does not affect the dividend. Supplying wages and +other known income does not disable either calculation. The regular PFD is included in federal gross income for nondependent people. A dependent child's PFD is not automatically added to the parent's adjusted @@ -74,16 +74,16 @@ calendar years before the dividend year. The Division [takes applications](https://pfd.alaska.gov/application/applying-for-a-child) for a child "born on or before December 31st of the qualifying year." -`age` cannot identify these children, because a child aged 0 may have been born in -either calendar year. Populace takes `age` from the CPS ASEC, which the Census +`age` cannot identify these children, because a child aged 0 may have a birth date +in either calendar year. Populace takes `age` from the CPS ASEC, which the Census Bureau [conducts in February, March and April](https://www.census.gov/topics/income-poverty/poverty/guidance/surveys-programs.html) and which records age [as of the survey date](https://www.census.gov/programs-surveys/cps/technical-documentation/subject-definitions.html). -Most children under one on those dates were born the year before, and those -children can qualify under (c). So the model pays a resident aged 0 unless the -take-up input says otherwise. Set that input to false for a child born during the -dividend year. +Most children under one on those dates have birth dates in the year before, and +those children can qualify under (c). So the model pays a resident aged 0 unless +the take-up input says otherwise. Set that input to false for a child born during +the dividend year. ### Recipients in microsimulation diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py index 5dd1b75abcc..af778e1fe29 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -12,7 +12,7 @@ class ak_permanent_fund_dividend_eligible(Variable): "the qualifying year, 72 consecutive hours of presence in the prior " "two years, allowable absences, Selective Service compliance, a " "felony sentence or conviction-based incarceration during that " - "year, or whether a child was born during the dividend year. To " + "year, or, for a child, a birth during the dividend year. To " "model a person who fails one of those, set " "takes_up_ak_permanent_fund_dividend_if_eligible to false. See " "docs/validation/alaska-pfd.md." From 6b8bab51a1694de1e739c3782648edb6015b7a5e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 13:08:43 -0500 Subject: [PATCH 07/11] Date children aged 0 and name the parent test in the dividend docs The children-under-one section now says how a record's survey date maps to the dividend year. If each record keeps its own survey date, 92% to 96% of the people aged 0 in Populace's 2024 Alaska records have birth dates in 2022 or 2023 and can qualify under AS 43.23.005(c); if every survey fell in the dividend year, 71% to 87% could. `birth_year` would put all of them in the dividend year. The section also says that a child born during the qualifying year qualifies only through an eligible parent, which the model does not check, and the TPS bullet says that Andrade did not address TPS. The eligibility variable's documentation adds the parent test to the list of tests the take-up input covers. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 29 +++++++++++++------ .../ak_permanent_fund_dividend_eligible.py | 5 ++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 62229ec724d..2f4e421d1bd 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -49,6 +49,8 @@ The other values of `immigration_status` stay out: fixed-term parolee a 2025 dividend. - `TPS` (8 U.S.C. 1254a) and `DEPORTATION_WITHHELD` (8 U.S.C. 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and `DACA` is not a status under those sections. + *Andrade*'s test alone might reach a TPS holder, but the court did not address + TPS, and PolicyEngine follows the regulation's range. - `UNDOCUMENTED` does not meet *Andrade*'s legal presence requirement. - `CUBAN_HAITIAN_ENTRANT` and `CONDITIONAL_ENTRANT` do not say whether the person may adopt the United States as their domicile. @@ -72,18 +74,27 @@ A child born during the dividend year cannot receive that year's dividend. The child fails AS 43.23.005(a)(3), and (c) reaches only children born in the two calendar years before the dividend year. The Division [takes applications](https://pfd.alaska.gov/application/applying-for-a-child) for a child -"born on or before December 31st of the qualifying year." - -`age` cannot identify these children, because a child aged 0 may have a birth date -in either calendar year. Populace takes `age` from the CPS ASEC, which the Census -Bureau [conducts in February, March and +"born on or before December 31st of the qualifying year." A child born during the +qualifying year qualifies only under (c), which also requires a parent who is +eligible for that dividend. `ak_permanent_fund_dividend_eligible` tests the +child's own status, not a parent's eligibility. + +`age` cannot identify a child born during the dividend year, because a child aged +0 may have a birth date in either calendar year. Populace takes `age` from the +CPS ASEC, which the Census Bureau [conducts in February, March and April](https://www.census.gov/topics/income-poverty/poverty/guidance/surveys-programs.html) and which records age [as of the survey date](https://www.census.gov/programs-surveys/cps/technical-documentation/subject-definitions.html). -Most children under one on those dates have birth dates in the year before, and -those children can qualify under (c). So the model pays a resident aged 0 unless -the take-up input says otherwise. Set that input to false for a child born during -the dividend year. +Each survey asks about income in the year before, which Populace records as +`source_year`. If each record keeps its own survey date, 92% to 96% of the people +aged 0 in Populace's 2024 Alaska records have birth dates in 2022 or 2023 and can +qualify under (c), because 74% of them come from the 2023 survey. If every survey +fell in the dividend year instead, 71% to 87% could. The ranges run from +mid-February to mid-April interviews. `birth_year`, which subtracts `age` from +the year, would put all of them in the dividend year. So the model pays a +resident aged 0 unless the take-up input says otherwise. Set that input to false +for a child born during the dividend year, or for a child born during the +qualifying year who has no eligible parent. ### Recipients in microsimulation diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py index af778e1fe29..bd62d49f5d9 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -12,8 +12,9 @@ class ak_permanent_fund_dividend_eligible(Variable): "the qualifying year, 72 consecutive hours of presence in the prior " "two years, allowable absences, Selective Service compliance, a " "felony sentence or conviction-based incarceration during that " - "year, or, for a child, a birth during the dividend year. To " - "model a person who fails one of those, set " + "year, a child's birth during the dividend year, or, for a child " + "born during the qualifying year, a parent's eligibility (AS " + "43.23.005(c)). To model a person who fails one of those, set " "takes_up_ak_permanent_fund_dividend_if_eligible to false. See " "docs/validation/alaska-pfd.md." ) From 2d0402dda8aa87f86c5c752c85f626f3edf9c74e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 15:00:25 -0500 Subject: [PATCH 08/11] Name who acts in the dividend docs, parameter comment and docstrings Text only. The Alaska Supreme Court held Andrade, the Department of Revenue applies its reading in 15 AAC 23.154(a), an alien's qualifying year starts under 15 AAC 23.154(c), a person coded DACA holds no status under 8 U.S.C. 1101 to 1189, and PolicyEngine calculates both payments. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 24 ++++++++-------- ...ividend_eligible_immigration_statuses.yaml | 28 ++++++++++--------- .../gov/states/ak/dor/ak_energy_relief.py | 12 ++++---- .../ak/dor/ak_permanent_fund_dividend.py | 7 +++-- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 2f4e421d1bd..0dde3faaa77 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -25,13 +25,14 @@ The regular dividend appears on [2022 Schedule 1, line 43.23.005(a)(5)](https://www.akleg.gov/basis/statutes.asp#43.23.005): a citizen, an alien lawfully admitted for permanent residence, a refugee, or an asylee. -*State v. Andrade*, 23 P.3d 58 (Alaska 2001), holds that the state phrase does -not incorporate the federal definition: an alien must be legally present and able -to form the intent to remain in Alaska. [15 AAC -23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154) applies that to a status -under 8 U.S.C. 1101 to 1189 that lets the alien adopt the United States as their -domicile. It names immigrants, nonimmigrants whose status requires no foreign -residence, people on indefinite parole, and conditional permanent residents. +In *State v. Andrade*, 23 P.3d 58 (Alaska 2001), the Alaska Supreme Court held +that the state phrase does not incorporate the federal definition: an alien must +be legally present and able to form the intent to remain in Alaska. In [15 AAC +23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154), the Department of +Revenue applies that reading to a status under 8 U.S.C. 1101 to 1189 that lets +the alien adopt the United States as their domicile. The regulation names +immigrants, nonimmigrants whose status requires no foreign residence, people on +indefinite parole, and conditional permanent residents. PolicyEngine has no status for the last three, so code them as `LEGAL_PERMANENT_RESIDENT`. Under 15 AAC 23.154(b) and (d), a status that requires a foreign residence fails, and a limited-stay nonimmigrant fails the residence @@ -48,7 +49,8 @@ The other values of `immigration_status` stay out: 25-3180-PFD](https://aws.state.ak.us/OAH/Decision/Display?rec=7350) denied a fixed-term parolee a 2025 dividend. - `TPS` (8 U.S.C. 1254a) and `DEPORTATION_WITHHELD` (8 U.S.C. 1231(b)(3)) fall - outside 8 U.S.C. 1101 to 1189, and `DACA` is not a status under those sections. + outside 8 U.S.C. 1101 to 1189, and a person coded `DACA` holds no status under + those sections. *Andrade*'s test alone might reach a TPS holder, but the court did not address TPS, and PolicyEngine follows the regulation's range. - `UNDOCUMENTED` does not meet *Andrade*'s legal presence requirement. @@ -56,9 +58,9 @@ The other values of `immigration_status` stay out: may adopt the United States as their domicile. Alaska tests the status held during the qualifying year and on the application -date (15 AAC 23.154(e)), and 15 AAC 23.154(c) starts an alien's qualifying year on -January 1 after the person gains the status. PolicyEngine has no status history, -so it reads the status for the dividend year. +date (15 AAC 23.154(e)), and under 15 AAC 23.154(c) an alien's qualifying year +starts on January 1 after the person gains the status. PolicyEngine has no status +history, so it reads the status for the dividend year. `takes_up_ak_permanent_fund_dividend_if_eligible` covers the statute's other tests and anyone who does not apply. It defaults to true, so a household diff --git a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml index e0f9a4a884b..0be431aec9b 100644 --- a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml +++ b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml @@ -1,23 +1,25 @@ description: Alaska limits the Permanent Fund Dividend to people with these immigration statuses. values: - # State v. Andrade, 23 P.3d 58 (Alaska 2001), holds that "lawfully admitted - # for permanent residence" in AS 43.23.005(a)(5)(B) does not incorporate the - # federal definition: an alien must be legally present and able to form the - # intent to remain in Alaska. 15 AAC 23.154(a) applies that to a status under - # 8 U.S.C. 1101 to 1189 that lets the alien adopt the United States as their - # domicile: an immigrant, a nonimmigrant whose status requires no foreign - # residence, a person on indefinite parole, or a conditional permanent - # resident. PolicyEngine has no status for the last three, so code them as - # LEGAL_PERMANENT_RESIDENT. + # In State v. Andrade, 23 P.3d 58 (Alaska 2001), the Alaska Supreme Court + # held that "lawfully admitted for permanent residence" in AS + # 43.23.005(a)(5)(B) does not incorporate the federal definition: an alien + # must be legally present and able to form the intent to remain in Alaska. + # In 15 AAC 23.154(a), the Department of Revenue applies that reading to a + # status under 8 U.S.C. 1101 to 1189 that lets the alien adopt the United + # States as their domicile: an immigrant, a nonimmigrant whose status + # requires no foreign residence, a person on indefinite parole, or a + # conditional permanent resident. PolicyEngine has no status for the last + # three, so code them as LEGAL_PERMANENT_RESIDENT. # # The other statuses stay out. PAROLED_ONE_YEAR does not say a parole runs # indefinitely, and OAH No. 25-3180-PFD denied a fixed-term parolee a # dividend. TPS (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. - # 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and DACA is not a status - # under those sections. UNDOCUMENTED does not meet Andrade's legal presence - # requirement. CUBAN_HAITIAN_ENTRANT and CONDITIONAL_ENTRANT do not say - # whether the person may adopt the United States as their domicile. + # 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and a person coded DACA + # holds no status under those sections. UNDOCUMENTED does not meet + # Andrade's legal presence requirement. CUBAN_HAITIAN_ENTRANT and + # CONDITIONAL_ENTRANT do not say whether the person may adopt the United + # States as their domicile. 2015-01-01: - CITIZEN - LEGAL_PERMANENT_RESIDENT diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py index e4e2da66d1f..def672e485f 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_energy_relief.py @@ -6,12 +6,12 @@ class ak_energy_relief(Variable): entity = Person label = "Alaska One Time Energy Relief" documentation = ( - "Energy relief calculated automatically for each Alaska resident who " - "passes ak_permanent_fund_dividend_eligible and takes up the dividend " - "(takes_up_ak_permanent_fund_dividend_if_eligible, true by default), " - "since Alaska pays it as part of the dividend. The model does not " - "otherwise check an application, prior-year residence, or actual " - "receipt. Supplying " + "PolicyEngine calculates energy relief automatically for each Alaska " + "resident who passes ak_permanent_fund_dividend_eligible and takes up " + "the dividend (takes_up_ak_permanent_fund_dividend_if_eligible, true " + "by default), since Alaska pays it as part of the dividend. The model " + "does not otherwise check an application, prior-year residence, or " + "actual receipt. Supplying " "other income does not disable the calculation. Override this " "person-level variable directly, which leaves " "ak_permanent_fund_dividend in place. To remove both Alaska payments " diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py index 7b0f014cd50..58b700b892a 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend.py @@ -6,9 +6,10 @@ class ak_permanent_fund_dividend(Variable): entity = Person label = "Alaska Permanent Fund Dividend" documentation = ( - "Regular PFD calculated automatically for each Alaska resident who " - "passes ak_permanent_fund_dividend_eligible and takes up the dividend " - "(takes_up_ak_permanent_fund_dividend_if_eligible, true by default). " + "PolicyEngine calculates the regular PFD automatically for each Alaska " + "resident who passes ak_permanent_fund_dividend_eligible and takes up " + "the dividend (takes_up_ak_permanent_fund_dividend_if_eligible, true " + "by default). " "The model does not otherwise check an application, prior-year " "residence, or actual receipt. Supplying wages or other income does " "not disable this calculation. Supply this " From f1a1a62a58fe9b006ee042c01ea179e7bfbf5c77 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 10 Nov 2026 23:57:55 -0500 Subject: [PATCH 09/11] Name the actors in the dividend docs, tests and docstrings The administrative law judge upheld the Division's denial in OAH No. 25-3180-PFD; the decision itself denied nothing, so the docs and the parameter comment now say who acted. Test names and docstrings take PolicyEngine or Alaska as subject where a document stood in before. Text only: no formula, parameter value or test expectation changes. Co-Authored-By: Claude Opus 5.5 --- changelog.d/ak-pfd-receipt.added.md | 2 +- docs/validation/alaska-pfd.md | 103 +++++++++--------- ...ividend_eligible_immigration_statuses.yaml | 13 ++- .../ak/dor/ak_permanent_fund_dividend.yaml | 2 +- .../ak_permanent_fund_dividend_eligible.yaml | 14 +-- ...test_ak_permanent_fund_dividend_receipt.py | 34 +++--- .../ak_permanent_fund_dividend_eligible.py | 2 +- ..._ak_permanent_fund_dividend_if_eligible.py | 2 +- 8 files changed, 87 insertions(+), 85 deletions(-) diff --git a/changelog.d/ak-pfd-receipt.added.md b/changelog.d/ak-pfd-receipt.added.md index 04b8cfe1262..c8382c693ae 100644 --- a/changelog.d/ak-pfd-receipt.added.md +++ b/changelog.d/ak-pfd-receipt.added.md @@ -1 +1 @@ -Limit the Alaska Permanent Fund Dividend and energy relief to the immigration statuses AS 43.23.005(a)(5) names, and add a default-true take-up input for residents Alaska does not pay. +Limit the Alaska Permanent Fund Dividend and energy relief to the immigration statuses in AS 43.23.005(a)(5), and add a default-true take-up input for residents Alaska does not pay. diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index 0dde3faaa77..ab1cb1cba0e 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -1,16 +1,16 @@ # Alaska Permanent Fund Dividend in model comparisons PolicyEngine automatically calculates `ak_permanent_fund_dividend` and -`ak_energy_relief` for each person who lives in Alaska, holds an immigration -status the dividend statute names, and takes up the dividend. These amounts are -calculated benefits, rather than imputed income or fixed default inputs. The -model's inputs cannot resolve the statute's other tests: an application, -residence on the application date and during all of the prior calendar year, 72 -consecutive hours of presence in the prior two years, allowable absences, -Selective Service compliance, or a felony sentence or conviction-based -incarceration during that year. `is_incarcerated` records no conviction and does -not tie to that year, so it does not affect the dividend. Supplying wages and -other known income does not disable either calculation. +`ak_energy_relief` for each person who lives in Alaska, holds one of the +immigration statuses in the dividend statute, and takes up the dividend. These +amounts are calculated benefits, rather than imputed income or fixed default +inputs. PolicyEngine cannot resolve the statute's other tests from its inputs: +an application, residence on the application date and during all of the prior +calendar year, 72 consecutive hours of presence in the prior two years, +allowable absences, Selective Service compliance, or a felony sentence or +conviction-based incarceration during that year. `is_incarcerated` records no +conviction and does not tie to that year, so it does not affect the dividend. +Supplying wages and other known income does not disable either calculation. The regular PFD is included in federal gross income for nondependent people. A dependent child's PFD is not automatically added to the parent's adjusted @@ -30,37 +30,37 @@ that the state phrase does not incorporate the federal definition: an alien must be legally present and able to form the intent to remain in Alaska. In [15 AAC 23.154(a)](https://www.akleg.gov/basis/aac.asp#15.23.154), the Department of Revenue applies that reading to a status under 8 U.S.C. 1101 to 1189 that lets -the alien adopt the United States as their domicile. The regulation names +the alien adopt the United States as their domicile. The department lists immigrants, nonimmigrants whose status requires no foreign residence, people on -indefinite parole, and conditional permanent residents. -PolicyEngine has no status for the last three, so code them as -`LEGAL_PERMANENT_RESIDENT`. Under 15 AAC 23.154(b) and (d), a status that requires -a foreign residence fails, and a limited-stay nonimmigrant fails the residence -test until they take a significant step toward a permanent or indefinite status. -The Division's [immigration -page](https://pfd.alaska.gov/eligibility/immigration) also describes paths for -US nationals and Compact of Free Association migrants. PolicyEngine has no status -for those either, so code a person the Division accepts as -`LEGAL_PERMANENT_RESIDENT`. - -The other values of `immigration_status` stay out: - -- `PAROLED_ONE_YEAR` does not say a parole runs indefinitely. [OAH No. - 25-3180-PFD](https://aws.state.ak.us/OAH/Decision/Display?rec=7350) denied a - fixed-term parolee a 2025 dividend. +indefinite parole, and conditional permanent residents. PolicyEngine has no +status for the last three, so code them as `LEGAL_PERMANENT_RESIDENT`. Under 15 +AAC 23.154(b) and (d), a person whose status requires a foreign residence fails, +and a limited-stay nonimmigrant fails the residence test until they take a +significant step toward a permanent or indefinite status. On its [immigration +page](https://pfd.alaska.gov/eligibility/immigration), the Division also +describes paths for US nationals and Compact of Free Association migrants. +PolicyEngine has no status for those either, so code a person the Division +accepts as `LEGAL_PERMANENT_RESIDENT`. + +PolicyEngine leaves the other values of `immigration_status` out: + +- `PAROLED_ONE_YEAR` does not say a parole runs indefinitely. In [OAH No. + 25-3180-PFD](https://aws.state.ak.us/OAH/Decision/Display?rec=7350), an + administrative law judge upheld the Division's denial of a 2025 dividend to a + fixed-term parolee. - `TPS` (8 U.S.C. 1254a) and `DEPORTATION_WITHHELD` (8 U.S.C. 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and a person coded `DACA` holds no status under - those sections. - *Andrade*'s test alone might reach a TPS holder, but the court did not address - TPS, and PolicyEngine follows the regulation's range. -- `UNDOCUMENTED` does not meet *Andrade*'s legal presence requirement. + those sections. A TPS holder might meet *Andrade*'s test alone, but the court + did not address TPS, and PolicyEngine follows the regulation's range. +- A person coded `UNDOCUMENTED` lacks the legal presence the court required in + *Andrade*. - `CUBAN_HAITIAN_ENTRANT` and `CONDITIONAL_ENTRANT` do not say whether the person may adopt the United States as their domicile. -Alaska tests the status held during the qualifying year and on the application -date (15 AAC 23.154(e)), and under 15 AAC 23.154(c) an alien's qualifying year -starts on January 1 after the person gains the status. PolicyEngine has no status -history, so it reads the status for the dividend year. +Alaska tests the status a person holds during the qualifying year and on the +application date (15 AAC 23.154(e)), and under 15 AAC 23.154(c) an alien's +qualifying year starts on January 1 after the person gains the status. +PolicyEngine has no status history, so it reads the status for the dividend year. `takes_up_ak_permanent_fund_dividend_if_eligible` covers the statute's other tests and anyone who does not apply. It defaults to true, so a household @@ -73,25 +73,26 @@ dividend ([sec. 13, ch. 34, SLA ### Children under one A child born during the dividend year cannot receive that year's dividend. The -child fails AS 43.23.005(a)(3), and (c) reaches only children born in the two -calendar years before the dividend year. The Division [takes +child fails AS 43.23.005(a)(3), and under (c) only children born in the two +calendar years before the dividend year qualify. The Division [takes applications](https://pfd.alaska.gov/application/applying-for-a-child) for a child "born on or before December 31st of the qualifying year." A child born during the -qualifying year qualifies only under (c), which also requires a parent who is -eligible for that dividend. `ak_permanent_fund_dividend_eligible` tests the -child's own status, not a parent's eligibility. - -`age` cannot identify a child born during the dividend year, because a child aged -0 may have a birth date in either calendar year. Populace takes `age` from the -CPS ASEC, which the Census Bureau [conducts in February, March and -April](https://www.census.gov/topics/income-poverty/poverty/guidance/surveys-programs.html) -and which records age [as of the survey +qualifying year qualifies only under (c), and only through a parent eligible for +that dividend. `ak_permanent_fund_dividend_eligible` tests the child's own +status, not a parent's eligibility. + +PolicyEngine cannot identify a child born during the dividend year from `age`, +because a child aged 0 may have a birth date in either calendar year. Populace +takes `age` from the CPS ASEC, which the Census Bureau [conducts in February, +March and +April](https://www.census.gov/topics/income-poverty/poverty/guidance/surveys-programs.html), +recording each person's age [as of the survey date](https://www.census.gov/programs-surveys/cps/technical-documentation/subject-definitions.html). -Each survey asks about income in the year before, which Populace records as -`source_year`. If each record keeps its own survey date, 92% to 96% of the people -aged 0 in Populace's 2024 Alaska records have birth dates in 2022 or 2023 and can -qualify under (c), because 74% of them come from the 2023 survey. If every survey -fell in the dividend year instead, 71% to 87% could. The ranges run from +In each survey, the Bureau asks about income in the year before, which Populace +records as `source_year`. Of the people aged 0 in Populace's 2024 Alaska records, +74% come from the 2023 survey. If each record keeps its own survey date, 92% to +96% of them have birth dates in 2022 or 2023 and can qualify under (c). If every +survey fell in the dividend year instead, 71% to 87% could. The ranges run from mid-February to mid-April interviews. `birth_year`, which subtracts `age` from the year, would put all of them in the dividend year. So the model pays a resident aged 0 unless the take-up input says otherwise. Set that input to false diff --git a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml index 0be431aec9b..f455f6191c0 100644 --- a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml +++ b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml @@ -12,12 +12,13 @@ values: # conditional permanent resident. PolicyEngine has no status for the last # three, so code them as LEGAL_PERMANENT_RESIDENT. # - # The other statuses stay out. PAROLED_ONE_YEAR does not say a parole runs - # indefinitely, and OAH No. 25-3180-PFD denied a fixed-term parolee a - # dividend. TPS (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. - # 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and a person coded DACA - # holds no status under those sections. UNDOCUMENTED does not meet - # Andrade's legal presence requirement. CUBAN_HAITIAN_ENTRANT and + # PolicyEngine leaves the other statuses out. PAROLED_ONE_YEAR does not say a + # parole runs indefinitely, and in OAH No. 25-3180-PFD an administrative law + # judge upheld the Division's denial of a fixed-term parolee's dividend. TPS + # (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. 1231(b)(3)) fall + # outside 8 U.S.C. 1101 to 1189, and a person coded DACA holds no status + # under those sections. A person coded UNDOCUMENTED lacks the legal presence + # the court required in Andrade. CUBAN_HAITIAN_ENTRANT and # CONDITIONAL_ENTRANT do not say whether the person may adopt the United # States as their domicile. 2015-01-01: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml index 48e799fbf71..dd9baa1eac4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend.yaml @@ -228,7 +228,7 @@ output: ak_permanent_fund_dividend: 1_000 -- name: Case 24, an undocumented child gets no dividend though a citizen parent does, since AS 43.23.005(c) waives (a)(2) to (4) but not the status test in (a)(5). +- name: Case 24, an undocumented child gets no dividend though a citizen parent does, since under AS 43.23.005(c) a child still needs a status in (a)(5). period: 2026 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml index 43be6995589..85319e79893 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml @@ -1,4 +1,4 @@ -- name: Case 1, a citizen in Alaska is eligible under AS 43.23.005(a)(5)(A). +- name: Case 1, a citizen in Alaska qualifies under AS 43.23.005(a)(5)(A). period: 2026 input: state_code: AK @@ -6,7 +6,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 2, a lawful permanent resident in Alaska is eligible under AS 43.23.005(a)(5)(B). +- name: Case 2, a lawful permanent resident in Alaska qualifies under AS 43.23.005(a)(5)(B). period: 2026 input: state_code: AK @@ -14,7 +14,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 3, a refugee in Alaska is eligible under AS 43.23.005(a)(5)(C). +- name: Case 3, a refugee in Alaska qualifies under AS 43.23.005(a)(5)(C). period: 2026 input: state_code: AK @@ -22,7 +22,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 4, an asylee in Alaska is eligible under AS 43.23.005(a)(5)(D). +- name: Case 4, an asylee in Alaska qualifies under AS 43.23.005(a)(5)(D). period: 2026 input: state_code: AK @@ -30,7 +30,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 5, the statuses AS 43.23.005(a)(5) and 15 AAC 23.154(a) do not reach are not eligible, including parole that does not say it runs indefinitely. +- name: Case 5, people with statuses outside AS 43.23.005(a)(5) and 15 AAC 23.154(a) do not qualify, including PAROLED_ONE_YEAR, which does not say a parole runs indefinitely. period: 2026 input: people: @@ -56,7 +56,7 @@ output: ak_permanent_fund_dividend_eligible: [false, false, false, false, false, false, false] -- name: Case 6, a citizen outside Alaska is not eligible. +- name: Case 6, a citizen outside Alaska does not qualify. period: 2026 input: state_code: WA @@ -64,7 +64,7 @@ output: ak_permanent_fund_dividend_eligible: false -- name: Case 7, the status test applies in 2015, PolicyEngine's first modeled dividend year. +- name: Case 7, PolicyEngine applies the status test in 2015, its first modeled dividend year. period: 2015 input: people: diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py index 402cfbc103b..79ce230392a 100644 --- a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -1,17 +1,18 @@ """Invariants for who receives the Alaska Permanent Fund Dividend and energy relief. -AS 43.23.005(a)(5) limits the dividend to citizens, lawful permanent residents, -refugees and asylees, and Alaska pays energy relief as part of the dividend -(sec. 13, ch. 34, SLA 2026). PolicyEngine also lets an input say a person does -not take up the dividend, for the statute's tests its inputs cannot resolve. These -properties hold for every modeled year, every mix of people in a household and -every age, unless an input overrides the eligibility variable or a payment: +Under AS 43.23.005(a)(5), Alaska limits the dividend to citizens, lawful +permanent residents, refugees and asylees, and it pays energy relief as part of +the dividend (sec. 13, ch. 34, SLA 2026). PolicyEngine also lets an input say a +person does not take up the dividend, for the statute's tests it cannot resolve +from its inputs. These properties hold for every modeled year, every mix of +people in a household and every age, unless an input overrides the eligibility +variable or a payment: 1. A person's dividend and energy relief each equal zero or the full amount for the year. -2. A person gets the dividend exactly when they live in Alaska, hold a status - the statute names, and take it up. A status or take-up flag left unset - counts as citizen and true, so a household calculation pays a resident +2. A person gets the dividend exactly when they live in Alaska, hold one of the + statute's statuses, and take it up. An unset status counts as citizen and an + unset take-up flag as true, so a household calculation pays a resident unless an input says otherwise. Age plays no part: age 0 does not show a birth during the dividend year. 3. Energy relief passes through the same gate as the dividend. @@ -19,12 +20,11 @@ 5. Turning one person's take-up off lowers that person's payment to zero and changes nobody else's. -ELIGIBLE_STATUSES restates AS 43.23.005(a)(5) here on purpose, so a change to -the parameter file that the statute does not support fails these tests. The -YAML tests pin examples. These tests run the real model on every combination of -state, status, take-up and representative age, and on drawn years, states, ages -and household mixes, through one shared tax-benefit system, so they build no -extra model and load no dataset. +ELIGIBLE_STATUSES restates AS 43.23.005(a)(5), so these tests fail if someone +edits the parameter's list alone. The YAML tests pin examples. These tests run +the real model on every combination of state, status, take-up and +representative age, and on drawn years, states, ages and household mixes. They +share one tax-benefit system, so they build no extra model and load no dataset. """ from collections import defaultdict, namedtuple @@ -58,8 +58,8 @@ EXHAUSTIVE_STATES = ("AK", "WA") ALL_STATES = tuple(state.name for state in StateCode) LAST_YEAR = 2100 -# 2015 starts the modeled years, 2022 and 2026 pay energy relief, and 2027 pays -# none. +# 2015 starts the modeled years. Alaska paid energy relief in 2022 and 2026 and +# has appropriated none for 2027. EXHAUSTIVE_YEARS = (2015, 2022, 2026, 2027) Person = namedtuple("Person", ["state", "status", "take_up", "age"]) diff --git a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py index bd62d49f5d9..ba3f8cc705a 100644 --- a/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.py @@ -7,7 +7,7 @@ class ak_permanent_fund_dividend_eligible(Variable): label = "Alaska Permanent Fund Dividend eligible" documentation = ( "Applies the immigration status test in AS 43.23.005(a)(5). " - "PolicyEngine's inputs cannot resolve the statute's other tests: an " + "PolicyEngine cannot resolve the statute's other tests from its inputs: an " "application, residence on the application date and during all of " "the qualifying year, 72 consecutive hours of presence in the prior " "two years, allowable absences, Selective Service compliance, a " diff --git a/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py index 55355085650..cd1a042dcc6 100644 --- a/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py +++ b/policyengine_us/variables/gov/states/ak/dor/takes_up_ak_permanent_fund_dividend_if_eligible.py @@ -9,7 +9,7 @@ class takes_up_ak_permanent_fund_dividend_if_eligible(Variable): "Whether Alaska pays this person the Permanent Fund Dividend and any " "energy relief when they pass ak_permanent_fund_dividend_eligible. Set " "this to false for someone who did not apply or who fails a test " - "PolicyEngine's inputs cannot resolve, such as residence during all " + "PolicyEngine cannot resolve from its inputs, such as residence during all " "of the prior calendar year or a child's birth during the dividend " "year. It defaults to true, so a household calculation pays every " "eligible Alaska resident unless an input says otherwise. " From 298134f50034f051c2f903b64c687a4710ac4c69 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 00:27:02 -0500 Subject: [PATCH 10/11] Introduce the Division by name and narrow two test descriptions The parameter comment and the validation doc now name the Permanent Fund Dividend Division at first mention. The property tests' docstring says they catch a changed status list only in a year they test, and Case 5's name covers the codes PolicyEngine leaves unmapped for other reasons. Text only: no formula, parameter value or test expectation changes. Co-Authored-By: Claude Opus 5.5 --- docs/validation/alaska-pfd.md | 8 ++++---- ..._dividend_eligible_immigration_statuses.yaml | 10 +++++----- .../ak_permanent_fund_dividend_eligible.yaml | 2 +- .../test_ak_permanent_fund_dividend_receipt.py | 17 +++++++++-------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/validation/alaska-pfd.md b/docs/validation/alaska-pfd.md index ab1cb1cba0e..190960c268e 100644 --- a/docs/validation/alaska-pfd.md +++ b/docs/validation/alaska-pfd.md @@ -37,10 +37,10 @@ status for the last three, so code them as `LEGAL_PERMANENT_RESIDENT`. Under 15 AAC 23.154(b) and (d), a person whose status requires a foreign residence fails, and a limited-stay nonimmigrant fails the residence test until they take a significant step toward a permanent or indefinite status. On its [immigration -page](https://pfd.alaska.gov/eligibility/immigration), the Division also -describes paths for US nationals and Compact of Free Association migrants. -PolicyEngine has no status for those either, so code a person the Division -accepts as `LEGAL_PERMANENT_RESIDENT`. +page](https://pfd.alaska.gov/eligibility/immigration), the department's +Permanent Fund Dividend Division also describes paths for US nationals and +Compact of Free Association migrants. PolicyEngine has no status for those +either, so code a person the Division accepts as `LEGAL_PERMANENT_RESIDENT`. PolicyEngine leaves the other values of `immigration_status` out: diff --git a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml index f455f6191c0..9c21d9de366 100644 --- a/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml +++ b/policyengine_us/parameters/gov/states/ak/dor/permanent_fund_dividend_eligible_immigration_statuses.yaml @@ -14,11 +14,11 @@ values: # # PolicyEngine leaves the other statuses out. PAROLED_ONE_YEAR does not say a # parole runs indefinitely, and in OAH No. 25-3180-PFD an administrative law - # judge upheld the Division's denial of a fixed-term parolee's dividend. TPS - # (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. 1231(b)(3)) fall - # outside 8 U.S.C. 1101 to 1189, and a person coded DACA holds no status - # under those sections. A person coded UNDOCUMENTED lacks the legal presence - # the court required in Andrade. CUBAN_HAITIAN_ENTRANT and + # judge upheld the Permanent Fund Dividend Division's denial of a fixed-term + # parolee's dividend. TPS (8 U.S.C. 1254a) and DEPORTATION_WITHHELD (8 U.S.C. + # 1231(b)(3)) fall outside 8 U.S.C. 1101 to 1189, and a person coded DACA + # holds no status under those sections. A person coded UNDOCUMENTED lacks the + # legal presence the court required in Andrade. CUBAN_HAITIAN_ENTRANT and # CONDITIONAL_ENTRANT do not say whether the person may adopt the United # States as their domicile. 2015-01-01: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml index 85319e79893..08e7ff9736d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ak/dor/ak_permanent_fund_dividend_eligible.yaml @@ -30,7 +30,7 @@ output: ak_permanent_fund_dividend_eligible: true -- name: Case 5, people with statuses outside AS 43.23.005(a)(5) and 15 AAC 23.154(a) do not qualify, including PAROLED_ONE_YEAR, which does not say a parole runs indefinitely. +- name: Case 5, people coded with statuses that PolicyEngine does not map to AS 43.23.005(a)(5) or 15 AAC 23.154(a) do not qualify, including PAROLED_ONE_YEAR, which does not say a parole runs indefinitely. period: 2026 input: people: diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py index 79ce230392a..92f5d1ab89b 100644 --- a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -3,10 +3,10 @@ Under AS 43.23.005(a)(5), Alaska limits the dividend to citizens, lawful permanent residents, refugees and asylees, and it pays energy relief as part of the dividend (sec. 13, ch. 34, SLA 2026). PolicyEngine also lets an input say a -person does not take up the dividend, for the statute's tests it cannot resolve -from its inputs. These properties hold for every modeled year, every mix of -people in a household and every age, unless an input overrides the eligibility -variable or a payment: +person does not take up the dividend, to cover the statute's tests that +PolicyEngine cannot resolve from its other inputs. These properties hold for +every modeled year, every mix of people in a household and every age, unless an +input overrides the eligibility variable or a payment: 1. A person's dividend and energy relief each equal zero or the full amount for the year. @@ -21,10 +21,11 @@ changes nobody else's. ELIGIBLE_STATUSES restates AS 43.23.005(a)(5), so these tests fail if someone -edits the parameter's list alone. The YAML tests pin examples. These tests run -the real model on every combination of state, status, take-up and -representative age, and on drawn years, states, ages and household mixes. They -share one tax-benefit system, so they build no extra model and load no dataset. +changes the parameter's statuses in a year they test without changing this file. +The YAML tests pin examples. These tests run the real model on every combination +of state, status, take-up and representative age, and on drawn years, states, +ages and household mixes. They share one tax-benefit system, so they build no +extra model and load no dataset. """ from collections import defaultdict, namedtuple From b16079b889142f05b307d68e03e4948be2a21370 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 01:02:56 -0500 Subject: [PATCH 11/11] Say which years the property tests cover The docstring said the tests catch a changed status list "in a year they test", where "they" could read as the person editing. It now says "in a year these tests cover". Docstring only. Co-Authored-By: Claude Opus 5.5 --- .../tests/test_ak_permanent_fund_dividend_receipt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py index 92f5d1ab89b..d5baa19c739 100644 --- a/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py +++ b/policyengine_us/tests/test_ak_permanent_fund_dividend_receipt.py @@ -21,11 +21,11 @@ changes nobody else's. ELIGIBLE_STATUSES restates AS 43.23.005(a)(5), so these tests fail if someone -changes the parameter's statuses in a year they test without changing this file. -The YAML tests pin examples. These tests run the real model on every combination -of state, status, take-up and representative age, and on drawn years, states, -ages and household mixes. They share one tax-benefit system, so they build no -extra model and load no dataset. +changes the parameter's statuses in a year these tests cover without changing +this file. The YAML tests pin examples. These tests run the real model on every +combination of state, status, take-up and representative age, and on drawn +years, states, ages and household mixes. They share one tax-benefit system, so +they build no extra model and load no dataset. """ from collections import defaultdict, namedtuple