Repository navigation
Bridge to PolicyEngine-US: federal and state tax-benefit effect of the minimum benefit on sample households - #496
Conversation
The bridge (src/populace_dynamics/bridge/policyengine_us.py, pure Python) turns bridge households into PolicyEngine-US situations, runs them in a separate policyengine-us interpreter (POPULACE_DYNAMICS_PE_US_PYTHON), and decomposes the change in household_net_income into leaf components that sum exactly, in cents, to the net change, after checking every aggregate against its parts and the root against the definition read at e4363903f3. scripts/pe_us_minimum_benefit_sample_households.py computes an illustrative worker's PIA and exercise 4's headline minimum (option 2) with the Track M rule code, carries both to 2026 with statutory COLAs, and runs three households (no other income, a $4,800 pension, a $30,000 pension) in California, Montana and Florida. Tests: Hypothesis properties for the situation builder, the decomposition identity and the COLA helpers (unit tier), and a live oracle-tier test that skips without the policyengine-us interpreter. The bridge modules join the birth-evidence historical exclusions; tier counts move to unit 5,179 and oracle_policyengine 194. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Output of scripts/pe_us_minimum_benefit_sample_households.py at 212a966 (code clean) with policyengine-us 1.822.5 at e4363903f3, payment year 2026: the JSON of inputs, results and provenance, the Markdown tables and one waterfall chart per household (PNG and SVG). Illustrative households, not survey data. Two runs gave byte-identical files; the same files are in the dynasim-parity evidence folder pe-us-bridge-20260930. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…branch Recount test tiers after the merge: unit 5,505 and oracle_policyengine 201 (master 5,418 and 189, plus this branch's 87 and 12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fix round 1 of the review of PR #496 (code and tests; the regenerated results follow in the next commit). - Source check (finding 3): inspect_installation reads which policyengine-us the interpreter imports; check_published_source refuses a git revision no origin branch contains, a modified checkout and any VCS, archive or local-directory install; verify_record hashes every installed file against the RECORD. The default interpreter is a uv venv with policyengine-us==2.18.0 from PyPI; the script pins that release (wheel SHA-256 and package RECORD digest) and reads the oracle's SSA parameters from it. - California (finding 1): the script sets the 2026 aged or disabled payment standard to CDSS's published $1,233.94 (SSI Total Monthly Payment Amounts 2026, Rev. 1/26) for the California cases, refusing if the release already has a different 2026 value. - Float32 guard (finding 2): trace_policyengine_us reruns cases with the tracer on and uncaused_changes reports any variable that changed while every variable it read held within a cent. The script traces every changed component, checks each traced value against the batched run, and refuses to write on a finding. Household C's pension moves from $30,000 (on the California use-tax edge) to $31,200. - Lows: non-finite aggregates are refused (4); carry_pia_forward refuses an empty range (5); the Commodity Supplemental Food Program (6) and federal and state refundable credits (7) get their own rows; a bare string is refused as a group; the invented history drops PSID fields; no absolute path is written (11). - Presentation: the reform is named plainly; tables and charts show the same rows everywhere; each chart adds a with-health-coverage row and explains Florida household B's Medicaid loss. - Tests: +27 unit, +12 artifact-pinning (unit tier) and +4 oracle tests; the oracle tier now also recognizes POPULACE_DYNAMICS_PE_US_PYTHON. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The memo reads is_qmb_eligible (a monthly variable) for all 12 months of 2026, and the text says "in every month of both runs" only when that holds. - The with-health valuation now says what policyengine-us 2.18.0 computes for one household: the state's 2023 Medicaid spending over its October 2024 enrollment, an average over enrollees of all ages, not uprated to 2026 (medicaid_cost_if_enrolled.py:11-22, medicaid_slcsp_state_denominator.py:28-33). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….18.0 Results from scripts/pe_us_minimum_benefit_sample_households.py at 287a118 (code clean), on policyengine-us 2.18.0 from PyPI with California's 2026 payment standard set to CDSS's $1,233.94: - B in California keeps $397 of the $2,052 increase (19%): she loses the $1,331 supplement and $324 of SNAP, which in 2.18.0 counts the supplement as unearned income. A in California keeps a $2,879 state benefit. - C (pension now $31,200) keeps $1,942 in California and Florida and $1,886 in Montana; nothing in California moves. - The float32 guard traced 44 changed leaves in 18 comparisons and found no step without a cause. The design doc is updated to 2.18.0 citations (checked line by line), the new numbers, the payment-year parameter groups, the Medicaid valuation, and the $30,000 use-tax artifact described as an artifact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes from an independent code review of the fix round (none changes a committed number): - Runner: every case runs in its own simulation, and cases with the same overrides share one reformed tax-benefit system passed as tax_benefit_system. 2.18.0 aggregates Medicaid's state-average cost index over the whole simulation, so batched households were not independent, and only the first reformed case had a baseline branch. - Float32 guard: trace nodes of branch simulations (2.18.0's itemizing and not_itemizing federal tax branches) are keyed apart from the main calculation; a branch node traced without reads is a copied value and reads its main node. A read counts as noise up to a cent or four float32 steps at its size, so the guard still sees steps above $131,072, where one float32 step exceeds a cent. - Source check: reachability is asked of origin now (ls-remote), not of stale remote-tracking refs; origin must be a GitHub repository and reachable; git-ignored package files count as modifications. - RECORD: verify_record also reports files the RECORD does not list; the package digest ignores pip's bytecode rows; run_policyengine_us and trace_policyengine_us verify an index install's files and can require a pinned package digest (the script passes 2.18.0's). - Script: the invariants raise InvariantError instead of assert; the QMB sentence needs the Medicare Savings Program to appear in the reform. - Tests: +16 unit (bridge), +29 unit for the script's own logic with fakes, +1 oracle (a wrong pinned digest is refused); the oracle guard test also checks the branch keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Results from scripts/pe_us_minimum_benefit_sample_households.py at 472f290 (code clean), policyengine-us 2.18.0 from PyPI, the pinned RECORD digest checked by the runner and the tracer, and no file added to, changed in or missing from the installed package. Every decomposition equals the previous run's to the cent. Only Medicaid-at-cost memo values move, by one float32 step (for example 9,236.4785 to 9,236.4795), now that each case has its own simulation. The float32 guard traced 65,907 variable-periods (branch simulations' nodes now counted apart) and found no step without a cause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_groups iterated its input twice (once for the bare-string check, once to build the groups), so a generator arrived empty the second time and failed the partition check with a misleading message. Nit from the code review of the fix round; +1 unit test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Results from scripts/pe_us_minimum_benefit_sample_households.py at 2f0bf63 (code clean); only the recorded commit changes (JSON, Markdown and chart footers). Numbers, guard statistics and provenance are otherwise identical to the previous run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The with-health figure values Medicaid at the state's average spending per enrollee of all ages. Nothing in the code shows that is an upper bound for an aged enrollee (an all-ages average may understate it), so the label now says what the number is: coverage at average program cost, not a cash loss. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Numbers are unchanged (results identical to the previous JSON); only the Medicaid valuation wording and the recorded commit differ. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The upstream fix for the stale California 2026 payment standard (review finding 1) is PolicyEngine/policyengine-us#9709. It is a draft, pending CI and a partner-test sign-off.
This PR pins policyengine-us 2.18.0, which does not include the fix. The override stays necessary until the bridge moves to a release that does. 🤖 Generated with Claude Code |
No computed result changes; the outputs are regenerated in the next commit. - M1: Montana's elderly homeowner and renter credit subtracts an income reduction, 5 percent of net household income, not net household income. The doc and the script's JSON evidence now keep the two apart ($27,516 -> $29,568 of income; $1,376 -> $1,478 of reduction), citing the 2.18.0 code and MCA 15-30-2337(8) and 15-30-2340(4). - L2: the Medicaid valuation is described exactly (2023 spending over October 2024 Medicaid and CHIP enrollment) and both ways. The script computes the same-year ratio from the release (Florida $7,034 against $9,200), refuses other entry dates, and checks every Medicaid memo against the ratio. - L3: California's CPI ends in 2025 and is extended with the federal uprating index; household C's $21 is derived in the doc. - L4: the wrong-way check now covers federal and state refundable credits, as the doc says. - L5: the doc labels Hypothesis properties and example tests apart. - L6: an oracle test runs a California override case batched, on a shared system built from another household, and alone. - Rounding: half-dollars round up in every table and chart label (+1,886.50 shows as +1,887). - N2, N3, N5-N8: the guard text states its noise band; an exact one-cent float32 step now counts as a change; full citations; the origin check says GitHub, not public GitHub; clearer SNAP caveat; the guard's false alarm is documented. - N4: public files name repository paths only. - The script's policyengine-us children may run up to two hours (--timeout), for loaded machines. - Tier counts: unit 5,590 -> 5,624; oracle_policyengine 206 -> 207. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The Medicaid check also covers the with-health decomposition's medicaid_cost leaf, the figure the with-health column counts. - The float32 guard's two limits are stated and pinned by tests: from $32,768 to $131,072 one or two float32 steps of noise count as a cent (a false alarm, not a miss), and the one-cent bound covers a stored value, not one computed from larger operands. - The guard summary states the threshold it applies. - _money refuses a non-number, such as a string. - The doc says half-dollars round up in magnitude, notes that MCA 15-30-2340(5)'s table uses gross household income, and describes the earlier cases instead of pointing at an unpublished review. - Tier counts: unit 5,624 -> 5,628. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Generated from the clean commit a915b96. The JSON results equal those at 9e104e2 exactly. The only table change is display rounding: household C in Montana gains $1,886.50, now shown as +1,887 rather than +1,886. The text changes are the round-2 fixes: Montana's credit evidence, the Medicaid valuation (with same-year ratios), the guard summary, the SNAP caveat, the full Medicaid limit path, and the checks list. The float32 guard, under the one-cent rule, found nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge audit (head d21171e) Round 1: request changes (independent Opus 5.5 lane, at 45fe6d2)
Round 2: request changes (a fresh independent lane, at 9e104e2)
Final check (an independent Opus 5.5 agent, at d21171e): approve.
Results. Annual 2026 dollars, illustrative households, not survey data. Social Security rises by $2,052 in every case; the net change is:
Upstream follow-ups. California's 2026 payment standard in policyengine-us is a separate task. The misleadingly named Montana variable is noted in the PR body. |
- The artifact test reads the default variant's offsets where the script writes them (beside the decomposition), which failed 3 of its 24 tests. - When the cut starts SSI in a state where SSI receipt confers Medicaid in PolicyEngine-US (read from the release's ssi_recipient parameters; Montana is covered and section 1634), the health text names that route too. The described pattern is refused for a couple, whose household memo sums would double the limit. - The category cross-check is described as it is: category_for names Social Security, SSI, SNAP and the income taxes, and places state benefits, state refundable credits and health by position. - The no-joint-change-without-health check moves into check_invariants, before anything is written. - #496's SNAP caveat, which describes losing California's supplement, is replaced by the direction-free fact it rests on. - An unrecorded counterfactual is cut from the design note; the summary chart's legend no longer sits on the 100 percent line. - Tests for each; unit tier 5,762. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What this adds
This is the first bridge from Microcosm Dynamics to PolicyEngine-US's tax-benefit model. It shows what exercise 4's headline minimum benefit becomes after federal and state taxes and means-tested transfers: a minimum benefit set at 73% of the aged poverty threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option 2). It does this for illustrative households, not survey data. Microcosm's four DYNASIM3 comparisons report pre-tax benefit changes; this is the after-tax, after-transfer view.
Every result comes from policyengine-us 2.18.0 from PyPI (uploaded 2026-09-30, policyengine-core 3.32.11). The wheel's SHA-256 is
28e32bc1339e8ffc1ed676ac1c9ecd468d191685608039643915f63e1357085f. All 17,551 installedpolicyengine_us/files match the wheel's RECORD.src/populace_dynamics/bridge/policyengine_us.pyis pure Python and does not importpolicyengine_us.Situations.
BridgePersonandBridgeHouseholdbuild PolicyEngine-US situations: people, Social Security by type, pensions and other income, assets, rent, and tax-unit, SPM-unit, marital-unit and family membership.Source check.
inspect_installationandcheck_published_sourcerefuse a PolicyEngine-US source that is not published:origincontains, asgit ls-remotereports it now;originthat is unreachable or not on GitHub;verify_recordhashes the installed files against the RECORD and reports added files. The runner and tracer check an index install's files and can require a pinned RECORD digest; the script pins 2.18.0's.Runner.
run_policyengine_usruns each case in its own simulation in a separate interpreter, located byPOPULACE_DYNAMICS_PE_US_PYTHON(default~/.venvs/policyengine-us-2.18.0/bin/python). Cases with the same overrides share one reformed tax-benefit system. It reads the component tree ofhousehold_net_incomefrom the model.Decomposition.
decomposesplits the change into leaf components that sum exactly, in cents, to the net change. It first checks every aggregate against its parts, refuses non-finite values, and checks the root against the definition read in 2.18.0.Float32 guard.
trace_policyengine_usanduncaused_changesreport any variable that changed by a cent or more while everything it read held within float noise: a step at a bracket edge taken on float32 noise.COLAs.
carry_pia_forwardandcola_rates_from_cpi_wapply the 42 USC 415(i) COLAs.scripts/pe_us_minimum_benefit_sample_households.pycomputes the worker's PIA and minimum with the repo's oracle and Track M rule code. Its SSA parameters come from the same pinned release. It carries both PIAs to 2026 and runs three households in three states. It refuses any release but 2.18.0 and traces every changed component before writing.docs/design/pe_us_bridge.mdcovers:household_net_incomeincludes and excludes (health coverage is excluded by default);Every file:line citation is against policyengine-us 2.18.0 or the statute. Two independent review rounds audited them; round 2 found one wrong mechanism (Montana's credit) and a few imprecise citations, all fixed below.
Outputs.
docs/analysis/pe_us_bridge_20260930/holds the JSON with provenance (repository- and release-relative paths only), the Markdown tables, and PNG and SVG waterfalls per household. They were generated at the source commita915b96and committed atd21171e, the head of this PR.Results (annual 2026 dollars, current law to the reform)
The worker was born in 1958, has 22 years of covered earnings at 25% of the average wage index, and was entitled in 2024.
Each figure is rounded to the dollar on its own, with half-dollars rounded up in magnitude. Household C's change in Montana is $1,886.50, so it shows as $1,887.
The last column values Medicaid as policyengine-us 2.18.0 does: the state's 2023 Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment. That is an average over enrollees of all ages, not uprated to 2026. It is not a same-year average and not specific to aged enrollees, so it could overstate or understate what covering an aged enrollee costs. The years matter: with 2023 enrollment, Florida's $9,200 would be $7,034. It values coverage at average program cost; it is not a cash loss.
unearned_spm_unit.yaml:13), so SNAP sees only $721 of new income and falls by 45% of that.msp_cost.py:28).Review round 1 (all findings addressed)
test__california_payment_standard_override__then_it_sets_the_supplement(oracle),test__artifact__then_california_uses_the_published_standard,..._the_california_supplement_follows_the_standardtest__use_tax_at_a_bracket_edge__then_the_float32_guard_flags_it(oracle, both pensions),test__given_step_on_float32_noise__then_the_guard_reports_its_origin+ 10 other guard tests,test__artifact__then_the_float32_guard_found_nothinge4363903f3test__given_local_commit_not_on_origin__then_source_is_refused+ 8 other source-check cases,test__live_interpreter__then_its_source_is_published_and_intact,test__artifact__then_the_release_is_the_pinned_pypi_onemath.isfiniterefusaltest__given_nan_intermediate_aggregate__then_decomposition_is_refused(the review's counterexample),test__given_any_nonfinite_value__then_decomposition_is_refusedtest__given_first_year_after_last__then_carry_is_refusedtest__artifact__then_rows_name_each_program_and_credittest__given_refundable_credit_on_zero_tax__then_it_is_not_a_tax_row, artifact testtest__artifact__then_florida_medicaid_loss_is_explainedtest__artifact__then_no_local_path_is_writtenls-remote, GitHub origin, ignored files; (4) the runner and tracer verify files and can pin the digest; (5) added files detected; (6) pip bytecode rows ignored in the digest; (7, 8) one simulation per case with a shared system; (9) the QMB sentence needs MSP to appear; (10) explicit raises, notassert; (11) 29 unit tests of the script's own logic; nits: the tracer checks the installation is unchanged, and household groups are read once (generators work)test__given_branch_copy_of_a_main_value__then_it_reads_the_main_node,test__given_step_on_noise_above_131072__then_the_guard_still_sees_it,test__given_branch_deleted_on_origin__then_a_stale_ref_does_not_count,test__given_modified_checkout__then_source_is_refused[ignored],test__given_installed_files__then_record_verification_checks_each,test__given_a_digest_other_than_the_release__then_the_run_is_refused(oracle),tests/bridge/test_pe_us_sample_households_script.pytest__given_bare_string_group__then_household_is_refused,test__artifact__then_the_invented_history_has_no_psid_fieldsReview round 2 (all findings addressed)
Round 2 reran all nine cases independently and confirmed every number. Its findings were wording, one display rounding and one test gap. No computed result changed: the regenerated JSON's
resultsequal those at9e104e2.The fixes are in
aab4960. An independent Opus 5.5 reviewer then read them against the 2.18.0 code and MCA 15-30-2337 and 15-30-2340, and confirmed every new mechanism statement and number. Its follow-ups are ina915b96: the Medicaid check also covers the with-health leaf, the guard's two float32 limits are stated and tested,_moneyrefuses non-numbers, and the doc's rounding and statute wording is tightened.…_net_household_incomevariable returns the reductiontest__montana_evidence__then_income_and_its_reduction_are_separate,test__artifact__then_montana_credit_is_described_exactlymedicaid_costleaf, against the release's ratiotest__given_the_release_totals__then_both_ratios_are_computed,test__given_other_enrollment_dates__then_the_valuation_is_refused,test__valuation_text__then_it_is_exact_and_two_sided,test__given_memo_off_the_ratio__then_writing_is_refused,test__given_with_health_medicaid_off_the_ratio__then_it_is_refused,test__artifact__then_the_medicaid_valuation_is_exact_both_waysgov.states.ca.cpiends in 2025 and is extended withgov.irs.uprating(ca/cpi.yaml:3-9,15). The exemption credit is uprated too. Household C's $21 is derived in the doc (tax $393.99, less two $156.47 exemption credits and the $60 renter's credit), checked on a 2.18.0 runWRONG_WAY_CATEGORIESadds federal and state refundable creditstest__given_a_refundable_credit_rising__then_writing_is_refused(2 cases),test__wrong_way_categories__then_they_cover_benefits_taxes_and_credits,test__artifact__then_the_script_checked_refundable_creditstest__override_cases_run_with_others__then_each_equals_the_case_alone(oracle)_moneyrounds half-dollars up in magnitude, in integer cents, for every table and chart label, and refuses a value that is not a number; the Markdown and chart footers say each amount is rounded on its owntest__given_cents__then_half_dollars_round_up(11 cases),test__given_any_cents__then_money_is_the_half_up_dollar(Hypothesis),test__artifact__then_half_dollars_round_uptest__artifact__then_the_guard_text_states_its_noise_band_counts_as_change: a variable's own change counts from a cent less one float32 step at its size, never under half a cent. Two limits are stated and pinned: from $32,768 to $131,072 one or two float32 steps of noise also count (a false alarm, not a miss), and the bound covers a stored value, not one computed from larger operandstest__given_exact_one_cent_float32_step__then_it_is_reported(the review's counterexample),test__given_any_one_cent_float32_step__then_it_is_reportedandtest__given_change_under_half_a_cent__then_it_is_not_reported(Hypothesis),test__given_one_float32_step_of_noise_at_70000__then_it_is_reported,test__given_one_cent_step_computed_from_larger_operands__then_missedtest__artifact__then_no_public_file_points_outside_the_repository:14-23; the full Medicaid limit path everywhere; CSFP's age fromcsfp/min_age.yaml:3test__artifact__then_the_medicaid_limit_is_cited_in_full_GITHUB_ORIGINchecks the host onlytest__given_origin_not_on_github__then_source_is_refused,test__given_origin_url__then_only_github_urls_matchInvariants
Hypothesis properties (unit tier), checked over generated inputs:
float32_stepis the float32 spacing. A jump of a cent or more over a sub-cent read, and a one-cent float32 step at any magnitude float32 can resolve, are always reported. A chain of real changes, identical runs and a change under half a cent never are.Example tests (unit tier), on chosen inputs:
household_net_incomedefinition, a cyclic tree and a minimal NaN counterexample are refused; categories and a refundable credit on a zero liability are labelled as documented.Oracle tests (live policyengine-us 2.18.0):
ca_use_taxat a $30,000 pension and nothing at $31,200, and keys the itemizing branches apart;The script checks every household-state pair, with and without health coverage, before writing:
medicaid_costleaf;The per-case runner reproduces the earlier batched runner's decompositions exactly, all 18. Only Medicaid-at-cost memo values move, by one float32 step, from the cross-case aggregation that per-case simulations remove.
Tests run (targeted; CI runs the full suite)
tests/bridge/, all four modules, withPOPULACE_DYNAMICS_PE_US_PYTHONset to the 2.18.0 interpreter, run from agit archiveexport ofd21171e: 228 passed, 0 skipped (28 min on a heavily loaded host).test_policyengine_us_bridge.py: 136 passed.test_pe_us_sample_households_script.py: 55 passed.test_pe_us_bridge_artifact.py: 19 passed.test_policyengine_us_bridge_oracle.py: 18 passed, against live policyengine-us 2.18.0.pytest -p no:xdist tests -k test__given_collected_suite__then_tiers_match_policy_manifest: passed, 11,027 collected.tests/conftest.pyalso treatsPOPULACE_DYNAMICS_PE_US_PYTHONas an oracle-tier indicator.black -l 79 --checkandruff checkare clean on all 11 changed Python files.engine/loop.py,engine/steps.py,gates.yamlandruns/*are untouched.a915b96. The JSONresultsequal those at9e104e2exactly. Of 492 Markdown table cells, the only changes are household C in Montana's +1,886 → +1,887 (three cells: the summary's two columns and its table's net change).Caveats
gov.states.ca.cpi, which is itself the federal IRS uprating index after 2025. Medicaid at cost is 2023 spending over October 2024 Medicaid and CHIP enrollment.mt_elderly_homeowner_or_renter_credit_net_household_incomevariable returns the income reduction despite its name. Fixing either is a PolicyEngine-US change.axiom: n/a: this PR encodes and changes no PolicyEngine-US policy; it reads the published 2.18.0 release and overrides one parameter at run time, with its source.
🤖 Generated with Claude Code