policyengine-us icon indicating copy to clipboard operation
policyengine-us copied to clipboard

Implement Ohio Works First (OWF) (TANF)

Open hua7450 opened this issue 4 months ago • 1 comments

Summary

Implements Ohio Works First (OWF), Ohio's TANF program, with income disregard formula verified against Ohio Revised Code.

Closes #6774

Status

  • [x] Parameters created (5 files)
  • [x] Variables implemented (6 files)
  • [x] Tests written (65 test cases)
  • [x] CI passing
  • [x] Formulas verified against Ohio statutes
  • [x] Ready for review

Key Changes

Implementation Summary

  • 6 new variables for Ohio OWF benefit calculation
  • 5 parameter files for payment standards and earned income disregard
  • 65 comprehensive test cases across 6 test files
  • Formulas verified against Ohio Revised Code § 5107.10
  • All tests passing

Formula (Per ORC 5107.10 and OAC 5101:1-23-40)

Earned Income Disregard: "$250 + 50% of Remainder"

remainder = max(gross_earned - $250, 0)
percent_disregarded = remainder × 50%
countable_earned = remainder - percent_disregarded
                 = remainder × 50%

Unearned Income: No disregards (full amount countable)

Benefit Calculation:

benefit = MAX(payment_standard - countable_income, 0)

Two-Tier Eligibility:

  • New applicants: Must pass BOTH (1) gross income < 50% FPL AND (2) countable < payment standard
  • Enrolled recipients: Only need countable < payment standard

Files Added

Parameters (5 files)

policyengine_us/parameters/gov/states/oh/odjfs/owf/
├── income/deductions/earned_income_disregard/
│   ├── flat_amount.yaml                    # $250
│   └── percent_of_remainder.yaml           # 50%
├── initial_eligibility/
│   └── income_limit_percent.yaml           # 50% of FPL
└── payment_standard/
    ├── additional_person_increment.yaml    # $128
    └── amounts.yaml                        # Nov 2024 amounts (sizes 1-8)

Variables (6 files)

policyengine_us/variables/gov/states/oh/odjfs/owf/
├── eligibility/
│   ├── oh_owf_eligible.py                  # Overall eligibility
│   ├── oh_owf_income_eligible.py           # Two-tier test
│   └── oh_owf_initial_income_eligible.py   # 50% FPL test
├── income/
│   └── oh_owf_countable_income.py          # $250 + 50% disregard
├── oh_owf_payment_standard.py              # By family size
└── oh_owf.py                               # Main benefit

Tests (6 files, 65 test cases)

policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/
├── integration.yaml (5 tests)
├── oh_owf.yaml (15 tests)
├── oh_owf_countable_income.yaml (14 tests)
├── oh_owf_eligible.yaml (11 tests)
├── oh_owf_income_eligible.yaml (9 tests)
└── oh_owf_payment_standard.yaml (11 tests)

Example Calculations

Real-World Scenario: Family of 3 with Earned Income

Household: Single parent with 2 children, enrolled
Income: $800/month employment income

Payment Standard: $623/month

Earned Income Disregard:
  $800 - $250 = $550 (remainder)
  $550 × 50% = $275 (disregard half)
  Countable: $275/month

Benefit: $623 - $275 = $348/month
Total income: $800 + $348 = $1,148/month

Low Income Example: Under $250/month

Income: $200/month earned

$200 - $250 = -$50 (clipped to $0)
Countable: $0 (all income disregarded)
Benefit: $623 - $0 = $623/month (maximum)

Key Insight: All earned income under $250/month is fully disregarded!


Testing & Verification

Test Results

✅ All 65 tests passing across 6 test files
   - 5 integration tests
   - 60 unit tests
   - 0 failures

Coverage Highlights

  • ✅ All family sizes (1-12+)
  • ✅ Earned, unearned, and mixed income
  • ✅ New applicants vs enrolled recipients
  • ✅ Initial and ongoing eligibility tests
  • ✅ Boundary cases (at $250, at payment standard, at FPL limit)
  • ✅ Edge cases (zero income, very high income)

How to Run

# All tests
policyengine-core test policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/ -c policyengine_us

# Integration only
policyengine-core test policyengine_us/tests/policy/baseline/gov/states/oh/odjfs/owf/integration.yaml -c policyengine_us

Implementation Highlights

Key Features

  • ✅ Payment standards for all family sizes (1-12+)
  • ✅ Earned income disregard ($250 + 50% of remainder)
  • ✅ Two-tier eligibility (stricter for new applicants)
  • ✅ No resource/asset limits (unique to Ohio)
  • ✅ Uses current year FPL (updates annually July 1st)
  • ✅ Proper vectorization throughout

Design Decisions

  1. Simpler Than PA TANF: Everyone gets the same earned income disregard

    • No conditional eligibility for disregard
    • No separate "Standard of Need" test
  2. No Resource Test: Ohio doesn't count assets

    • Vehicles and homes excluded
    • More generous than most states
  3. Current Year FPL: Uses tanf_fpg (current year)

    • Updates annually on July 1st per ORC 5107.10(D)(2)
    • NOT static 1997 values
  4. Federal Rule Reuse: Uses federal TANF variables

    • is_demographic_tanf_eligible
    • is_citizen_or_legal_immigrant
    • tanf_gross_earned_income
    • tanf_gross_unearned_income

References

Official Sources


Branch Information

Branch: oh-tanf-simple
Base: master
Status: ✅ All formulas verified against Ohio statutes, 65 tests passing


Implementation by: @hua7450
Issue: #6774
Ready for: Code review and testing feedback

hua7450 avatar Nov 04 '25 21:11 hua7450

Codecov Report

:x: Patch coverage is 98.21429% with 2 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 98.21%. Comparing base (ba52386) to head (5de401c). :warning: Report is 42 commits behind head on master.

Files with missing lines Patch % Lines
...us/variables/gov/states/oh/odjfs/owf/oh_owf_fpg.py 90.47% 1 Missing and 1 partial :warning:
Additional details and impacted files
@@             Coverage Diff             @@
##            master    #6788      +/-   ##
===========================================
- Coverage   100.00%   98.21%   -1.79%     
===========================================
  Files            2        7       +5     
  Lines           26      112      +86     
  Branches         0        1       +1     
===========================================
+ Hits            26      110      +84     
- Misses           0        1       +1     
- Partials         0        1       +1     
Flag Coverage Δ
unittests 98.21% <98.21%> (-1.79%) :arrow_down:

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

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

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

codecov[bot] avatar Nov 04 '25 22:11 codecov[bot]