Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .claude/agents/document_collector.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,7 @@ After you commit documentation:
3. Both agents will reference your `working_references.md` file
4. **ci-fixer** agent will merge all branches and run CI checks

## Special Rules for TANF
1. xxx
2. xxxx
Remember: Your documentation is the single source of truth for all other agents. Accuracy and completeness are paramount.
33 changes: 26 additions & 7 deletions .claude/commands/encode-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,24 +112,43 @@ Invoke @rules-reviewer to validate the complete implementation against documenta
- Proper parameter usage
- Edge case handling

## Phase 10: CI Fix & PR Finalization
## Phase 10: Local Testing & CI Finalization
**CRITICAL: ALWAYS invoke @ci-fixer agent - do NOT manually fix issues**

### Step 1: Local Test Execution & Fixes

Invoke @ci-fixer agent to:
- Find the draft PR created in Phase 0
- Merge test-creator and rules-engineer branches
- Monitor CI pipeline for ALL failures
- Fix failing tests, linting, formatting automatically
- Run all tests locally: `policyengine-core test policyengine_us/tests/policy/baseline/gov/states/[STATE]/[PROGRAM] -c policyengine_us -v`
- Identify ALL failing tests
- For each failing test:
- Read the test file to understand expected values
- Read the actual test output to see what was calculated
- Determine root cause: incorrect test expectations OR bug in implementation
- Fix the issue:
- If test expectations are wrong: update the test file with correct values
- If implementation is wrong: fix the variable/parameter code
- Re-run tests to verify fix
- Iterate until ALL tests pass locally
- Run `make format` before committing fixes

### Step 2: PR & CI Monitoring (if PR exists)

If a draft PR was created in Phase 1:
- Push all local fixes to the PR branch
- Monitor GitHub Actions CI pipeline for ALL failures
- Fix any additional environment-specific issues found in CI
- Address any entity-level issues in tests
- Fix parameter validation errors
- Clean up working_references.md
- Clean up working_references.md and temporary files
- Iterate until ALL CI checks pass
- Mark PR as ready for review

**Success Metrics**:
- All tests pass locally (green output)
- All CI checks passing (tests, lint, format)
- Test and implementation branches merged
- PR marked ready (not draft)
- No temporary files in commits
- PR marked ready (not draft) if PR exists
- Clean commit history showing agent work


Expand Down
45 changes: 45 additions & 0 deletions .claude/commands/fetch-pdf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# fetch-pdf

Download and extract text from a PDF URL using curl and a PDF text extraction tool.

## Usage

```bash
# Download PDF
curl -L -o document.pdf "URL"

# Extract all text from PDF (requires pdftotext from poppler-utils)
pdftotext document.pdf output.txt

# Or use Python's pdfplumber for better text extraction
python3 -c "import pdfplumber; pdf = pdfplumber.open('document.pdf'); text = '\n\n'.join([page.extract_text() for page in pdf.pages]); print(text)"
```

## Installation

### macOS
```bash
brew install poppler # for pdftotext
# or
pip install pdfplumber # for Python approach
```

### Linux
```bash
sudo apt-get install poppler-utils # for pdftotext
# or
pip install pdfplumber # for Python approach
```

## Example

```bash
# Download a PDF
curl -L -o document.pdf "https://example.gov/document.pdf"

# Extract all text
pdftotext document.pdf document.txt

# View the extracted text
cat document.txt
```
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: patch
changes:
changed:
- Refactor is_snap_ineligible_student formula to align with 7 USC 2015(e).
12 changes: 12 additions & 0 deletions policyengine_us/parameters/gov/usda/snap/student/young_child.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
description: The Department of Agriculture includes student in the Supplemental Nutrition Assistance Program unit if they are parents caring for a young child under this age.

values:
2005-01-01: 6

metadata:
unit: year
period: year
label: SNAP student parent young child age threshold
reference:
- title: 7 U.S. Code § 2015 - Eligibility disqualifications (e)(5)
href: https://www.law.cornell.edu/uscode/text/7/2015#e
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
description: The Department of Agriculture includes student in the Supplemental Nutrition Assistance Program unit if they are parent caring for a younger child under this age.

values:
2005-01-01: 12

metadata:
unit: year
period: year
label: SNAP student parent younger child age threshold
reference:
- title: 7 U.S. Code § 2015 - Eligibility disqualifications (e)(8)
href: https://www.law.cornell.edu/uscode/text/7/2015#e
Original file line number Diff line number Diff line change
@@ -1,21 +1,139 @@
- name: Student who does not meet any exemption criteria
- name: College student who does not meet any exemption criteria
period: 2022
input:
is_full_time_student: true
is_full_time_college_student: true
age: 18
is_disabled: false
weekly_hours_worked: false
weekly_hours_worked: 0
is_parent: false
output:
is_snap_ineligible_student: true

- name: Student who is under the age limit
- name: College student who is under the age limit
period: 2022
input:
is_full_time_student: true
is_full_time_college_student: true
age: 17
is_disabled: false
weekly_hours_worked: false
weekly_hours_worked: 0
is_parent: false
output:
is_snap_ineligible_student: false

