Skip to content

Backdate Kentucky K-TAP parameters#7497

Merged
PavelMakarchuk merged 17 commits intoPolicyEngine:mainfrom
hua7450:backdate-ky-ktap
Mar 23, 2026
Merged

Backdate Kentucky K-TAP parameters#7497
PavelMakarchuk merged 17 commits intoPolicyEngine:mainfrom
hua7450:backdate-ky-ktap

Conversation

@hua7450
Copy link
Copy Markdown
Collaborator

@hua7450 hua7450 commented Feb 26, 2026

Summary

Backdates Kentucky K-TAP (TANF) parameters to 1997 (program inception), adds a unified earned income disregard formula supporting both the pre-2023 AFDC-era "$30 + 1/3" and post-2023 "50%" disregards, adds Nov 2025 payment maximum reductions, and makes all deductions fully per-person per MS 2840.

Closes #7496

Regulatory Authority

Income Eligibility Tests

Gross income test: Gross income ≤ 185% × standard of need

Resource limit: Liquid assets ≤ $2,000 (pre-2023) / $10,000 (post-2023)

Income Deductions & Exemptions

Applied per-person per MS 2840, then summed to unit level:

  1. Work expense standard deduction: $90/month (pre-2023) / $175/month (post-2023) per earner

  2. Dependent care disregard: Up to $175/month per dependent (full-time cap; $150 part-time not separately modeled)

  3. Earned income disregard (EID):

    • Pre-2023: $30 flat + 1/3 of remainder (months 1-4), then $30 only (months 5-12)
    • Post-2023: 50% of earned income (months 1-6), then no disregard
    • Source: 921 KAR 2:016 Section 5(3)(e)
  4. Child support disregard: First $50/month excluded from unearned income

Income Standards

Standard of Need (by number of eligible persons)

Size ERA 1 (1997-07-01) ERA 2 (2023-02-16)
1 $401 $481
2 $460 $552
3 $526 $631
4 $592 $710
5 $658 $790
6 $724 $869
7 $790 $948

Standard of need was not changed in Nov 2025; remains at ERA 2 levels.

Payment Maximum (by number of eligible persons)

Size ERA 1 (1997-07-01) ERA 2 (2023-02-16) ERA 3 (2025-11-01)
1 $186 $372 $242
2 $225 $450 $293
3 $262 $524 $341
4 $328 $656 $426
5 $383 $766 $498
6 $432 $864 $562
7 $482 $964 $627

Benefit Calculation

benefit = min(rate × max(standard_of_need − countable_income, 0), payment_maximum)

Open Issues

Nov 2025 payment maximums: source and mapping uncertainty

The Nov 2025 values are sourced from the KY FACES web application, which is an informational page for kinship caregivers — not a regulatory instrument. No formal KAR amendment, administrative order, or TANF State Plan amendment has been identified authorizing these reductions. A pending 1/21/2025 KAR amendment is referenced on Cornell/LII but its text is not yet published.

Additionally, the KY FACES table uses "Number of Children" while the KAR regulation uses "Number of Eligible Persons" (which includes parents). Our implementation indexes by total assistance unit size (matching the KAR framework), but the Nov 2025 values may need re-mapping once the official regulatory text is available.

Known Limitations

  • EID time limits use calendar-month proxy (resets each January) instead of tracking cumulative participation months
  • MS 2520 six-month 100% earned income exclusion for new employment not modeled (requires employment history tracking)
  • Part-time dependent care cap ($150/month) not separately modeled (uses $175 full-time cap for all)
  • Gross income limit uses formula (185% × SoN) rather than regulation's pre-computed table (max ~$1.65 rounding difference)

Test Plan

  • All 149 K-TAP tests pass (47 historical/boundary + 45 edge cases + 57 unit tests)
  • Microsimulation runs without errors
  • make format clean
  • Full CI suite

🤖 Generated with Claude Code

