Skip to content

Set the DC EITC with-children match to 85% for 2026-2028 - #10638

Merged
MaxGhenis merged 3 commits into
mainfrom
dc-eitc-match-2025
Nov 3, 2026
Merged

MaxGhenis merged 3 commits into
mainfrom
dc-eitc-match-2025

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

The DC EITC match for filers with qualifying children stays at 100% for TY2025, drops to 85% for 2026-2028, and returns to 100% from 2029. On main it was 100% for every year from 2025.

Law (D.C. Code § 47-1806.04(f)(1))

  • Permanent text (DC Council codification, publication 2026-10-28): (B-2) gives "85% of the earned income tax credit allowed under section 32" for years "beginning after December 31, 2024". (B-3) gives 100% for years "beginning after December 31, 2028".
  • Temporary text. An earlier publication carried D.C. Law 26-89 § 2(i). It set (B-2) to 100% and showed (B-3) as "[Repealed]". The 2026-10-28 publication has dropped it.
  • So TY2025 gets 100%, and from TY2026 the permanent schedule applies.

Change

with_children/match.yaml: 2025: 1 (D.C. Act 26-214 and D.C. Law 26-89), 2026: 0.85 ((B-2)), 2029: 1 ((B-3)). I added references to both acts.

Tests

dc_eitc_with_qualifying_child.yaml has 3 new cases: an HoH filer, $10,000 wages, one child. Federal EITC = 34% x $10,000 = $3,400 in each year.

Year Expected Hand check
2026 2,890 0.85 x 3,400
2028 2,890 0.85 x 3,400
2029 3,400 1.00 x 3,400

The existing 2025 cases (3,400 = 100%) don't change.

Sources and the TY2025 reading

The hub read these on its own host:

So TY2025 stays at 100%. Read only against the current permanent text, it would be 85%.

source-conflict: us-dc:eitc-with-children-2025-rate in source_conflicts/candidates.yaml records the question, with PE-US following the emergency and temporary acts for TY2025.

impact: we estimate that DC's EITC falls $19.7 million (14%) in 2026. About 38,900 tax units with qualifying children (50 records in the default dataset) each receive 85% of their credit under main. DC income tax rises, and household net income falls, by the same $19.7 million; federal income tax and benefits don't change (pair).
axiom: TheAxiomFoundation/rulespec-us#1420 (ruling 7 added)

🤖 Generated with Claude Code

…pired

D.C. Law 26-89 set the with-children match in 47-1806.04(f)(1)(B-2) to 100%
from TY2025 and repealed (B-3); it expired on September 25, 2026. The
permanent text gives 85% for taxable years after 2024 and 100% from 2029
under (B-3). The parameter held 100% for every year from 2025.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JoAx1PqHmDjxg51eh
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: ec04a940e5e6f5497965d4465af05955083ae924

Item: DC EITC with-children match from 2025

  • Check. I read D.C. Code § 47-1806.04(f)(1)(B-2)/(B-3) in two DC Council codifications.
    • Current (publication 2026-10-28): 85% for years after 2024, 100% after 2028.
    • Earlier publication: D.C. Law 26-89 § 2(i) set 100% from 2025 and repealed (B-3), with a note that it "will expire on September 25, 2026".
  • Defect. Main held 1.0 for every year from 2025, so 2026-2028 were wrong. The 85% that Let a qualifying child without an SSN establish EITC eligibility from 2021 #10583's body cites matches the permanent (B-2). For TY2025 it differs from the temporary law that was in force at filing. I didn't touch Let a qualifying child without an SSN establish EITC eligibility from 2021 #10583.
  • Fix. policyengine_us/parameters/gov/states/dc/tax/income/credits/eitc/with_children/match.yaml:22-30 now reads 2025: 1, 2026: 0.85, 2029: 1. A D.C. Law 26-89 reference is at :9-10.

Tests (one file per process)

  • dc_eitc_with_qualifying_child.yaml 8 passed (3 new, at lines 129, 158, 186).
  • dc_eitc.yaml 25 passed; section_911_state_eitc.yaml 6 passed.
  • test_eitc_qualifying_child_invariants.py 3 passed. hypothesis isn't in the synced env, so I ran it with uv run --with hypothesis.
  • code_health/test_source_conflicts.py 103 passed.

Hand computations

HoH filer, $10,000 wages, one child. Federal EITC = 34% x 10,000 = 3,400.

  • 2026 and 2028: 0.85 x 3,400 = 2,890.
  • 2029: 3,400.

