Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions changelog.d/eval-driven-skill-improvements.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Eval-driven improvements to parameter-patterns, variable-patterns, and code-style skills based on RI CCAP test run
14 changes: 14 additions & 0 deletions skills/technical-patterns/policyengine-code-style-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ def formula(spm_unit, period, parameters):
return age_eligible & income_eligible
```

### Use Framework Constants, Not Custom Parameters

PolicyEngine provides constants for universal conversion factors. Don't create parameters for these:

```python
# ❌ BAD — created a weeks_per_month.yaml parameter:
weekly_subsidy * p.weeks_per_month

# ✅ GOOD — use framework constants:
weekly_subsidy * (WEEKS_IN_YEAR / MONTHS_IN_YEAR)
```

Available constants: `MONTHS_IN_YEAR` (12), `WEEKS_IN_YEAR` (52). Derive others from these.

---

## Pattern 6: Streamline Variable Access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ metadata:
## 1. File Naming Conventions

### Study Reference Implementations First
Before naming, examine:
- DC TANF: `/parameters/gov/states/dc/dhs/tanf/`
- IL TANF: `/parameters/gov/states/il/dhs/tanf/`
- TX TANF: `/parameters/gov/states/tx/hhs/tanf/`
**Before creating ANY parameter files, read 3-5 files from a similar program in another state.** Match their structure — don't invent your own. This is the single most effective way to produce correct parameter files.

Search broadly by program concept (not just program name):
```bash
# For a childcare program, search by concept keywords:
Glob 'policyengine_us/parameters/gov/states/*/*/*child*care*/'
Glob 'policyengine_us/parameters/gov/states/*/*/*ccap*/'
Glob 'policyengine_us/parameters/gov/states/*/*/*ccfa*/'
```

Good references by program type:
- **TANF**: DC, IL, TX — `/parameters/gov/states/{st}/{agency}/tanf/`
- **Childcare**: MA CCFA, CO CCAP — `/parameters/gov/states/{st}/{agency}/ccfa/` or `ccap/`
- **LIHEAP**: AZ — `/parameters/gov/states/az/{agency}/liheap/`

### Naming Patterns

Expand Down Expand Up @@ -101,6 +111,18 @@ description: [State] provides this amount as the payment standard under the Temp
description: [State] excludes this share of earnings from countable income under the Temporary Assistance for Needy Families program.
```

### Keep Descriptions Practical

Descriptions should focus on what matters for simulation, not copy regulatory language verbatim. Omit regulatory details that have no practical impact:

```yaml
# ❌ Too literal — no one simulates a 1-week-old:
description: Rhode Island defines the infant/toddler age range as 1 week up to 3 years under the Child Care Assistance Program.

# ✅ Practical:
description: Rhode Island defines the infant/toddler age range as up to 3 years under the Child Care Assistance Program.
```

### Description Validation Checklist

Run this check on EVERY description:
Expand Down Expand Up @@ -192,6 +214,17 @@ label: California SNAP resource limit
2. Must contain the actual value
3. **Title: Include FULL section path** (all subsections and sub-subsections)
4. **PDF links: Add `#page=XX` at end of href ONLY** (never in title)
5. **Verify page numbers** — open the PDF and confirm the page number is correct before using it

**Reference Source Hierarchy — prefer online over PDF:**
1. **Online statute/regulation** (best): Cornell LII, state legislature sites, public.law
- Example: `https://www.law.cornell.edu/regulations/rhode-island/218-RICR-20-00-4.6`
2. **Official government HTML page**: `.gov` pages with section anchors
3. **PDF with verified page number** (last resort): Only when no online HTML version exists
- Rate schedules, policy manuals without HTML versions
- Always verify the `#page=XX` is correct

Why: Online references are linkable, searchable, and don't require page number verification. PDF page numbers are error-prone (file page vs printed page) and links break when documents are updated.

**Title Format - Include ALL subsection levels (NO page numbers):**
```yaml
Expand Down Expand Up @@ -514,6 +547,12 @@ brackets:
2024-01-01: 0.07
```

**BOUNDARY CHECK — do this for every bracket parameter:**
1. Read the regulation's exact wording for each threshold
2. If it says "above X" or "more than X" → shift by 0.0001
3. If it says "at or above X" or "X or more" → no shift needed
4. Apply consistently to ALL thresholds in the bracket

**When to apply the 0.0001 shift:**
- Regulation says "above X%" or "more than X%" (exclusive of the boundary)
- Apply consistently to ALL thresholds in the bracket, not just the first
Expand All @@ -522,6 +561,80 @@ brackets:
- Regulation says "at or above X%" or "X% or more" (inclusive — matches PolicyEngine's default)
- Regulation says "at least X%" (inclusive)

### Multi-Dimensional Rate Tables (Enum Breakdowns)

**When a rate table has multiple dimensions (e.g., age group × star rating × authorization level), use Enum breakdowns in a small number of parameter files — NOT one file per combination.**

**❌ BAD — 45 files (one per age × time level):**
```
rates/licensed_center/infant/full_time.yaml # breakdown: [star_rating]
rates/licensed_center/infant/half_time.yaml
rates/licensed_center/toddler/full_time.yaml
... (16 files for center alone, 45 total)
```

**✅ GOOD — 3 files (one per provider type) with multi-dimensional Enum breakdowns:**
```
rates/licensed_center.yaml # breakdown: [time_category, star_rating, age_group]
rates/licensed_family.yaml # breakdown: [time_category, star_rating, age_group]
rates/license_exempt.yaml # breakdown: [time_category, step_rating, age_group]
```

**Example rate file with 3D Enum breakdown:**
```yaml
# rates/licensed_center.yaml
description: Rhode Island provides these weekly reimbursement rates for licensed child care centers under the Child Care Assistance Program.
metadata:
period: week
unit: currency-USD
label: Rhode Island CCAP licensed center weekly rates
breakdown:
- ri_ccap_time_category
- ri_ccap_star_rating
- ri_ccap_center_age_group
reference:
- title: 218-RICR-20-00-4.12
href: https://www.law.cornell.edu/regulations/rhode-island/218-RICR-20-00-4.12

FULL_TIME:
STAR_1:
INFANT:
2025-07-01: 334
TODDLER:
2025-07-01: 278
PRESCHOOL:
2025-07-01: 236
SCHOOL_AGE:
2025-07-01: 210
STAR_2:
INFANT:
2025-07-01: 341
...
```

**The variable lookup becomes simple Enum indexing:**
```python
p.licensed_center[time_category][star_rating][center_age]
```

**When dimensions differ by category** (e.g., licensed centers have 4 age groups, family care has 3):
- Use separate Enum variables per category (`ri_ccap_center_age_group`, `ri_ccap_family_age_group`)
- Each rate file references the appropriate Enum in its breakdown
- The variable uses `select()` on the top-level category (provider type) — each branch uses its own Enums

**When quality ratings differ** (e.g., star ratings 1-5 for licensed, step ratings 1-4 for exempt):
- Use separate Enum variables (`ri_ccap_star_rating`, `ri_ccap_step_rating`)
- Each rate file references the appropriate Enum

**Rule of thumb:** If you need helper functions in the variable to do the rate lookup, your parameter structure is too granular. Restructure the parameters.

### Don't Create Unnecessary Parameters

**Don't create parameters for:**
- **Universal conversion factors** — Use framework constants instead. `WEEKS_IN_YEAR / MONTHS_IN_YEAR` gives weeks-per-month. `MONTHS_IN_YEAR` gives 12. Don't create a `weeks_per_month.yaml` parameter.
- **Thresholds with no practical simulation impact** — A minimum age of "1 week" for childcare has no simulation value. No one models newborns. Skip it.
- **Values derivable from existing parameters** — If FPL tables already exist, don't recreate them as program-specific parameters.

### Parameter Structure Transitions (Flat → Bracket)

**When a parameter changes structure over time** (e.g., a flat rate becomes a tiered/marginal rate in a later year), you CANNOT put both structures in a single YAML file. Instead, split into separate files with a boolean toggle.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,51 @@ Before creating any enum variable, check:
2. Does an existing input variable provide the numeric value? (Grep the codebase)
3. If yes to both → use a bracket parameter + formula, not a bare input

### Rate Table Dimensions: One Enum Variable Per Dimension

**When a rate table has multiple dimensions (provider type, age group, star rating, authorization level), create a separate Enum variable for each dimension.** This enables simple Enum bracket indexing in the rate lookup variable.

**Pattern:**
```python
# Each dimension is its own Enum variable:
provider_type = person("ri_ccap_provider_type", period)
time_category = person("ri_ccap_time_category", period)
star_rating = person("ri_ccap_star_rating", period)
age_group = person("ri_ccap_center_age_group", period)

# Rate lookup is just Enum indexing:
rate = p.licensed_center[time_category][star_rating][age_group]
```

**When dimensions differ by category, use separate Enums:**
- Licensed centers have 4 age groups (Infant, Toddler, Preschool, School Age)
- Family child care has 3 age groups (Infant/Toddler, Preschool, School Age)
- → Create `ri_ccap_center_age_group` and `ri_ccap_family_age_group`

**When quality ratings differ by provider type:**
- Licensed centers/family use star ratings (1-5)
- License-exempt uses step ratings (1-4)
- → Create `ri_ccap_star_rating` and `ri_ccap_step_rating` as separate Enums

**Use Enum (not int) for constrained categories:**
```python
# ❌ BAD — star rating as int (what values are valid? unclear)
class ri_ccap_star_rating(Variable):
value_type = int

# ✅ GOOD — star rating as Enum (self-documenting, validated)
class RICCAPStarRating(Enum):
STAR_1 = "1 Star"
STAR_2 = "2 Stars"
STAR_3 = "3 Stars"
STAR_4 = "4 Stars"
STAR_5 = "5 Stars"
```

**If you need helper functions in a rate lookup, your parameter structure is too granular.** Restructure the parameter files to use Enum breakdowns (see parameter-patterns skill) so the variable is just `select()` + indexing.

**Reference implementation:** MA CCFA reimbursement — `ma_ccfa_center_based_early_education_reimbursement.py` is 3 lines: get region, get age category, index into parameter.

#### State Variables to AVOID Creating

For TANF implementations:
Expand Down