hua7450 and others added 2 commits February 26, 2026 12:17
Starting implementation of backdating Kentucky K-TAP (TANF) parameters
to program inception. Documentation and parallel development will follow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 1997-07-01 historical entries to all 10 parameter files (frozen AFDC-era values)
- Add Nov 2025 payment maximum reduction (~35% cut)
- Create earned_income_flat_disregard parameter for pre-2023 $30+1/3 formula
- Rewrite EID formula as unified expression (no branching, both eras via parameters)
- Add 92 new tests: 47 historical/boundary + 45 edge cases across all 3 eras
- Add dual references (current + superseded 921 KAR 2:016)
- Fix gross_income_limit_rate pre-2023 section citation (1(11) → 1(12))
- Clean up hardcoded comments in benefit and dependent care variables

Ref PolicyEngine#7496

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (29d9713) to head (2fcac43).
⚠️ Report is 24 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #7497    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           11         5     -6     
  Lines          329        87   -242     
  Branches         0         1     +1     
==========================================
- Hits           329        87   -242     
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 Sentry.
📢 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.

hua7450 and others added 4 commits February 26, 2026 13:14
…urce

- Fix earned_income_flat_disregard.yaml: subsection 5(3)(c)-(d) → 5(3)(e), add current ref
- Fix earned_income_disregard_rate.yaml: pre-2023 subsection 5(3)(c)-(d) → 5(3)(e)
- Add WKMS news source citation for Nov 2025 payment maximum values

Ref PolicyEngine#7496

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 parameters had identical values at both 1997-07-01 and 2023-03-01.
Per parameter patterns skill, only keep earliest date when value is unchanged:
- gross_income_limit_rate: 1.85 (single entry)
- rate: 0.55 (single entry)
- max_unit_size: 7 (single entry)
- child_support_disregard: 50 (single entry)

Ref PolicyEngine#7496

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create earned_income_flat_disregard_in_effect.yaml (true 1997, false 2023)
- Remove 2023-03-01: 0 sentinel from earned_income_flat_disregard.yaml
- Use if p.in_effect: branching in formula instead of relying on flat=0

The zero-sentinel anti-pattern (value=0 meaning "not in effect") violates
PolicyEngine conventions. An explicit boolean is clearer and self-documenting.

Ref PolicyEngine#7496

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hua7450 hua7450 marked this pull request as ready for review March 1, 2026 01:49
hua7450 and others added 6 commits March 1, 2026 00:29
- Convert earned income disregard rate to month-based bracket parameter
  (ND calendar month proxy pattern) for time-limited EID
- Restructure countable earned income formula: work expense, dependent
  care, and EID all applied per-person then summed to unit
- Allocate dependent care to primary earner per MS 2840 B.2.c
- Add dependent filter to dependent care disregard
- Update tests for month-varying disregard rates and per-person math

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…yment maximums

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@PavelMakarchuk
Copy link
Copy Markdown
Collaborator

