Skip to content

Commit a08b612

Browse files
authored
Merge pull request #120 from hua7450/eval-driven-skill-improvements
Eval-driven improvements to technical-patterns skills
2 parents 16d4d88 + 4d4c661 commit a08b612

File tree

4 files changed

+177
-4
lines changed

4 files changed

+177
-4
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Eval-driven improvements to parameter-patterns, variable-patterns, and code-style skills based on RI CCAP test run

skills/technical-patterns/policyengine-code-style-skill/SKILL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,20 @@ def formula(spm_unit, period, parameters):
147147
return age_eligible & income_eligible
148148
```
149149

150+
### Use Framework Constants, Not Custom Parameters
151+
152+
PolicyEngine provides constants for universal conversion factors. Don't create parameters for these:
153+
154+
```python
155+
# ❌ BAD — created a weeks_per_month.yaml parameter:
156+
weekly_subsidy * p.weeks_per_month
157+
158+
# ✅ GOOD — use framework constants:
159+
weekly_subsidy * (WEEKS_IN_YEAR / MONTHS_IN_YEAR)
160+
```
161+
162+
Available constants: `MONTHS_IN_YEAR` (12), `WEEKS_IN_YEAR` (52). Derive others from these.
163+
150164
---
151165

152166
## Pattern 6: Streamline Variable Access

skills/technical-patterns/policyengine-parameter-patterns-skill/SKILL.md

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,20 @@ metadata:
3131
## 1. File Naming Conventions
3232
3333
### Study Reference Implementations First
34-
Before naming, examine:
35-
- DC TANF: `/parameters/gov/states/dc/dhs/tanf/`
36-
- IL TANF: `/parameters/gov/states/il/dhs/tanf/`
37-
- TX TANF: `/parameters/gov/states/tx/hhs/tanf/`
34+
**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.
35+
36+
Search broadly by program concept (not just program name):
37+
```bash
38+
# For a childcare program, search by concept keywords:
39+
Glob 'policyengine_us/parameters/gov/states/*/*/*child*care*/'
40+
Glob 'policyengine_us/parameters/gov/states/*/*/*ccap*/'
41+
Glob 'policyengine_us/parameters/gov/states/*/*/*ccfa*/'
42+
```
43+
44+
Good references by program type:
45+
- **TANF**: DC, IL, TX — `/parameters/gov/states/{st}/{agency}/tanf/`
46+
- **Childcare**: MA CCFA, CO CCAP — `/parameters/gov/states/{st}/{agency}/ccfa/` or `ccap/`
47+
- **LIHEAP**: AZ — `/parameters/gov/states/az/{agency}/liheap/`
3848

3949
### Naming Patterns
4050

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

114+
### Keep Descriptions Practical
115+
116+
Descriptions should focus on what matters for simulation, not copy regulatory language verbatim. Omit regulatory details that have no practical impact:
117+
118+
```yaml
119+
# ❌ Too literal — no one simulates a 1-week-old:
120+
description: Rhode Island defines the infant/toddler age range as 1 week up to 3 years under the Child Care Assistance Program.
121+
122+
# ✅ Practical:
123+
description: Rhode Island defines the infant/toddler age range as up to 3 years under the Child Care Assistance Program.
124+
```
125+
104126
### Description Validation Checklist
105127

106128
Run this check on EVERY description:
@@ -192,6 +214,17 @@ label: California SNAP resource limit
192214
2. Must contain the actual value
193215
3. **Title: Include FULL section path** (all subsections and sub-subsections)
194216
4. **PDF links: Add `#page=XX` at end of href ONLY** (never in title)
217+
5. **Verify page numbers** — open the PDF and confirm the page number is correct before using it
218+
219+
**Reference Source Hierarchy — prefer online over PDF:**
220+
1. **Online statute/regulation** (best): Cornell LII, state legislature sites, public.law
221+
- Example: `https://www.law.cornell.edu/regulations/rhode-island/218-RICR-20-00-4.6`
222+
2. **Official government HTML page**: `.gov` pages with section anchors
223+
3. **PDF with verified page number** (last resort): Only when no online HTML version exists
224+
- Rate schedules, policy manuals without HTML versions
225+
- Always verify the `#page=XX` is correct
226+
227+
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.
195228