Mutants (each in its own process, file restored after)

  • main's match.yaml: 2 fail (2026, 2028).
  • 2029-01-01 line deleted: 1 fail (2029).

CI

Not run: the PR is a draft.

Judgment calls

  • TY2025 kept at 100%. That is the temporary law in force during the filing season. Per search results, OTR lists 2025 at 100% and 2026-2028 at 85%. Unverified: otr.cfo.dc.gov returned a proxy 403.
  • Disapproval. Search results say H.J.Res. 142 (P.L. 119-78) disapproved the temporary act, and that a DC AG opinion says it stayed valid. I couldn't fetch either one. If the hub treats TY2025 as a source conflict, it needs a register entry and a marker. I added no marker.
  • Sources. code.dccouncil.gov, otr.cfo.dc.gov, congress.gov and govinfo.gov were unreachable (proxy 403). I read the statute from DCCouncil/law-xml-codified on GitHub. I didn't check the 2025 D-40 booklet reference or the new D.C. Law 26-89 URL.

Proposed follow-up

  • DC CDCC rate from 2026 follows an expired temporary law. dc/tax/income/credits/cdcc sets 0.2425 from 2026. The 2026-10-28 codification of 47-1806.04(c) reads 32% with no end date; the 24.25% sat in the expired D.C. Law 26-89 text.
  • DC 2025+ standard deduction amounts. The DC-specific 2025 amounts are attributed to D.C. Act 26-214. The current codification of 47-1801.04(26) still points to IRC 63(c) for years after 2017, so the amounts need to be rechecked against the permanent law.

Generated by Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review November 2, 2026 23:37
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
ec04a94
METHODOLOGY_CHOICE: yes

The head hasn't moved since ec04a940e5. The PR is no longer a draft. The hand-off's "CI: Not run: the PR is a draft" was true when it was written and is now out of date: see CI below.

The law, by tax year

I read every version of D.C. Code § 47-1806.04 in the DC Council's codified-law repository (DCCouncil/law-xml-codified, branch publication/2026-05-23.2026-10-28-01, and its history). That repository is the source behind code.dccouncil.gov.

Codification (f)(1)(B-2), years after Dec 31, 2024 (B-3), years after Dec 31, 2028 Header note / history
2024-10-11 85% 100% none
2025-12-08 100% "[Repealed]" "emergency legislation that will expire on March 3, 2026"; § 2(i) of D.C. Act 26-214, Dec. 3, 2025
2026-02-13, 2026-07-20 100% "[Repealed]" "temporary legislation that will expire on September 25, 2026"; § 2(i) of D.C. Law 26-89, Feb. 12, 2026, 73 DCR 9
2026-09-29 (still current at the 2026-10-28 publication) 85% 100% expiry note gone

So the emergency act (in force Dec. 3, 2025 to Mar. 3, 2026) and then the temporary law (Feb. 12 to Sep. 25, 2026) set 100% for years after 2024. Today's codified text is back to 85% / 100%.

Tax year Rate Basis
2025 100% Act 26-214 was in force when TY2025 ended (Dec. 31, 2025). Law 26-89 was in force through the filing season. OTR's EITC FAQ (search result only) lists "2025 · 100%". The 2025 D-40 line 27 extract in search results multiplies by "1.00"
2026, 2027, 2028 85% Current codified (B-2). Thomson Reuters (search result only) reports that the FY2027 Budget Support Act of 2026 cuts the credit to 85% for years after Dec. 31, 2025 and before Jan. 1, 2029. OTR's FAQ (search result only) lists "2026 - 2028 · 85%"
2029 100% Current codified (B-3). Matches OTR's FAQ (search result only)

The PR's values (2025: 1, 2026: 0.85, 2029: 1) match every year.

Disapproval. According to search results, H.J.Res. 142 disapproves the temporary act (D.C. Act 26-217, later D.C. Law 26-89). The Senate passed it on February 12, 2026, and it became P.L. 119-78 on February 18. Search summaries of the DC Attorney General's February 24, 2026 opinion say the Home Rule Act's review window closed on February 11, so the resolution did not stop the act from taking effect on February 12. They say the opinion also holds that, even if it repealed the act, TY2025 liability stands under the emergency act, the presumption against retroactivity, and 1 U.S.C. § 109. The DC Council's own codification kept Law 26-89 in force until its September 25 expiry, which is consistent with that. I could not read the resolution, the public law or the opinion itself.