- name: College student receiving TANF benefits
period: 2022
input:
is_full_time_college_student: true
age: 20
is_disabled: false
weekly_hours_worked: 0
is_parent: false
tanf_person: 500
output:
is_snap_ineligible_student: false

- name: K-12 student is not affected by student rule
period: 2022
input:
is_full_time_college_student: false
is_in_k12_school: true
age: 16
is_disabled: false
weekly_hours_worked: 0
is_parent: false
output:
is_snap_ineligible_student: false

- name: Single parent college student with child under 12 is eligible
period: 2022
input:
people:
parent:
is_full_time_college_student: true
age: 25
is_disabled: false
weekly_hours_worked: 0
tanf_person: 0
child:
age: 8
tanf_person: 0
tax_units:
tax_unit:
members: [parent, child]
spm_units:
spm_unit:
members: [parent, child]
output:
is_snap_ineligible_student: [false, false]

- name: Single parent college student with child over 12 is ineligible
period: 2022
input:
people:
parent:
is_full_time_college_student: true
age: 35
is_disabled: false
weekly_hours_worked: 0
tanf_person: 0
child:
age: 14
tanf_person: 0
tax_units:
tax_unit:
members: [parent, child]
spm_units:
spm_unit:
members: [parent, child]
output:
is_snap_ineligible_student: [true, false]

- name: Two parent household with child under 6 is eligible
period: 2022
input:
people:
parent1:
is_full_time_college_student: true
age: 28
is_disabled: false
weekly_hours_worked: 0
tanf_person: 0
parent2:
age: 30
tanf_person: 0
child:
age: 4
tanf_person: 0
tax_units:
tax_unit:
members: [parent1, parent2, child]
spm_units:
spm_unit:
members: [parent1, parent2, child]
output:
is_snap_ineligible_student: [false, false, false]

- name: Two parent household with child age 6-12 is ineligible
period: 2022
input:
people:
parent1:
is_full_time_college_student: true
age: 32
is_disabled: false
weekly_hours_worked: 0
tanf_person: 0
parent2:
age: 34
tanf_person: 0
child:
age: 8
tanf_person: 0
tax_units:
tax_unit:
members: [parent1, parent2, child]
spm_units:
spm_unit:
members: [parent1, parent2, child]
output:
is_snap_ineligible_student: [true, false, false]
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,69 @@ class is_snap_ineligible_student(Variable):
value_type = bool
entity = Person
label = "Is an ineligible student for SNAP"
documentation = "Whether this person is an ineligible student for SNAP and can not be counted towards the household size"
documentation = (
"Whether this person is ineligible for SNAP due to student status. "
"Under 7 USC 2015(e), individuals enrolled at least half-time in "
"higher education are generally ineligible, unless they meet one of "
"eight statutory exceptions."
)
definition_period = YEAR
reference = "https://www.law.cornell.edu/uscode/text/7/2015"
reference = "https://www.law.cornell.edu/uscode/text/7/2015#e"

def formula(person, period, parameters):
student = person(
"is_full_time_student", period
) # part-time students should also count
# Base rule: Students enrolled at least half-time in higher education
# are ineligible (K-12 students are not affected by this rule)
# Note: Currently we only check full-time college students, but the
# statute covers half-time or more
is_higher_ed_student = person("is_full_time_college_student", period)

# Eight statutory exceptions that make students eligible:

# Exception 1: Under 18 or age 50 or older
age = person("age", period)
p = parameters(period).gov.usda.snap.student
age_eligible = p.age_threshold.calc(age)
disabled = person("is_disabled", period)
meets_age_exception = p.age_threshold.calc(age)

# Exception 2: Not physically or mentally fit (disabled)
meets_disability_exception = person("is_disabled", period)

# Exception 3: Employment/training programs (WIOA, career/technical ed)

# Exception 4: Employed at least 20 hours per week or work-study
hours_worked = person("weekly_hours_worked", period)
hours_eligible = hours_worked >= p.working_hours_threshold
# The parental status applies to children under 6 (under 12
# if no care available)
# Single parents with children under 12 are also eligible
is_parent = person("is_parent", period)
return student & ~(
age_eligible | disabled | hours_eligible | is_parent
meets_work_hours_exception = hours_worked >= p.working_hours_threshold

# Exception 5: Parent caring for dependent child under 6.
# Exception 8: Single parent with responsibility for dependent under 12
is_parent = person("is_tax_unit_head_or_spouse", period)
parent_count = person.spm_unit.sum(is_parent)

# Check if there are children in the household under the age thresholds
household_member_ages = person.spm_unit.members("age", period)
has_young_child = person.spm_unit.any(
household_member_ages < p.young_child
)
has_younger_child = person.spm_unit.any(
household_member_ages < p.younger_child
)

parent_exception_requirement = where(
parent_count > 1, has_young_child, has_younger_child
)
meets_parent_exception = is_parent & parent_exception_requirement

# Exception 6: Receiving TANF benefits
tanf = person("tanf_person", period)
receives_tanf = tanf > 0

# Student is INELIGIBLE if they are a higher ed student AND
# they do NOT meet ANY of the eight exceptions
meets_any_exception = (
meets_age_exception
| meets_disability_exception
| meets_work_hours_exception
| meets_parent_exception
| receives_tanf
)

return is_higher_ed_student & ~meets_any_exception