Skip to content

Book Connecticut child tax rebate in 2021 eligibility year (like RI/NM) - #9274

Open
DTrim99 wants to merge 4 commits into
PolicyEngine:mainfrom
DTrim99:fix/ct-child-rebate-2021
Open

Book Connecticut child tax rebate in 2021 eligibility year (like RI/NM)#9274
DTrim99 wants to merge 4 commits into
PolicyEngine:mainfrom
DTrim99:fix/ct-child-rebate-2021

Conversation

@DTrim99

@DTrim99 DTrim99 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #9270. Surfaced by PolicyEngine/policyengine-taxsim#1127.

Problem

The Connecticut 2022 Child Tax Rebate (PA 22-118) is $250/child (max 3), determined by the 2021 return, claimed by a separate application (June–July 2022), and paid in fall 2022 — CT DRS overview. It is not a line on the CT-1040.

PolicyEngine listed ct_child_tax_rebate among Connecticut's 2022 non-refundable income-tax credits, so it reduced 2022 CT income tax (e.g. a HoH with one child and CT AGI $54,934: CT income tax 2,094 → 1,844) and computed eligibility from 2022 income/dependents. TaxAct's 2022 CT-1040 shows CT income tax $2,094 with no rebate.

PolicyEngine already books the directly analogous prior-year rebates in the eligibility year: ri_child_tax_rebate (RI's $250/child, 2021-return-based, paid Oct 2022) and nm_2021_income_rebate are both in their 2021 refundable lists. Connecticut's was the outlier.

Fix

Mirror ri_child_tax_rebate:

  • Remove ct_child_tax_rebate from ct/tax/income/credits/non_refundable.yaml (2022).
  • Add it to ct/tax/income/credits/refundable.yaml under 2021-01-01 (removed 2022) — refundable, since the rebate is a check paid regardless of tax liability.
  • Shift the rebate parameters (amount, child_cap, reduction/*) from 2022-01-01 to 2021-01-01, so eligibility is computed from 2021 income/dependents.
  • Move the ct_child_tax_rebate unit tests to period 2021 (amounts unchanged).
  • Zero ct_child_tax_rebate in the two 2021 CT integration tests (their expected ct_income_tax comes from TAXSIM35, which does not model the rebate) — following the existing ri_child_tax_rebate: 0 convention in the RI integration test.

Result

2022 CT income tax no longer includes the rebate ($2,094, matching the CT-1040), and Connecticut is consistent with the eligibility-year treatment PolicyEngine uses for Rhode Island and New Mexico. Full CT baseline suite passes.

@DTrim99
DTrim99 marked this pull request as ready for review August 12, 2026 20:47
@DTrim99
DTrim99 requested a review from PavelMakarchuk August 12, 2026 20:47

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The re-booking mechanics themselves check out — parameter start dates, the refundable/non-refundable list moves, the RI/NM analog pattern, no impact on the CT contrib reforms, and the full CT tax suite (136 tests) plus the cross-state income tax suite (67 tests) pass on this branch.

Requesting changes for four findings, detailed inline:

  1. Phase-out uses ct_agi instead of federal AGI (ct_child_tax_rebate.py:13) — PA 22-118 phased out on federal AGI from the 2021 return. Pre-existing line, but this PR is what books the formula into the live 2021 baseline.
  2. Counts all dependents, not children 18 or younger (ct_child_tax_rebate.py:25) — the program required a dependent child age 18 or younger; cf. ri_child_tax_rebate's age check.
  3. No 2022 sunset on the rebate parametersct_child_tax_rebate returns $250/child for 2023+ when queried directly.
  4. Stale test name — "Head of household with $170,000 AGI" vs. ct_agi: 165_000 input.

Findings 1 and 2 are the substantive ones: they change real 2021 microsim outputs, and the current unit tests mask both by injecting ct_agi and tax_unit_count_dependents directly.

Sources: CT DRS Child Tax Rebate overview, CT OLR 2026-R-0020


Generated by Claude Code

@DTrim99

DTrim99 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Fixes applied from @PavelMakarchuk's review

All four findings addressed and pushed (CT tax suite 140/140). Thanks — the substantive two were both masked by the injected-input tests, exactly as you noted.

F1 — phase-out on federal AGI (fixed)

Switched the phase-out income measure in ct_child_tax_rebate.py from ct_agi to adjusted_gross_income, matching PA 22-118 (federal AGI from the 2021 return). Your case — federal AGI $110k with CT subtractions dropping ct_agi to $95k — now correctly triggers the reduction instead of paying the full rebate.

F2 — count only dependent children ≤18 (fixed)

Replaced tax_unit_count_dependents with the RI-style age gate: (age <= p.age_limit) & is_tax_unit_dependent, summed over members and capped. Added a ct/tax/income/rebate/age_limit parameter (18, referenced to CT DRS / OLR 2026-R-0020 / PA 22-118 §410). A unit whose only dependent is 21 now gets 0; mixed minor+adult units count only the minors.

F3 — 2022 sunset (fixed)

Added 2022-01-01: 0 to amount.yaml and child_cap.yaml (with a note that the credit was a one-time 2021 payment), so the variable is inert for 2022+ and can't be silently resurrected. I did not zero the reduction.increment (it's a divisor — that would introduce a div-by-zero); zeroing amount and cap fully sunsets the rebate.

F4 + test coverage

  • Fixed the stale name: the case is now "$165,000 federal AGI" to match its input (expected 250).
  • Since F1/F2 changed which inputs the formula reads, rewrote the 3 existing cases to inject adjusted_gross_income and real dependents with ages instead of ct_agi/tax_unit_count_dependents.
  • Added cases that each fail if the corresponding fix is reverted: age-21-only dependent → 0; mixed minor+adult → 250; federal AGI $130k with ct_agi $90k → 0 (federal phase-out); 2023 unit with a child → 0 (sunset).

No 2021 parameter values changed.

🤖 Generated with Claude Code

@DTrim99
DTrim99 requested a review from PavelMakarchuk August 17, 2026 14:03
@PavelMakarchuk

Copy link
Copy Markdown
Collaborator

PR Review (follow-up)

Re-reviewed head e78b7f2 against PA 22-118 §411 and DRS TSSB 2022-5. All four items from the prior review are addressed (federal AGI, age-18 dependent gate, 2022 sunset of amount/child_cap, renamed test) and the RI-style 2021 refundable booking is correct. One substantive issue remains — it is pre-existing on main, but this PR moves the formula into the live 2021 baseline and adds tests that lock it in, so it should be fixed here.

🔴 Critical (Must Fix)

None.

🟡 Should Address

  1. Phase-out reduces by a flat $100 per $1,000 instead of 10% of the rebate per $1,000. policyengine_us/variables/gov/states/ct/tax/income/rebate/ct_child_tax_rebate.py:17-21,32 computes reduction_per_increment = p.reduction.rate * increment (= 0.1 × $1,000 = $100) and subtracts increments × $100 from the total rebate. The statute (§411(b)(2)) and DRS TSSB 2022-5 Q12 say the rebate amount "begins to phase out by 10% for each $1,000 of income above the income limit", so every filer is fully phased out at threshold + $10,000 (DRS: ineligible above $110,000 single/MFS, $170,000 HOH, $210,000 joint/QW — regardless of number of children). Under the current code a 1-child single filer zeroes at $102,500 and a 3-child at $107,500, and mid-band amounts are wrong. The new tests cement this: tests/.../rebate/ct_child_tax_rebate.yaml case 1 (single, $101,000, 2 children) expects 400 — statute: 500 × (1 − 0.1) = 450; case 3 (HOH, $165,000, 5 children → capped 3) expects 250 — statute: 750 × (1 − 0.5) = 375.
    Fix: reduction_share = min_(increments * p.reduction.rate, 1); return total_rebate * (1 - reduction_share). Update the two expected values, and add boundary cases at threshold + $10,000 (→ 0) and threshold + $9,001 (→ 10% of rebate). reduction/rate.yaml (0.1, /1) then reads as the share it actually is.
  2. Stale parameter descriptions and section cites. parameters/gov/states/ct/tax/income/rebate/reduction/start.yaml:1 and increment.yaml:1 say "state adjusted gross income" — the formula now (correctly) uses federal AGI; reduction/rate.yaml:1 says "reduces the personal exemption amount" (copy-paste). amount.yaml, age_limit.yaml, start.yaml cite "Sec. 410 (xxvi)"; the rebate is Sec. 411 ((a)(1) age definition, (b)(1) child cap, (b)(2) amount/thresholds/phase-out).

🟢 Suggestions

  1. ct_child_tax_rebate.py has no reference attribute (the RI analog does) — add PA 22-118 §411 and TSSB 2022-5.
  2. Add a 2021 ct_refundable_credits / ct_income_tax case with a qualifying child asserting the rebate flows through the refundable aggregate (currently only asserted indirectly via zeroing in the TAXSIM integration cases), and a 2022-period case asserting ct_child_tax_rebate: 0 (the sunset test only covers 2023).

Validation Summary

Check Result
Regulatory Accuracy 1 issue (phase-out mechanics); thresholds 100k/160k/200k (SEPARATE 100k, SURVIVING_SPOUSE 200k), $250 × ≤3 children, age ≤ 18 dependent, 2021 eligibility year, refundable, no effect on 2022 tax — all verified
Reference Quality 1 issue (stale descriptions / §410 vs §411)
Code Patterns 0 issues — vectorized, no hard-codes, matches ri_child_tax_rebate pattern; no remaining consumers of the removed non-refundable entry (repo-wide grep incl. reforms; no partner tests reference ct_child_tax_rebate)
Test Coverage 2 suggestions; boundary at full phase-out missing
CI Status Passing
Prior review All 4 items addressed in e78b7f2

Verdict: REQUEST_CHANGES — for the phase-out formula + the two test expectations; small, self-contained fix. Everything else is ready.

@DTrim99

DTrim99 commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up review addressed — thanks @PavelMakarchuk

Good catch on the phase-out mechanics. All items from your 2026-08-18 review applied (CT tax suite 144/144).

Should #1 — phase-out formula (the substantive fix)
The reduction was subtracting a flat $100 per $1,000 over the limit; per PA 22-118 §411(b)(2) and DRS TSSB 2022-5 Q12 the rebate phases out by 10% per $1,000, so every filer is fully phased out at threshold + $10,000 regardless of child count. Rewrote it as a capped share:

reduction_share = min_(np.ceil(excess / p.reduction.increment) * p.reduction.rate, 1)
return total_rebate * (1 - reduction_share)

Updated the two expected values you flagged — single $101k / 2 children → 450 (500 × 0.9); HoH $165k / 3-capped → 375 (750 × 0.5) — and added boundary cases: threshold + $10,000 → 0 (full phase-out) and threshold + $9,000 → 25 (10% of a 1-child rebate). reduction/rate.yaml (0.1) now reads as the per-$1,000 share it actually is.

Should #2 — descriptions & cites
Fixed reduction/start.yaml and increment.yaml ("state" → federal AGI), rewrote reduction/rate.yaml's copy-pasted "personal exemption" description to the phase-out share, and corrected the section cites from §410 → §411 (§411(a)(1) age, (b)(1) child cap, (b)(2) amount/thresholds/phase-out). No parameter values changed.

Suggestions

  • Added a reference to ct_child_tax_rebate.py (PA 22-118 §411, DRS TSSB 2022-5).
  • Added a 2021 case asserting the rebate flows through ct_refundable_credits, and a 2022-period case asserting ct_child_tax_rebate: 0 (the sunset test previously only covered 2023).

Fixed with Claude Code assistance.

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (57743a2) to head (3e8aad4).
⚠️ Report is 22 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9274   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         1    -2     
  Lines           65        25   -40     
=========================================
- Hits            65        25   -40     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review (complete:review-pr — re-review after 8/18 commits)

All four findings from my 8/16 review are addressed, and each fix was verified against the statute extracted mechanically (pdftotext -layout, PA 22-118 pp. 548–549):

  1. Phase-out base → federal AGI ✓ — §411(b)(2): "…of federal adjusted gross income over (A) one hundred thousand dollars…". The ct_agi: 90_000 vs adjusted_gross_income: 130_000 test pins the switch.
  2. Age-18 dependent gate ✓ — §411(a)(1): "'child' means an individual who is eighteen years of age or under as of December 31, 2021". age <= 18 is correctly inclusive (TSSB 2022-5 Q14 confirms an 18-year-old qualifies).
  3. 2022 sunset ✓ — amount/child_cap zeroed at 2022-01-01 plus removal from the 2022 refundable list; 2022 and 2023 sunset tests both present.
  4. Test name/input mismatch ✓ — now $165,000 in both.

The phase-out rewrite to a share basis is itself a real bug fix: §411(b)(2) "such amount shall be reduced ten per cent for every one thousand dollars, or fraction thereof" — corroborated by TSSB 2022-5 Q12's $110k/$170k/$210k exhaustion bands, which only work under the share reading. All 11 test expectations recompute correctly by hand; np.ceil implements "or fraction thereof"; SEPARATE ($100k) and SURVIVING_SPOUSE ($200k) thresholds match the statute clause-for-clause. CI fully green.

🟡 Should address (non-blocking; fine as a fast-follow)

  1. age_limit.yaml OLR reference doesn't corroborate the value — OLR 2026-R-0020 never mentions the age-18 limit (grep over all 11 pages: zero hits for "eighteen"/"age 18"), and its one relevant sentence ("available only in 2022") superficially reads against the 2021 booking. The §411(a)(1) cite carries the value; suggest swapping the OLR link for TSSB 2022-5 Q15 (#page=3), which states the rule verbatim.
  2. Age boundary untested — add age: 18 → 250 and age: 19 → 0; that's the exact statutory line and the highest-value regression guard for the new gate. Also worth one SEPARATE and one SURVIVING_SPOUSE case (both thresholds are explicit in §411(b)(2) and in the breakdown), an at-threshold case (100,000 → full), and a fractional-increment case (e.g. 100,500 → ceil to 1).
  3. child_cap.yaml metadataunit: currency-USD for a count of children, and the description reads "caps the child for tax rebate at this number"; cheap drive-by fix while the file is open.

🟢 Suggestions

  • PR body: state explicitly that 2021 ct_income_tax now intentionally diverges from the CT-1040/TAXSIM (the rebate "is not a credit and may not be claimed on any Connecticut income tax return", TSSB Q5 — hence the two integration-test overrides), and that the §411(b)(3) application requirement (filed by 7/31/2022) is not modeled, so aggregates reflect full statutory eligibility rather than take-up.
  • The reference tuple's TSSB link could carry a page anchor like the parameter files do.

Validation summary

Check Result
Regulatory accuracy 0 issues (statute verified verbatim; 2 genuine legal errors fixed)
Reference quality 1 non-corroborating secondary ref (OLR on age_limit)
Code patterns Clean (vectorized, no hard-coding, RI-precedent-consistent)
Test coverage 11/11 expectations recompute; age/status boundary gaps noted
CI Passing (full suite)

Emulator follow-up tracked on our side: policyengine-taxsim CT 2021 comparisons need a decision on ct_child_tax_rebate vs TAXSIM's eligibility-year rebate convention.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up from a deeper review pass — one blocking finding that reverses my earlier approval.

system.py backdates each parameter's earliest entry to 2015-01-01 (backdate_parameters(..., first_instant="2015-01-01")), so the 2021-first-dated rebate/amount.yaml, rebate/child_cap.yaml, and credits/refundable.yaml entries leak backward: a simulated 2020 CT household with one child receives a phantom $250 ct_child_tax_rebate (verified at runtime against this branch with the pinned core 3.30.2). Pre-PR there was no exposure — the rebate only lived in the 2022 non-refundable list — so this PR introduces up to $750/family of phantom refundable credits in every 2015–2020 CT simulation year.

Fix: add 2015-01-01: 0 to amount.yaml and child_cap.yaml (and/or 2015-01-01: [ct_eitc] to refundable.yaml), plus a 2020 test asserting ct_child_tax_rebate: 0. Same class as the CT pension backward-leak fixed in #9008. (RI/NM's analogous rebates share the latent shape — worth a separate issue.)

Full review report follows in the next comment; everything else in it is non-blocking polish.

@PavelMakarchuk

Copy link
Copy Markdown
Collaborator

PR 9274 — CT Child Tax Rebate (PA 22-118 §411): move to 2021 refundable credit

Program Review

Program: Connecticut Child Tax Rebate — one-time rebate under PA 22-118 §411, moved from a 2022 non-refundable credit to a 2021 refundable credit ($250/child, max 3, 10%-per-$1,000 phase-out over $100k/$160k/$200k federal AGI, sunset to 0 in 2022).

Source Documents

# Document Link Pages
1 CT Public Act 22-118 (H.B. 5506), enrolled session law — §411 at file pp. 548–550 (offset 0) https://cga.ct.gov/2022/ACT/PA/PDF/2022PA-00118-R00HB-05506-PA.PDF 739 (pp. 540–560 rendered)
2 DRS TSSB 2022-5, Child Tax Rebate FAQ https://portal.ct.gov/-/media/drs/publications/tssb/2022/tssb-2022-5.pdf 4 (all rendered)
3 OLR Report 2026-R-0020, State Tax Deductions and Credits for Children https://cga.ct.gov/2026/rpt/pdf/2026-R-0020.pdf 11 (all rendered)
4 CT DRS "2022 Child Tax Rebate" overview (HTML, text-extracted) https://portal.ct.gov/drs/credit-programs/child-tax-rebate/overview n/a
  • Year: 2021
  • Scope: PR changes only
  • Reviewed head SHA: 7faa02a
  • Mode: full

Note: TSSB 2022-5 cites the authority as "Section 415 of H.B. No. 5506" — the bill's section number; the enrolled act renumbers it §411 (same provision).


Critical

C1 — Phantom pre-2021 rebate: backdating exposes the rebate for tax years 2015–2020 (CONFIRMED at runtime)

  • policyengine_us/parameters/gov/states/ct/tax/income/rebate/amount.yaml:15 (2021-01-01: 250, no earlier entry)
  • policyengine_us/parameters/gov/states/ct/tax/income/rebate/child_cap.yaml:15 (2021-01-01: 3)
  • policyengine_us/parameters/gov/states/ct/tax/income/credits/refundable.yaml:11-13 (first dated entry 2021-01-01 includes ct_child_tax_rebate)

Code-path verified empirically against the PR snapshot with pinned core 3.30.2 (satisfying policyengine-core>=3.30.1): policyengine_us/system.py:116 calls backdate_parameters(..., first_instant="2015-01-01") (policyengine_us/tools/parameters.py), which re-dates each parameter's earliest entry to 2015-01-01 at system-build time. Core's raw Parameter._get_at_instant does return None pre-2021, but the built system resolves amount=250, child_cap=3, and a refundable list containing ct_child_tax_rebate for every instant 2015-01-01 through 2020-12-31. A simulated 2020 CT household (1 child age 8, $50k income) receives a phantom ct_child_tax_rebate of $250 flowing into ct_refundable_credits and reducing/refunding ct_income_tax.

Introduced by this PR: pre-PR, the rebate was only consumed via the 2022 entry of non_refundable.yaml, so no pre-2022 exposure existed; post-PR the backdated refundable.yaml list contains the rebate for 2015–2020. Direction/magnitude for microsim users: overstates CT refundable credits by up to $750 per CT tax unit with dependent children ≤18 in any 2015–2020 simulation year (phase-out applies). The RI (ri_child_tax_rebate) and NM (nm_2021_income_rebate) precedents share the identical exposure shape (pre-existing there, not introduced here).

Fix: add an explicit zero entry at or before 2015-01-01 to amount.yaml (e.g. 2015-01-01: 0) — this alone zeroes the rebate for 2015–2020 — and mirror it in child_cap.yaml; alternatively or additionally give refundable.yaml an earlier entry 2015-01-01: [ct_eitc].


Should

A1 — credits/refundable.yaml has no reference for the load-bearing booking claim

policyengine_us/parameters/gov/states/ct/tax/income/credits/refundable.yaml:2-5. The PR's central claim (rebate booked as a 2021 refundable credit) carries no reference, while TSSB Q5 ("not a credit and may not be claimed on any Connecticut income tax return", p. 1) and OLR p. 2 ("available only in 2022") read against it on their face. Add §411(b)(1) #page=548 and §411(b)(4) #page=549. Severity call: SHOULD, not CRITICAL — this is a unit: list parameter whose membership claim is corroborated in-file by the values-block comment (RI/NM precedent) and by the fully referenced sibling value parameters; 9 of 36 state refundable.yaml files lack a reference, so this is a pattern gap, not missing evidence for a legal value.

A2 — Phase-out boundary mechanics are untested: np.ceil ("or fraction thereof") and the threshold boundary

policyengine_us/tests/policy/baseline/gov/states/ct/tax/income/rebate/ct_child_tax_rebate.yaml. All five phase-out cases use exact $1,000 multiples, so deleting np.ceil (or swapping for np.floor) changes no expected value — yet "or fraction thereof" is the statute's distinctive mechanic (§411(b)(2), p. 549). Add: (1) SINGLE, AGI 100,001, 1 child → 225 (one full increment from $1 of excess); (2) SINGLE, AGI exactly 100,000, 1 child → 250 (reduction only for AGI "over" the threshold; TSSB Q11 "less than or equal to $100,000", p. 2); optionally a mid-fraction case (AGI 100,500, 2 children → 450). Downgraded from the edge-case checker's critical rating per policy: the variable has 11 passing tests, all arithmetically verified.

A3 — Age-gate and dependency-gate boundaries are untested

Same test file. Covered ages are 5–15 (in) and 21 (out); nothing at exactly 18 or 19, so an age_limit: 17 or < vs <= flip passes every current case — the statute says "eighteen years of age or under" (§411(a)(1), p. 548). Likewise every age-eligible person is flagged is_tax_unit_dependent: true, so the & dependent conjunction in ct_child_tax_rebate.py:26 is never exercised — deleting it breaks no test. Add: dependent aged exactly 18 → 250; aged 19 → 0; a non-dependent minor (own-return 17-year-old, or is_tax_unit_dependent: false) → 0. Downgraded from critical per the same policy.

A4 — Filing-status and aggregate coverage gaps

Same test file plus reduction/start.yaml:20-23. SEPARATE and SURVIVING_SPOUSE (both named explicitly in §411(b)(2)(A)/(C), p. 549) have no cases; the JOINT threshold is unpinned (case 2 sits below it and passes for any threshold ≥ 180,001); the headline $750 maximum (HOH at 160,000 with 3 children) is never asserted. The refundable-aggregate case (line 237) is degenerate (ct_eitc: 0), so it passes even if ct_eitc were dropped from the list — add a 2021 case with positive earned income asserting ct_eitc, ct_child_tax_rebate, ct_refundable_credits, and ct_income_tax end-to-end. Case 11's comment claims the 2022 list-drop is tested when it is not observable there; fix the comment or add the assertion.

A5 — child_cap.yaml declares a child count as US dollars

policyengine_us/parameters/gov/states/ct/tax/income/rebate/child_cap.yaml:4unit: currency-USD for the value 3 children ("up to a maximum of three children", §411(b)(1), p. 548); renders as $3 in the parameter browser. The RI analogue (ri/.../child_tax_rebate/limit/child.yaml) correctly uses unit: person. File is modified by this PR, so in scope.

A6 — OLR 2026-R-0020 citation in age_limit.yaml does not corroborate the value

policyengine_us/parameters/gov/states/ct/tax/income/rebate/age_limit.yaml:11-12. Verified by full extraction: the report's only substantive rebate mention (p. 2) contains no age limit, amount, cap, or thresholds — and the one thing it does say ("available only in 2022", the payment year) cuts against the 2021 keying on this parameter's face. The citation also lacks a #page= anchor (newly introduced file). Drop it, or keep with #page=2 solely for the one-time character; the §411(a)(1) and DRS-overview citations already in the file fully support the value.

A7 — "The legal code section has been repealed" is factually wrong in five files

amount.yaml:13, child_cap.yaml:13, reduction/increment.yaml:9, reduction/rate.yaml:9, reduction/start.yaml:13. §411 was never codified and contains no repeal language — it is uncodified session law ("Effective from passage", p. 548; contrast §412 "(NEW)" and §413's repeal-and-substitute clause, p. 550) that expired by its own terms. Reword to e.g.: "PA 22-118 §411 is an uncodified, one-time rebate keyed to TY2021; never added to the General Statutes; no effect after 2021, so the value sunsets to 0 in 2022." In increment/rate the comment is also mis-indented under the href.

A8 — 100% take-up assumption is undocumented (§411(b)(3) application requirement)

policyengine_us/variables/gov/states/ct/tax/income/rebate/ct_child_tax_rebate.py:16-35. The statute conditions receipt on an electronic application to DRS by July 31, 2022 (§411(b)(3), p. 549); DRS: "The only way to receive the Child Tax Rebate is by submitting an application" (TSSB Q5, p. 1). The codebase has no per-program take-up mechanism, so modeling it is out of scope — but state the assumption in the docstring or amount.yaml comment. Direction for microsim users: the 2021 aggregate is an upper bound; magnitude unquantifiable from the acquired sources (no application-count report). DRS's offset of the rebate against outstanding tax debts (Q19) is likewise unmodeled.

A9 — Domicile (§411(a)(2)) is approximated by 2021 residence; document the approximation

ct_child_tax_rebate.py:10 (defined_for = StateCode.CT). The statutory test is domicile at application time in mid-2022 (TSSB Q10/Q18, p. 2–3), not 2021 residence; a CT domiciliary temporarily out-of-state in 2021 is wrongly excluded, and vice versa. PE has no domicile concept and the RI analogue makes the identical approximation — documentation fix only, but the 2021-vs-2022 measurement mismatch is a direct consequence of the year change this PR makes.


Suggestions

S1 — Dependency test is structural, not a federal-claim test

ct_child_tax_rebate.py:25-27 uses is_tax_unit_dependent (simply ~head & ~spouse), while §411(b)(1) requires the child be "validly claim[ed] as a dependent" on the 2021 federal return (p. 548; TSSB Q17: no 2021 federal return → ineligible). Divergence is small in practice and matches the RI analogue; worth a one-line comment. Relatedly, TSSB Q15's relationship list is IRC §152 restated — the statute imposes no relationship test, so the implementation follows the statute; no action. Reinvented-variable check passed: no existing "dependents aged ≤N" counter fits; the inline sum is the established HI/RI pattern.

S2 — Record the non-credit booking rationale in refundable.yaml

TSSB Q5 says the rebate "is not a credit"; §411(b)(4) shields it from state-program eligibility determinations. Booking as a refundable credit matches the RI/NM convention and does not contaminate SALT (verified: ct_withheld_income_tax never touches refundable credits), but household_net_income does rise — the correct cash treatment. Note the choice in the values-block comment (dovetails with A1's reference fix).

S3 — child_cap.yaml wording and label; amount.yaml description

child_cap.yaml:1 "caps the child for tax rebate" is garbled; child_cap.yaml:5 label Connecticut tax rebate child cap inverts the program name relative to all five siblings (Connecticut child tax rebate …). amount.yaml:1 has a stray comma and could close with "under the Child Tax Rebate program".

S4 — Variable reference tuple lacks page anchors for the new age gate

ct_child_tax_rebate.py:12-13 cites PA 22-118 at #page=549 only, but the age/dependency rule the formula newly implements sits on p. 548; the TSSB link carries no fragment while every other TSSB citation in the diff does. Add #page=548 and #page=1.

S5 — YAML style nits

reduction/rate.yaml:11 .10.1; age_limit.yaml:2-4 orders values before metadata, unlike all six siblings (loader accepts both; cosmetic).

S6 — Reference polish

TSSB titles use "(4)"/"(11)" for FAQ question numbers — write Q4/Q11. Add a one-line comment noting the H.B. 5506 §415 → enrolled §411 renumbering (prevents a future false "correction"). The amount.yaml sunset entry cites (b)(2); the one-time character comes from (b)(1) p. 548 (or OLR #page=2). credits/non_refundable.yaml has no reference (pre-existing). Confirm the cga.ct.gov hrefs resolve for ordinary readers — the host serves an incomplete certificate chain (OLR PDF needed --insecure; act PDF fetched via Wayback).

S7 — Age measured at the period, not December 31, 2021

ct_child_tax_rebate.py:24: §411(a)(1) fixes the measurement instant "as of December 31, 2021" (p. 548); PE's age carries no within-year date. PE-wide limitation, at most one year of age at the boundary; <= is the correct operator given it. Informational.

S8 — Test polish

Add absolute_error_margin: 0.01 on currency outputs (case 9's expectation passes on the implicit 1e-3 fallback with ~3e-14 headroom); adopt the Case N, description. naming and personN member names (file is new, cheap to conform); nice-to-have cases: newborn (age 0), negative AGI (floors via max_), non-CT household (defined_for), and — once C1 is fixed — a 2020 case asserting 0.


Confirmed correct (highlights)

  • The 2022 → 2021 move is right: every determinant (child age, dependency, AGI, filing status) is keyed to "the taxable year commencing January 1, 2021" (§411(a)(1), (b)(1), (b)(2)(A)–(C), pp. 548–549); OLR's "available only in 2022" and TSSB Q25's late-August-2022 checks describe the payment year, which the established RI/NM one-time-rebate convention deliberately does not use.
  • Income base fix (ct_agiadjusted_gross_income) is statutory: "federal adjusted gross income" (§411(b)(2), p. 549); the test at line 140 pins the distinction (fed 130k vs ct_agi 90k → 0).
  • Phase-out rewrite fixes a real arithmetic error: old code subtracted a flat $100/increment; the statute reduces the per-child amount proportionally. All 11 test expectations independently re-derived and verified; the $110,000 → $0 case correctly prefers the statute's arithmetic over TSSB Q12's wording.
  • All 9 #page= anchors verified against file pages (offset 0 confirmed via printed footer); the PR fixes two previously wrong "Sec. 410 (xxvi)" citations.
  • §411(b)(5) non-interaction with §12-704e and TSSB Q21 one-rebate-per-joint-return satisfied by construction; integration-test neutralizations (ct_child_tax_rebate: 0 at integration.yaml:18,48) are exactly the two 2021 cases that need them, and the two that don't are correctly untouched.
  • Code patterns clean: no hard-coded policy values, fully vectorized, correct tuple-typed reference, correct entity/period usage, changelog fragment present and correctly typed (fixed → patch).

PDF Audit Summary

Category Count
Confirmed correct (repo value = PDF) 15
Mismatches verified 0
Mismatches rejected 0
Unmodeled program features (noted, mostly standard PE abstractions) 6
Pre-existing (outside PR scope) 0

Unmodeled: application/take-up (→A8), domicile (→A9), Dec-31 age date (→S7), relationship gloss (informational), §411(b)(4) interaction shields (satisfied for CT income tax; other programs out of scope), §411(b)(5) (satisfied by construction).

Validation Summary

Validator Result
Regulatory PASS — 0 critical; 5 should, 7 suggestions (all consolidated above)
Reference 9 page anchors verified; refundable.yaml gap → A1; OLR citation → A6
Code PASS — 0 critical; unit/metadata and backdating findings consolidated (C1, A5)
Formatting PASS with nits (S3, S5; covered by code audit)
Tests 11/11 expectations verified vs statute; boundary gaps → A2–A4 (downgraded per policy, variable has coverage)
PDF audit 15 matches, 0 mismatches across 36 rendered pages
CI Passing YES (all checks pass)

Branch Status

The branch is 291 commits behind base (3 ahead). Rebase recommended before merge. Staleness did not affect any finding: all findings were verified against the PR snapshot at head SHA 7faa02a, and the pre-2021 backdating behavior (C1) was confirmed against the pinned core version the snapshot builds with.

Review Severity: REQUEST_CHANGES

One critical finding (C1 — confirmed phantom pre-2021 rebate introduced by this PR, one-line parameter fix). Everything else is documentation, metadata, and test-coverage polish on a substantively correct and clearly improved implementation.

Next Steps

Run the fix-pr workflow against this report: apply C1's parameter fix (2015-01-01: 0 entries and/or 2015-01-01: [ct_eitc]), then the A-items (reference, unit, comments, boundary tests), rebase on base, and re-run CI.

DTrim99 and others added 4 commits August 24, 2026 09:46
The Connecticut 2022 child tax rebate (PA 22-118) is determined by the
2021 return, claimed by a separate application, and paid in fall 2022 —
it is not a line on the CT-1040. PolicyEngine was applying it as a 2022
non-refundable income-tax credit, reducing 2022 CT income tax and
computing eligibility from 2022 data, while it books every other
prior-year rebate (ri_child_tax_rebate, nm_2021_income_rebate) in the
eligibility year.

Move ct_child_tax_rebate to the 2021 refundable-credit list (removed in
2022) and shift its parameters to 2021, mirroring ri_child_tax_rebate.
2022 CT income tax no longer includes the rebate, matching the CT-1040.
Zero ct_child_tax_rebate in the two 2021 CT integration tests (TAXSIM35
does not model it), following the ri_child_tax_rebate convention.

Fixes PolicyEngine#9270. Surfaced by PolicyEngine/policyengine-taxsim#1127.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-out, age gate, sunset

Booking this credit into the live 2021 baseline surfaced three formula/parameter issues:
- F1: the phase-out read ct_agi; PA 22-118 phased out on FEDERAL AGI from the 2021
  return. Switch the income measure to adjusted_gross_income.
- F2: the count used tax_unit_count_dependents (all dependents); the program required a
  dependent child age 18 or younger. Count only age<=18 tax-unit dependents, RI-style,
  with a new ct rebate age_limit parameter (18).
- F3: the rebate parameters started 2021 with no sunset, so the variable returned phantom
  nonzero values for 2022+. Sunset amount and child_cap to 0 at 2022-01-01 (one-time rebate).
- F4/tests: the prior cases injected ct_agi and tax_unit_count_dependents, masking F1 and F2.
  Rewrite them to drive federal AGI + real dependents with ages (and fix the 70k/165k name
  mismatch), and add age-gate, federal-AGI-vs-ct_agi, and 2023-sunset cases. CT tax suite 140/140.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mechanics

- Phase-out was subtracting a flat $100 per $1,000 over the income limit; PA 22-118
  §411(b)(2) and DRS TSSB 2022-5 Q12 phase the REBATE out by 10% per $1,000, so every
  filer is fully phased out at threshold + $10,000 regardless of child count. Rewrite to
  reduction_share = min(ceil(excess/1000) * rate, 1); rebate * (1 - share).
- Update expected values: single $101k/2 children 400 -> 450; HoH $165k/3-capped 250 ->
  375. Add boundary cases (threshold + $10,000 -> 0; partial band -> 10% of rebate) and a
  2021 refundable-flow case + a 2022 sunset case.
- Add a reference (PA 22-118 §411, TSSB 2022-5) to ct_child_tax_rebate.py.
- Fix stale parameter descriptions (federal, not state, AGI; rate = phase-out share) and
  correct the section cites from Sec. 410 to Sec. 411. No parameter values changed.

CT tax suite 144/144.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…es, coverage (CT child tax rebate)

- C1 (critical): parameter backdating re-dated the rebate's earliest entry to 2015-01-01,
  paying a phantom rebate for 2015-2020. Add explicit 2015-01-01: 0 to amount.yaml and
  child_cap.yaml (verified: a 2020 CT household with a child now gets $0; 2021 unchanged).
  Add a 2020 regression-guard test.
- A1/A6/S2/S4/S6: add PA 22-118 §411(b)(1)/(b)(4) refs to refundable.yaml; drop the
  non-corroborating OLR citation on age_limit; add page anchors + booking-rationale notes.
- A5: child_cap unit currency-USD -> person. A7: reword the false 'repealed' comment in 5
  files to uncodified session law. A8/A9/S1: document the take-up, domicile, and dependency
  approximations. S3/S5: fix garbled child_cap wording/label, amount comma, .1->0.1, YAML order.
- A2/A3/A4: phase-out ceil + threshold-boundary tests; age-18/19 and non-dependent-minor gates;
  SEPARATE/SURVIVING_SPOUSE/JOINT threshold pins; $750 HOH max; positive-EITC refundable e2e.
  S8: margins, newborn/negative-AGI/non-CT cases.

Only new param value is the 2015-01-01: 0 sunset floor. CT child tax rebate suite 28/28.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DTrim99
DTrim99 force-pushed the fix/ct-child-rebate-2021 branch from 7faa02a to 3e8aad4 Compare August 24, 2026 13:47
@DTrim99

DTrim99 commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up review addressed — thanks @PavelMakarchuk

Applied the critical fix and all A- and S-items from your 2026-08-23 review (CT child tax rebate suite 28/28, rebased on main). The only new parameter value is the 2015 sunset floor.

Critical

  • C1 — phantom pre-2021 rebate. Confirmed: backdate_parameters re-dates the earliest entry to 2015-01-01, so the rebate was paying out for 2015–2020. Added an explicit 2015-01-01: 0 to amount.yaml and child_cap.yaml. Empirically verified — a 2020 CT household (1 child, $50k) now gets $0 (was $250); 2021 stays $250. Added a 2020 regression-guard test.

Should

  • A1 / A6 / S2 / S4 / S6 — added PA 22-118 §411(b)(1) #page=548 + §411(b)(4) #page=549 to refundable.yaml; dropped the non-corroborating OLR citation on age_limit.yaml; added #page anchors to the variable's PA/TSSB refs; recorded the non-credit booking rationale (TSSB Q5) and the §415→§411 renumbering.
  • A5child_cap.yaml unit: currency-USDunit: person (it's a child count, not dollars).
  • A7 — reworded the factually-wrong "the legal code section has been repealed" comment across all five rebate files to "uncodified session law keyed to TY2021, never added to the General Statutes, sunsets to 0 in 2022"; fixed the mis-indented comments in increment/rate.
  • A8 / A9 / S1 — documented the 100% take-up assumption (§411(b)(3) DRS application), the domicile approximation (2022 domicile ≈ 2021 CT residence), and that is_tax_unit_dependent proxies the federal-claim requirement.
  • A2 / A3 / A4 — added the missing boundary/mechanic tests: phase-out ceil "or fraction thereof" (AGI 100,001 → 225; exactly 100,000 → 250; 100,500/2ch → 450); the age gate at exactly 18 → 250 and 19 → 0 plus a non-dependent minor → 0 (exercises the & dependent conjunction); SEPARATE and SURVIVING_SPOUSE cases pinning their thresholds; the JOINT threshold pin; the headline $750 max (HOH, $160k, 3 children); and a positive-EITC end-to-end case asserting ct_eitc / ct_child_tax_rebate / ct_refundable_credits / ct_income_tax together (the old aggregate case was degenerate with ct_eitc: 0). Fixed Case 11's comment (the 2022 list-drop isn't observable there) and added the aggregate assertions.
  • S3 / S5 / S8 — fixed the garbled child_cap description + inverted label, the amount.yaml comma, .10.1, the age_limit.yaml metadata/values order; added absolute_error_margin and newborn / negative-AGI / non-CT cases.

S7 (age measured at period, not Dec 31 2021) is a PE-wide limitation with <= already the correct operator — noted, no code change.

Fixed with Claude Code assistance.

@DTrim99
DTrim99 requested a review from PavelMakarchuk August 24, 2026 14:29
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.

Connecticut child tax rebate wrongly reduces 2022 CT income tax (separate 2021-based application rebate)

2 participants