Findings

P2-1. TY2025 at 100% is a reading the hub should rule on and record

policyengine_us/parameters/gov/states/dc/tax/income/credits/eitc/with_children/match.yaml:22-25

The PR keeps 100% for TY2025, as main does. That rests on the expired emergency and temporary acts, while today's codified (B-2) literally says 85% for "a full calendar or fiscal year beginning after December 31, 2024". This PR changes no 2025 output. But it now writes down a reading where the text allows two, so I'd give the hub this row for the register:

  • Question. Which rate governs a DC filer with qualifying children for TY2025: the temporary and emergency 100%, or the permanent 85% now codified for "years beginning after December 31, 2024"?
  • Reading A (PR, main, OTR). 100%. The emergency act was in force when the year ended. The temporary law was in force through the filing season. Per search summaries, the AG holds the disapproval came too late and would not reach TY2025 liability in any case.
  • Reading B. 85%. The current permanent text covers 2025, and a disapproval (if it took effect) voids the temporary act from enactment.
  • Worked example. Head of household, $10,000 wages, one child, TY2025. Federal EITC = 34% × $10,000 = $3,400. Reading A gives dc_eitc $3,400; reading B gives 0.85 × $3,400 = $2,890.

I recommend reading A: it matches the law in force and OTR's practice. If the hub registers it, the deciding line needs # Source conflict dc:<id> at match.yaml:25. The PR adds no marker, and test_source_conflicts.py passes because no register entry exists yet.

P3-1. The comment cites only the temporary law. The emergency act governed TY2025 at year end.

match.yaml:8-10 and :22-24

The comment says Law 26-89 "took effect on February 12, 2026, for the TY2025 filing season". Act 26-214 set the same 100% from December 3, 2025. Replacement for lines 22-24:

  # D.C. Act 26-214 § 2(i) (emergency, Dec. 3, 2025 to Mar. 3, 2026) and
  # D.C. Law 26-89 § 2(i) (temporary, Feb. 12 to Sep. 25, 2026) set (B-2) to
  # 100% for taxable years after 2024 and repealed (B-3).

Then add a reference:

    - title: D.C. Income and Franchise Tax Conformity and Revision Emergency Amendment Act of 2025 (D.C. Act 26-214), § 2(i)
      href: https://code.dccouncil.gov/us/dc/council/acts/26-214

P3-2. The 2026-2028 rationale may soon point at superseded text

match.yaml:26-29

The comment derives 2026-2028 from the "permanent (B-2)" for "years beginning after December 31, 2024". If Thomson Reuters' report is right, the FY2027 Budget Support Act of 2026 enacts 85% for years after 2025 and before 2029, and the codified (B-2) will change wording when it is codified. It is not in the 2026-10-28 codification of § 47-1806.04. The values stay right under either text. Once someone can read the Act, the 2026 line should cite it, and the hub's axiom: legal id should name the provision that actually governs.

Verified by running

Defect at main. I copied the PR's test file into an origin/main worktree (d4d391788d) and ran it: 2 failed, 6 passed (the 2026 and 2028 cases). At head: 8 passed. Witness, HoH filer, $10,000 wages, one child:

Year match main → head dc_eitc main → head dc_income_tax main → head
2025 1 → 1 3,400 → 3,400 -3,400 → -3,400
2026 1 → 0.85 3,400 → 2,890 -4,400 → -3,890
2027 1 → 0.85 3,400 → 2,890 -4,425 → -3,915
2028 1 → 0.85 3,400 → 2,890 -4,450 → -3,940
2029 1 → 1 3,400 → 3,400 -4,470 → -4,470

Hand values. I computed them before reading the code: 34% × $10,000 = $3,400 (phase-in, under the one-child maximum); 0.85 × 3,400 = 2,890. The $510 tax change each year from 2026 to 2028 equals 3,400 − 2,890.

Rule in code. dc_eitc_with_qualifying_child.py:48 multiplies the federal-like credit by p.with_children.match for every filing status. The TaxUnit is the only entity, and the formula has no year branch, so the parameter carries the change to every year and status.