196229
**Title Format - Include ALL subsection levels (NO page numbers):**
197230
```yaml
@@ -514,6 +547,12 @@ brackets:
514547
2024-01-01: 0.07
515548
```
516549
550+
**BOUNDARY CHECK — do this for every bracket parameter:**
551+
1. Read the regulation's exact wording for each threshold
552+
2. If it says "above X" or "more than X" → shift by 0.0001
553+
3. If it says "at or above X" or "X or more" → no shift needed
554+
4. Apply consistently to ALL thresholds in the bracket
555+
517556
**When to apply the 0.0001 shift:**
518557
- Regulation says "above X%" or "more than X%" (exclusive of the boundary)
519558
- Apply consistently to ALL thresholds in the bracket, not just the first
@@ -522,6 +561,80 @@ brackets:
522561
- Regulation says "at or above X%" or "X% or more" (inclusive — matches PolicyEngine's default)
523562
- Regulation says "at least X%" (inclusive)
524563
564+
### Multi-Dimensional Rate Tables (Enum Breakdowns)
565+
566+
**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.**
567+
568+
**❌ BAD — 45 files (one per age × time level):**
569+
```
570+
rates/licensed_center/infant/full_time.yaml # breakdown: [star_rating]
571+
rates/licensed_center/infant/half_time.yaml
572+
rates/licensed_center/toddler/full_time.yaml
573+
... (16 files for center alone, 45 total)
574+
```
575+
576+
**✅ GOOD — 3 files (one per provider type) with multi-dimensional Enum breakdowns:**
577+
```
578+
rates/licensed_center.yaml # breakdown: [time_category, star_rating, age_group]
579+
rates/licensed_family.yaml # breakdown: [time_category, star_rating, age_group]
580+
rates/license_exempt.yaml # breakdown: [time_category, step_rating, age_group]
581+
```
582+
583+
**Example rate file with 3D Enum breakdown:**
584+
```yaml
585+
# rates/licensed_center.yaml
586+
description: Rhode Island provides these weekly reimbursement rates for licensed child care centers under the Child Care Assistance Program.
587+
metadata:
588+
period: week
589+
unit: currency-USD
590+
label: Rhode Island CCAP licensed center weekly rates
591+
breakdown:
592+
- ri_ccap_time_category
593+
- ri_ccap_star_rating
594+
- ri_ccap_center_age_group
595+
reference:
596+
- title: 218-RICR-20-00-4.12
597+
href: https://www.law.cornell.edu/regulations/rhode-island/218-RICR-20-00-4.12
598+
599+
FULL_TIME:
600+
STAR_1:
601+
INFANT:
602+
2025-07-01: 334
603+
TODDLER:
604+
2025-07-01: 278
605+
PRESCHOOL:
606+
2025-07-01: 236
607+
SCHOOL_AGE:
608+
2025-07-01: 210
609+
STAR_2:
610+
INFANT:
611+
2025-07-01: 341
612+
...
613+
```
614+
615+
**The variable lookup becomes simple Enum indexing:**
616+
```python
617+
p.licensed_center[time_category][star_rating][center_age]
618+
```
619+
620+
**When dimensions differ by category** (e.g., licensed centers have 4 age groups, family care has 3):
621+
- Use separate Enum variables per category (`ri_ccap_center_age_group`, `ri_ccap_family_age_group`)
622+
- Each rate file references the appropriate Enum in its breakdown
623+
- The variable uses `select()` on the top-level category (provider type) — each branch uses its own Enums
624+
625+
**When quality ratings differ** (e.g., star ratings 1-5 for licensed, step ratings 1-4 for exempt):
626+
- Use separate Enum variables (`ri_ccap_star_rating`, `ri_ccap_step_rating`)
627+
- Each rate file references the appropriate Enum
628+
629+
**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.
630+
631+
### Don't Create Unnecessary Parameters
632+
633+
**Don't create parameters for:**
634+
- **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.
635+
- **Thresholds with no practical simulation impact** — A minimum age of "1 week" for childcare has no simulation value. No one models newborns. Skip it.
636+
- **Values derivable from existing parameters** — If FPL tables already exist, don't recreate them as program-specific parameters.
637+
525638
### Parameter Structure Transitions (Flat → Bracket)
526639

527640
**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.

skills/technical-patterns/policyengine-variable-patterns-skill/SKILL.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,51 @@ Before creating any enum variable, check:
810810
2. Does an existing input variable provide the numeric value? (Grep the codebase)
811811
3. If yes to both → use a bracket parameter + formula, not a bare input
812812
813+
### Rate Table Dimensions: One Enum Variable Per Dimension
814+
815+
**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.
816+
817+
**Pattern:**
818+
```python
819+
# Each dimension is its own Enum variable:
820+
provider_type = person("ri_ccap_provider_type", period)
821+
time_category = person("ri_ccap_time_category", period)
822+
star_rating = person("ri_ccap_star_rating", period)
823+
age_group = person("ri_ccap_center_age_group", period)
824+
825+
# Rate lookup is just Enum indexing:
826+
rate = p.licensed_center[time_category][star_rating][age_group]
827+
```
828+
829+
**When dimensions differ by category, use separate Enums:**
830+
- Licensed centers have 4 age groups (Infant, Toddler, Preschool, School Age)
831+
- Family child care has 3 age groups (Infant/Toddler, Preschool, School Age)
832+
- → Create `ri_ccap_center_age_group` and `ri_ccap_family_age_group`
833+
834+
**When quality ratings differ by provider type:**
835+
- Licensed centers/family use star ratings (1-5)
836+
- License-exempt uses step ratings (1-4)
837+
- → Create `ri_ccap_star_rating` and `ri_ccap_step_rating` as separate Enums
838+
839+
**Use Enum (not int) for constrained categories:**
840+
```python
841+
# ❌ BAD — star rating as int (what values are valid? unclear)
842+
class ri_ccap_star_rating(Variable):
843+
value_type = int
844+
845+
# ✅ GOOD — star rating as Enum (self-documenting, validated)
846+
class RICCAPStarRating(Enum):
847+
STAR_1 = "1 Star"
848+
STAR_2 = "2 Stars"
849+
STAR_3 = "3 Stars"
850+
STAR_4 = "4 Stars"
851+
STAR_5 = "5 Stars"
852+
```
853+
854+
**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.
855+
856+
**Reference implementation:** MA CCFA reimbursement — `ma_ccfa_center_based_early_education_reimbursement.py` is 3 lines: get region, get age category, index into parameter.
857+
813858
#### State Variables to AVOID Creating
814859

815860
For TANF implementations:

0 commit comments

Comments
 (0)