Program Review: Kentucky K-TAP (PR #7497)

Source Documents


Critical (Must Fix)

  1. Gross income limit reference cites wrong section number.
    policyengine_us/parameters/gov/states/ky/dcbs/ktap/eligibility/gross_income_limit_rate.yaml line 8: existing reference title says "Section 1(11)" but the gross income limitation standard is defined at Section 1(12) in both the current and pre-2023 regulation. Section 1(11) is "Full-time school attendance." The new pre-2023 reference on line 11 correctly cites Section 1(12), making the inconsistency obvious. Fix: change Section 1(11) to Section 1(12) in the current-version reference.

  2. Missing period metadata in earned_income_disregard_rate.yaml.
    policyengine_us/parameters/gov/states/ky/dcbs/ktap/income/deductions/earned_income_disregard_rate.yaml metadata section (lines 22-26): the metadata has type: single_amount, threshold_unit: month, amount_unit: /1, and label but is missing the required period field. The companion bracket parameter dependent_care.yaml includes period: month in its metadata, as do all other 11 parameter files in this PR. Fix: add period: month to the metadata block.

  3. November 2025 payment_maximum values lack authoritative regulatory source.
    policyengine_us/parameters/gov/states/ky/dcbs/ktap/benefit/payment_maximum.yaml: the 2025-11-01 values ($242-$627) are sourced solely from the KY FACES kinship care web page, which is an informational application, not a regulation, administrative order, or TANF State Plan amendment. Additionally, the KY FACES table uses "Number of Children" while the KAR regulation uses "Number of Eligible Persons" -- these are different concepts (a case with 1 child typically includes at least 1 parent = 2 eligible persons). The mapping question cannot be resolved without the actual regulatory instrument. Fix: locate the official regulatory source (KAR amendment, administrative order, or DCBS policy manual update) and verify the children-vs-persons mapping. If no regulatory source exists yet, add a comment documenting that these values are from an informal state web page and that the size mapping may need revision.

  4. Standard of need missing November 2025 confirmation.
    policyengine_us/parameters/gov/states/ky/dcbs/ktap/benefit/standard_of_need.yaml: if the payment maximum changed in November 2025, the standard of need was either also updated or explicitly kept the same. The diff does not add 2025-11-01 entries for standard_of_need. The KY FACES page only shows payment maximums. The test suite uses the old SoN values ($552 for size 2) for ERA 3 tests (e.g., edge case 31 at period 2026), implying the SoN is unchanged, and news reporting only mentioned payment maximum reductions. Fix: add a comment in standard_of_need.yaml confirming it is unchanged in November 2025, citing the WKMS article or other source.


Should Address

  1. EID rate 0.333 is a truncated approximation of 1/3.
    policyengine_us/parameters/gov/states/ky/dcbs/ktap/income/deductions/earned_income_disregard_rate.yaml line 13: the regulation says "one-third (1/3)." The value 0.333 truncates 0.33333..., causing systematic rounding differences (e.g., $480 remainder: 0.333 * 480 = $159.84 vs 480/3 = $160.00). Tests pass with absolute_error_margin: 0.1 which masks this. Fix: use 0.33333 for higher precision and update test expected values accordingly.

  2. Effective date 1997-07-01 is not corroborated by any cited source.
    All 12 parameter files use 1997-07-01 as the backdated start date. The KAR regulatory history shows no amendment on that date -- the closest 1997-era entry is "24 Ky.R. 1409; 1724; eff. 3-16-1998." Fix: either cite the specific KAR amendment entry, use a more defensible date like 1998-03-16, or add a prominent comment explaining that 1997-07-01 is a proxy for "earliest expected simulation date."

  3. Current regulation effective date uses 2023-03-01 instead of 2023-02-01.
    921 KAR 2:016 (current) became effective February 16, 2023 (per "49 Ky.R. 676, 1288; eff. 2-16-2023"). All parameters use 2023-03-01. PolicyEngine convention is first-of-month; 2023-02-01 would be closer (16 days early vs 13 days late). Using 2023-03-01 means simulations use pre-2023 values for Feb 16-28, 2023. Fix: either change to 2023-02-01 or add a comment explaining why 2023-03-01 was chosen.

  4. breakdown_labels inconsistency between payment_maximum.yaml and standard_of_need.yaml.
    standard_of_need.yaml uses "Assistance group size" while payment_maximum.yaml uses "Assistance unit size." Fix: update standard_of_need.yaml to match.

  5. Redundant threshold entry in earned_income_disregard_rate.yaml.
    Lines 9-10: the first bracket threshold is 1 for both 1997-07-01 and 2023-03-01. Since the threshold does not change between eras, the 2023-03-01: 1 entry is redundant. Fix: remove the redundant entry.

  6. No dedicated test file for ky_ktap_dependent_care_disregard_person.
    policyengine_us/variables/gov/states/ky/dcbs/ktap/income/ky_ktap_dependent_care_disregard_person.py is a new variable with non-trivial allocation logic (primary earner attribution, tie-breaking, zero-earner guard) but is only tested indirectly through ky_ktap_countable_earned_income.yaml cases 8 and 10. Fix: add a dedicated test file.

  7. is_tax_unit_dependent accessed with period instead of period.this_year.
    policyengine_us/variables/gov/states/ky/dcbs/ktap/income/ky_ktap_dependent_care_disregard.py line 17. The variable is_tax_unit_dependent has definition_period = YEAR and value_type = bool. Accessing with bare period from a MONTH formula is the overwhelming codebase convention (20+ usages across reforms and state programs), and for bool value types the auto-conversion returns the same boolean value (no division by 12). The AZ TANF file uses period.this_year but is the exception. Fix: change to period.this_year for defensive consistency, though this is not a correctness issue.

  8. Parameter descriptions use non-standard verbs and placeholders.
    Several descriptions use verbs not in the approved list ("caps," "applies") and non-standard placeholders ("this value," "this liquid asset limit," "this flat amount"). Fix: update to standard patterns.

  9. Test case naming uses descriptive person names instead of sequential.
    Multiple test cases in ky_ktap_edge_cases.yaml and ky_ktap_historical.yaml use names like parent, child, infant, toddler, teen. Per PolicyEngine testing patterns, these should be person1, person2, etc.


Suggestions

  1. Part-time dependent care cap ($150 vs $175) is an acknowledged simplification.
    The regulation specifies $175/month for full-time and $150/month for part-time employment. The code uses $175 for all and documents this with the comment "Full-time/part-time distinction not modeled" in ky_ktap_dependent_care_disregard.py line 14. This is reasonable since PolicyEngine does not track full-time vs part-time status for TANF recipients.

  2. EID lifetime limit and calendar-month proxy are acknowledged PolicyEngine limitations.
    The regulation tracks cumulative participation months per individual (12-month lifetime for pre-2023 $30 flat, 4-month for 1/3 rate, 6-month for post-2023 50%). The code uses period.start.month as a calendar-month proxy, resetting each January. This is well documented in parameter comments and is a reasonable approximation given PolicyEngine's single-period architecture.

  3. Pre-2023 version label "(pre-2023 version)" is informal.
    Reference titles could be more precise, e.g., "(version eff. 11-18-2015, superseded 2-16-2023)." Current annotation is serviceable and unambiguous.

  4. Comments in YAML parameter file could be moved to variable file.
    earned_income_disregard_rate.yaml has 5 lines of explanatory comments that would be better placed in ky_ktap_countable_earned_income.py alongside the code, though having them in both places is acceptable.

  5. earned_income_flat_disregard.yaml could add explicit 2023-03-01 zero entry.
    Only has 1997-07-01: 30. Adding 2023-03-01: 0 would be self-documenting about the disregard's discontinuation, though functionally unnecessary since it is gated by the in_effect boolean.

  6. Gross income limits differ from PDF by $0.10-$0.90 due to rounding.
    The repo computes limits as standard_of_need * 1.85 (continuous product), while the PDF's pre-calculated table appears to round up. For size 3: $631 * 1.85 = $1,167.35 vs PDF $1,169; for size 4: $710 * 1.85 = $1,313.50 vs PDF $1,315. These differences are unlikely to affect real eligibility outcomes.

  7. Multiple regulation provisions not modeled (typical TANF simplifications).
    Not modeled: new-employment/increased-wages disregard (Section 5(3)(f)/(g)), stepparent/sponsor income deeming (Sections 7-8), lump-sum ineligibility (Section 4(3)), resource transfer penalties (Section 3(4)), COLA adjustment provision (Section 9(6)), self-employment rules (Section 10(2)(c)), dependent care caregiver constraints (Section 5(3)(b)1), EID disqualification for refusing employment (Section 5(4)), and recoupment/overpayment provisions (Sections 11-15). These are standard omissions for a microsimulation model.

  8. KY FACES reference should be demoted to supplementary once regulatory source is found.
    Per PolicyEngine source priority, official statutes/regulations should be primary. The KY FACES page is a .ky.gov state web application but not an authoritative regulatory source.


PDF Audit Summary

Category Count
Confirmed correct 45+ individual values (payment max 7x2 eras + 7 Nov 2025, SoN 7x2, resource limit 2, work expense 2, child support 1, benefit rate 2, EID rate/flat 4, max unit size 2, gross income rate 2)
Mismatches (code-path confirmed + visually verified) 0
Mismatches rejected (code-path cleared) 0
Acknowledged simplifications 2 (part-time dep care cap $150 vs $175; EID lifetime limit / calendar-month proxy)
Unmodeled items 11 categories of regulatory provisions

Validation Summary

Check Result
Regulatory Accuracy 0 value mismatches; all core values confirmed correct across 3 eras
Reference Quality 2 issues (wrong section number C1; informal-only source for Nov 2025 C3)
Code Patterns 2 issues (missing period metadata C2; 0.333 truncation S1)
Test Coverage 149 cases across 11 files; excellent coverage of all eras, family sizes, boundaries, per-person interactions; minor gaps in person naming and dep care person tests
PDF Value Audit 0 mismatches / 45+ confirmed correct; 2 acknowledged simplifications; 11 unmodeled provision categories
CI Status Passing

Review Severity: COMMENT

Rationale: Four issues are flagged as Critical, but their severity is nuanced:

  • C1 (wrong section number in reference) is a metadata error, not a code logic or value error -- the actual rate value (1.85) is correct.
  • C2 (missing period metadata) is a formatting omission in one parameter file.
  • C3 and C4 (Nov 2025 source and SoN confirmation) are reference/documentation gaps for parameters that match available secondary sources (WKMS news article, KY FACES state web app).

No actual parameter VALUES are wrong. No formulas are incorrect. All 149 tests pass. The implementation correctly encodes all three regulatory eras with comprehensive test coverage. The regulatory review independently verified 5 test cases by manual calculation and all matched. These issues are important to address for long-term maintainability but are not blocking -- they are educational/improvement items rather than "the code produces wrong results" issues. If the Nov 2025 children-vs-persons mapping question (C3) is confirmed to be correct, all criticals reduce to documentation fixes.

Next Steps

To auto-fix issues: /fix-pr 7497

hua7450 and others added 2 commits March 2, 2026 17:08
…riptions

- Fix gross income limit reference: Section 1(11) → Section 1(12)
- Add missing period metadata to earned_income_disregard_rate
- Add comments documenting Nov 2025 source limitations (payment_maximum)
- Add comment confirming standard_of_need unchanged in Nov 2025
- Update regulation effective date from 2023-03-01 to 2023-02-16 (actual eff. date)
- Fix breakdown_labels inconsistency in standard_of_need
- Remove redundant threshold entry in earned_income_disregard_rate
- Standardize parameter description verbs and placeholders

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hua7450 hua7450 requested a review from PavelMakarchuk March 3, 2026 18:56
PavelMakarchuk and others added 2 commits March 23, 2026 11:33
- Fix gross_income_limit_rate.yaml section reference (1(12) → 1(11) for current KAR)
- Fix payment_maximum.yaml FACES mapping comment to match actual data
- Standardize parameter descriptions with approved verbs
- Update ky_ktap_countable_income.py reference to tuple with both KAR URLs
- Add 3 edge case tests: non-dependent filter, negative income floor, child support exceeds unearned

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@PavelMakarchuk PavelMakarchuk merged commit 563185b into PolicyEngine:main Mar 23, 2026
9 checks passed
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.

Backdate Kentucky K-TAP parameters to program inception

2 participants