Tests. I ran one file per process at head:

  • dc_eitc_with_qualifying_child.yaml: 8 passed
  • dc_eitc.yaml: 25 passed
  • dc_eitc_without_qualifying_children.yaml: 7 passed
  • section_911_state_eitc.yaml: 6 passed
  • test_eitc_qualifying_child_invariants.py: 3 passed (needs hypothesis, which isn't in the synced env)
  • code_health/test_source_conflicts.py: 103 passed

Each new case asserts the exact value (margin 0.01), and both boundaries are tested: 2025/2026 by the existing 2025 cases plus the new 2026 case, 2028/2029 by the new cases.

Mutants. Each ran in its own process, and I restored the file after each. git status was clean at the end.

Mutant Result Killed by
M1: main's match.yaml 2 failed 2026 and 2028 cases
M2: drop 2029-01-01 1 failed 2029 case
M3 (mine): add 2027-01-01: 1 1 failed 2028 case (2027 carries forward)
M4 (mine): 2025-01-01: 0.85 2 failed existing 2025 cases

Readers. I grepped the .py, .yaml, .ipynb and .md files:

  • with_children.match has two readers: dc_eitc_with_qualifying_child.py:48 and the invariants test, which reads the parameter per year (YEARS = [2021, 2022, 2023, 2025]) and so tracks it.
  • Downstream, dc_eitc → dc_refundable_credits → dc_income_tax, plus state_eitcs.yaml and the DC refundable.yaml list.
  • No reader becomes reachable that wasn't already, and nothing gets worse beyond the intended 15% cut for 2026-2028.
  • No partner test sets state_code: DC.

Docs and body.

  • The changelog fragment is a top-level .fixed.md and states the change correctly.
  • The body's claims hold against the codification: the 2026-10-28 text, Law 26-89 effective Feb. 12, 2026, and the Sep. 25, 2026 expiry.
  • impact: the hub will measure and axiom: (hub files pe-parity before landing) are both present, as the brief asked.
  • Baseline output moves. Every DC tax unit with a qualifying child and a federal EITC in 2026-2028 loses 15% of its DC EITC, and dc_income_tax rises by the same amount. The hub needs a population run for those years: DC EITC total and DC income tax revenue for 2026-2028. TY2025 and TY2029+ are unchanged.

Scope. Three files changed: the changelog fragment, match.yaml and the test YAML. None is under .github/ or partners/. Overlap search: no other open PR changes match.yaml. #10050 and #10583 edit dc_eitc.yaml and the DC EITC variables, but their DC cases are all 2025 or earlier, which this PR doesn't move.

#10583. Its current body no longer cites 85% for 2025. Its 2025 DC figures use 1.0 (e.g. 1,020 / 2,720), and its DC cases are 2022-2023, which this PR doesn't move. It needs no change. Its review comment asked someone to read (B-2), and this PR answers that.

CI at ec04a940e5, run 41546879073 (Pull request): these jobs succeeded:

  • PR validation, Household API Partners, Quick Feedback, Package and compatibility
  • all 11 Baseline shards
  • all 7 Python Rest shards

Still in progress when I posted: Microsimulation and 9 Contrib shards. The earlier run 41544625950 was skipped (draft).

Hand-off follow-ups

Both are real defects against the current codification, identical on main, so they're follow-ups, not this PR's defects. Each carries the same caveat: the FY2027 Budget Support Act may have re-enacted them, and I couldn't read it.

  1. DC CDCC 24.25% from 2026. The temporary codification (2026-02-13) of § 47-1806.04(c)(1)(B) reads 24.25% for years "beginning after December 31, 2025". The current codification has one (c)(1): 32% for years after 1988, with no end date. cdcc/match.yaml holds 0.2425 from 2026 and attributes it to the FY2025 BSA (D.C. Act 25-506 § 7032). But the 2024-10-11 codification, made after that act, has no 24.25%, so the citation is wrong too. Witness at head, TY2026: federal cdcc_potential 1,500 gives dc_cdcc_potential 363.75 (24.25%), against 480 under the current text (32%).
  2. DC standard deduction from 2026. The temporary codification added § 47-1801.04(3A) ($15,000 / $22,500 / $30,000 for TY2025, then indexed) and (44)(A)(v). The current codification has no (3A), and (44)(A)(iv) gives "the standard deduction as prescribed in section 63(c)" for years after 2017. Witness at head, TY2026: dc_standard_deduction is 15,300 single, 30,650 joint and 23,000 HoH. The federal basic_standard_deduction is 16,100, 32,200 and 24,150. TY2025 at 15,000 matches the (3A) that was in force. One more problem: the hub's item 2 (a separate filer whose spouse itemizes) may resolve to "follows IRC 63(c)(6)" from 2026 under (44)(A)(iv).

What I couldn't check

  • I couldn't reach code.dccouncil.gov, lims.dccouncil.gov, otr.cfo.dc.gov, oag.dc.gov, congress.gov, govinfo.gov or web.archive.org: the proxy returned 403 on CONNECT, and the fetch tool couldn't resolve them. The same went for tax.thomsonreuters.com and afslaw.com.
  • So I haven't read D.C. Law 26-89, D.C. Act 26-214, H.J.Res. 142, P.L. 119-78, the AG opinion, the FY2027 Budget Support Act, OTR's FAQ or the 2025 D-40 booklet. Everything I say about them comes from search-result summaries. The laws/26-89 href format matches the search result's URL, but I didn't load it.
  • I ran no population impact: the hub measures that.
  • I can't see edit history for Let a qualifying child without an SSN establish EITC eligibility from 2021 #10583's body, so I couldn't confirm it once said 85%.

Generated by Claude Code

D.C. Act 26-214 set (B-2) to 100% from December 3, 2025, as of January 1, 2025, so it held at the close of 2025. The temporary law carried that through the filing season. Add the act to the references and record the TY2025 question in source_conflicts/candidates.yaml: the Attorney General's opinion, OTR's FAQ and the 2025 D-40 booklet all use 100%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hub impact pair, progress

The base side of the 2026 pair finished. The head side runs next, on the same household chunks.

  • Base: 4e71481e19b31e97b25edc578569849d691fad90, the merge base of head 7c638705d0b71951ec78dd1adc16de42466fb002 with main. The side imported policyengine_us from the base tree.
  • Year 2026, 8 household chunks, through calculate_chunked from Add household-chunked microsimulation calculations #10561 at c545c47877c70b367a6fab2322037e453364997e.
  • Dataset: hf://datasets/policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260909. The local file's SHA-256 is 6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84, which matches DEFAULT_DATASET_SHA256.
Variable Entity Base weighted sum ($)
dc_eitc tax_unit 140,731,752
income_tax tax_unit 2,595,105,975,380
state_income_tax tax_unit 578,044,599,362
household_net_income household 17,161,428,039,502
household_benefits household 2,059,793,926,050
  • Wall time: 933 s (15 min 33 s) inside pair_side.py's timer; 16:28.47 (m:ss) for the whole process under /usr/bin/time -v.
  • Maximum resident set size: 3,814,720 kB (3.64 GiB).

Exact weighted sums:

{
 "dc_eitc": 140731752.08650315,
 "household_benefits": 2059793926050.1262,
 "household_net_income": 17161428039502.375,
 "income_tax": 2595105975379.617,
 "state_income_tax": 578044599361.5671
}

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
IMPACT PAIR: differs
head 7c63870
base 4e71481

The PR head hasn't moved: it is still 7c638705d0. The base is its merge base with main, which is also the PR's recorded base.

Setup

  • Dataset. hf://datasets/policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260909, at Hugging Face commit 9a814a3b3b53c0ecd6e1737b6ec862c31300ef6f (826,917,837 bytes). Both trees hold the same DEFAULT_DATASET and DEFAULT_DATASET_SHA256, and policyengine_us/system.py doesn't differ between them. I hashed the local file before the base run and again after it. Both times its SHA-256 was 6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84, which matches DEFAULT_DATASET_SHA256, 6496cc4393d4d3c6574f76eca231de5898c803b9067645591fd5c4d3e65aee84.
  • Year and chunks. 2026, 8 chunks. The helper splits the 57,240 households (79,729 tax units) into balanced consecutive chunks, so both sides ran the same 8 partitions.
  • Code. I built one environment with uv sync in the head worktree and ran both sides in it, because pyproject.toml and uv.lock don't differ between base and head. Each side set PYTHONPATH to its own worktree, which picked that side's formulas. Each side's summary.json confirms it: the base side imported policyengine_us from the base worktree, and the head side from the head worktree. The environment ran Python 3.14.6 and policyengine-core 3.34.2.
  • Helper. Both sides loaded the same calculate_chunked.py, from Add household-chunked microsimulation calculations #10561 at c545c47877c70b367a6fab2322037e453364997e, by path. Add household-chunked microsimulation calculations #10561 has since moved to f8ad24ebd189d7f14b709b94d9c3c8b93e5773ee. In the helper, that commit moves the one-marital-unit check ahead of the chunk loop and rewords the docstring, and changes nothing else. With 8 chunks of about 7,000 households, that check can't fire in either version.

Results, 2026

Variable Entity Base sum ($) Head sum ($) Change ($) Rows that differ Weighted rows up Weighted rows down Largest absolute difference ($)
dc_eitc tax_unit 140,731,752 120,994,336 −19,737,416 50 0 38,893 1,097.40
income_tax tax_unit 2,595,105,975,380 2,595,105,975,380 0 0 0 0 0.00
state_income_tax tax_unit 578,044,599,362 578,064,336,777 +19,737,416 50 38,893 0 1,097.40
household_net_income household 17,161,428,039,502 17,161,408,302,102 −19,737,401 50 0 38,893 1,097.40
household_benefits household 2,059,793,926,050 2,059,793,926,050 0 0 0 0 0.00

Change is head minus base before rounding, so it can differ by $1 from the difference of the rounded sums.

What moves

dc_eitc, state_income_tax and household_net_income move. income_tax and household_benefits don't.

  • dc_eitc falls by $19.74 million (14.0%), in 50 tax units with a combined weight of 38,893. In each of those tax units, the head value is 85% of the base value (ratios 0.8499999 to 0.8500001). Their weighted total goes from $131,582,783 to $111,845,367, a mean cut of $507.48 per weighted tax unit. The other 21 tax units with a positive DC EITC ($9,148,969 weighted) don't change. In the code, dc_eitc takes dc_eitc_with_qualifying_child when dc_eitc_has_qualifying_child is true, and that amount multiplies the federal-like credit by gov.states.dc.tax.income.credits.eitc.with_children.match. That parameter reads 1.0 for 2026 at base and 0.85 at head. The largest change, $1,097.40, is 15% of a $7,316 base credit.
  • state_income_tax rises by the same amount, in the same 50 tax units. dc_eitc is on DC's refundable-credit list (gov.states.dc.tax.income.credits.refundable), which dc_refundable_credits adds. state_refundable_credits adds the list gov.states.household.state_refundable_credits, which includes dc_refundable_credits, and state_income_tax subtracts state_refundable_credits. DC's tax before refundable credits (dc_income_tax_before_refundable_credits) subtracts only the non-refundable list, which holds dc_cdcc, so the EITC enters DC tax once. For each tax unit, the state tax change is minus the DC EITC change, within float32 rounding (at most $0.0005). The weighted totals differ by $0.46.
  • household_net_income falls by the same amount, in the 50 households that hold those tax units. It adds household_refundable_tax_credits, which adds household_refundable_state_tax_credits, whose list (gov.household.household_refundable_state_credits) includes dc_refundable_credits. The tax it subtracts, household_tax_before_refundable_credits, takes state tax before refundable credits (state_income_tax_before_refundable_credits and state_use_tax), so the credit counts once. For each household, the change equals the DC EITC change within float32 rounding (at most $0.0059). The weighted totals differ by $15.46.
  • income_tax and household_benefits have no row that differs. I checked two routes that could carry the change, and neither does. First, the federal SALT deduction's sources (gov.irs.deductions.itemized.salt_and_real_estate.sources) include state_and_local_sales_or_income_tax, which reads state_withheld_income_tax, not state_income_tax. For DC, dc_withheld_income_tax applies DC's rate schedule to each person's AGI less the federal single standard deduction. Second, the behavioral responses read household_net_income, but labor_supply_behavioral_response and capital_gains_behavioral_response both return 0 when simulation.baseline is None. policyengine-core sets it to None for a simulation built without a reform, and neither side used one.

Every changed household is in DC (state_fips 11 in the dataset's household table).

Runs

Side Tree policyengine_us came from pair_side.py wall time /usr/bin/time elapsed (m:ss) Maximum resident set size
base base worktree 933.0 s 16:28.47 3,814,720 kB (3.64 GiB)
head head worktree 922.8 s 16:17.56 3,814,736 kB (3.64 GiB)

The VM has 4 vCPUs and 16 GB of memory. I ran the sides one at a time. Neither came near 14 GB, so I didn't rerun with 16 chunks. With these five outputs, each side ran for about 16 minutes and peaked at 3.64 GiB. The hub had planned for about 70 minutes and 9.75 GiB per side, from a run of nine outputs.

Dropped variables

None. All five variables exist on both sides on the same entity: dc_eitc, income_tax and state_income_tax on tax_unit, and household_net_income and household_benefits on household. The helper's guard rejected none of them, and Microsimulation accepted dataset_end_year.

Commands

pair_side.py and compare.py are the brief's scripts, unchanged. I ran these from a scratch directory. $F stands for the local path that hf_hub_download printed.

curl -sI https://huggingface.co/api/datasets/policyengine/populace-us | head -1   # the proxy's "HTTP/1.1 200 Connection Established"; the download below reached Hugging Face
git clone https://github.com/PolicyEngine/policyengine-us.git pe && cd pe
git fetch origin pull/10638/head:pr-10638 pull/10561/head:pr-10561   # pr-10561 was c545c47877c70b367a6fab2322037e453364997e
HEAD_SHA=$(git rev-parse pr-10638); BASE_SHA=$(git merge-base origin/main $HEAD_SHA); echo $HEAD_SHA $BASE_SHA
git diff --stat $BASE_SHA $HEAD_SHA -- pyproject.toml uv.lock   # empty
git diff --stat $BASE_SHA $HEAD_SHA -- policyengine_us/system.py   # empty
git worktree add --detach ../head $HEAD_SHA
git worktree add --detach ../base $BASE_SHA
git show pr-10561:policyengine_us/tools/calculate_chunked.py > ../calculate_chunked.py
(cd ../head && uv sync)
PY=$PWD/../head/.venv/bin/python
# Fetch the file once, with the call _resolve_dataset_path makes, so neither side's time includes the download.
( time $PY -I -c 'from huggingface_hub import hf_hub_download; p = hf_hub_download(repo_id="policyengine/populace-us", filename="populace_us_2024.h5", repo_type="dataset", revision="populace-us-2024-spm-20260909"); print("PATH", p)' ) > ../download.log 2>&1
sha256sum "$(readlink -f "$F")"
# preflight.py prints the imported package's directory, DEFAULT_DATASET, DEFAULT_DATASET_SHA256, each variable's entity, and the match for 2025, 2026, 2028 and 2029.
for side in base head; do PYTHONPATH=$PWD/../$side $PY ../preflight.py 2> ../preflight_$side.err; done
apt-get install -y time   # the VM had no /usr/bin/time
mkdir -p ../out
H=$PWD/../calculate_chunked.py; PY=$PWD/../head/.venv/bin/python
export OMP_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 MKL_NUM_THREADS=4
PYTHONPATH=$PWD/../base /usr/bin/time -v $PY ../pair_side.py $H 8 2026 ../out/base > ../out/base.log 2> ../out/base.err
sha256sum "$(readlink -f "$F")"
PYTHONPATH=$PWD/../head /usr/bin/time -v $PY ../pair_side.py $H 8 2026 ../out/head > ../out/head.log 2> ../out/head.err
$PY ../compare.py ../out/base ../out/head ../out/compare.json
readlink -f "$F" > ../out/dataset.path
(cd .. && head/.venv/bin/python -I make_result.py .)   # reconciles the rows and writes this comment

I launched each side's line detached (setsid nohup), inside a small wrapper script, so a tool timeout couldn't stop it. The wrapper also sampled the side's VmRSS and VmHWM once a minute. make_result.py reads tax_unit.pkl and household.pkl from both sides, plus the dataset's person and household tables for tax-unit-to-household links and state_fips.

compare.json
{
 "household_net_income": {
  "entity": "household",
  "base_sum": 17161428039502.375,
  "head_sum": 17161408302101.852,
  "change": -19737400.5234375,
  "records_changed": 50,
  "weighted_changed": 38893.243959940846,
  "weighted_up": 0.0,
  "weighted_down": 38893.243959940846,
  "largest_abs": 1097.3984375,
  "rows": [
   {
    "id": 1133799,
    "base": 44171.890625,
    "head": 43074.4921875
   },
   {
    "id": 1076749,
    "base": 33258.37109375,
    "head": 32160.97265625
   },
   {
    "id": 133799,
    "base": 44211.734375,
    "head": 43114.3359375
   },
   {
    "id": 1020411,
    "base": 49659.71484375,
    "head": 48562.31640625
   },
   {
    "id": 20411,
    "base": 49116.625,
    "head": 48019.2265625
   }
  ]
 },
 "household_benefits": {
  "entity": "household",
  "base_sum": 2059793926050.1262,
  "head_sum": 2059793926050.1262,
  "change": 0.0,
  "records_changed": 0,
  "weighted_changed": 0.0,
  "weighted_up": 0.0,
  "weighted_down": 0.0,
  "largest_abs": 0.0,
  "rows": []
 },
 "dc_eitc": {
  "entity": "tax_unit",
  "base_sum": 140731752.08650315,
  "head_sum": 120994336.10348399,
  "change": -19737415.98301916,
  "records_changed": 50,
  "weighted_changed": 38893.243959940846,
  "weighted_up": 0.0,
  "weighted_down": 38893.243959940846,
  "largest_abs": 1097.39990234375,
  "rows": [
   {
    "id": 1181985,
    "base": 7316.0,
    "head": 6218.60009765625
   },
   {
    "id": 1103177,
    "base": 7316.0,
    "head": 6218.60009765625
   },
   {
    "id": 181985,
    "base": 7316.0,
    "head": 6218.60009765625
   },
   {
    "id": 1026757,
    "base": 7316.0,
    "head": 6218.60009765625
   },
   {
    "id": 26757,
    "base": 7316.0,
    "head": 6218.60009765625
   }
  ]
 },
 "income_tax": {
  "entity": "tax_unit",
  "base_sum": 2595105975379.617,
  "head_sum": 2595105975379.617,
  "change": 0.0,
  "records_changed": 0,
  "weighted_changed": 0.0,
  "weighted_up": 0.0,
  "weighted_down": 0.0,
  "largest_abs": 0.0,
  "rows": []
 },
 "state_income_tax": {
  "entity": "tax_unit",
  "base_sum": 578044599361.5671,
  "head_sum": 578064336777.087,
  "change": 19737415.51989746,
  "records_changed": 50,
  "weighted_changed": 38893.243959940846,
  "weighted_up": 38893.243959940846,
  "weighted_down": 0.0,
  "largest_abs": 1097.400390625,
  "rows": [
   {
    "id": 1181985,
    "base": -10443.572265625,
    "head": -9346.171875
   },
   {
    "id": 1103177,
    "base": -10766.0,
    "head": -9668.599609375
   },
   {
    "id": 181985,
    "base": -10441.73046875,
    "head": -9344.330078125
   },
   {
    "id": 1026757,
    "base": -9995.853515625,
    "head": -8898.453125
   },
   {
    "id": 27544,
    "base": -9316.0,
    "head": -8218.599609375
   }
  ]
 }
}

Generated by Claude Code

@MaxGhenis
MaxGhenis merged commit 7b1b0c8 into main Nov 3, 2026
35 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10638 at 0cc48f202b2d5fffb4f55b79c7466591d2e7da30 (squash): Set the DC EITC with-children match to 85% for 2026-2028.

  • Review: a cloud reviewer read every codified version of D.C. Code § 47-1806.04 and approved ec04a940e5 (review). It asked the hub to rule on TY2025 and record the reading. The hub ruled and, in 7c638705d0, added the source-conflict entry, cited D.C. Act 26-214 and rewrote the parameter's comment, leaving every value as reviewed. The hub merged main 56e0a3a8f7 into 7c638705d0 with GitHub's branch update, giving 0cc48f202b, and each file's patch matches the one at 7c638705d0.
  • Source conflict: for us-dc:eitc-with-children-2025-rate, we apply 100% for TY2025. The Council set (B-2) to 100% for years after 2024 in D.C. Act 26-214 (emergency, from December 3, 2025) and D.C. Law 26-89 (temporary, February 12 to September 25, 2026). The emergency act stayed in force at the close of 2025, and the temporary law through the filing season. The Attorney General reached the same reading in a February 24, 2026 opinion. OTR applies 100% in its EITC FAQs and on line 27d of the 2025 D-40 booklet. From 2026 we follow the codified text: 85% through 2028 and 100% from 2029.
  • Gates the hub checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head concluded success;
    • MERGEABLE;
    • not a draft;
    • no reviewer's latest review requests changes;
    • the semantic-overlap guard;
    • head pinned.
  • Impact (pair at 7c638705d0 against main 4e71481e19): we estimate that DC's EITC falls $19.7 million (14%) in 2026, from $140.7 million to $121.0 million. About 38,900 tax units with qualifying children (50 records in the default dataset) each receive 85% of their credit under main, a mean cut of $507. DC income tax rises, and household net income falls, by the same $19.7 million; federal income tax and benefits don't change.
  • Axiom: we recorded the TY2025 reading as ruling 7 on TheAxiomFoundation/rulespec-us#1420, the issue for encoding § 47-1806.04(f).

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants