Claude
Skills
Sign in
Back

policyengine-reform-patterns

Included with Lifetime
$97 forever

PolicyEngine reform patterns - factory functions, contrib parameters, in_effect toggles, registration, and test keys for contributed policy reforms

Code Review

What this skill does


# PolicyEngine Reform Patterns

Comprehensive patterns for implementing policy reforms (proposed legislation, contributed tax/benefit changes) in policyengine-us. Reforms differ from baseline policy in that they are **optional** — toggled on/off via parameters — and live under `contrib/` paths.

**Baseline skills cover the foundational YAML/Python/test syntax. This skill covers the reform-specific layer on top.**

---

## Precedence: Where Reform Rules Override Baseline Rules

**When implementing a reform, this skill takes precedence over baseline skills wherever they conflict.** The baseline skills (parameter-patterns, variable-patterns, code-organization, code-style, review-patterns) are written for enacted-law variables under `variables/`. Reforms have fundamentally different structure. The table below lists every known conflict:

### Variables & Code Organization

| # | Baseline Rule (from skill) | Reform Override | Why |
|---|---|---|---|
| 1 | **One variable per file** in separate `.py` files under `variables/` (code-organization, review-patterns) | **All variables in ONE `.py` file** inside the factory function closure under `reforms/` | Reform variables must be defined inside the closure so the Reform class can reference them. Splitting into separate files breaks the factory pattern. |
| 2 | **Folder structure** — variables organized into `eligibility/`, `income/`, `deductions/` subfolders (code-organization) | **No subfolder tree** — single `.py` file + `__init__.py` in `reforms/states/{st}/{bill}/` | The reform's "organization" is sections within the single file, not a folder hierarchy. |
| 3 | **Variables go in** `variables/gov/states/{st}/...` (code-organization) | **Reform variables go in** `reforms/states/{st}/{bill}/` or `reforms/{org}/{topic}/` | Reform variables are NOT standalone — they're part of the reform module. |

### Parameters

| # | Baseline Rule (from skill) | Reform Override | Why |
|---|---|---|---|
| 4 | **Exact dates from sources** — never use arbitrary dates like `2000-01-01` (parameter-patterns) | **`in_effect.yaml` MUST use `0000-01-01: false`** | This sentinel date means "false for all time until explicitly enabled." It's the only parameter that uses this pattern. All other reform parameters (amounts, rates, thresholds) still use exact dates from sources. |
| 5 | **Description format**: `[State] [verb] [category] to [this X] under the [Full Program Name] program.` (parameter-patterns) | **`in_effect.yaml` uses**: `[State] [Bill Number] [short description] applies if this is true.` Other reform params (amounts, rates) still follow baseline description rules. | The `in_effect` parameter describes a toggle, not a policy value. Only `in_effect.yaml` deviates — all other reform parameters follow baseline description format. |
| 6 | **Label format**: `[State] [PROGRAM] [description]` (parameter-patterns) | **`in_effect.yaml` label**: `[ST] [Bill] [short name] in effect`. Other reform params still follow baseline label rules. | Same rationale — only `in_effect.yaml` deviates. |

### Tests

| # | Baseline Rule (from skill) | Reform Override | Why |
|---|---|---|---|
| 7 | **Test path**: `tests/policy/baseline/gov/states/...` (testing-patterns) | **Reform test path**: `tests/policy/contrib/states/{st}/{bill}/` or `tests/policy/contrib/{org}/{topic}/` | Different directory tree for contributed reforms. |
| 8 | **No `reforms:` key** — tests run against baseline automatically (testing-patterns) | **Every test MUST have `reforms:` key** with full dotted import path to the module-level bypass instance (e.g., `reforms: policyengine_us.reforms.states.mt.hb268.mt_hb268_reform`) | Without this key, the test runs against baseline code and the reform variables don't exist. |
| 9 | **No parameter toggles** in test input section (testing-patterns) | **Every test MUST set `gov.contrib.{...}.in_effect: true`** in the input section | The reform is off by default (`0000-01-01: false`). Tests must explicitly enable it or the reform logic won't execute. |
| 10 | **Error margin**: `absolute_error_margin: 0.1` — "never use 1" (testing-patterns) | **Reform tests commonly use `absolute_error_margin: 1`** | Reform calculations often involve larger numbers (tax amounts, credits) where $1 tolerance is appropriate. Use `0.1` only when testing boolean-like outputs. |
| 11 | **Person names**: always `person1`, `person2` — never descriptive (testing-patterns) | **Reform tests may use** `person1`/`child1` to clarify tax unit structure | Reform tests often need to distinguish adults from dependents for filing status and credit eligibility. `child1` is acceptable when it clarifies the test setup. Follow whichever convention the existing reform tests in the codebase use. |

