Repository navigation
Conversation
TAXSIM `otheritem` was accepted but silently dropped on the 2021+ PolicyEngine path (#484). The bundled taxsimtest binary treats `otheritem` and `mortgage` identically on the federal return: each is deducted in full on Schedule A (no AGI floor, outside the SALT cap) and neither is added back to AMT income, despite the documentation calling `otheritem` an AMT preference. Sum both into PolicyEngine's `deductible_mortgage_interest`, the input with exactly that treatment. Tests run the bundled binary and the emulator on paired records that move the same dollars between the two columns (2021-2026, SALT-capped and AMT-sensitive cases, a California itemizer). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The eCPS comparison inputs (cps_households.csv) were built in 4ccfead by vectorized_validation.py, whose FIPS-to-SOI table had no entry for FIPS 1 and fell back to mapping.get(fips, 0). All 1,155 Alabama tax units (taxsimid 88052-89206) therefore had state 0, which TAXSIM reads as "no state tax": neither engine computed Alabama income tax, every one counted as a state match, and get_state_code(0) defaulted to "TX", so the dashboard's TX row held 8,337 = 7,182 Texas + 1,155 Alabama households and Alabama never appeared. Re-running the converter on every candidate eCPS H5 revision confirms all state-0 rows have state_fips 1. - Recode those 1,155 rows 0 -> 1 (no other byte changes). - convert_h5_to_taxsim.py raises on an unknown FIPS instead of using 0. - refresh_dashboard.py reads cps_households.csv, sets the year per run, and rejects state 0, any invalid code, or a missing state before any model runs; it no longer deletes its source. - utils: get_state_code(0) returns None (reporting), get_state_label(0) is TAXSIM's "State not specified", and the Texas proxy lives only in get_calculation_state_code/get_calculation_fips. Output echoes the input state (0 stays 0, as TAXSIM does); invalid codes raise instead of being simulated as California under a TX label; state_breakdown no longer merges state 0 into TX. The R wrapper stops on unknown codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ates Pin it with a strict xfail on a Kansas record, correct the AMT-income rule (v26 also nets the QBI deduction), cite Dan Feenberg's #484 description of otheritem as Schedule A line 16, and scope the CE claim to federal tax. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refresh run 35859275514 on 2b69146: same pinned environment (policyengine-us 2.6.17, policyengine-core 3.32.6), TAXSIM binary and flags as the Sept 21 data, with inputs from the corrected cps_households.csv. Alabama now has its own row (1,155 households) and Texas drops from 8,337 to its 7,182 real households. No non-Alabama household changed any match flag; the only output difference outside Alabama is one PolicyEngine fiitax that moved by at most $0.03. Full CSVs are in the draft release full-ecps-comparison-run-35859275514, which must be published for the dashboard's download links to work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Enhanced CPS behind the benchmark was archived on 2026-07-02. This converts the Populace US 2024 build that policyengine.py certifies (populace-us-2024-spm-20260915) into TAXSIM inputs and makes it the dashboard's default population, keeping the Enhanced CPS as a secondary dataset because Populace lacks its high-income tail. - scripts/convert_h5_to_taxsim.py: vectorized rewrite. Downloads the H5 at its pinned Hugging Face commit, verifies sha256, refuses any model but the certified policyengine-us 2.2.1 / core 3.32.5, and writes populace_households.csv plus provenance JSON. Incomes are the filers'; expenses are summed per tax unit (no household double counting). proptax, mortgage and otheritem follow the taxsimtest definitions, populated for the first time. Units with no filer are dropped. - scripts/refresh_dashboard.py: DATASETS registry and --dataset flag; dataset provenance, taxsimtest build and a not-endorsed note in every summary; deterministic Populace drill-down sample; release notes generated from provenance files. - Refresh workflow takes a dataset input; artifacts and release tags are named by dataset. - Dashboard: dataset toggle (Populace default, ?dataset=ecps), data under public/data/<dataset>/<year>, dataset caveats and the comparison note beside the rates; landing, docs and web-runner copy updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rate-benchmark-to-populace # Conflicts: # dashboard/public/config-data.json # dashboard/src/constants/index.js
The first 5,000-record Populace worker for 2021 exceeded the 5 GiB budget; its records populate more inputs than the eCPS rows. Batch size now defaults per dataset (DATASETS batchSize); the eCPS keeps 5,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- validate_state_number treats pd.NA and NaT (nullable dtypes) as a missing state, i.e. state 0, as it already did None, blank and NaN; error messages show the exact value (51.0000001, not 51). - The R wrapper sets NA states to 0 before handing the frame to Python, since reticulate passes an integer NA as its raw sentinel by default. - check_source rejects non-integer, blank and NaN state codes (it used to truncate 1.5 to 1) and names the first offending taxsimid. - detailed_report widens its State column for the "State not specified" row and lists it after the real states. - Tests: comparator state_code is blank for state 0; api._validate_csv rejects 52, -1 and 0.5 and accepts 0 and blank; nullable missing state runs as state 0; -1 is tested separately (TAXSIM expands it, the emulator does not support it). - Docs and comments: TAXSIM stops at the first invalid code (it does not reject -1); Alabama was coded 0 until September 23, 2026; the source lacks Alabama's CPS-income half (d317b05); sources are no longer deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Converter: pin PolicyEngine's is_tax_unit_head/spouse/dependent and filing_status to the build's tax_unit_role_input and filing_status_input before calculating. policyengine-us has no variable that reads them and takes the two oldest adults as head and spouse, so adult dependents (5,343 in the build) became spouses and 252 lone teenage heads became filer-less units. - Dashboard: ignore superseded dataset loads, so a slower earlier load cannot replace the selected dataset; treat a dataset with no summary or records as missing instead of 0% agreement. - Release notes refuse partial (smoke) provenance. - Point the Populace downloads at the rerun's release tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rate-benchmark-to-populace # Conflicts: # docs/dashboard-refresh.md # scripts/refresh_dashboard.py
…turns) All 79,729 tax units are kept: with tax_unit_role_input pinned, every unit has a head, and the 35,592 joint returns match the build's JOINT filing status exactly. Counts in the dashboard docs, web runner and tests updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows CI checked the CSVs out with CRLF endings, so their sha256 no longer matched the provenance. Mark the hashed input CSVs -text, normalize line endings when hashing the converter, and read text files as UTF-8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The August Linux taxsimtest build died with SIGFPE in mttax24_ (taxsim.f:20897, Montana 2024+) on a 2,000-record Populace batch for 2025; the same batch runs cleanly on the macOS build and under amd64 emulation, like the Maryland crash in #1214. When the binary dies from a signal, the worker now reruns the batch in halves until each crash is pinned to one record, scores every other record, and records each excluded taxsimid and routine in the part checkpoint, the summary and provenance metadata (taxsimCrashes), and the release notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Converter: return no source roles for variable-centric (legacy) H5 files instead of failing; inputs regenerated byte-identically. - Keep the converter script LF on every platform so its recorded hash holds on Windows regenerations. - Refuse to summarize a year in which no record could be scored. - Dashboard: treat legacy result CSVs as data, keep the dataset toggle on the error screen, and word the eCPS caveat precisely (its three deduction inputs are zero; TAXSIM still itemizes some records). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five years of summaries and drill-down samples for the default dataset. 2025 scores 79,719 of 79,729 records: ten Montana records crash the August Linux taxsimtest build (SIGFPE in mttax24_) and are listed in the summary metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
September 24, 2026 07:20
This was referenced Sep 24, 2026
Closed
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the TAXSIM-vs-PolicyEngine benchmark and its dashboard from the archived Enhanced CPS to the Populace US build that policyengine.py certifies. The Enhanced CPS stays on the dashboard as a secondary dataset, because the Populace build lacks its high-income tail (check 1).
Stacked on #1204 (Alabama) and #1205 (otheritem routing), plus main. Both are merged into this branch, so the diff includes them until they land. Merge them first. The benchmark results below include both.
Data build and model versions
Build:
populace-us-2024-spm-20260915. This is the US release in policyengine.py's bundle manifest on main. I checked it through the GitHub API on 2026-09-23 and again at 2026-09-24 03:10 UTC; main was at 4dc5959, and the manifest's last change was on 2026-09-22.hf://policyengine/populace-us, filepopulace_us_2024.h5, pinned to Hugging Face commit8ab57ffc2ca41d8af631ff62ebbce95e966299f3.6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84.I did not use the newer tag
populace-us-2024-spm-receipts-20260923:populace_us_2024_receipt_qualified).policyengine-us ==2.2.1with no certification record.Version conflict, resolved by splitting the two jobs.
>=2.0.0,<2.3.scripts/convert_h5_to_taxsim.pyreads the certified build only in that environment, pinned inscripts/populace-convert-requirements.txt. On that default path it refuses any other version and verifies the H5's sha256. The--h5and--datasetoptions exist for other files and skip both checks.TaxsimMicrosimDatasetinpolicyengine_runner.py). The loader, role-variable and dataset-pin compatibility that the certification covers therefore never comes into play at 2.6.17.deductible_mortgage_interest, the medical, charitable and casualty deductions,rentandtax_unit_childcare_expenses. The only difference underparameters/gov/irs/deductions/itemizedis the itemized-deduction limitation, which no input uses. AGI, which sets the medical and charitable floors, can still differ between versions.Covering 2021–2025. This follows the eCPS benchmark:
yearchanged.Input definitions
The converter follows the taxsimtest documentation, because taxsimtest is the benchmark binary. Every definition is in the converter's docstring. Differences from the eCPS inputs:
intrecadds non-qualified dividends, which the TAXSIM docs allow inintrecfor years after 2003.pensionsadds IRA and other retirement-account distributions, matching TAXSIM's definition of the field (taxable pensions plus IRA distributions).transfersis veterans' benefits, workers' compensation and child support received. The emulator maps it togeneral_assistance(Map TAXSIM transfers to general_assistance (#455) #1052).nonpropstays 0. The emulator maps it to no PolicyEngine variable, so a value would be dropped on the PolicyEngine side only. This is a follow-up.tax_unit_role_input(HEAD, SPOUSE, DEPENDENT) and each unit'sfiling_status_input.is_tax_unit_headis the oldest adult andis_tax_unit_spousethe next-oldest.filing_statusvariables to the build's values before calculating anything. All 79,729 units are kept. The 35,592 joint returns match the build's JOINT count exactly.mstat2 and all othersmstat1. That collapses 8,554 heads of household (TAXSIM infers the status from dependents), 876 married filing separately and 554 surviving spouses. The emulator's Microsimulation path treatsmstat6 as a two-person unit (has_spouse = np.isin(mstat, [2, 6])), so I did not use it. This is a follow-up.Check 1: top-income coverage (Populace lacks the tail)
Counts are unweighted TAXSIM records. "Total income" is the sum of the 16 TAXSIM income fields. That definition reproduces the eCPS counts in the brief exactly, and
tests/test_populace_inputs.pypins both columns.The Populace build has essentially no top tail. A separate session has been opened for it: "Fix Microcosm top-income tail (no returns above $10M AGI)". This matters for how the headline is read. On the eCPS, the emulator does worst exactly there: for records with $1M+ total income, federal agreement within 1% is 29–37% (2021–2025), against 89–91% overall. On Populace, federal agreement within 1% for its 1,291 records with $1M+ total income is 77–79% (2021–2025). The Populace headline rates therefore exclude most of the records where the emulator disagrees most, and they should not be compared one-for-one with the eCPS rates.
The eCPS is therefore kept, not dropped:
?dataset=ecpslinks straight to the eCPS view.Check 2: itemized deductions are now tested
In the eCPS inputs,
proptax,mortgageandotheritemare zero in every record. In the Populace inputs, the nonzero counts are:proptaxmortgageotheritemThis builds on #1205 and does not conflict with it. #1205 routes
mortgageandotheriteminto PolicyEngine'sdeductible_mortgage_interest, and it is merged into this branch. From that session's taxsimtest sweeps:mortgage, which is why the converter supplies net deductible amounts.mortgagemakes taxsimtest stop the whole run (exit 1), so the converter rejects negative values.mortgageandotheritemchanges state itemized deductions in AR, DE, IA (2020–22), KS, ME, MS, NC and WI (2023+), and can move federal tax through the state tax. The Route TAXSIM otheritem into itemized deductions alongside mortgage #1205 sweep covered those state-years for its test inputs.mortgagetootheritemraised state tax by $929.63 and lowered federal tax by $111.56.Why
otheritemholds only investment interest for 2021–2025. TAXSIM's otherotheritemcomponents are empty or not PolicyEngine inputs in these years:Extracted evidence (pdftotext -layout; Cornell LII HTML), fetched 2026-09-23
For contrast, the 2017 Form 6251 line 2 reads "Reserved for future use". Line 1's elided text and line 4's MFS threshold are omitted above. Part I is otherwise complete.
Check 3: every state, including Alabama
check_source(Code Alabama as TAXSIM state 1 and stop reporting state 0 as Texas #1204) rejects state 0 before any model runs.test_populace_inputs_cover_every_state_including_alabamapins the committed inputs.DatasetTests.test_every_dataset_source_is_completechecks both datasets. Code Alabama as TAXSIM state 1 and stop reporting state 0 as Texas #1204'stests/test_state_codes.pypasses against the new converter.Check 4: releases, metadata and before/after rates
Releases. The release notes are generated from the provenance files. They carry the not-endorsed label, the dataset caveat, the build and hashes, the environment and the rates table.
populace-comparison-run-35958762943(run 35958762943, commit 0d9befa).ecps-comparison-run-35950220750(run 35950220750, commit 480361f).The Populace workflow staged its own release. The eCPS workflow's
stage-releasestep gotHTTP 403: Resource not accessible by integrationfromgh release create, including on a retry; the same step succeeded before and after. I staged that draft from the run's ownfull-data-ecps-*artifacts, with the notes the workflow generates, and checked every CSV's sha256 against its provenanceoutputSha256. The notes say this. Both releases are now published, and the dashboard's "Full data" buttons point at them.2025 has ten excluded records. The August Linux taxsimtest build crashes with SIGFPE in
mttax24_, its Montana routine, on ten Populace records for 2025. Each record crashes on its own, and one is simply a single filer aged 23 with $25,000 of wages (taxsimid 54307). The macOS build and the Linux build under amd64 emulation both compute these records.metadata.taxsimCrashes), the provenance, the release notes and on the dashboard.Summary JSON metadata (
dashboard/public/data/<dataset>/<year>/summary_<year>.json→metadata):dataset: id, label, caveat, source file and sha256, records. For Populace it addsbuildId,hfRepo,hfRevision,hfCommit,h5File,h5Sha256,dataYear,certifiedBy,conversionModel(pe-us 2.2.1 / core 3.32.5) andconverterSha256.policyengineUsVersion2.6.17,policyengineCoreVersion.taxsimtestBuildcd2026081819,taxsimBinarySha25600a321d2…a89b.emulatorCommit,scriptSha256,requirementsSha256,batchSize,peakWorkerRssMiB,comparisonNote.Which code produced each run.
policyengine_taxsim/changed only in Code Alabama as TAXSIM state 1 and stop reporting state 0 as Texas #1204's review commit (7d75045). I read that diff: it touches validation of missing or invalid state codes, report formatting and docstrings. Every input here has a valid state from 1 to 51, so both runs use the same calculation code.Before and after, by year.
The federal difference is the top tail, not better emulation. Without its 11,454 records with $1M+ total income, the eCPS scores 96.3–97.1% federal agreement within 1% (2021–2025). Populace scores 96.4–98.1%.
State agreement within $15 is lower on Populace. In most of the states that moved, the drop sits in records with itemized-deduction inputs, which the eCPS never had (see the table below).
Subgroups (Populace), from the full CSVs, using the same match rules as the summaries:
Subgroups (eCPS), for comparison. Records with $1M+ total income are where federal agreement is lowest:
Ten states whose rates moved most, eCPS → Populace (same code). States are ranked by the mean, over 2021–2025, of the largest absolute change among the four rates. Values shown are for 2024.
Ranked by the mean over 2021-2025 of the largest absolute change among the four rates. Values are 2024: eCPS -> Populace (live dashboard in brackets; the live dashboard counted Alabama under TX).
What drives most of the state moves (2024, Populace). State agreement within $15 split by whether the record has an itemized-deduction input. This is observed agreement, not a diagnosis. In most of these states the drop sits in the records the eCPS never tested; CT and OR do not follow the pattern.
Merge order, conflicts and follow-ups
dashboard/public/data/<year>/. This PR moves that data todata/ecps/<year>/and supersedes it with the same-code eCPS rerun.scripts/refresh_dashboard.pyand the refresh workflow. Whichever lands second has to reconcile withDATASETS,--datasetand per-dataset batch sizes.nonprop;mstat6 as a two-person unit;tax_unit_role_inputandfiling_status_input. Every policyengine.py simulation on this build re-infers roles by age.POPULACE_BUILD, regenerate the inputs and rerun. The eCPS view can then retire.Test plan
tests/test_populace_inputs.py, run locally. Converter behavior on synthetic sims:mortgagerejected, uncertified model refused.Committed-file checks:
tests/test_dashboard_refresh.py: dataset registry completeness, provenance validation, deterministic state-stratified sample, release notes (label, caveat, hashes, rates, rejection of smoke runs).tests/test_state_codes.py(Code Alabama as TAXSIM state 1 and stop reporting state 0 as Texas #1204) against the new converter, in the pinned 2.6.17 refresh environment.Refresh smoke tests and full 2021–2025 runs for both datasets on Actions.
bun run buildof the dashboard. Checked the static export in a browser: the eCPS view shows the toggle, caveat, not-endorsed note and hero provenance. The dashboard vitest suite is already broken on main (tests import a nonexistent../LandingPage); Fix dashboard vitest suite and run it in CI #1160 fixes it.Two independent reviews (Subfleet review lanes). The second rebuilt all 79,729 Populace records from the H5 and the build's roles with zero mismatches. Findings fixed in this branch:
CI matrix (lint, changelog, tests on Ubuntu, macOS and Windows with Python 3.10 and 3.11) on the final head; confirmed before the PR was marked ready.
🤖 Generated with Claude Code