From 839d49c434758e61c4ec5e8306a8cd29b02b4a44 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 5 Nov 2026 11:16:26 -0500 Subject: [PATCH 1/9] Own the slide 9 and Build figures, and name the file behind 2.2.1's run Slide 11's caption and the notes now say we ran slide 9 on 2.90.2 and estimate each state cost, in place of giving those verbs to a release. EVIDENCE names the local-area file for the 2.2.1 run in place of "the same records", which read as the national file above it, and records that #1445 will rerun acs_local without the -1 id. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 4 ++-- slideshows/appam-2026/SPEAKER-NOTES.md | 4 ++-- slideshows/appam-2026/slides/WebAppsSlide.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index c18bf669..371073a9 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -105,7 +105,7 @@ The script keeps input variables and IDs. It renames the stored Social Security - The file's 36,491 Massachusetts household records come from 36,197 source households. They hold an effective sample of about 301 households, or 132 among those with children. - We estimate a Massachusetts child poverty rate of 21.4% for 2025 and 23.1% for 2026. Census reports 12.8%, with a margin of error of 2.0 points, for the 2023 to 2025 average (P60-290 Table 18, as used in `slideshows/cpid-webinar-2026`). That difference ranks third in size among the 51 jurisdictions, after Delaware and Louisiana. - On the national file, we estimate that Massachusetts would forgo $555 million and 29.8% of households would gain. The poverty gap would shrink by $167 million (`data/massachusetts_reform_default.json`). -- On the same records at policyengine-us 2.2.1, the version `policyengine.py` 6.12.0 pins, we estimate that Massachusetts would forgo $657 million and 34.1% of households would gain. The child poverty rate would fall 0.5 points, from 22.2% to 21.7% (`data/massachusetts_reform_221.json`). We changed how the model computes the credit between those versions. On a joint return, 2.2.1 counts a filer with a disability as a qualifying individual, and 2.90.2 counts a filer incapable of self-care. 2.90.2 also counts a dependent incapable of self-care at any age. We did not separate how much of the difference those changes explain. +- On the local-area file at policyengine-us 2.2.1, the version `policyengine.py` 6.12.0 pins, we estimate that Massachusetts would forgo $657 million and 34.1% of households would gain. The child poverty rate would fall 0.5 points, from 22.2% to 21.7% (`data/massachusetts_reform_221.json`). We changed how the model computes the credit between those versions. On a joint return, 2.2.1 counts a filer with a disability as a qualifying individual, and 2.90.2 counts a filer incapable of self-care. 2.90.2 also counts a dependent incapable of self-care at any age. We did not separate how much of the difference those changes explain. - On policyengine-us 2.2.1 and the Microcosm 2024 population, we estimate that 16.6% of children nationally lived below the threshold in 2025, and Census reports 13.4% (`BaselineVsCensusSlide` in `slideshows/cpid-webinar-2026`). ## State baselines @@ -115,7 +115,7 @@ The script keeps input variables and IDs. It renames the stored Social Security - Our rates differ from Census's 2023 to 2025 averages by 3.2 points on average and exceed Census's by 0.9 points on net. They exceed Census's in 30 of 51 jurisdictions. In 29 of 51 jurisdictions the difference exceeds Census's margin of error. - Effective samples of households with children run from 53 in Maine to 407 in California, with a median of 108. Across all households they run from 157 in Kentucky to 905 in California, with a median of 237 (`data/effective_samples.json`). - `policyengine.py` leaves ACS group quarters out of SPM measures, and we keep them. Leaving them out moves no state's rate by more than 0.023 points (Connecticut) and moves Massachusetts's by 0.01 (`child_spm_percent_without_group_quarters` in `data/state_baseline.json`). -- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. +- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. We plan to rerun `acs_local` in #1445 without the −1, and this match covers 247f1a88 alone. - On the national file, our rates differ from Census's by 4.8 points on average. They exceed Census's in 40 of 51 jurisdictions and fall outside its margin in 38 (`data/state_baseline_default.json`). The median effective sample includes 63 households with children (`data/effective_samples.json`). ## Effective samples diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index 4273e126..580696ed 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -88,7 +88,7 @@ CliffWatch and Build need no code. CliffWatch charts a household's net income as its earnings rise and marks where a raise leaves it with less. For example, a single parent of two school-age children in Massachusetts loses the state's cash assistance, TAFDC, when earnings pass $18,000. CliffWatch counts health coverage in net income. When you try it, the chart can take up to a minute to finish drawing. -In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. The run takes about three minutes, so the recording cuts to the finished report. Build runs an older release, PolicyEngine US 2.2.1, which puts the state's cost at $657 million. This deck's 2.90.2 gives the $577 million on slide 9. +In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. The run takes about three minutes, so the recording cuts to the finished report. Build runs an older release, PolicyEngine US 2.2.1, and on it we estimate the state's cost at $657 million. On this deck's 2.90.2, we estimate the $577 million on slide 9. *The recording ends on the report's results. Press next when you finish.* @@ -186,7 +186,7 @@ We test a new applicant in every month, so Alabama's five months don't describe **Do all these interfaces run one version?** They run different releases of the same code. This deck runs PolicyEngine US 2.90.2, CliffWatch ran 1.755.5 on November 4, Build ran 2.2.1 on November 5, and PyPI carried 2.95.0 on November 5. -**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and slide 9 runs 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed which filers and dependents the credit counts, and we haven't separated how much of the difference those changes explain. Build's 45.7% counts people, and slide 9's 29.4% counts households. +**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed which filers and dependents the credit counts, and we haven't separated how much of the difference those changes explain. Build's 45.7% counts people, and slide 9's 29.4% counts households. **Why does CliffWatch's net income exceed slide 5's net resources?** CliffWatch counts Medicaid, CHIP and premium tax credits in net income, and we leave health coverage out of net resources. Slide 11's family also pays nothing for child care, and slide 5's family pays $1,500 a month before any subsidy. diff --git a/slideshows/appam-2026/slides/WebAppsSlide.tsx b/slideshows/appam-2026/slides/WebAppsSlide.tsx index 26d41e6c..ef90200e 100644 --- a/slideshows/appam-2026/slides/WebAppsSlide.tsx +++ b/slideshows/appam-2026/slides/WebAppsSlide.tsx @@ -23,7 +23,7 @@ const apps = [ height: 640, alt: 'Build’s report on doubling the Massachusetts child and family tax credit amount from $440 to $880 from January 1, 2026: $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% decrease in child poverty', caption: - 'Find a parameter by name, set a new value and run a report. For example, we doubled the Massachusetts child and family credit. Build runs PolicyEngine US 2.2.1, and slide 9 runs 2.90.2.', + 'Find a parameter by name, set a new value and run a report. For example, we doubled the Massachusetts child and family credit. Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2.', }, ]; From d11cd698a8acbc2a732c9d1dbb70618348811ff3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 5 Nov 2026 11:45:22 -0500 Subject: [PATCH 2/9] Fold the deck review's wording fixes into the notes and slide 15 Speaker notes: drop the PDF lines, state what a Build report takes instead of narrating the recording, call differences from Census differences, and add answers for Build's 2.3% and 45.7% and for Georgia's late-report rule. Slide 15: mark Georgia as an example and print the graph's "total countable gross earned income". Ledger: state when the still gives way to the live tracker, and record that section 3650 narrows the loss section 3612 states. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 3 ++- slideshows/appam-2026/SPEAKER-NOTES.md | 29 ++++++++++++--------- slideshows/appam-2026/slides/AxiomSlide.tsx | 8 +++--- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index 371073a9..5094815b 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -195,7 +195,7 @@ We quote these on slide 7 and in the notes' Questions section, and we model none ## The web apps -We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. Slide 12 loads that page in a live frame at the still's crop, and the still shows until the tracker renders and stands in for it in the PDF. +We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. Slide 12 loads that page in a live frame at the still's crop. The still shows until the tracker prints that snapshot, and an export prints the still alone. We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's report on November 5, 2026, from the live sites. We scripted the recording, built it from screenshots and drew its cursor and address bar. CliffWatch drew that run's chart in under a second. Other runs took longer. @@ -210,3 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind its caption in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). +- Section 3612 says a household loses the deduction if it fails to report earned income on time, and `3612.yaml` encodes that sentence as losing all of it. Section 3650 of the manual denies the deduction only for the income reported late, when Georgia computes an overpayment, and 7 CFR 273.18(c)(1)(ii)(B) does the same. We haven't encoded section 3650. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index 580696ed..163e4a62 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -6,13 +6,12 @@ Sixteen slides in about 12 minutes. ## Before presenting -- If the room's laptop won't have internet, send Cynthia's team the PDF on Thursday morning. - Before the session, scan slide 16's QR code and check that it opens this deck. - Before the session, open Build's Massachusetts report at app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77 and check that it loads. - About 15 minutes before the session, click the CliffWatch address on slide 11 and give CliffWatch up to a minute to finish drawing the chart. If it fails, try again a minute later. Slide 11's recording doesn't depend on it. - Then press next on slide 11 to play its recording once, in the browser you'll present from. If it doesn't start, the slide keeps its screenshots, so talk over those and press next again. -- Slide 3 opens on the models' answers. Press next once to show PolicyEngine's answer. The PDF shows that second state. -- Keep these notes on your phone and the PDF on your laptop. Present from the live deck at policyengine.org/slides/appam-2026, and if the network fails, present from the PDF. +- Slide 3 opens on the models' answers. Press next once to show PolicyEngine's answer. +- Keep these notes on your phone, and present from the live deck at policyengine.org/slides/appam-2026. ## 1. PolicyEngine for state policy research (0:00, 30 seconds) @@ -88,19 +87,19 @@ CliffWatch and Build need no code. CliffWatch charts a household's net income as its earnings rise and marks where a raise leaves it with less. For example, a single parent of two school-age children in Massachusetts loses the state's cash assistance, TAFDC, when earnings pass $18,000. CliffWatch counts health coverage in net income. When you try it, the chart can take up to a minute to finish drawing. -In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. The run takes about three minutes, so the recording cuts to the finished report. Build runs an older release, PolicyEngine US 2.2.1, and on it we estimate the state's cost at $657 million. On this deck's 2.90.2, we estimate the $577 million on slide 9. +In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. A report takes about three minutes to run. Build runs an older release, PolicyEngine US 2.2.1, and reports a $657 million cost. On 2.90.2, we estimate the $577 million on slide 9. *The recording ends on the report's results. Press next when you finish.* ## 12. We model federal, state and local tax and benefit rules (9:25, 25 seconds) -We model federal income and payroll taxes and credits and every state income tax, in 44 jurisdictions. We model TANF and child care subsidies in all 50 states and DC, plus SNAP, WIC, SSI, school meals, ACA premium tax credits, Medicaid and CHIP eligibility and local taxes such as Philadelphia's wage tax. Our coverage tracker lists 109 programs and marks 87 complete, 18 partial and 4 in progress. +We model federal income and payroll taxes and credits and every state income tax, in 44 jurisdictions. We model TANF and child care subsidies in all 50 states and DC, plus SNAP, WIC, SSI, school meals, ACA premium tax credits, Medicaid and CHIP eligibility and local taxes such as Philadelphia's wage tax. We list 109 programs in our coverage tracker and mark 87 complete, 18 partial and 4 in progress. -*The tracker runs live in the frame, so you can click and scroll in it, and the arrows keep moving the deck. If you click its search box, click the slide outside the frame, which also moves to the next step.* +*The tracker runs live in the frame, so you can click and scroll in it, and the arrows keep moving the deck. If you click its search box, the arrows stay in the box until you click the slide outside the frame, and that click also moves the deck to the next step.* ## 13. We build Microcosm, our microdata, from surveys and official totals (9:50, 50 seconds) -We build Microcosm in two files. In the national file, we fit household weights against 5,659 targets, including IRS SOI and Census population totals. We use SSA payment totals to rescale four Social Security benefit inputs, and when we draw SNAP and Medicaid take-up, we keep everyone who reports them. We use IRS PUF records to impute 65 tax-detail variables into copies of CPS ASEC households, and add inputs from SCF 2022, SIPP 2023 and CPS ORG 2024. For state results, we build the local-area file. We transfer inputs from a finished ASEC and PUF donor to 1.5 million 2024 ACS records, draw each record's county and district within its PUMA, and fit 4,765 targets by state and congressional district. +We build two Microcosm files. In the national file, we fit household weights against 5,659 targets, including IRS SOI and Census population totals. We use SSA payment totals to rescale four Social Security benefit inputs, and when we draw SNAP and Medicaid take-up, we keep everyone who reports them. We use IRS PUF records to impute 65 tax-detail variables into copies of CPS ASEC households, and add inputs from SCF 2022, SIPP 2023 and CPS ORG 2024. For state results, we build the local-area file. We transfer inputs from a finished ASEC and PUF donor to 1.5 million 2024 ACS records, draw each record's county and district within its PUMA, and fit 4,765 targets by state and congressional district. ## 14. We're working on Medicaid rules, heating assistance, state poverty accuracy and local taxes (10:40, 30 seconds) @@ -108,7 +107,7 @@ On the rules, we're applying each state's Medicaid rule for an applicant whose s ## 15. The Axiom Foundation encodes rules with source text and dates (11:10, 50 seconds) -I also lead the Axiom Foundation. We publish open, machine-readable encodings of rules, starting with tax and benefit policy. Congress sets SNAP's 20% earned income deduction, and Georgia's Division of Family and Children Services applies it in section 3612 of its SNAP manual. We record the manual's words and effective date beside each value, and our site draws the rule as this graph. Georgia allows the deduction when a household has earned income and reports it on time, and applies the rate to its gross earned income. +I also lead the Axiom Foundation. We publish open, machine-readable encodings of rules, starting with tax and benefit policy. For example, Congress sets SNAP's 20% earned income deduction, and Georgia's Division of Family and Children Services applies it in section 3612 of its SNAP manual. We record the manual's words and effective date beside each value, and our site draws the rule as this graph. Georgia allows the deduction when a household has earned income and reports it on time, and applies the rate to its total countable gross earned income. When we run this rule through the Axiom engine, a family earning $30,000 a year gets a $500 monthly deduction. Ask for November 2019, before the manual section we encoded took effect, and the engine refuses. @@ -168,7 +167,7 @@ We test a new applicant in every month, so Alabama's five months don't describe **Why does Indiana turn this family down all year?** Indiana's limit since April 5, 135% of the poverty guideline, comes to $3,074 a month for a family of three, and this family earns $3,333. We found no chart Indiana posted before April. Our model reads 150% of the 2026 guideline through April, or $3,415 a month. But 150% of the 2025 guideline gives $3,331, $2 under this family's income. PN3 prints 140% of poverty for 2025 and 137% for 2026 in its Roadmap, and reports that a bill to raise the limit to 150% didn't pass. So we hold Indiana to $3,331 before April. Chalkbeat reported in April that Indiana would restart child care help in May after a 15-month freeze. At the model's limit, we estimate four months of subsidy and $31,487. -**Why does Indiana sit below Idaho when PN3 calls Idaho's limit the lowest?** This family misses the limit in both states, so neither pays any of its child care bill, and taxes set the order. Indiana collects $522 in state income tax after its $392 earned income credit, plus $626 in Marion County income tax. Idaho pays the family a net $455: a $465 grocery credit less a $10 building tax. Those amounts make up the $1,603 gap. Without the county tax, Indiana would still sit lowest, at $27,188. +**Why does Indiana sit below Idaho when PN3 calls Idaho's limit the lowest?** This family's income exceeds the limit in both states, so neither pays any of its child care bill, and taxes set the order. Indiana collects $522 in state income tax after its $392 earned income credit, plus $626 in Marion County income tax. Idaho pays the family a net $455: a $465 grocery credit less a $10 building tax. Those amounts make up the $1,603 gap. Without the county tax, Indiana would still sit lowest, at $27,188. **What about a family already in the program?** It keeps the subsidy to a higher limit, then meets its own cliff. We estimate that cliff at $15,572 past about $110,000 in Maryland and $6,817 past about $68,000 in Iowa. Idaho's department told families in the program that it applies the lower limit when they recertify, and said it was preparing a phase-out. At $40,000, we estimate that a family in the program gets $16,200 in Nevada, $14,776 in Indiana and $14,394 in Ohio, whose first program charges a copay. @@ -176,7 +175,7 @@ We test a new applicant in every month, so Alabama's five months don't describe **How accurate are your state poverty rates?** Our local-area file holds thin state samples, about 108 effective households with children in the median state. Our child poverty estimates on that file differ from Census's 2023 to 2025 averages by 3.2 points on average. In 29 of 51 jurisdictions, they fall outside Census's margin of error. For state child poverty rates, bring your own microdata. -**Could sampling error explain the misses against Census?** Our own sampling error could produce misses of that size. We estimate a standard error of 4.6 points on Massachusetts's 2026 child poverty rate, more than twice the 2.0-point margin of error Census reports for the state. Our file holds fewer effective households with children in the median state, about 108 against Massachusetts's 132. +**Could sampling error explain the differences from Census?** Our own sampling error could produce differences of that size. We estimate a standard error of 4.6 points on Massachusetts's 2026 child poverty rate, more than twice the 2.0-point margin of error Census reports for the state. Our file holds fewer effective households with children in the median state, about 108 against Massachusetts's 132. **How well do your weights fit?** We fit 95.69% of the national file's 5,659 targets within 10%, and 96.08% of the local-area file's 4,765. Those shares describe the fit in sample. The two files hold effective samples of 9,174 and about 12,124 households. @@ -184,9 +183,15 @@ We test a new applicant in every month, so Alabama's five months don't describe **What else does the simulated eligibility measure show?** We estimate that the same 3,000 households' children qualify for Medicaid or CHIP at rates from 39.8% in Idaho to 66.1% in Hawaii. -**Do all these interfaces run one version?** They run different releases of the same code. This deck runs PolicyEngine US 2.90.2, CliffWatch ran 1.755.5 on November 4, Build ran 2.2.1 on November 5, and PyPI carried 2.95.0 on November 5. +**Do all these interfaces run one version?** They run different releases of the same code. This deck runs PolicyEngine US 2.90.2, our coverage tracker reads 2.72.3, CliffWatch ran 1.755.5 on November 4, Build ran 2.2.1 on November 5, and PyPI carried 2.95.0 on November 5. -**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed which filers and dependents the credit counts, and we haven't separated how much of the difference those changes explain. Build's 45.7% counts people, and slide 9's 29.4% counts households. +**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed which filers and dependents qualify for the credit, and we haven't separated how much of the difference those changes explain. + +**Why does Build's report show a 2.3% decrease in child poverty?** Build reports a relative change. When we run 2.2.1 on slide 9's local-area file, we estimate a fall from 22.2% to 21.7%, or 0.5 points. On 2.90.2, we estimate a fall from 23.1% to 22.9%, or 0.2 points. + +**Why does Build's report show 45.7% gaining and slide 9 show 29.4%?** Build's 45.7% counts people, and slide 9's 29.4% counts households. When we run 2.2.1 on slide 9's local-area file, we estimate that 34.1% of households would gain. + +**Does a late report cost a Georgia household the whole deduction?** Section 3612 says a household loses the deduction if it fails to report earned income on time, and we encoded that sentence as losing all of it. Georgia's section 3650 denies the deduction only for the income reported late, when it computes an overpayment, and USDA's claims rule at 7 CFR 273.18 does the same. We haven't encoded section 3650. **Why does CliffWatch's net income exceed slide 5's net resources?** CliffWatch counts Medicaid, CHIP and premium tax credits in net income, and we leave health coverage out of net resources. Slide 11's family also pays nothing for child care, and slide 5's family pays $1,500 a month before any subsidy. diff --git a/slideshows/appam-2026/slides/AxiomSlide.tsx b/slideshows/appam-2026/slides/AxiomSlide.tsx index 6f7126fa..6167a8a8 100644 --- a/slideshows/appam-2026/slides/AxiomSlide.tsx +++ b/slideshows/appam-2026/slides/AxiomSlide.tsx @@ -37,15 +37,15 @@ export default function AxiomSlide() { axiom.org . Georgia allows the deduction when a household has earned income and reports it on time, and - applies the rate to its gross earned income. + applies the rate to its total countable gross earned income.

- Congress sets SNAP’s 20% earned income deduction, and Georgia’s Division of Family - and Children Services applies it in section 3612 of its SNAP manual. Axiom encodes that rule - and records the manual’s words beside each value: “20% earned income + For example, Congress sets SNAP’s 20% earned income deduction, and Georgia’s Division + of Family and Children Services applies it in section 3612 of its SNAP manual. Axiom encodes that + rule and records the manual’s words beside each value: “20% earned income deduction” and “Effective Date: December 2019.”

From 31b59c3c7bd8d6113302506a6c6f7cbca0fd75d5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 5 Nov 2026 12:08:07 -0500 Subject: [PATCH 3/9] Hold slide 12's still until the tracker rests, and fix slide 15's Georgia line Slide 12's frame: - Keep the still until the tracker's four count cards reach full opacity and full size. WebKit showed the frame while three cards still read opacity 0. - Pin the snapshot line to its commit as well as its release. - Bring the still back when the frame's page goes away, so a reload can't show the fallback counts. - Drop the same-origin shortcut, so a branch preview keeps the still for good. - Let the arrows drive the deck from the year menu. Only a text field keeps them. Slide 15 and its spoken line said Georgia allows the deduction when a household "reports it on time". Section 3650 and 7 CFR 273.18 limit that loss to the income reported late, in an overpayment computation, so the caption now states the rate and its base, and Questions carries the limit. Add the 2.2.1 run on the national file. It gives $628.7 million and no change in child poverty, so Build's $657.4m and 2.3% match the local-area file, though the report's record names the national file. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 6 +- slideshows/appam-2026/SPEAKER-NOTES.md | 6 +- .../analysis/massachusetts_reform_national.py | 58 +++++ .../massachusetts_reform_221_national.json | 15 ++ slideshows/appam-2026/slides/AxiomSlide.tsx | 4 +- .../appam-2026/slides/CoverageEmbed.test.tsx | 235 +++++++++++++++++- .../appam-2026/slides/CoverageEmbed.tsx | 55 +++- 7 files changed, 346 insertions(+), 33 deletions(-) create mode 100644 slideshows/appam-2026/analysis/massachusetts_reform_national.py create mode 100644 slideshows/appam-2026/data/massachusetts_reform_221_national.json diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index 5094815b..d076776b 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -195,14 +195,14 @@ We quote these on slide 7 and in the notes' Questions section, and we model none ## The web apps -We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. Slide 12 loads that page in a live frame at the still's crop. The still shows until the tracker prints that snapshot, and an export prints the still alone. +We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. We load that page in a live frame on slide 12 at the still's crop, show the still until the tracker prints that snapshot line and its count cards come to rest, and print the still alone in an export. We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's report on November 5, 2026, from the live sites. We scripted the recording, built it from screenshots and drew its cursor and address bar. CliffWatch drew that run's chart in under a second. Other runs took longer. - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- The report's record names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`. Its $657.4m matches our 2.2.1 run on the local-area file (`data/massachusetts_reform_221.json`, $657,356,800), and `policyengine.py` 6.12.0 selects that file for state regions. We haven't resolved which file the run read, so the notes compare releases and name no file for Build. +- The report's record names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and its figures match the local-area file, which `policyengine.py` 6.12.0 selects for state regions. At 2.2.1, our run on the local-area file gives $657,356,800 and a 2.3% relative fall in child poverty, from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). Our run on the national file gives $628.7 million and no change in the child poverty rate (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. That national run gives 46.7% of people gaining more than $1 and 44.5% gaining more than 0.1% of household income, and our local-area run doesn't count people. The notes compare releases and name no file for Build. ## Axiom @@ -210,4 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind its caption in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- Section 3612 says a household loses the deduction if it fails to report earned income on time, and `3612.yaml` encodes that sentence as losing all of it. Section 3650 of the manual denies the deduction only for the income reported late, when Georgia computes an overpayment, and 7 CFR 273.18(c)(1)(ii)(B) does the same. We haven't encoded section 3650. +- Section 3612 says a household loses the deduction if it fails to report earned income on time, and `3612.yaml` encodes that sentence as losing all of it. Section 3650 of the manual denies the deduction only for the income reported late, when Georgia computes an overpayment. 7 U.S.C. 2014(e)(2)(C) and 7 CFR 273.18(c)(1)(ii)(B) set the same limit, and the regulation keeps the deduction in an agency-error claim. We haven't encoded section 3650, so slide 15's caption states the rate and its base, and leaves the late-report path to the graph and the notes' Questions. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index 163e4a62..1c3b2068 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -95,7 +95,7 @@ In Build, you can open the same Massachusetts credit, set it to $880, pick Massa We model federal income and payroll taxes and credits and every state income tax, in 44 jurisdictions. We model TANF and child care subsidies in all 50 states and DC, plus SNAP, WIC, SSI, school meals, ACA premium tax credits, Medicaid and CHIP eligibility and local taxes such as Philadelphia's wage tax. We list 109 programs in our coverage tracker and mark 87 complete, 18 partial and 4 in progress. -*The tracker runs live in the frame, so you can click and scroll in it, and the arrows keep moving the deck. If you click its search box, the arrows stay in the box until you click the slide outside the frame, and that click also moves the deck to the next step.* +*Once the tracker loads, you can click and scroll in the frame, and the arrows keep moving the deck. A click in its search box stops the arrows until you click elsewhere in the frame. Choosing a year in its menu changes the counts.* ## 13. We build Microcosm, our microdata, from surveys and official totals (9:50, 50 seconds) @@ -107,7 +107,7 @@ On the rules, we're applying each state's Medicaid rule for an applicant whose s ## 15. The Axiom Foundation encodes rules with source text and dates (11:10, 50 seconds) -I also lead the Axiom Foundation. We publish open, machine-readable encodings of rules, starting with tax and benefit policy. For example, Congress sets SNAP's 20% earned income deduction, and Georgia's Division of Family and Children Services applies it in section 3612 of its SNAP manual. We record the manual's words and effective date beside each value, and our site draws the rule as this graph. Georgia allows the deduction when a household has earned income and reports it on time, and applies the rate to its total countable gross earned income. +I also lead the Axiom Foundation. We publish open, machine-readable encodings of rules, starting with tax and benefit policy. For example, Congress sets SNAP's 20% earned income deduction, and Georgia's Division of Family and Children Services applies it in section 3612 of its SNAP manual. We record the manual's words and effective date beside each value, and our site draws the rule as this graph. Georgia allows the deduction for a household with earned income, and applies the rate to its total countable gross earned income. When we run this rule through the Axiom engine, a family earning $30,000 a year gets a $500 monthly deduction. Ask for November 2019, before the manual section we encoded took effect, and the engine refuses. @@ -191,7 +191,7 @@ We test a new applicant in every month, so Alabama's five months don't describe **Why does Build's report show 45.7% gaining and slide 9 show 29.4%?** Build's 45.7% counts people, and slide 9's 29.4% counts households. When we run 2.2.1 on slide 9's local-area file, we estimate that 34.1% of households would gain. -**Does a late report cost a Georgia household the whole deduction?** Section 3612 says a household loses the deduction if it fails to report earned income on time, and we encoded that sentence as losing all of it. Georgia's section 3650 denies the deduction only for the income reported late, when it computes an overpayment, and USDA's claims rule at 7 CFR 273.18 does the same. We haven't encoded section 3650. +**Does a late report cost a Georgia household the whole deduction?** No. Georgia says in section 3612 that a household loses the deduction if it fails to report earned income on time, and we encoded that sentence as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment, and USDA does the same in its claims rule at 7 CFR 273.18. We haven't encoded section 3650. **Why does CliffWatch's net income exceed slide 5's net resources?** CliffWatch counts Medicaid, CHIP and premium tax credits in net income, and we leave health coverage out of net resources. Slide 11's family also pays nothing for child care, and slide 5's family pays $1,500 a month before any subsidy. diff --git a/slideshows/appam-2026/analysis/massachusetts_reform_national.py b/slideshows/appam-2026/analysis/massachusetts_reform_national.py new file mode 100644 index 00000000..547d2db9 --- /dev/null +++ b/slideshows/appam-2026/analysis/massachusetts_reform_national.py @@ -0,0 +1,58 @@ +"""Double the Massachusetts credit on the national Microcosm file, at policyengine-us 2.2.1. + +Build's report 4940 names this file in its record and prints a state cost, a fall in child poverty +and a share of people gaining, so this run scores all three. Install policyengine-us 2.2.1 first. +Usage: massachusetts_reform_national.py +""" +import json +import sys +from importlib.metadata import version +from pathlib import Path + +import numpy as np +import pandas as pd + +import massachusetts_reform as mr +import state_baseline as sb +from policyengine_core.data import Dataset +from policyengine_core.reforms import Reform +from policyengine_us import CountryTaxBenefitSystem, Microsimulation + +NATIONAL = "hf://datasets/policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915" + +sb.known = set(CountryTaxBenefitSystem().variables) +with pd.HDFStore(sb.download(NATIONAL), "r") as store: + sb.tables = {name: store.select(name) for name in sb.TABLES} +people = sb.state_file(mr.MASSACHUSETTS_FIPS) +for name in list(sb.tables): + del sb.tables[name] + + +def sim(reform=None): + return Microsimulation(dataset=Dataset.from_dataframe(people, sb.DATA_YEAR), reform=reform) + + +reform = Reform.from_dict({mr.PARAMETER: {f"{mr.YEAR}-01-01.2100-12-31": mr.REFORMED}}, country_id="us") +base_sim, new_sim = sim(), sim(reform) +base, new = mr.results(base_sim), mr.results(new_sim) +gains = new["household_net_income"] - base["household_net_income"] +weights = base["household_weight"] +heads = base_sim.calculate("household_count_people", mr.YEAR).values +relative = gains / np.maximum(base["household_net_income"], 1) +out = { + "policyengine_us_version": version("policyengine-us"), + "dataset": NATIONAL, + "household_records": len(weights), + "state_cost": base["state_income_tax"] - new["state_income_tax"], + "household_gain": float(np.sum(gains * weights)), + "households_gaining_share": float(np.sum(weights[gains > 1]) / np.sum(weights)), + "people_gaining_share_over_1_dollar": float(np.sum((weights * heads)[gains > 1]) / np.sum(weights * heads)), + "people_gaining_share_over_0_1_percent": float(np.sum((weights * heads)[relative > 0.001]) / np.sum(weights * heads)), + "baseline_child_poverty": base["child_poverty"], + "reformed_child_poverty": new["child_poverty"], + "child_poverty_relative_change": (new["child_poverty"] - base["child_poverty"]) / base["child_poverty"], + "baseline_poverty_gap": base["poverty_gap"], + "reformed_poverty_gap": new["poverty_gap"], +} +Path(sys.argv[1]).write_text(json.dumps(out, indent=1) + "\n") +print(json.dumps(out, indent=1)) diff --git a/slideshows/appam-2026/data/massachusetts_reform_221_national.json b/slideshows/appam-2026/data/massachusetts_reform_221_national.json new file mode 100644 index 00000000..44cdb5b4 --- /dev/null +++ b/slideshows/appam-2026/data/massachusetts_reform_221_national.json @@ -0,0 +1,15 @@ +{ + "policyengine_us_version": "2.2.1", + "dataset": "hf://datasets/policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915", + "household_records": 1522, + "state_cost": 628748288.0, + "household_gain": 628749504.0, + "households_gaining_share": 0.3399246037006378, + "people_gaining_share_over_1_dollar": 0.4673285902286119, + "people_gaining_share_over_0_1_percent": 0.4447628863353932, + "baseline_child_poverty": 0.21420167239953303, + "reformed_child_poverty": 0.21420166107374483, + "child_poverty_relative_change": -5.287441535496692e-08, + "baseline_poverty_gap": 10494078976.0, + "reformed_poverty_gap": 10309666816.0 +} diff --git a/slideshows/appam-2026/slides/AxiomSlide.tsx b/slideshows/appam-2026/slides/AxiomSlide.tsx index 6167a8a8..cd4cb2ec 100644 --- a/slideshows/appam-2026/slides/AxiomSlide.tsx +++ b/slideshows/appam-2026/slides/AxiomSlide.tsx @@ -36,8 +36,8 @@ export default function AxiomSlide() { > axiom.org - . Georgia allows the deduction when a household has earned income and reports it on time, and - applies the rate to its total countable gross earned income. + . Georgia allows the deduction for a household with earned income, and applies the rate to its + total countable gross earned income.
diff --git a/slideshows/appam-2026/slides/CoverageEmbed.test.tsx b/slideshows/appam-2026/slides/CoverageEmbed.test.tsx index 4ee9df29..be389b6a 100644 --- a/slideshows/appam-2026/slides/CoverageEmbed.test.tsx +++ b/slideshows/appam-2026/slides/CoverageEmbed.test.tsx @@ -3,6 +3,8 @@ import { createRoot, type Root } from 'react-dom/client'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import CoverageEmbed, { TRACKER_URL, forwardKey, trackerAgrees } from './CoverageEmbed'; +(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + let container: HTMLDivElement; let root: Root; @@ -43,31 +45,203 @@ describe('the coverage tracker on the rules slide', () => { }); }); +const SNAPSHOT = 'Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). 109 Total programs'; +const FALLBACK = 'Coverage reflects the bundled fallback registry. 64 Total programs'; + +const card = (label: string, style = '') => `

${label}

`; +/** The tracker's four cards, with the given attributes on the last one. */ +const cards = (lastStyle = '') => + [card('Total programs'), card('Complete'), card('Partial'), card('In progress', lastStyle)].join(''); +const CARD_STATES = { + 'at rest': { html: cards(), rests: true }, + 'at rest under an identity matrix': { html: cards('data-transform="matrix(1, 0, 0, 1, 0, 0)"'), rests: true }, + 'still fading in': { html: cards('data-opacity="0"'), rests: false }, + 'nearly faded in': { html: cards('data-opacity="0.99"'), rests: false }, + 'still growing': { html: cards('data-transform="matrix(0.9, 0, 0, 0.9, 0, 0)"'), rests: false }, + 'missing a card': { html: [card('Total programs'), card('Complete'), card('Partial')].join(''), rests: false }, + 'not drawn yet': { html: '', rests: false }, +}; + +/** A page whose computed styles come from data attributes, since jsdom computes neither property. */ +const stubPage = (innerText: string, status = 'loaded', html = cards()) => { + const body = document.createElement('div'); + body.innerHTML = html; + Object.defineProperty(body, 'innerText', { value: innerText }); + return { + body, + fonts: { status }, + defaultView: { + getComputedStyle: (element: HTMLElement) => ({ + opacity: element.dataset.opacity ?? '1', + transform: element.dataset.transform ?? 'none', + }), + }, + } as unknown as Document; +}; + describe('the check that shows the frame over the still', () => { - const page = (innerText: string, status = 'loaded') => - ({ body: { innerText }, fonts: { status } }) as unknown as Document; - const snapshot = 'Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). 109 Total programs'; + const TEXTS = { + 'the still’s snapshot': { text: SNAPSHOT, matches: true }, + 'the fallback counts': { text: FALLBACK, matches: false }, + 'another release': { text: 'Coverage reflects policyengine-us 2.95.0 (main @ abc1234). 120 Total programs', matches: false }, + 'another commit of the same release': { + text: 'Coverage reflects policyengine-us 2.72.3 (main @ abc1234, fetched 2026-11-01). 110 Total programs', + matches: false, + }, + 'nothing yet': { text: '', matches: false }, + }; - it('keeps the still while the tracker prints its fallback counts', () => { - expect(trackerAgrees(page('Coverage reflects the bundled fallback registry. 64 Total programs'))).toBe(false); + it('passes for every page that prints the snapshot, with fonts loaded and cards at rest, and no other', () => { + for (const [textName, { text, matches }] of Object.entries(TEXTS)) { + for (const status of ['loaded', 'loading']) { + for (const [cardName, { html, rests }] of Object.entries(CARD_STATES)) { + const expected = matches && status === 'loaded' && rests; + expect(trackerAgrees(stubPage(text, status, html)), `${textName}, ${status}, ${cardName}`).toBe(expected); + } + } + } }); - it('keeps the still when the tracker moves to another snapshot', () => { - expect(trackerAgrees(page('Coverage reflects policyengine-us 2.95.0 (main @ abc1234). 120 Total programs'))).toBe(false); + it('holds the still for a fading ancestor of a card', () => { + const html = `
${cards()}
`; + expect(trackerAgrees(stubPage(SNAPSHOT, 'loaded', html))).toBe(false); }); - it('keeps the still on an empty or unreadable page', () => { - expect(trackerAgrees(page(''))).toBe(false); + it('keeps the still when it can’t read the page', () => { expect(trackerAgrees(null)).toBe(false); expect(trackerAgrees(undefined)).toBe(false); }); - it('keeps the still while the page loads its fonts', () => { - expect(trackerAgrees(page(snapshot, 'loading'))).toBe(false); + it('keeps the still when the page has no window to read styles from', () => { + const page = { ...stubPage(SNAPSHOT), defaultView: null } as unknown as Document; + expect(trackerAgrees(page)).toBe(false); + }); +}); + +describe('when the frame shows', () => { + let resize: (width: number) => void; + + beforeEach(() => { + vi.useFakeTimers({ toFake: ['setInterval', 'clearInterval'] }); + vi.stubGlobal( + 'ResizeObserver', + class { + constructor(callback: (entries: { contentRect: { width: number } }[]) => void) { + resize = (width) => callback([{ contentRect: { width } }]); + } + observe() {} + disconnect() {} + }, + ); + }); + + afterEach(() => { + vi.useRealTimers(); + vi.unstubAllGlobals(); + }); + + const mount = async (page: Document | null) => { + window.history.replaceState(null, '', '/slides/appam-2026?slide=13'); + await renderEmbed(); + const frame = container.querySelector('iframe') as HTMLIFrameElement; + setPage(frame, page); + return frame; + }; + const setPage = (frame: HTMLIFrameElement, page: Document | null) => + Object.defineProperty(frame, 'contentDocument', { value: page, configurable: true }); + const show = (width: number) => act(async () => resize(width)); + const load = (frame: HTMLIFrameElement) => act(async () => void frame.dispatchEvent(new Event('load'))); + const poll = () => act(async () => void vi.advanceTimersByTime(1200)); + const shown = (frame: HTMLIFrameElement) => frame.style.opacity === '1' && frame.style.pointerEvents === 'auto'; + const hidden = (frame: HTMLIFrameElement) => frame.style.opacity === '0' && frame.style.pointerEvents === 'none'; + + it('needs the frame loaded, the slide showing and a page that agrees, in every combination', async () => { + const pages = { agreeing: true, fallback: false, unreadable: false }; + for (const loaded of [false, true]) { + for (const showing of [false, true]) { + for (const [name, agrees] of Object.entries(pages)) { + const page = name === 'agreeing' ? stubPage(SNAPSHOT) : name === 'fallback' ? stubPage(FALLBACK) : null; + const frame = await mount(page); + if (showing) await show(1171.5); + if (loaded) await load(frame); + await poll(); + const label = `loaded ${loaded}, showing ${showing}, ${name}`; + expect(shown(frame), label).toBe(loaded && showing && agrees); + expect(hidden(frame) || shown(frame), label).toBe(true); + await act(async () => root.unmount()); + root = createRoot(container); + } + } + } }); - it('shows the frame once the tracker matches the still', () => { - expect(trackerAgrees(page(snapshot))).toBe(true); + it('waits for the slide to show even when the hidden page already agrees', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await load(frame); + await poll(); + expect(hidden(frame)).toBe(true); + await show(1171.5); + expect(hidden(frame)).toBe(true); + await poll(); + expect(shown(frame)).toBe(true); + }); + + it('holds the still until the cards come to rest', async () => { + const frame = await mount(stubPage(SNAPSHOT, 'loaded', cards('data-opacity="0"'))); + await show(1171.5); + await load(frame); + await poll(); + expect(hidden(frame)).toBe(true); + setPage(frame, stubPage(SNAPSHOT)); + await poll(); + expect(shown(frame)).toBe(true); + }); + + it('brings the still back when the frame’s page goes away, until the check passes again', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await show(1171.5); + await load(frame); + await poll(); + expect(shown(frame)).toBe(true); + + setPage(frame, stubPage(FALLBACK)); + await act(async () => void frame.contentWindow?.dispatchEvent(new Event('pagehide'))); + expect(hidden(frame)).toBe(true); + await load(frame); + await poll(); + expect(hidden(frame)).toBe(true); + + setPage(frame, stubPage(SNAPSHOT)); + await poll(); + expect(shown(frame)).toBe(true); + }); + + it('stays on the still across origins, where the browser denies the frame’s window and document', async () => { + const frame = await mount(null); + const denied = new Proxy( + {}, + { + get() { + throw new DOMException('Blocked a frame with another origin', 'SecurityError'); + }, + }, + ); + Object.defineProperty(frame, 'contentWindow', { value: denied, configurable: true }); + await show(1171.5); + await load(frame); + await poll(); + expect(hidden(frame)).toBe(true); + }); + + it('keeps showing the frame when the slide leaves and comes back', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await show(1171.5); + await load(frame); + await poll(); + await show(0); + expect(frame.style.opacity).toBe('0'); + await show(1171.5); + expect(shown(frame)).toBe(true); }); }); @@ -79,6 +253,41 @@ describe('a key pressed inside the frame', () => { return event; }; + it('reaches the deck from every element except a text field', () => { + const editable = document.createElement('div'); + Object.defineProperty(editable, 'isContentEditable', { value: true }); + const targets: [HTMLElement, boolean][] = [ + [document.createElement('div'), true], + [document.createElement('select'), true], + [document.createElement('button'), true], + [document.createElement('a'), true], + [document.createElement('input'), false], + [document.createElement('textarea'), false], + [editable, false], + ]; + for (const key of ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', ' ', 'PageDown', 'Home', 'f']) { + for (const [target, reaches] of targets) { + const deck = vi.fn(); + window.addEventListener('keydown', deck); + press(key, target); + window.removeEventListener('keydown', deck); + expect(deck.mock.calls.length, `${key} on ${target.tagName}`).toBe(reaches ? 1 : 0); + } + } + }); + + it('moves the deck from the year menu, and leaves the menu the keys the deck ignores', () => { + const deck = vi.fn((event: KeyboardEvent) => { + if (event.key === 'ArrowRight') event.preventDefault(); + }); + window.addEventListener('keydown', deck); + const right = press('ArrowRight', document.createElement('select')); + const down = press('ArrowDown', document.createElement('select')); + window.removeEventListener('keydown', deck); + expect(right.defaultPrevented).toBe(true); + expect(down.defaultPrevented).toBe(false); + }); + it('reaches the deck, and the frame leaves a key the deck uses alone', () => { const deck = vi.fn((event: KeyboardEvent) => event.preventDefault()); window.addEventListener('keydown', deck); diff --git a/slideshows/appam-2026/slides/CoverageEmbed.tsx b/slideshows/appam-2026/slides/CoverageEmbed.tsx index 5b69c2d8..7c7446fd 100644 --- a/slideshows/appam-2026/slides/CoverageEmbed.tsx +++ b/slideshows/appam-2026/slides/CoverageEmbed.tsx @@ -19,17 +19,45 @@ const CROP = { left: 404.5, top: 105.5, width: STILL.width / 2, height: STILL.he * other counts under another line, and a loading or error page prints neither, * so the still stays until the tracker agrees with it. */ -const TRACKER_TEXT = 'Coverage reflects policyengine-us 2.72.3'; +const TRACKER_TEXT = 'Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82'; +/** The labels on the still's four count cards, which the tracker fades in. */ +const COUNT_LABELS = ['Total programs', 'Complete', 'Partial', 'In progress']; +const IDENTITY = 'matrix(1, 0, 0, 1, 0, 0)'; -/** Whether the page in the frame shows the still's snapshot in its own fonts. */ +/** Whether an element and everything around it have reached full opacity and size. */ +function atRest(element: Element, view: Window) { + for (let node: Element | null = element; node; node = node.parentElement) { + const { opacity, transform } = view.getComputedStyle(node); + if (Number(opacity) < 1 || (transform !== 'none' && transform !== IDENTITY)) return false; + } + return true; +} + +/** Whether the tracker has finished drawing the four count cards the still shows. */ +function countsAtRest(page: Document) { + const view = page.defaultView; + if (!view) return false; + const leaves = [...page.body.querySelectorAll('*')].filter((element) => element.children.length === 0); + return COUNT_LABELS.every((label) => { + const card = leaves.find((leaf) => leaf.textContent?.trim() === label); + return Boolean(card) && atRest(card as Element, view); + }); +} + +/** + * Whether the page in the frame prints the still's snapshot, with no font still + * loading and its count cards at rest. + */ export function trackerAgrees(page: Document | null | undefined) { if (!page?.body?.innerText?.includes(TRACKER_TEXT)) return false; - return (page.fonts?.status ?? 'loaded') === 'loaded'; + if ((page.fonts?.status ?? 'loaded') !== 'loaded') return false; + return countsAtRest(page); } +/** Only a text field keeps the arrows, so the year menu still lets them drive the deck. */ const isTyping = (target: EventTarget | null) => { const element = target as HTMLElement | null; - return Boolean(element?.isContentEditable) || ['INPUT', 'TEXTAREA', 'SELECT'].includes(element?.tagName ?? ''); + return Boolean(element?.isContentEditable) || ['INPUT', 'TEXTAREA'].includes(element?.tagName ?? ''); }; /** Hands a key pressed inside the frame to the deck, unless the key is typing. */ @@ -41,10 +69,11 @@ export function forwardKey(event: KeyboardEvent) { /** * The tracker, live on the slide, over a still of the same crop. The still - * shows until the tracker prints the still's snapshot, and stays if it never does. Clicks and - * scrolling go to the tracker. Arrow keys drive the deck after a click in the - * frame too, because the deck and the tracker share an origin in production; - * on a branch preview they stay with the frame until the slide is clicked. + * shows until the tracker prints the still's snapshot and its count cards come + * to rest, and it stays if they never do. It stays for good on a branch preview, + * where a cross-origin frame's document reads as null. Clicks and scrolling go + * to the tracker, and arrow keys still drive the deck after a click in the + * frame, unless the click lands in a text field. */ export default function CoverageEmbed({ alt }: { alt: string }) { const box = useRef(null); @@ -61,13 +90,12 @@ export default function CoverageEmbed({ alt }: { alt: string }) { return () => observer.disconnect(); }, [exporting]); - // A hidden frame starts no font loads, so the check waits for the slide to show. + // A hidden frame's cards read as at rest before they fade in, so the check waits for the slide to show. const visible = boxWidth > 0; useEffect(() => { if (!loaded || ready || !visible) return; - const sameOrigin = new URL(TRACKER_URL).origin === window.location.origin; const timer = setInterval(() => { - if (!sameOrigin || trackerAgrees(frame.current?.contentDocument)) setReady(true); + if (trackerAgrees(frame.current?.contentDocument)) setReady(true); }, 400); return () => clearInterval(timer); }, [loaded, ready, visible]); @@ -75,7 +103,10 @@ export default function CoverageEmbed({ alt }: { alt: string }) { const handleLoad = () => { setLoaded(true); try { - frame.current?.contentWindow?.addEventListener('keydown', forwardKey); + const page = frame.current?.contentWindow; + page?.addEventListener('keydown', forwardKey); + // A reload prints the fallback counts first, so the still returns until the check passes again. + page?.addEventListener('pagehide', () => setReady(false)); } catch { // A cross-origin frame, on a branch preview. } From ea96fa60f2e78373f664536f03dd48ee82ad5162 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 5 Nov 2026 19:44:22 -0500 Subject: [PATCH 4/9] Reword slide 12's stage direction and the Build and Georgia evidence, and remeasure the PDF links The notes now say what the search box and the year menu do to the arrow keys and the counts: after a click in the search box the arrows move its cursor, and a year limits the counts to programs we mark verified for it. EVIDENCE gives Build's figures "we estimate" and the Georgia entry its actors (Georgia, Congress, USDA, Axiom). finish_pdf.py takes the anchor rectangles measured on the 31b59c3c preview for slides 11, 12, 14 and 15. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 6 +++--- slideshows/appam-2026/SPEAKER-NOTES.md | 2 +- slideshows/appam-2026/analysis/finish_pdf.py | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index d076776b..4aa38e4e 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -195,14 +195,14 @@ We quote these on slide 7 and in the notes' Questions section, and we model none ## The web apps -We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. We load that page in a live frame on slide 12 at the still's crop, show the still until the tracker prints that snapshot line and its count cards come to rest, and print the still alone in an export. +We captured slide 12's coverage tracker from www.policyengine.org/us/model/rules/coverage on November 5, 2026. It shows 109 total programs, 87 complete, 18 partial and 4 in progress, and its top line reads "Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetched 2026-10-27). The production API served 2.2.1 when the snapshot was fetched." The deck's own runs use 2.90.2. We load that page in a live frame on slide 12 at the still's crop, and print the still alone in an export. We show the still until the tracker prints that snapshot line and its four count cards reach full opacity and size. We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's report on November 5, 2026, from the live sites. We scripted the recording, built it from screenshots and drew its cursor and address bar. CliffWatch drew that run's chart in under a second. Other runs took longer. - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- The report's record names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and its figures match the local-area file, which `policyengine.py` 6.12.0 selects for state regions. At 2.2.1, our run on the local-area file gives $657,356,800 and a 2.3% relative fall in child poverty, from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). Our run on the national file gives $628.7 million and no change in the child poverty rate (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. That national run gives 46.7% of people gaining more than $1 and 44.5% gaining more than 0.1% of household income, and our local-area run doesn't count people. The notes compare releases and name no file for Build. +- Report 4940's record names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, but the report's figures match the local-area file, which `policyengine.py` 6.12.0 selects for state regions. On the local-area file at 2.2.1, we estimate a $657,356,800 state revenue loss and a 2.3% relative fall in child poverty, from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. ## Axiom @@ -210,4 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind its caption in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- Section 3612 says a household loses the deduction if it fails to report earned income on time, and `3612.yaml` encodes that sentence as losing all of it. Section 3650 of the manual denies the deduction only for the income reported late, when Georgia computes an overpayment. 7 U.S.C. 2014(e)(2)(C) and 7 CFR 273.18(c)(1)(ii)(B) set the same limit, and the regulation keeps the deduction in an agency-error claim. We haven't encoded section 3650, so slide 15's caption states the rate and its base, and leaves the late-report path to the graph and the notes' Questions. +- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment. Congress sets the same limit in 7 U.S.C. 2014(e)(2)(C), and USDA sets it in 7 CFR 273.18(c)(1)(ii)(B), where it keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption, and leave the late-report case to the graph and the notes' Questions. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index 1c3b2068..c0d5c9bc 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -95,7 +95,7 @@ In Build, you can open the same Massachusetts credit, set it to $880, pick Massa We model federal income and payroll taxes and credits and every state income tax, in 44 jurisdictions. We model TANF and child care subsidies in all 50 states and DC, plus SNAP, WIC, SSI, school meals, ACA premium tax credits, Medicaid and CHIP eligibility and local taxes such as Philadelphia's wage tax. We list 109 programs in our coverage tracker and mark 87 complete, 18 partial and 4 in progress. -*Once the tracker loads, you can click and scroll in the frame, and the arrows keep moving the deck. A click in its search box stops the arrows until you click elsewhere in the frame. Choosing a year in its menu changes the counts.* +*Once the tracker loads, you can click and scroll in the frame, and the arrow keys still move the deck. After you click in its search box, the arrow keys move the box's cursor until you click elsewhere in the frame. If you pick a year in its menu, the tracker counts only the programs we mark verified for that year.* ## 13. We build Microcosm, our microdata, from surveys and official totals (9:50, 50 seconds) diff --git a/slideshows/appam-2026/analysis/finish_pdf.py b/slideshows/appam-2026/analysis/finish_pdf.py index 022c8782..52e5f5c1 100644 --- a/slideshows/appam-2026/analysis/finish_pdf.py +++ b/slideshows/appam-2026/analysis/finish_pdf.py @@ -41,15 +41,15 @@ (10, (807.28, 464.41, 1205.28, 483.41), "https://www.policyengine.org/us/child-poverty-impact-dashboard"), (10, (807.28, 569.63, 1150.28, 588.63), "https://github.com/PolicyEngine/policyengine-claude"), (10, (807.28, 650.56, 1117.28, 669.56), "https://github.com/PolicyEngine/policyengine-us"), - (11, (64, 491.14, 325, 513.14), "https://www.policyengine.org/us/cliffwatch?s=MA&zip=02139&ms=unmarried&p=33:a::0,6:c::0,10:c::0"), - (11, (814.64, 655.8, 1070.64, 677.8), "https://app.policyengine.org/us/build"), - (12, (64, 640, 500, 662), "https://www.policyengine.org/us/model/rules/coverage"), - (14, (1167, 343.09, 1321, 365.09), "https://github.com/PolicyEngine/microcosm/pull/1445"), - (14, (1167, 417.38, 1376, 439.38), "https://github.com/PolicyEngine/policyengine-us/pull/10640"), - (14, (1167, 463.42, 1374, 485.42), "https://github.com/PolicyEngine/policyengine-us/pull/10607"), - (14, (1167, 509.47, 1320, 531.47), "https://github.com/PolicyEngine/microcosm/pull/1453"), - (14, (1167, 583.75, 1320, 605.75), "https://github.com/PolicyEngine/microcosm/pull/1246"), - (15, (1123.16, 618.86, 1208.16, 640.86), "https://axiom.org/app?compose=us-ga:policies/dfcs/snap/3612"), + (11, (64, 525.2, 325, 547.2), "https://www.policyengine.org/us/cliffwatch?s=MA&zip=02139&ms=unmarried&p=33:a::0,6:c::0,10:c::0"), + (11, (712.3, 517.7, 968.3, 539.7), "https://app.policyengine.org/us/build"), + (12, (341, 665.58, 710, 687.58), "https://www.policyengine.org/us/model/rules/coverage"), + (14, (1167, 352.81, 1376, 374.81), "https://github.com/PolicyEngine/policyengine-us/pull/10640"), + (14, (1167, 394, 1374, 416), "https://github.com/PolicyEngine/policyengine-us/pull/10607"), + (14, (1167, 480.45, 1321, 502.45), "https://github.com/PolicyEngine/microcosm/pull/1445"), + (14, (1167, 549.88, 1320, 571.88), "https://github.com/PolicyEngine/microcosm/pull/1453"), + (14, (1167, 619.3, 1320, 641.3), "https://github.com/PolicyEngine/microcosm/pull/1246"), + (15, (352, 486.91, 421, 504.91), "https://axiom.org/app?compose=us-ga:policies/dfcs/snap/3612"), (16, (330.5, 434, 473.5, 455), "https://www.policyengine.org"), (16, (330.5, 506, 715.5, 527), "https://github.com/PolicyEngine/policyengine-slides/tree/main/slideshows/appam-2026"), (16, (330.5, 542, 526.5, 563), "mailto:max@policyengine.org"), From 4d3ec671578cb5f16c56223d5af0b11673f5cd2a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 5 Nov 2026 19:49:52 -0500 Subject: [PATCH 5/9] Save Build report 4940's record and state its figures beside our two runs api.policyengine.org/us/report/4940 names and hashes the national file and holds $657,359,316 and child poverty from 22.23% to 21.72%. EVIDENCE now cites that record, so the claim no longer rests on the ledger, and sets it beside our local-area run ($657,356,800; 22.25% to 21.73%) and our national-file run ($628.7 million; no change). Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 2 +- slideshows/appam-2026/data/build_report_4940.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 slideshows/appam-2026/data/build_report_4940.json diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index 4aa38e4e..cd3970c0 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -202,7 +202,7 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- Report 4940's record names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, but the report's figures match the local-area file, which `policyengine.py` 6.12.0 selects for state regions. On the local-area file at 2.2.1, we estimate a $657,356,800 state revenue loss and a 2.3% relative fall in child poverty, from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. +- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and gives that file's SHA-256. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657,356,800 loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. ## Axiom diff --git a/slideshows/appam-2026/data/build_report_4940.json b/slideshows/appam-2026/data/build_report_4940.json new file mode 100644 index 00000000..bf5a3089 --- /dev/null +++ b/slideshows/appam-2026/data/build_report_4940.json @@ -0,0 +1 @@ +{"status": "ok", "message": null, "result": {"id": 4940, "country_id": "us", "simulation_1_id": 531, "simulation_2_id": 2861, "api_version": "r3cd4f99c1", "status": "complete", "output": "{\"budget\": {\"households\": 2752376.754213915, \"budgetary_impact\": -657359306.4026487, \"tax_revenue_impact\": -657359306.4026487, \"baseline_net_income\": 449014129516.1675, \"benefit_spending_impact\": 0, \"state_tax_revenue_impact\": -657359315.8783416}, \"decile\": {\"average\": {\"1\": 169.0506134279458, \"2\": 407.66174684496946, \"3\": 302.79104219000146, \"4\": 124.9150847368728, \"5\": 377.8319233969087, \"6\": 168.73273619700922, \"7\": 255.6717140758701, \"8\": 212.3860602476925, \"9\": 248.9765715036192, \"10\": 192.42620133247692}, \"relative\": {\"1\": 0.0068684864950124495, \"2\": 0.00826162719678738, \"3\": 0.004515148587564584, \"4\": 0.001413550571714371, \"5\": 0.003388262163839411, \"6\": 0.0012546835925008734, \"7\": 0.001564662669889994, \"8\": 0.0009779402343100454, \"9\": 0.0008862126436438879, \"10\": 0.00029438122712336436}}, \"dataset\": \"hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915\", \"poverty\": {\"poverty\": {\"all\": {\"reform\": 0.17569433300240975, \"baseline\": 0.17785373527032217}, \"adult\": {\"reform\": 0.16904543177055703, \"baseline\": 0.1707006691782805}, \"child\": {\"reform\": 0.2171916166012827, \"baseline\": 0.2223479031949453}, \"senior\": {\"reform\": 0.1461339524560749, \"baseline\": 0.1462397380398915}}, \"deep_poverty\": {\"all\": {\"reform\": 0.0229179914104088, \"baseline\": 0.02315997651571578}, \"adult\": {\"reform\": 0.020469813918389142, \"baseline\": 0.020632511811919583}, \"child\": {\"reform\": 0.021882270468662153, \"baseline\": 0.022482810428736073}, \"senior\": {\"reform\": 0.03250410650264021, \"baseline\": 0.032564593609114935}}}, \"inequality\": {\"gini\": {\"reform\": 0.49934585420135214, \"baseline\": 0.5001007002909077}, \"top_1_pct_share\": {\"reform\": 0.09658191138455252, \"baseline\": 0.0966979984405194}, \"top_10_pct_share\": {\"reform\": 0.38370720553714904, \"baseline\": 0.3841010630129487}}, \"data_version\": \"populace-us-2024-spm-20260915\", \"intra_decile\": {\"all\": {\"No change\": 0.5430600367306659, \"Gain less than 5%\": 0.4563171209744957, \"Gain more than 5%\": 0.0006228422948384568, \"Lose less than 5%\": 0, \"Lose more than 5%\": 0}, \"deciles\": {\"No change\": [0.5760016561289629, 0.4081208358463244, 0.40256163574168535, 0.6876368211860562, 0.3318737229108287, 0.6100551621969162, 0.517375813843974, 0.5099618068430598, 0.5911748719527309, 0.7958380406561201], \"Gain less than 5%\": [0.41776993147959646, 0.5918791641536756, 0.5974383537013709, 0.3123631788139439, 0.6681262770891713, 0.3899448378030837, 0.4826241861560259, 0.4900381931569401, 0.408825128047269, 0.20416195934388004], \"Gain more than 5%\": [0.006228412391440736, 0, 1.05569438318498e-08, 0, 0, 0, 0, 0, 0, 0], \"Lose less than 5%\": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], \"Lose more than 5%\": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}}, \"model_version\": \"2.2.1\", \"spm_provenance\": {\"reform\": {\"receipt\": {\"as_of\": null, \"years\": [\"2026\"], \"scenario\": \"ce_trend\", \"forecast_id\": \"spm-rolling-2026-09-09\", \"geography_id\": null, \"county_vintage\": \"2020\", \"geography_kind\": \"county\", \"schema_version\": \"canonical-spm-provenance-v2\", \"forecast_sha256\": \"3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173\", \"runtime_versions\": {\"policyengine\": \"6.12.0\", \"spm-calculator\": \"1.0.0\", \"policyengine-us\": \"2.2.1\", \"policyengine-core\": \"3.33.3\"}}, \"execution_count\": 1}, \"baseline\": {\"receipt\": {\"as_of\": null, \"years\": [\"2026\"], \"scenario\": \"ce_trend\", \"forecast_id\": \"spm-rolling-2026-09-09\", \"geography_id\": null, \"county_vintage\": \"2020\", \"geography_kind\": \"county\", \"schema_version\": \"canonical-spm-provenance-v2\", \"forecast_sha256\": \"3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173\", \"runtime_versions\": {\"policyengine\": \"6.12.0\", \"spm-calculator\": \"1.0.0\", \"policyengine-us\": \"2.2.1\", \"policyengine-core\": \"3.33.3\"}}, \"execution_count\": 1}, \"schema_version\": \"canonical-spm-comparison-v2\"}, \"detailed_budget\": {\"ctc\": {\"is_tax\": false, \"reform\": 2929461478.2166295, \"baseline\": 2929461478.2166295, \"difference\": 0}, \"ssi\": {\"is_tax\": false, \"reform\": 1110284929.5676117, \"baseline\": 1110284929.5676117, \"difference\": 0}, \"eitc\": {\"is_tax\": false, \"reform\": 802124665.3495702, \"baseline\": 802124665.3495702, \"difference\": 0}, \"snap\": {\"is_tax\": false, \"reform\": 2508395199.9432874, \"baseline\": 2508395199.9432874, \"difference\": 0}, \"tanf\": {\"is_tax\": false, \"reform\": 529621898.2676943, \"baseline\": 529621898.2676943, \"difference\": 0}, \"medicaid\": {\"is_tax\": false, \"reform\": 17051393952.836697, \"baseline\": 17051393952.836697, \"difference\": 0}, \"income_tax\": {\"is_tax\": true, \"reform\": 87039010905.5309, \"baseline\": 87039010905.5309, \"difference\": 0}, \"medicare_cost\": {\"is_tax\": false, \"reform\": 14643164382.72422, \"baseline\": 14643164382.72422, \"difference\": 0}, \"social_security\": {\"is_tax\": false, \"reform\": 26956139063.134735, \"baseline\": 26956139063.134735, \"difference\": 0}, \"state_income_tax\": {\"is_tax\": true, \"reform\": 23890657915.861897, \"baseline\": 24548017230.79172, \"difference\": -657359314.929821}, \"employee_payroll_tax\": {\"is_tax\": true, \"reform\": 24007545225.630745, \"baseline\": 24007545225.630745, \"difference\": 0}}, \"poverty_by_race\": {\"poverty\": {\"black\": {\"reform\": 0.20772767183655463, \"baseline\": 0.20772767183655463}, \"other\": {\"reform\": 0.13033103439917235, \"baseline\": 0.13293422821028725}, \"white\": {\"reform\": 0.17593189495314204, \"baseline\": 0.17593189500572234}, \"hispanic\": {\"reform\": 0.29332424194132306, \"baseline\": 0.3021046331922458}}}, \"poverty_by_gender\": {\"poverty\": {\"male\": {\"reform\": 0.16611518226643188, \"baseline\": 0.1683270064392656}, \"female\": {\"reform\": 0.18500530039469737, \"baseline\": 0.18711374839110723}}, \"deep_poverty\": {\"male\": {\"reform\": 0.021699461784393307, \"baseline\": 0.021879703112853477}, \"female\": {\"reform\": 0.024102406389051294, \"baseline\": 0.024404406657366227}}}, \"resolved_app_name\": \"policyengine-simulation-py6-12-0\", \"policyengine_bundle\": {\"spm\": {\"defaults\": {\"as_of\": null, \"scenario\": \"ce_trend\", \"geography_id\": null, \"county_vintage\": \"2020\", \"geography_kind\": \"county\", \"forecast_content_sha256\": \"3d86d5c4c0423480e6b69b75d222ffa4a7a2639e4094df5ba2504af01be17173\"}, \"contract_version\": \"canonical-spm-v1\"}, \"dataset\": \"hf://policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260915\", \"data_version\": \"populace-us-2024-spm-20260915\", \"model_version\": \"2.2.1\", \"dataset_sha256\": \"6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84\", \"policyengine_version\": \"6.12.0\"}, \"policyengine_version\": \"6.12.0\", \"labor_supply_response\": {\"hours\": {\"change\": 0, \"reform\": 0, \"baseline\": 0, \"income_effect\": 0, \"substitution_effect\": 0}, \"decile\": {\"average\": {\"income\": {\"1\": 0, \"2\": 0, \"3\": 0, \"4\": 0, \"5\": 0, \"6\": 0, \"7\": 0, \"8\": 0, \"9\": 0, \"-1\": 0, \"10\": 0}, \"substitution\": {\"1\": 0, \"2\": 0, \"3\": 0, \"4\": 0, \"5\": 0, \"6\": 0, \"7\": 0, \"8\": 0, \"9\": 0, \"-1\": 0, \"10\": 0}}, \"relative\": {\"income\": {\"1\": 0, \"2\": 0, \"3\": 0, \"4\": 0, \"5\": 0, \"6\": 0, \"7\": 0, \"8\": 0, \"9\": 0, \"10\": 0}, \"substitution\": {\"1\": 0, \"2\": 0, \"3\": 0, \"4\": 0, \"5\": 0, \"6\": 0, \"7\": 0, \"8\": 0, \"9\": 0, \"10\": 0}}}, \"income_lsr\": 0, \"relative_lsr\": {\"income\": 0, \"substitution\": 0}, \"total_change\": 0, \"revenue_change\": 0, \"substitution_lsr\": 0}, \"congressional_district_impact\": {\"districts\": [{\"district\": \"MA-01\", \"population\": 304083.40625, \"loser_percentage\": 0, \"winner_percentage\": 0.37865671939518497, \"no_change_percentage\": 0.6213432806048151, \"average_household_income_change\": 188.79535949686505, \"relative_household_income_change\": 0.0008229552855090194}, {\"district\": \"MA-02\", \"population\": 295719.5625, \"loser_percentage\": 0, \"winner_percentage\": 0.4576922770646472, \"no_change_percentage\": 0.5423077229353528, \"average_household_income_change\": 275.9526874384579, \"relative_household_income_change\": 0.0018610331438204677}, {\"district\": \"MA-03\", \"population\": 288867.4375, \"loser_percentage\": 0, \"winner_percentage\": 0.4921354182336787, \"no_change_percentage\": 0.5078645817663213, \"average_household_income_change\": 327.4333473463931, \"relative_household_income_change\": 0.0022784862334221945}, {\"district\": \"MA-04\", \"population\": 306825.40625, \"loser_percentage\": 0, \"winner_percentage\": 0.4965667993676427, \"no_change_percentage\": 0.5034332006323572, \"average_household_income_change\": 222.57807407368176, \"relative_household_income_change\": 0.001377343903020689}, {\"district\": \"MA-05\", \"population\": 299389.21875, \"loser_percentage\": 0, \"winner_percentage\": 0.5684686762241966, \"no_change_percentage\": 0.43153132377580344, \"average_household_income_change\": 294.172890953509, \"relative_household_income_change\": 0.002122633360546633}, {\"district\": \"MA-06\", \"population\": 311297.96875, \"loser_percentage\": 0, \"winner_percentage\": 0.4925189057402336, \"no_change_percentage\": 0.5074810942597665, \"average_household_income_change\": 255.5904888152278, \"relative_household_income_change\": 0.001430691556157937}, {\"district\": \"MA-07\", \"population\": 315841.5625, \"loser_percentage\": 0, \"winner_percentage\": 0.37075721201873896, \"no_change_percentage\": 0.629242787981261, \"average_household_income_change\": 156.8543405366417, \"relative_household_income_change\": 0.0011655292437502407}, {\"district\": \"MA-08\", \"population\": 302121.375, \"loser_percentage\": 0, \"winner_percentage\": 0.4164762646333965, \"no_change_percentage\": 0.5835237353666035, \"average_household_income_change\": 236.95737516089352, \"relative_household_income_change\": 0.0013032365571947755}, {\"district\": \"MA-09\", \"population\": 328230.75, \"loser_percentage\": 0, \"winner_percentage\": 0.4354370125889784, \"no_change_percentage\": 0.5645629874110214, \"average_household_income_change\": 203.15854014287208, \"relative_household_income_change\": 0.0013398894640572756}]}}", "error_message": null, "year": "2026", "report_kind": "economy_comparison", "report_spec_json": {"country_id": "us", "report_kind": "economy_comparison", "time_period": "2026", "region": "state/ma", "baseline_policy_id": 2, "reform_policy_id": 99124, "dataset": "default", "target": "general", "options": {}}, "report_spec_schema_version": 1, "report_spec_status": "backfilled_assumed", "active_run_id": null, "latest_successful_run_id": "f7b4279e-8caa-460f-b23e-500f2e7fbbc8", "requested_at": "2026-11-05T15:32:41Z", "started_at": "2026-11-05T15:32:41Z", "finished_at": "2026-11-05T15:35:22Z"}} \ No newline at end of file From ebb86a87546434efaf98c24f69beb4f11ae976c6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 06:55:36 -0500 Subject: [PATCH 6/9] Correct the Georgia and Build entries and close the tracker's test gaps The ledger said Congress sets the same limit as Georgia. Congress limits the denial to determining an overissuance, and only section 3650 and 7 CFR 273.18 limit it to the income reported late. The Build entry now sets the local-area run's household gain against the record's budgetary impact, says we haven't resolved which file the run read, and states that the record's 45.69% equals the simple mean of its ten decile shares. After a reload inside the frame, the deck now waits for the new page's load before it polls, so the new page gets its key listener first. New tests cover the key listener, the 400 ms interval, the legend that repeats three card labels, and every Build figure in the ledger and the notes. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 4 +- slideshows/appam-2026/SPEAKER-NOTES.md | 4 +- slideshows/appam-2026/content.test.ts | 50 +++++++++++++++++++ .../appam-2026/slides/CoverageEmbed.test.tsx | 43 ++++++++++++++-- .../appam-2026/slides/CoverageEmbed.tsx | 7 ++- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index cd3970c0..ce2a300a 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -202,7 +202,7 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and gives that file's SHA-256. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657,356,800 loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't reproduced the report's 45.7% of people gaining. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. +- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and gives that file's SHA-256. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). That run's household gain, $657,359,360, differs by $54 from the record's $657,359,306 budgetary impact. On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read. We haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares, and our national-file shares divide the people gaining by all people. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. ## Axiom @@ -210,4 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind its caption in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment. Congress sets the same limit in 7 U.S.C. 2014(e)(2)(C), and USDA sets it in 7 CFR 273.18(c)(1)(ii)(B), where it keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption, and leave the late-report case to the graph and the notes' Questions. +- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C), and USDA limits it to the income reported late in 7 CFR 273.18(c)(1)(ii)(B), where it keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption, and leave the late-report case to the graph and the notes' Questions. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index c0d5c9bc..aa7ed96c 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -87,7 +87,7 @@ CliffWatch and Build need no code. CliffWatch charts a household's net income as its earnings rise and marks where a raise leaves it with less. For example, a single parent of two school-age children in Massachusetts loses the state's cash assistance, TAFDC, when earnings pass $18,000. CliffWatch counts health coverage in net income. When you try it, the chart can take up to a minute to finish drawing. -In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. A report takes about three minutes to run. Build runs an older release, PolicyEngine US 2.2.1, and reports a $657 million cost. On 2.90.2, we estimate the $577 million on slide 9. +In Build, you can open the same Massachusetts credit, set it to $880, pick Massachusetts and run a report. Our report took about three minutes to run. Build runs an older release, PolicyEngine US 2.2.1, and reports a $657 million cost, while on 2.90.2 we estimate the $577 million on slide 9. *The recording ends on the report's results. Press next when you finish.* @@ -95,7 +95,7 @@ In Build, you can open the same Massachusetts credit, set it to $880, pick Massa We model federal income and payroll taxes and credits and every state income tax, in 44 jurisdictions. We model TANF and child care subsidies in all 50 states and DC, plus SNAP, WIC, SSI, school meals, ACA premium tax credits, Medicaid and CHIP eligibility and local taxes such as Philadelphia's wage tax. We list 109 programs in our coverage tracker and mark 87 complete, 18 partial and 4 in progress. -*Once the tracker loads, you can click and scroll in the frame, and the arrow keys still move the deck. After you click in its search box, the arrow keys move the box's cursor until you click elsewhere in the frame. If you pick a year in its menu, the tracker counts only the programs we mark verified for that year.* +*Once the tracker loads, you can click and scroll in the frame, and the left and right arrow keys still move the deck. After you click in its search box, they move the box's cursor until you click elsewhere in the frame. If you pick a year in its menu, the tracker counts only the programs we mark verified for that year.* ## 13. We build Microcosm, our microdata, from surveys and official totals (9:50, 50 seconds) diff --git a/slideshows/appam-2026/content.test.ts b/slideshows/appam-2026/content.test.ts index cddd6bc7..c49893b3 100644 --- a/slideshows/appam-2026/content.test.ts +++ b/slideshows/appam-2026/content.test.ts @@ -52,6 +52,8 @@ const census = JSON.parse(read('data/census_child_spm.json')); const reformRun = JSON.parse(read('data/massachusetts_reform.json')); const defaultReform = JSON.parse(read('data/massachusetts_reform_default.json')); const pinnedReform = JSON.parse(read('data/massachusetts_reform_221.json')); +const nationalReform = JSON.parse(read('data/massachusetts_reform_221_national.json')); +const buildReport = JSON.parse(JSON.parse(read('data/build_report_4940.json')).result.output); const precision = JSON.parse(read('data/massachusetts_precision.json')); const defaultBaseline = JSON.parse(read('data/state_baseline_default.json')); const localBaseline = JSON.parse(read('data/state_baseline.json')); @@ -985,6 +987,54 @@ describe('typed figures', () => { } }); + describe('on Build’s report', () => { + const hundredths = (share: number) => `${(share * 100).toFixed(2)}%`; + const tenths = (value: number) => `$${(value / 1_000_000).toFixed(1)} million`; + const gaining = (row: Record) => row['Gain less than 5%'] + row['Gain more than 5%']; + const { budget, intra_decile: gains } = buildReport; + const buildChild = buildReport.poverty.poverty.child; + const buildGaining = gaining(gains.all); + + it('match the record and our two 2.2.1 runs in the evidence ledger', () => { + for (const figure of [ + `${usd(-budget.state_tax_revenue_impact)} state revenue loss`, + `falling from ${hundredths(buildChild.baseline)} to ${hundredths(buildChild.reform)}`, + `$${(pinnedReform.state_cost / 1_000_000).toFixed(2)} million loss and a fall from ${hundredths(pinnedReform.baseline_child_poverty)} to ${hundredths(pinnedReform.reformed_child_poverty)}`, + `household gain, ${usd(pinnedReform.household_gain)}, differs by ${usd(pinnedReform.household_gain + budget.budgetary_impact)} from the record's ${usd(-budget.budgetary_impact)} budgetary impact`, + `${tenths(nationalReform.state_cost)} loss, and the child poverty rate stays at ${percent(nationalReform.baseline_child_poverty)}`, + `record's ${hundredths(buildGaining)} equals the simple mean`, + `${percent(nationalReform.people_gaining_share_over_1_dollar)} of people gain more than $1`, + `${percent(nationalReform.people_gaining_share_over_0_1_percent)} gain more than 0.1%`, + ]) { + expect(ledger).toContain(figure); + } + expect(percent(nationalReform.reformed_child_poverty)).toBe(percent(nationalReform.baseline_child_poverty)); + }); + + it('find the record’s overall share in the simple mean of its ten decile shares', () => { + const deciles = [gains.deciles['Gain less than 5%'], gains.deciles['Gain more than 5%']]; + expect(deciles.map((shares) => shares.length)).toEqual([10, 10]); + const mean = deciles.flat().reduce((total: number, share: number) => total + share, 0) / 10; + expect(mean).toBeCloseTo(buildGaining, 12); + }); + + it('match the record and our runs in the speaker notes', () => { + const fall = (run: { baseline_child_poverty: number; reformed_child_poverty: number }) => + `we estimate a fall from ${percent(run.baseline_child_poverty)} to ${percent(run.reformed_child_poverty)}, or ${(100 * (run.baseline_child_poverty - run.reformed_child_poverty)).toFixed(1)} points`; + for (const figure of [ + `Build's report show ${tenths(-budget.state_tax_revenue_impact)} and slide 9 show ${millions(massachusetts.cost)}`, + `we also estimate ${tenths(pinnedReform.state_cost)}`, + `${(100 * (1 - buildChild.reform / buildChild.baseline)).toFixed(1)}% decrease in child poverty`, + fall(pinnedReform), + fall(reformRun), + `Build's ${percent(buildGaining)} counts people, and slide 9's ${percent(reformRun.households_gaining_share)} counts households`, + `we estimate that ${percent(pinnedReform.households_gaining_share)} of households would gain`, + ]) { + expect(notes).toContain(figure); + } + }); + }); + it('rank the size of Massachusetts’s gap from Census where the ledger says', () => { const gap = (row: BaselineRow) => Math.abs(row.child_spm_percent - row.census_percent); const order = [...stateBaseline.rows].sort((a, b) => gap(b) - gap(a)); diff --git a/slideshows/appam-2026/slides/CoverageEmbed.test.tsx b/slideshows/appam-2026/slides/CoverageEmbed.test.tsx index be389b6a..a3007a43 100644 --- a/slideshows/appam-2026/slides/CoverageEmbed.test.tsx +++ b/slideshows/appam-2026/slides/CoverageEmbed.test.tsx @@ -30,7 +30,7 @@ describe('the coverage tracker on the rules slide', () => { expect(container.querySelector('img')?.getAttribute('alt')).toBe('The tracker'); }); - it('keeps the frame hidden until the tracker renders', async () => { + it('keeps the frame hidden on first render', async () => { window.history.replaceState(null, '', '/slides/appam-2026?slide=13'); await renderEmbed(); expect(container.querySelector('iframe')?.style.opacity).toBe('0'); @@ -49,9 +49,11 @@ const SNAPSHOT = 'Coverage reflects policyengine-us 2.72.3 (main @ 4c65c82, fetc const FALLBACK = 'Coverage reflects the bundled fallback registry. 64 Total programs'; const card = (label: string, style = '') => `

${label}

`; -/** The tracker's four cards, with the given attributes on the last one. */ +/** The legend after the tracker's cards, which repeats three of their labels and never fades. */ +const LEGEND = ['Complete', 'Partial', 'In progress', 'Not started'].map((label) => `${label}`).join(''); +/** The tracker's four cards and its legend, with the given attributes on the last card. */ const cards = (lastStyle = '') => - [card('Total programs'), card('Complete'), card('Partial'), card('In progress', lastStyle)].join(''); + [card('Total programs'), card('Complete'), card('Partial'), card('In progress', lastStyle), LEGEND].join(''); const CARD_STATES = { 'at rest': { html: cards(), rests: true }, 'at rest under an identity matrix': { html: cards('data-transform="matrix(1, 0, 0, 1, 0, 0)"'), rests: true }, @@ -216,6 +218,41 @@ describe('when the frame shows', () => { expect(shown(frame)).toBe(true); }); + it('waits for the new page to load before it checks again', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await show(1171.5); + await load(frame); + await poll(); + await act(async () => void frame.contentWindow?.dispatchEvent(new Event('pagehide'))); + await poll(); + expect(hidden(frame)).toBe(true); + await load(frame); + await poll(); + expect(shown(frame)).toBe(true); + }); + + it('first checks the page 400 ms after the slide shows', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await load(frame); + await show(1171.5); + await act(async () => void vi.advanceTimersByTime(399)); + expect(hidden(frame)).toBe(true); + await act(async () => void vi.advanceTimersByTime(1)); + expect(shown(frame)).toBe(true); + }); + + it('hands a key pressed in the frame to the deck once, however many times the frame loads', async () => { + const frame = await mount(stubPage(SNAPSHOT)); + await load(frame); + await load(frame); + const deck = vi.fn(); + window.addEventListener('keydown', deck); + frame.contentWindow?.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', cancelable: true })); + window.removeEventListener('keydown', deck); + expect(deck).toHaveBeenCalledOnce(); + expect(deck.mock.calls[0][0].key).toBe('ArrowRight'); + }); + it('stays on the still across origins, where the browser denies the frame’s window and document', async () => { const frame = await mount(null); const denied = new Proxy( diff --git a/slideshows/appam-2026/slides/CoverageEmbed.tsx b/slideshows/appam-2026/slides/CoverageEmbed.tsx index 7c7446fd..f34c7bd4 100644 --- a/slideshows/appam-2026/slides/CoverageEmbed.tsx +++ b/slideshows/appam-2026/slides/CoverageEmbed.tsx @@ -105,8 +105,11 @@ export default function CoverageEmbed({ alt }: { alt: string }) { try { const page = frame.current?.contentWindow; page?.addEventListener('keydown', forwardKey); - // A reload prints the fallback counts first, so the still returns until the check passes again. - page?.addEventListener('pagehide', () => setReady(false)); + // A reload prints the fallback counts first, so the still returns until the new page loads and passes the check. + page?.addEventListener('pagehide', () => { + setLoaded(false); + setReady(false); + }); } catch { // A cross-origin frame, on a branch preview. } From 377c4b2a600783903726f9365c029230a4d9d9ff Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 15:09:26 -0500 Subject: [PATCH 7/9] Answer the claims review in the notes, ledger and Build tests - Notes: say we changed how we model who qualifies between releases, show where Build's 2.3% comes from, and give the 3612 encoding to Axiom and the overpayment limits to Congress and USDA. - Ledger: cite the SHA-256 match with policyengine.py 6.12.0's manifest and the report spec's fields, state the float32 step behind the household-gain match, cite #1445's rerun plan, and name the household that reports late. - Tests: pin every Build figure in the ledger and notes against the record and our runs. Each of 33 swaps of those figures fails them. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 8 +++--- slideshows/appam-2026/SPEAKER-NOTES.md | 6 ++-- slideshows/appam-2026/content.test.ts | 38 ++++++++++++++++++++------ 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index ce2a300a..b7afd5c4 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -115,7 +115,7 @@ The script keeps input variables and IDs. It renames the stored Social Security - Our rates differ from Census's 2023 to 2025 averages by 3.2 points on average and exceed Census's by 0.9 points on net. They exceed Census's in 30 of 51 jurisdictions. In 29 of 51 jurisdictions the difference exceeds Census's margin of error. - Effective samples of households with children run from 53 in Maine to 407 in California, with a median of 108. Across all households they run from 157 in Kentucky to 905 in California, with a median of 237 (`data/effective_samples.json`). - `policyengine.py` leaves ACS group quarters out of SPM measures, and we keep them. Leaving them out moves no state's rate by more than 0.023 points (Connecticut) and moves Massachusetts's by 0.01 (`child_spm_percent_without_group_quarters` in `data/state_baseline.json`). -- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. We plan to rerun `acs_local` in #1445 without the −1, and this match covers 247f1a88 alone. +- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. We plan a 51-state rerun of `acs_local` in #1445 that sets blank links to 0, and this match covers 247f1a88 alone. - On the national file, our rates differ from Census's by 4.8 points on average. They exceed Census's in 40 of 51 jurisdictions and fall outside its margin in 38 (`data/state_baseline_default.json`). The median effective sample includes 63 households with children (`data/effective_samples.json`). ## Effective samples @@ -202,12 +202,12 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, and gives that file's SHA-256. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). That run's household gain, $657,359,360, differs by $54 from the record's $657,359,306 budgetary impact. On the national file, we estimate a $628.7 million loss, and the child poverty rate stays at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read. We haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares, and our national-file shares divide the people gaining by all people. On the national file, we estimate that 46.7% of people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. +- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, with a SHA-256 that matches `populace_us_2024` in `policyengine.py` 6.12.0's manifest. Its report spec reads `"dataset": "default"` and `"region": "state/ma"`, with the status `backfilled_assumed`. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). We sum that run in float32, which steps by $64 at that size, and its household gain, $657,359,360, lands within one step of the record's $657,359,306 budgetary impact. On the national file at 2.2.1, we estimate a $628.7 million loss and no change in the child poverty rate, at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read, and we haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares. On the national file, we estimate that 46.7% of all people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. ## Axiom - Axiom publishes `us-ga/policies/dfcs/snap/3612.yaml` in `TheAxiomFoundation/rulespec-us` under CC BY 4.0. - Axiom publishes `axiom-rules-engine` under an MIT license. -- We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind its caption in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. +- We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind the graph in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C), and USDA limits it to the income reported late in 7 CFR 273.18(c)(1)(ii)(B), where it keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption, and leave the late-report case to the graph and the notes' Questions. +- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income the household reported late, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C). USDA limits the denial to the income the household reported late in 7 CFR 273.18(c)(1)(ii)(B), and keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption and answer the late-report case in the notes' Questions. The graph on slide 15 includes `3612.yaml`'s late-report condition. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index aa7ed96c..ad6e1fed 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -185,13 +185,13 @@ We test a new applicant in every month, so Alabama's five months don't describe **Do all these interfaces run one version?** They run different releases of the same code. This deck runs PolicyEngine US 2.90.2, our coverage tracker reads 2.72.3, CliffWatch ran 1.755.5 on November 4, Build ran 2.2.1 on November 5, and PyPI carried 2.95.0 on November 5. -**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed which filers and dependents qualify for the credit, and we haven't separated how much of the difference those changes explain. +**Why does Build's report show $657.4 million and slide 9 show $577 million?** Build runs PolicyEngine US 2.2.1, and we ran slide 9 on 2.90.2. When we run 2.2.1 on slide 9's local-area file, we also estimate $657.4 million. Between those releases, we changed how we model which filers and dependents qualify for the credit, and we haven't separated how much of the difference those changes explain. -**Why does Build's report show a 2.3% decrease in child poverty?** Build reports a relative change. When we run 2.2.1 on slide 9's local-area file, we estimate a fall from 22.2% to 21.7%, or 0.5 points. On 2.90.2, we estimate a fall from 23.1% to 22.9%, or 0.2 points. +**Why does Build's report show a 2.3% decrease in child poverty?** Build reports the fall as a share of the starting rate. When we run 2.2.1 on slide 9's local-area file, we estimate a fall from 22.2% to 21.7%: 0.5 points, or 2.3% of 22.2%. **Why does Build's report show 45.7% gaining and slide 9 show 29.4%?** Build's 45.7% counts people, and slide 9's 29.4% counts households. When we run 2.2.1 on slide 9's local-area file, we estimate that 34.1% of households would gain. -**Does a late report cost a Georgia household the whole deduction?** No. Georgia says in section 3612 that a household loses the deduction if it fails to report earned income on time, and we encoded that sentence as losing all of it. In section 3650, Georgia denies the deduction only for the income reported late, when it computes an overpayment, and USDA does the same in its claims rule at 7 CFR 273.18. We haven't encoded section 3650. +**Does a late report cost a Georgia household the whole deduction?** No. Georgia says in section 3612 that a household loses the deduction if it fails to report earned income on time, and Axiom encoded that sentence as losing all of it. In section 3650, Georgia denies the deduction only for the income the household reported late, when it computes an overpayment. Congress limits the denial to computing an overpayment, in 7 U.S.C. 2014(e)(2)(C), and USDA limits it to the income the household reported late, in 7 CFR 273.18. Axiom hasn't encoded section 3650. **Why does CliffWatch's net income exceed slide 5's net resources?** CliffWatch counts Medicaid, CHIP and premium tax credits in net income, and we leave health coverage out of net resources. Slide 11's family also pays nothing for child care, and slide 5's family pays $1,500 a month before any subsidy. diff --git a/slideshows/appam-2026/content.test.ts b/slideshows/appam-2026/content.test.ts index c49893b3..d72401f1 100644 --- a/slideshows/appam-2026/content.test.ts +++ b/slideshows/appam-2026/content.test.ts @@ -990,27 +990,47 @@ describe('typed figures', () => { describe('on Build’s report', () => { const hundredths = (share: number) => `${(share * 100).toFixed(2)}%`; const tenths = (value: number) => `$${(value / 1_000_000).toFixed(1)} million`; + const relativeFall = (before: number, after: number) => `${(100 * (1 - after / before)).toFixed(1)}%`; + // A float32 number from 2^n up to 2^(n+1) moves in steps of 2^(n-23). + const float32Step = (value: number) => 2 ** (Math.floor(Math.log2(Math.abs(value))) - 23); const gaining = (row: Record) => row['Gain less than 5%'] + row['Gain more than 5%']; + const record = JSON.parse(read('data/build_report_4940.json')).result; const { budget, intra_decile: gains } = buildReport; const buildChild = buildReport.poverty.poverty.child; const buildGaining = gaining(gains.all); + it('match the report as the ledger describes it', () => { + expect(ledger).toContain( + `$${(-budget.state_tax_revenue_impact / 1_000_000).toFixed(1)}m annual state revenue loss, ${percent(buildGaining)} of people gaining income and a ${relativeFall(buildChild.baseline, buildChild.reform)} relative decrease in child poverty, on PolicyEngine US ${buildReport.model_version} and \`policyengine\` ${buildReport.policyengine_version}`, + ); + }); + it('match the record and our two 2.2.1 runs in the evidence ledger', () => { + const step = float32Step(pinnedReform.household_gain); for (const figure of [ + `Its report spec reads \`"dataset": "${record.report_spec_json.dataset}"\` and \`"region": "${record.report_spec_json.region}"\`, with the status \`${record.report_spec_status}\``, `${usd(-budget.state_tax_revenue_impact)} state revenue loss`, `falling from ${hundredths(buildChild.baseline)} to ${hundredths(buildChild.reform)}`, `$${(pinnedReform.state_cost / 1_000_000).toFixed(2)} million loss and a fall from ${hundredths(pinnedReform.baseline_child_poverty)} to ${hundredths(pinnedReform.reformed_child_poverty)}`, - `household gain, ${usd(pinnedReform.household_gain)}, differs by ${usd(pinnedReform.household_gain + budget.budgetary_impact)} from the record's ${usd(-budget.budgetary_impact)} budgetary impact`, - `${tenths(nationalReform.state_cost)} loss, and the child poverty rate stays at ${percent(nationalReform.baseline_child_poverty)}`, + `We sum that run in float32, which steps by ${usd(step)} at that size, and its household gain, ${usd(pinnedReform.household_gain)}, lands within one step of the record's ${usd(-budget.budgetary_impact)} budgetary impact`, + `${tenths(nationalReform.state_cost)} loss and no change in the child poverty rate, at ${percent(nationalReform.baseline_child_poverty)}`, `record's ${hundredths(buildGaining)} equals the simple mean`, - `${percent(nationalReform.people_gaining_share_over_1_dollar)} of people gain more than $1`, + `${percent(nationalReform.people_gaining_share_over_1_dollar)} of all people gain more than $1`, `${percent(nationalReform.people_gaining_share_over_0_1_percent)} gain more than 0.1%`, ]) { expect(ledger).toContain(figure); } + expect(Math.fround(pinnedReform.household_gain)).toBe(pinnedReform.household_gain); + expect(Math.abs(pinnedReform.household_gain + budget.budgetary_impact)).toBeLessThan(step); expect(percent(nationalReform.reformed_child_poverty)).toBe(percent(nationalReform.baseline_child_poverty)); }); + it('state the thresholds the national run applies', () => { + const script = read('analysis/massachusetts_reform_national.py'); + expect(script).toContain('np.sum((weights * heads)[gains > 1])'); + expect(script).toContain('np.sum((weights * heads)[relative > 0.001])'); + }); + it('find the record’s overall share in the simple mean of its ten decile shares', () => { const deciles = [gains.deciles['Gain less than 5%'], gains.deciles['Gain more than 5%']]; expect(deciles.map((shares) => shares.length)).toEqual([10, 10]); @@ -1019,19 +1039,21 @@ describe('typed figures', () => { }); it('match the record and our runs in the speaker notes', () => { - const fall = (run: { baseline_child_poverty: number; reformed_child_poverty: number }) => - `we estimate a fall from ${percent(run.baseline_child_poverty)} to ${percent(run.reformed_child_poverty)}, or ${(100 * (run.baseline_child_poverty - run.reformed_child_poverty)).toFixed(1)} points`; + const before = pinnedReform.baseline_child_poverty; + const after = pinnedReform.reformed_child_poverty; for (const figure of [ + `reports a ${millions(-budget.state_tax_revenue_impact)} cost`, `Build's report show ${tenths(-budget.state_tax_revenue_impact)} and slide 9 show ${millions(massachusetts.cost)}`, `we also estimate ${tenths(pinnedReform.state_cost)}`, - `${(100 * (1 - buildChild.reform / buildChild.baseline)).toFixed(1)}% decrease in child poverty`, - fall(pinnedReform), - fall(reformRun), + `${relativeFall(buildChild.baseline, buildChild.reform)} decrease in child poverty`, + `we estimate a fall from ${percent(before)} to ${percent(after)}: ${(100 * (before - after)).toFixed(1)} points, or ${relativeFall(before, after)} of ${percent(before)}`, `Build's ${percent(buildGaining)} counts people, and slide 9's ${percent(reformRun.households_gaining_share)} counts households`, `we estimate that ${percent(pinnedReform.households_gaining_share)} of households would gain`, ]) { expect(notes).toContain(figure); } + // The notes answer Build's relative fall with ours, so the two have to agree. + expect(relativeFall(before, after)).toBe(relativeFall(buildChild.baseline, buildChild.reform)); }); }); From 28fd57234ebeb778271b0e0332e3ac944c38b8d4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 22:12:36 -0500 Subject: [PATCH 8/9] Answer the c5bd2ed review of the Georgia reading and Build's record - Say "the income the household didn't report on time" in the Georgia and USDA readings, as section 3650 and 7 CFR 273.18(c)(1)(ii)(B) do, and ask the notes' Georgia question the same way. - State the national run's 0.000001-point fall in child poverty in place of "no change", and pin it exactly. - Pin the record's file, the national run's file and release, both copies of the report's 45.7% and the run time from the record's timestamps. - Give the counting in the 45.7% answer to Build and to us, link the #1445 hold comment in the ledger, and cut the ledger's sentence about the notes. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 6 +++--- slideshows/appam-2026/SPEAKER-NOTES.md | 4 ++-- slideshows/appam-2026/content.test.ts | 13 ++++++++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index 0216358e..e7af156c 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -115,7 +115,7 @@ The script keeps input variables and IDs. It renames the stored Social Security - Our rates differ from Census's 2023 to 2025 averages by 3.2 points on average and exceed Census's by 0.9 points on net. They exceed Census's in 30 of 51 jurisdictions. In 29 of 51 jurisdictions the difference exceeds Census's margin of error. - Effective samples of households with children run from 53 in Maine to 407 in California, with a median of 108. Across all households they run from 157 in Kentucky to 905 in California, with a median of 237 (`data/effective_samples.json`). - `policyengine.py` leaves ACS group quarters out of SPM measures, and we keep them. Leaving them out moves no state's rate by more than 0.023 points (Connecticut) and moves Massachusetts's by 0.01 (`child_spm_percent_without_group_quarters` in `data/state_baseline.json`). -- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. We plan a 51-state rerun of `acs_local` in #1445 that sets blank links to 0, and this match covers 247f1a88 alone. +- On the same file and model version, our rates match `acs_local` in PolicyEngine/microcosm#1445 at 247f1a88 (`docs/evidence/us-state-child-spm-2026-11/outputs/acs_eval.csv`) to within 0.000001 points in 48 of 51 jurisdictions (`data/microcosm_1445_rates.csv`). That run gives blank parent links the id −1, and ours gives them 0, the model's value for an unknown parent. California moves by 0.56 points, Delaware by 0.006 and Missouri by 0.00001. We plan a 51-state rerun of `acs_local` in #1445 that sets blank links to 0 (https://github.com/PolicyEngine/microcosm/pull/1445#issuecomment-6591562570), and this match covers 247f1a88 alone. - On the national file, our rates differ from Census's by 4.8 points on average. They exceed Census's in 40 of 51 jurisdictions and fall outside its margin in 38 (`data/state_baseline_default.json`). The median effective sample includes 63 households with children (`data/effective_samples.json`). ## Effective samples @@ -202,7 +202,7 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, with a SHA-256 that matches `populace_us_2024` in `policyengine.py` 6.12.0's manifest. Its report spec reads `"dataset": "default"` and `"region": "state/ma"`, with the status `backfilled_assumed`. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). We sum that run in float32, which steps by $64 at that size, and its household gain, $657,359,360, lands within one step of the record's $657,359,306 budgetary impact. On the national file at 2.2.1, we estimate a $628.7 million loss and no change in the child poverty rate, at 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read, and we haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares. On the national file, we estimate that 46.7% of all people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. In the notes, we compare releases and name no file for Build. +- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, with a SHA-256 that matches `populace_us_2024` in `policyengine.py` 6.12.0's manifest. Its report spec reads `"dataset": "default"` and `"region": "state/ma"`, with the status `backfilled_assumed`. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). We sum that run in float32, which steps by $64 at that size, and its household gain, $657,359,360, lands within one step of the record's $657,359,306 budgetary impact. On the national file at 2.2.1, we estimate a $628.7 million loss and a fall of 0.000001 points in the child poverty rate, from 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read, and we haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares. On the national file, we estimate that 46.7% of all people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. ## Axiom @@ -210,4 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind the graph in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income the household reported late, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C). USDA limits the denial to the income the household reported late in 7 CFR 273.18(c)(1)(ii)(B), and keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption and answer the late-report case in the notes' Questions. The graph on slide 15 includes `3612.yaml`'s late-report condition. +- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income the household didn't report on time, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C). USDA limits the denial to the income the household didn't report on time in 7 CFR 273.18(c)(1)(ii)(B), and keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption. In the notes' Questions, we answer what happens when a household doesn't report earned income on time. The graph on slide 15 includes `3612.yaml`'s on-time reporting test. diff --git a/slideshows/appam-2026/SPEAKER-NOTES.md b/slideshows/appam-2026/SPEAKER-NOTES.md index ba1a4340..877e67ef 100644 --- a/slideshows/appam-2026/SPEAKER-NOTES.md +++ b/slideshows/appam-2026/SPEAKER-NOTES.md @@ -189,9 +189,9 @@ We test a new applicant in every month, so Alabama's five months don't describe **Why does Build's report show a 2.3% decrease in child poverty?** Build reports the fall as a share of the starting rate. When we run 2.2.1 on slide 9's local-area file, we estimate a fall from 22.2% to 21.7%: 0.5 points, or 2.3% of 22.2%. -**Why does Build's report show 45.7% gaining and slide 9 show 29.4%?** Build's 45.7% counts people, and slide 9's 29.4% counts households. When we run 2.2.1 on slide 9's local-area file, we estimate that 34.1% of households would gain. +**Why does Build's report show 45.7% gaining and slide 9 show 29.4%?** Build counts people in its 45.7%, and we counted households in slide 9's 29.4%. When we run 2.2.1 on slide 9's local-area file, we estimate that 34.1% of households would gain. -**Does a late report cost a Georgia household the whole deduction?** No. Georgia says in section 3612 that a household loses the deduction if it fails to report earned income on time, and Axiom encoded that sentence as losing all of it. In section 3650, Georgia denies the deduction only for the income the household reported late, when it computes an overpayment. Congress limits the denial to computing an overpayment, in 7 U.S.C. 2014(e)(2)(C), and USDA limits it to the income the household reported late, in 7 CFR 273.18. Axiom hasn't encoded section 3650. +**If a Georgia household doesn't report earned income on time, does it lose the whole deduction?** No. Georgia says in section 3612 that a household loses the deduction if it fails to report earned income on time, and Axiom encoded that sentence as losing all of it. In section 3650, Georgia denies the deduction only for the income the household didn't report on time, when it computes an overpayment. Congress limits the denial to computing an overpayment, in 7 U.S.C. 2014(e)(2)(C), and USDA limits it to the income the household didn't report on time, in 7 CFR 273.18. Axiom hasn't encoded section 3650. **Why does CliffWatch's net income exceed slide 5's net resources?** CliffWatch counts Medicaid, CHIP and premium tax credits in net income, and we leave health coverage out of net resources. Slide 11's family also pays nothing for child care, and slide 5's family pays $1,500 a month before any subsidy. diff --git a/slideshows/appam-2026/content.test.ts b/slideshows/appam-2026/content.test.ts index d72401f1..52a04f0d 100644 --- a/slideshows/appam-2026/content.test.ts +++ b/slideshows/appam-2026/content.test.ts @@ -1008,12 +1008,13 @@ describe('typed figures', () => { it('match the record and our two 2.2.1 runs in the evidence ledger', () => { const step = float32Step(pinnedReform.household_gain); for (const figure of [ + `names the national file, \`${buildReport.dataset.split('/').pop()}\``, `Its report spec reads \`"dataset": "${record.report_spec_json.dataset}"\` and \`"region": "${record.report_spec_json.region}"\`, with the status \`${record.report_spec_status}\``, `${usd(-budget.state_tax_revenue_impact)} state revenue loss`, `falling from ${hundredths(buildChild.baseline)} to ${hundredths(buildChild.reform)}`, `$${(pinnedReform.state_cost / 1_000_000).toFixed(2)} million loss and a fall from ${hundredths(pinnedReform.baseline_child_poverty)} to ${hundredths(pinnedReform.reformed_child_poverty)}`, `We sum that run in float32, which steps by ${usd(step)} at that size, and its household gain, ${usd(pinnedReform.household_gain)}, lands within one step of the record's ${usd(-budget.budgetary_impact)} budgetary impact`, - `${tenths(nationalReform.state_cost)} loss and no change in the child poverty rate, at ${percent(nationalReform.baseline_child_poverty)}`, + `On the national file at ${nationalReform.policyengine_us_version}, we estimate a ${tenths(nationalReform.state_cost)} loss and a fall of ${(100 * (nationalReform.baseline_child_poverty - nationalReform.reformed_child_poverty)).toFixed(6)} points in the child poverty rate, from ${percent(nationalReform.baseline_child_poverty)}`, `record's ${hundredths(buildGaining)} equals the simple mean`, `${percent(nationalReform.people_gaining_share_over_1_dollar)} of all people gain more than $1`, `${percent(nationalReform.people_gaining_share_over_0_1_percent)} gain more than 0.1%`, @@ -1022,7 +1023,11 @@ describe('typed figures', () => { } expect(Math.fround(pinnedReform.household_gain)).toBe(pinnedReform.household_gain); expect(Math.abs(pinnedReform.household_gain + budget.budgetary_impact)).toBeLessThan(step); - expect(percent(nationalReform.reformed_child_poverty)).toBe(percent(nationalReform.baseline_child_poverty)); + // The national run reads the file the record names, at the record's release. + expect(nationalReform.dataset.split('/').pop()).toBe(buildReport.dataset.split('/').pop()); + expect(nationalReform.policyengine_us_version).toBe(buildReport.model_version); + // The ledger quotes the report's share of people gaining in two places. + expect(ledger.split(`${percent(buildGaining)} of people gaining`).length - 1).toBe(2); }); it('state the thresholds the national run applies', () => { @@ -1041,13 +1046,15 @@ describe('typed figures', () => { it('match the record and our runs in the speaker notes', () => { const before = pinnedReform.baseline_child_poverty; const after = pinnedReform.reformed_child_poverty; + const minutes = Math.round((Date.parse(record.finished_at) - Date.parse(record.requested_at)) / 60_000); for (const figure of [ + `Our report took about ${['one', 'two', 'three', 'four', 'five'][minutes - 1]} minutes to run`, `reports a ${millions(-budget.state_tax_revenue_impact)} cost`, `Build's report show ${tenths(-budget.state_tax_revenue_impact)} and slide 9 show ${millions(massachusetts.cost)}`, `we also estimate ${tenths(pinnedReform.state_cost)}`, `${relativeFall(buildChild.baseline, buildChild.reform)} decrease in child poverty`, `we estimate a fall from ${percent(before)} to ${percent(after)}: ${(100 * (before - after)).toFixed(1)} points, or ${relativeFall(before, after)} of ${percent(before)}`, - `Build's ${percent(buildGaining)} counts people, and slide 9's ${percent(reformRun.households_gaining_share)} counts households`, + `Build counts people in its ${percent(buildGaining)}, and we counted households in slide 9's ${percent(reformRun.households_gaining_share)}`, `we estimate that ${percent(pinnedReform.households_gaining_share)} of households would gain`, ]) { expect(notes).toContain(figure); From 8831afdcbac16dd9bfbe62d91ee8a9acef3239e7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 6 Nov 2026 23:27:50 -0500 Subject: [PATCH 9/9] Answer the 28fd572 review's three nits - Print the national run's child poverty fall as 0.0000011 points, which exceeds the 0.000001-point bound of the ledger's microcosm#1445 match; six decimals printed the bound itself. - Pin the fall to seven decimals in content.test.ts. - Cut the ledger's last Georgia sentence, which repeated the entry on slide 15's graph and gave the verb to the graph. Co-Authored-By: Claude Opus 5.5 --- slideshows/appam-2026/EVIDENCE.md | 4 ++-- slideshows/appam-2026/content.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/slideshows/appam-2026/EVIDENCE.md b/slideshows/appam-2026/EVIDENCE.md index e7af156c..d3fb0542 100644 --- a/slideshows/appam-2026/EVIDENCE.md +++ b/slideshows/appam-2026/EVIDENCE.md @@ -202,7 +202,7 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - CliffWatch (policyengine.org/us/cliffwatch) ran PolicyEngine US 1.755.5, as its `/api/metadata` reports. We entered a single parent, 33, with children aged 6 and 10 in ZIP code 02139, and left rent and child care at zero. We kept its default programs, which include Medicaid, CHIP and premium tax credits. CliffWatch passes PolicyEngine US the state and no county, so the model computes those credits from Barnstable County's benchmark plan. The notes' TAFDC example rests on the deck's pin: on PolicyEngine US 2.90.2, this family draws $7,912 of TAFDC at $18,000 of earnings and none at $18,100. - In Build (app.policyengine.org/us/build), we set the Massachusetts child and family tax credit amount, `gov.states.ma.tax.income.credits.child_and_family.amount`, from $440 to $880 from 2026, and picked Massachusetts as the population. For Build, we set the browser's time zone to UTC. - That click at 15:32:41Z on November 5 ran report 4940 (app.policyengine.org/us/report/8adb8bf2-355f-4f00-b7b9-1ef56a1deb77), which finished at 15:35:22Z. The recording cuts from the click to the finished report. The report shows a $657.4m annual state revenue loss, 45.7% of people gaining income and a 2.3% relative decrease in child poverty, on PolicyEngine US 2.2.1 and `policyengine` 6.12.0. -- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, with a SHA-256 that matches `populace_us_2024` in `policyengine.py` 6.12.0's manifest. Its report spec reads `"dataset": "default"` and `"region": "state/ma"`, with the status `backfilled_assumed`. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). We sum that run in float32, which steps by $64 at that size, and its household gain, $657,359,360, lands within one step of the record's $657,359,306 budgetary impact. On the national file at 2.2.1, we estimate a $628.7 million loss and a fall of 0.000001 points in the child poverty rate, from 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read, and we haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares. On the national file, we estimate that 46.7% of all people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. +- Report 4940's record (`data/build_report_4940.json`, which api.policyengine.org/us/report/4940 returned on November 6, 2026) names the national file, `populace_us_2024.h5@populace-us-2024-spm-20260915`, with a SHA-256 that matches `populace_us_2024` in `policyengine.py` 6.12.0's manifest. Its report spec reads `"dataset": "default"` and `"region": "state/ma"`, with the status `backfilled_assumed`. The record holds a $657,359,316 state revenue loss and child poverty falling from 22.23% to 21.72%. On the local-area file at 2.2.1, which `policyengine.py` 6.12.0 selects for state regions, we estimate a $657.36 million loss and a fall from 22.25% to 21.73% (`data/massachusetts_reform_221.json`). We sum that run in float32, which steps by $64 at that size, and its household gain, $657,359,360, lands within one step of the record's $657,359,306 budgetary impact. On the national file at 2.2.1, we estimate a $628.7 million loss and a fall of 0.0000011 points in the child poverty rate, from 21.4% (`analysis/massachusetts_reform_national.py` → `data/massachusetts_reform_221_national.json`). We haven't resolved which file the run read, and we haven't reproduced the report's 45.7% of people gaining. The record's 45.69% equals the simple mean of its ten decile shares. On the national file, we estimate that 46.7% of all people gain more than $1 and 44.5% gain more than 0.1% of household income, and on the local-area file we counted households only. ## Axiom @@ -210,4 +210,4 @@ We captured slide 11's CliffWatch screenshot on November 4, 2026, and Build's re - Axiom publishes `axiom-rules-engine` under an MIT license. - We captured slide 15's graph from axiom.org/app?compose=us-ga:policies/dfcs/snap/3612 on November 5, 2026, and read the rule behind the graph in `3612.yaml`: `earned_income_deduction_allowed` tests `total_countable_gross_earned_income > 0` and not `assistance_unit_failed_to_report_earned_income_timely`, and `earned_income_deduction` multiplies the rate by that income. - Georgia's Division of Family and Children Services publishes the rule text and December 2019 effective date in its public SNAP manual (pamms.dhs.ga.gov/dfcs/snap/3612). -- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income the household didn't report on time, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C). USDA limits the denial to the income the household didn't report on time in 7 CFR 273.18(c)(1)(ii)(B), and keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption. In the notes' Questions, we answer what happens when a household doesn't report earned income on time. The graph on slide 15 includes `3612.yaml`'s on-time reporting test. +- In section 3612, Georgia says a household loses the deduction if it fails to report earned income on time, and Axiom encodes that sentence in `3612.yaml` as losing all of it. In section 3650, Georgia denies the deduction only for the income the household didn't report on time, when it computes an overpayment. Congress limits the denial to determining an overissuance in 7 U.S.C. 2014(e)(2)(C). USDA limits the denial to the income the household didn't report on time in 7 CFR 273.18(c)(1)(ii)(B), and keeps the deduction in an agency-error claim. Axiom hasn't encoded section 3650, so on slide 15 we state the rate and its base in the caption. In the notes' Questions, we answer what happens when a household doesn't report earned income on time. diff --git a/slideshows/appam-2026/content.test.ts b/slideshows/appam-2026/content.test.ts index 52a04f0d..1942af9e 100644 --- a/slideshows/appam-2026/content.test.ts +++ b/slideshows/appam-2026/content.test.ts @@ -1014,7 +1014,7 @@ describe('typed figures', () => { `falling from ${hundredths(buildChild.baseline)} to ${hundredths(buildChild.reform)}`, `$${(pinnedReform.state_cost / 1_000_000).toFixed(2)} million loss and a fall from ${hundredths(pinnedReform.baseline_child_poverty)} to ${hundredths(pinnedReform.reformed_child_poverty)}`, `We sum that run in float32, which steps by ${usd(step)} at that size, and its household gain, ${usd(pinnedReform.household_gain)}, lands within one step of the record's ${usd(-budget.budgetary_impact)} budgetary impact`, - `On the national file at ${nationalReform.policyengine_us_version}, we estimate a ${tenths(nationalReform.state_cost)} loss and a fall of ${(100 * (nationalReform.baseline_child_poverty - nationalReform.reformed_child_poverty)).toFixed(6)} points in the child poverty rate, from ${percent(nationalReform.baseline_child_poverty)}`, + `On the national file at ${nationalReform.policyengine_us_version}, we estimate a ${tenths(nationalReform.state_cost)} loss and a fall of ${(100 * (nationalReform.baseline_child_poverty - nationalReform.reformed_child_poverty)).toFixed(7)} points in the child poverty rate, from ${percent(nationalReform.baseline_child_poverty)}`, `record's ${hundredths(buildGaining)} equals the simple mean`, `${percent(nationalReform.people_gaining_share_over_1_dollar)} of all people gain more than $1`, `${percent(nationalReform.people_gaining_share_over_0_1_percent)} gain more than 0.1%`,