**Summary for agents loading both baseline + reform skills:**
- If working on a **reform** (file under `reforms/` or params under `gov/contrib/`): follow this skill's rules
- If working on **baseline policy** (file under `variables/` or params under `gov/states/`): follow baseline skill rules
- **When in doubt**: check the parameter path — `gov/contrib/` = reform rules apply

---

## 1. When Is It a Reform vs Baseline?

| Type | Description | Parameter path | Variable location | Toggle |
|------|-------------|----------------|-------------------|--------|
| **Baseline** | Enacted law (current statute) | `gov/states/{st}/...` or `gov/irs/...` | `variables/gov/states/{st}/...` | None — always active |
| **Contributed reform** | Proposed bill or policy experiment | `gov/contrib/states/{st}/{bill}/...` or `gov/contrib/{org}/{topic}/...` | `reforms/states/{st}/{bill}/...` or `reforms/{topic}/...` | `in_effect` parameter |
| **Enacted local tax** | Enacted sub-state law | `gov/local/{st}/{jurisdiction}/...` | `variables/gov/local/{st}/{jurisdiction}/...` | None — always active |

**Rule of thumb:** If it's a bill number (HB, SB, AB, A0xxxx), a proposal, or a policy experiment, it's a contributed reform.

---

## 2. File Structure

Every contributed reform follows this layout:

```
policyengine_us/
├── parameters/gov/contrib/states/{st}/{bill_id}/
│   ├── in_effect.yaml              # Boolean toggle (REQUIRED)
│   ├── amount.yaml                 # Policy amounts
│   ├── age_limit.yaml              # Eligibility thresholds
│   ├── rates.yaml                  # Rate brackets (or rates/ folder)
│   └── phaseout/                   # Phase-out sub-parameters
│       ├── threshold.yaml          # Often with filing_status breakdown
│       ├── rate.yaml
│       └── ...
│
├── reforms/states/{st}/{bill_id}/
│   ├── {reform_name}.py            # Factory function + variable definitions
│   └── __init__.py                 # Exports
│
├── reforms/reforms.py              # Registration (import + call factory)
│
├── tests/policy/contrib/states/{st}/{bill_id}/
│   └── {reform_name}.yaml          # YAML test cases
│
└── changelog.d/{branch-name}.added.md  # Changelog fragment
```

### Federal / org-specific reforms

Federal reforms and organization-specific proposals (not state-specific) use a path without `states/{st}`:

```
parameters/gov/contrib/{org}/{topic}/...      # e.g., gov/contrib/crfb/surtax/
reforms/{org}/{topic}/...                     # e.g., reforms/crfb/agi_surtax.py
tests/policy/contrib/{org}/{topic}/...        # e.g., tests/policy/contrib/crfb/agi_surtax.yaml
```

### Multiple reforms in one PR

When two related bills modify the same variables (e.g., KY HB 13 and HB 152 both change income tax brackets):
- **Separate parameter folders** per bill (`gov/contrib/states/ky/hb13/`, `gov/contrib/states/ky/hb152/`)
- **Single reform Python file** with shared calculation logic
- **Separate test files** per bill
- **Precedence logic** using nested `where()`: `where(hb13_active, hb13_result, where(hb152_active, hb152_result, baseline))`

---

## 3. The `in_effect` Parameter (Required for Every Reform)

Every reform MUST have an `in_effect.yaml` toggle:

```yaml
description: >-
  [State] [Bill Number] [

Related in Code Review