# 2951 Backdate Program 8fcf3159

> Orchestrates multi-agent workflow to backdate and quality-improve existing state program parameters

- Skill: `tools-only/2951-backdate-program-8fcf3159` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/2951-backdate-program-8fcf3159`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/2951-backdate-program-8fcf3159/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/tools-only/2951-backdate-program-8fcf3159

---


# Backdating $ARGUMENTS

Coordinate a multi-agent workflow to add historical date entries, fix reference quality, review formula correctness, and improve test coverage for an existing state program's parameters.

**READ THE PLAN**: Detailed agent prompts, lessons learned, and state-specific notes are in the memory file `state-tanf-backdate-plan.md`. This command is the executable orchestration layer.

**GLOBAL RULE — PDF Page Numbers**: Every PDF reference href MUST end with `#page=XX` (the file page number, NOT the printed page number). The ONLY exception is single-page PDFs. This rule applies to ALL agents in ALL phases — research, implementation, audit, and finalize. Include this instruction in every agent prompt that touches parameter YAML files.

## Arguments

`$ARGUMENTS` should contain:
- **State and program** (required) — e.g., `CT TFA`, `IN TANF`, `KY K-TAP`
- **Target year** (optional) — how far back to research, e.g., `1997`. Defaults to program inception.
- **Options**:
  - `--skip-review` — skip Phase 6 (built-in /review-program)
  - `--values-only` — skip reference/formula audit (Phase 2), only backdate parameter values
  - `--research-only` — stop after Phase 1 (research), produce impl spec but don't implement
  - `--600dpi` — render all PDFs at 600 DPI instead of 300 DPI (use for scanned docs, poor-quality PDFs, or dense tables that are hard to read at 300 DPI)

**Examples:**
```
/backdate-program CT TFA
/backdate-program IN TANF 2005
/backdate-program KY K-TAP --values-only
/backdate-program NE ADC --research-only
/backdate-program VA TANF --600dpi
```

---

## YOUR ROLE: ORCHESTRATOR ONLY

**CRITICAL — Context Window Protection:**
- You are an orchestrator. You do NOT read raw file contents, grep output, research findings, or PDF data.
- ALL information-gathering work is delegated to agents.
- You only read files marked "Short" in the handoff table (max 25 lines each).
- ALL data flows through files on disk (see Phase 1 consolidation).
- When spawning agents, point them to files on disk — do NOT paste data into prompts.

**You MUST NOT:**
- Read parameter YAML files or variable .py files
- Read research findings from task descriptions
- Read audit reports in full
- Paste file contents or research data into agent prompts

**You DO:**
- Parse arguments
- Create team and tasks
- Spawn agents (in parallel where possible)
- Read SHORT summary files (≤25 lines)
- Present checkpoints to user
- Shut down agents when done

---

## Phase 0: Parse Arguments & Inventory

### Step 0A: Parse Arguments & Clean Up

**Clean up leftover files from previous runs** (prevents stale data from confusing agents):
```bash
# Clean /backdate-program files (use {st}-{prog} prefix after parsing)
rm -f /tmp/{st}-{prog}-*.md
# Clean /review-program files (Phase 6 invokes /review-program)
rm -f /tmp/review-program-*.md /tmp/review-pdf-*.{pdf,txt,png} /tmp/review-600dpi-*.png /tmp/review-ext-*.{pdf,txt,png,md}
```

```
Parse $ARGUMENTS:
- STATE: state abbreviation (e.g., "ct", "in")
- STATE_FULL: full state name (e.g., "Connecticut")
- PROGRAM: program abbreviation (e.g., "tfa", "tanf")
- TARGET_YEAR: target year (default: program inception year, typically 1996-1997)
- OPTIONS: --skip-review, --values-only, --research-only, --600dpi
- DPI: 600 if --600dpi, else 300
```

**Resolve LESSONS_PATH** (used in Phase 3 agent prompts):
```bash
# The auto-memory directory for this project — resolve the concrete path
LESSONS_PATH=$(ls -d ~/.claude/projects/*/memory 2>/dev/null | head -1)/agent-lessons.md
```
Pass `{LESSONS_PATH}` to all implementation agent prompts (Phase 3, Phase 6C).

### Step 0B: Issue + Inventory (SPAWN BOTH IN ONE MESSAGE)

These two agents have no dependency on each other. Spawn them in a **single message** so they run concurrently:

**Agent 1: issue-manager** — searches GitHub (network calls)

```
subagent_type: "complete:country-models:issue-manager"
name: "issue-manager"
run_in_background: true

"Find or create a GitHub issue and draft PR for backdating {STATE_FULL} {PROGRAM} parameters.

1. Search for existing issues related to '{STATE_FULL} {PROGRAM}' backdating.
   If none found, create one with title: 'Backdate {STATE_FULL} {PROGRAM} parameters to {TARGET_YEAR}'.
2. Search for existing PRs related to '{STATE_FULL} {PROGRAM}'.
   If none found, create a new branch and a draft PR. To create the initial commit:
   - Preferred: create a changelog fragment (echo 'Backdate {STATE_FULL} {PROGRAM} parameters.' > changelog.d/{branch}.added.md)
   - Fallback: if the repo rejects that, use --allow-empty for the commit
3. Return both the issue number and PR number."
```

**Agent 2: inventory** — scans local files (disk reads)

Spawn a `general-purpose` agent (needs Write tool) to inventory existing files:

```
subagent_type: "general-purpose"
name: "inventory"
run_in_background: true

"Inventory {STATE} {PROGRAM} parameter and variable files. Write TWO files:

1. FULL inventory for agents: /tmp/{st}-{prog}-inventory.md
   - List of parameter YAML files (full paths)
   - Earliest date entry in each file
   - YAML structure pattern (family-size breakdown vs scalar vs scale)
   - List of variable .py files (full paths)
   - List of test .yaml files (full paths)
   No line limit — agents need the complete picture.

2. SHORT summary for orchestrator: /tmp/{st}-{prog}-inventory-summary.md (MAX 10 LINES)
   - Parameter files: {count}
   - Variable files: {count}
   - Test files: {count}
   - Earliest date found: {YYYY-MM-DD}
   - Program path: parameters/gov/states/{st}/{prog}/
   Numbers only — no file paths."
```

**After both agents complete**:

- Read ONLY `/tmp/{st}-{prog}-inventory-summary.md` (max 10 lines) — just counts and the program path
- Store from issue-manager:
  - **ISSUE_NUMBER** — referenced in commit messages, changelog, and final report
  - **PR_NUMBER** — used by `/review-program` in Phase 6, and by `pr-pusher` in Phase 7
  - **BRANCH** — the working branch for all implementation

These are used throughout the workflow:
- Review-fix loop commits: `"Review-fix round {N}: address critical issues (ref #{ISSUE_NUMBER})"`
- Phase 6: `/review-program {PR_NUMBER} --local --full`
- Phase 7: `pr-pusher` pushes to the branch, reporter writes PR description
- Final report: links to issue and PR

### Step 0C: Create Team

```
TeamCreate(team_name="{st}-{prog}-backdate")
```

### Step 0D: Create Tasks

Create all tasks upfront with dependencies. Adjust count based on inventory.

| Task | Description | Blocked By |
|------|-------------|-----------|
| `discover-sources` | Find all historical PDFs for {STATE} {PROGRAM} | — |
| `secondary-validation` | Download WRDTP/CRS/CBPP cross-check tables | — |
| `prep-pdf-1` | Download and render first PDF (slim — no page mapping) | `discover-sources` |
| `prep-pdf-2` | Download and render second PDF (slim — no page mapping) | `discover-sources` |
| `research-pdf-1-{a,b,...}` | Self-map sections + extract parameter values from first PDF (1-5 agents based on page count) | `prep-pdf-1` |
| `research-pdf-2-{a,b,...}` | Self-map sections + extract parameter values from second PDF (1-5 agents based on page count) | `prep-pdf-2` |
| `consolidate` | Merge findings into implementation spec | all research + secondary |
| `audit-references` | Validate all existing reference URLs and citations | `consolidate` |
| `audit-formulas` | Review variable formulas vs. regulations | `consolidate` |
| `impl-parameters` | Add date entries + fix references in parameter files | `audit-references`, `audit-formulas` |
| `impl-formulas` | Apply formula fixes (if user-approved) | `audit-formulas` |
| `impl-tests` | Add historical + boundary + dimension tests | `impl-parameters` |
| `impl-edge-cases` | Generate edge case tests | `impl-tests` |
| `validate-and-fix` | implementation-validator + ci-fixer + make format | `impl-edge-cases` |
| `push-implementation` | Commit + push all Phase 3-5 work to remote | `validate-and-fix` |
| `review-fix-loop` | Review-fix loop: /review-program → fix → re-review until 0 critical (max 3 rounds) | `push-implementation` |
| `finalize` | Changelog, push, final report | `review-fix-loop` |

Skip `audit-references`, `audit-formulas`, `impl-formulas` if `--values-only`.
Stop after `consolidate` if `--research-only`.
Skip `review-fix-loop` if `--skip-review`.

### Step 0E: Spawn Research Agents

Spawn ALL research agents in a **single message** for maximum parallelism:

| Agent Name | Type | Starts On |
|------------|------|-----------|
| **discovery** | `complete:country-models:document-collector` | `discover-sources` (immediate) |
| **secondary-validator** | `general-purpose` | `secondary-validation` (immediate) |
| **prep-1** | `general-purpose` | Waits for discovery message |
| **prep-2** | `general-purpose` | Waits for discovery message |

**Research agents are spawned AFTER prep agents report page counts** — see "Large PDF Splitting" below.

**Agent type rationale:**
- `document-collector` is purpose-built for discovering regulatory sources (WebSearch, WebFetch, Bash for curl/pdftotext). It writes to `sources/working_references.md`.
- Prep agents need Bash (pdftoppm, pdfinfo) — `general-purpose` is required for PDF rendering. **Prep agents are intentionally slim** — they only download, render, and report page count. They do NOT create page maps or read full PDF text (this caused context blowouts in past runs).
- Research agents need Read (PNG screenshots) + Read (YAML files) + SendMessage — `general-purpose` is required for PDF reading. Research agents **self-map** their assigned pages (identify sections before extracting values).
- Secondary validator needs WebSearch + WebFetch for WRDTP/CBPP — `general-purpose` works.

Agents communicate directly via `SendMessage` — you do NOT relay.

```
discovery → finds PDF URL → messages prep-1: "Download and render: [URL]"
prep-1 → downloads, renders at {DPI} DPI → messages Main Claude: "Ready: {path}, {page_count} pages, TOC hint: ..."
Main Claude → reads page count → spawns research agents with page-range assignments
research agents → self-map sections in their page range → extract values → update task with findings
```

**Agent prompts must include:**
- The inventory file path: `/tmp/{st}-{prog}-inventory.md`
- PDF rendering DPI: `{DPI}` (pass `pdftoppm -png -r {DPI}` to prep agents)
- HISTORICAL ERA AWARENESS: check for predecessor program values (AFDC→TANF, FSP→SNAP)
- Use Wayback Machine for archived sources
- Escalation rules: EXTERNAL DOCUMENT NEEDED, CROSS-REFERENCE NEEDED
- Continue working while waiting — never block on an escalation

**document-collector prompt additions:**
```
"In addition to your standard research workflow, also:
- Search for ALL historical state plan periods (not just the current one)
- Search Wayback Machine for archived versions of web-based sources
- Check ACF (federal) for approved state plans: site:acf.hhs.gov {State} {PROGRAM}
- When you find a PDF, message prep-{N}: 'Download and render: [URL] — [title]'
- Continue searching while prep agents work — don't block"
```

**Prep agent prompt — SLIM (context-safe):**
```
"You are a lightweight PDF renderer. Your ONLY job is to download, render, and report — NOT to analyze content.

When you receive a message from the discovery agent with a PDF URL:

1. Download:
   curl -L -o /tmp/{st}-{prog}-{doc_id}.pdf '[URL]'

2. Get page count:
   pdfinfo /tmp/{st}-{prog}-{doc_id}.pdf | grep Pages

3. Render at {DPI} DPI:
   pdftoppm -png -r {DPI} /tmp/{st}-{prog}-{doc_id}.pdf /tmp/{st}-{prog}-{doc_id}-page

4. Quick TOC hint (ONLY first 5 pages — do NOT read more):
   pdftotext -f 1 -l 5 /tmp/{st}-{prog}-{doc_id}.pdf - | head -80

5. Message Main Claude (NOT research agents) with:
   - PDF file path
   - Total page count
   - Screenshot path pattern (e.g., /tmp/{st}-{prog}-{doc_id}-page-*.png)
   - TOC hint (the first ~80 lines of text, if a table of contents was found)
   - Page offset if obvious from the first few pages (e.g., cover page before page 1)

DO NOT:
- Read the full pdftotext output (this blows your context window)
- Read any PNG screenshots (research agents will do this)
- Create a detailed page map (research agents self-map their assigned pages)
- Analyze the PDF content in any way

If the PDF fails to download or is corrupt, message the discovery agent:
  'DOWNLOAD FAILED: [URL] — [error]. Can you find an alternative source?'

Main Claude will decide how many research agents to spawn based on page count."
```

### Large PDF Splitting for Research Agents

**Main Claude decides the research agent count** after each prep agent reports back. Each research agent should read **at most ~40 pages**.

| PDF page count | Research agents per PDF | Assignment |
|----------------|----------------------|------------|
| ≤40 | 1 | Full PDF |
| 41-80 | 2 | Split at midpoint |
| 81-120 | 3 | ~40 pages each |
| 121-160 | 4 | ~40 pages each |
| 161+ | 5 | ~32-40 pages each |

**Spawn all research agents for a PDF in a single message** for maximum parallelism. Each gets:
- Its assigned page range (e.g., pages 1-40, 41-80, 81-120)
- The SAME inventory file and parameter file list
- The SAME escalation rules (CROSS-REFERENCE NEEDED, EXTERNAL DOCUMENT NEEDED)
- The TOC hint from prep (if available) — helps orient but is NOT a substitute for reading pages
- Instructions to self-map their section, then extract values

**Research agent prompt template:**
```
"You are extracting {PROGRAM} parameter values from a PDF for {STATE}.

Your assigned page range: pages {START}-{END} of /tmp/{st}-{prog}-{doc_id}-page-*.png
TOC hint from prep agent (first 5 pages only): {TOC_HINT_OR_'None available'}

STEP 1 — SELF-MAP (do this first):
Quickly scan your assigned page screenshots to identify what sections/topics they cover:
- Payment standards tables
- Income eligibility limits (gross/net)
- Earned income disregards / deductions
- Resource limits
- Eligibility criteria
- Special provisions
Note the page numbers for each section you find.

STEP 2 — EXTRACT VALUES:
For EVERY parameter value you find, record:
- Parameter name (payment standard, need standard, gross income limit, etc.)
- Value (dollar amount, percentage, etc.)
- Family size (if applicable — record the full table)
- Effective date (look for 'effective [date]', fiscal year headers, amendment dates)
- PDF page number (for citation — use file page number, NOT printed page number)
- Exact quote or table header that confirms the value

STEP 3 — CROSS-REFERENCE:
Read the existing repo parameter files listed in /tmp/{st}-{prog}-inventory.md.
For each repo parameter, note whether you found a corresponding value in your pages.
If not found, note 'NOT FOUND IN MY PAGE RANGE' (another agent may have it).

STEP 4 — REPORT:
Update your task with findings. Include your section map at the top so the
consolidator knows what topics each page range covered.

ESCALATION RULES:
- If your pages reference a different document: 'EXTERNAL DOCUMENT NEEDED: [title] at [URL]'
- If a value depends on content outside your page range: 'CROSS-REFERENCE NEEDED: [description]'
- Continue working on other values — do NOT block on escalations."
```

**Example**: A 150-page state plan → prep-1 reports 150 pages → Main Claude spawns 4 research agents:
```
research-1a: pages 1-38
research-1b: pages 39-76
research-1c: pages 77-114
research-1d: pages 115-150
```

All 4 run in parallel. Each self-maps its section, then extracts values. The consolidator (Phase 1) merges their findings and reconciles cross-references.

---

## Phase 1: Consolidation & Checkpoint

### Consolidation (DELEGATED — biggest context saver)

**DO NOT read research findings.** Spawn a consolidation agent:

```
subagent_type: "general-purpose", team_name: "{st}-{prog}-backdate", name: "consolidator"

"Merge all research findings for {STATE} {PROGRAM} backdating.
1. Read ALL task findings from task list (TaskList + TaskGet)
2. Read inventory at /tmp/{st}-{prog}-inventory.md
3. Read existing parameter YAML files listed in inventory
4. Read sources/working_references.md from document-collector
5. Merge into FINAL IMPLEMENTATION SPEC: /tmp/{st}-{prog}-impl-spec.md
   - For EACH parameter file: exact date entries to add, with values + PDF citations
   - Reconcile conflicts (later documents supersede earlier)
   - Reconcile secondary source discrepancies (primary sources win)
   - Categorize: Tier A (YAML backdating), Tier B (new params), Tier C (formula changes)
   - Flag duplicate values (same value at multiple dates — only keep earliest)
5. Write SHORT summary (max 20 lines): /tmp/{st}-{prog}-impl-summary.md"
```

### Regulatory Checkpoint 1

Read ONLY `/tmp/{st}-{prog}-impl-summary.md`. Present to user:
- Number of files affected, date entries to add
- Any Tier B/C items (require user confirmation before proceeding)
- Source gaps or unresolved conflicts
- **Stop here if `--research-only`**

---

## Phase 2: Reference & Formula Audit

**Skip this phase if `--values-only`.**

Spawn two agents in parallel:

### Reference Auditor

```
subagent_type: "complete:reference-validator",
  team_name: "{st}-{prog}-backdate", name: "ref-auditor"
```

The `reference-validator` agent is purpose-built for this — it validates that all parameters have proper references that corroborate values. It checks: missing references, format (page numbers, detailed sections), value corroboration, and jurisdiction match.

**Additional instructions beyond its defaults:**
```
"Also check these backdate-specific reference issues:

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

1. URL LIVENESS: Test every href with curl -sI. Record broken/redirected URLs.
2. STATUTE SPECIFICITY: Must cite specific subsection, not parent section.
   BAD: '§ 17b-112'  GOOD: '§ 17b-112(c)'
3. REFERENCE TITLE DESCRIPTIVENESS: Title must distinguish what this ref is FOR.
   BAD: 'State Plan 2024-2026'  GOOD: 'State Plan 2024-2026, High Earnings Provision'
4. SESSION LAW vs PERMANENT STATUTE: Flag session law refs (Public Act, SB, HB)
   that should cite permanent statutes instead.
5. INSTRUCTION PAGE vs PDF PAGE: Verify #page=XX is the file page, not the printed
   page number. Render the page and confirm content matches.
6. HISTORICAL PLAN COVERAGE: Check for refs to ALL relevant plan periods.
Write findings to /tmp/{st}-{prog}-ref-audit.md."
```

### Formula Reviewer

```
subagent_type: "complete:country-models:program-reviewer",
  team_name: "{st}-{prog}-backdate", name: "formula-reviewer"
```

The `program-reviewer` agent is purpose-built for this — it researches regulations FIRST (independently of code), then validates code against legal requirements. This catches formula gaps and missing provisions.

**Additional instructions beyond its defaults:**
```
"Focus on these backdate-specific formula issues:
1. UNUSED PARAMETERS: Check every parameter YAML — is each one used in a formula?
   A parameter that exists but is never read means the feature is unimplemented.
2. ZERO-SENTINEL ANTI-PATTERN: Flag params where value=0 means 'not in effect'.
   Should be an explicit in_effect boolean parameter instead.
3. REDUNDANT LOGIC: Flag mathematically unnecessary operations.
4. HARDCODED COMMENTS: Flag comments with specific numbers (e.g., '92%', '171% FPG').
5. ERA HANDLING: Verify formula uses parameter-driven branching, NOT year-checks.
Read impl spec at /tmp/{st}-{prog}-impl-spec.md for regulatory context.
Write findings to /tmp/{st}-{prog}-formula-audit.md.
Write SHORT summary (max 15 lines) to /tmp/{st}-{prog}-phase2-summary.md."
```

### Regulatory Checkpoint 2

Read ONLY `/tmp/{st}-{prog}-phase2-summary.md`. Present to user:
- Broken URLs, generic refs, missing subsections count
- Unused parameters, zero-sentinels, missing provisions count
- **Formula fixes require user confirmation** before implementation

---

## Phase 3: Implementation

Spawn implementation agents in parallel. Each reads specs from disk — NOT from your prompt.

### Tier A: Parameter Backdating (most common)

```
subagent_type: "complete:country-models:parameter-architect",
  team_name: "{st}-{prog}-backdate", name: "impl-parameters"
```

The `parameter-architect` agent designs and modifies parameter structures with proper federal/state separation and zero hard-coding. It has Read, Write, Edit, MultiEdit, Grep, Glob, Skill access.

**Instructions:**
```
"Add historical date entries to {STATE} {PROGRAM} parameter files AND apply reference fixes.
Load skills: /policyengine-parameter-patterns, /policyengine-period-patterns.
Read impl spec at /tmp/{st}-{prog}-impl-spec.md (parameter values to add).
Read ref audit at /tmp/{st}-{prog}-ref-audit.md (reference fixes to apply).

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

RULES:
- Preserve existing YAML structure EXACTLY (indentation, key ordering, metadata)
- Add entries in chronological order (earliest first, before existing entries)
- NO DUPLICATE VALUES: if value unchanged, one entry at earliest date only
- Descriptions one sentence
- PDF hrefs include #page=XX (file page number, NOT printed page number)
- Fix all reference issues from ref-audit alongside value backdating
- Use federal fiscal year dates (YYYY-10-01) unless source specifies otherwise

REUSE EXISTING VARIABLES AND PARAMETERS:
PolicyEngine-US has hundreds of existing variables for common concepts (fpg, smi,
tanf_fpg, is_tanf_enrolled, ssi, tanf_gross_earned_income, snap_gross_income, etc.).
Before creating ANY non-program-specific parameter or variable, Grep the codebase to
check if it already exists. Only create new ones for state-program-specific concepts.

PATTERNS FOR NEW PARAMETERS (Tier B):
When the impl spec calls for a NEW parameter that didn't exist before, follow these patterns.
Study existing files in the same program first to match naming and structure.

Pattern 1 — in_effect boolean (provision that starts/ends at a specific date):
  Create a new YAML file at the appropriate subfolder with in_effect.yaml:
  ```yaml
  # e.g., payment/high_earnings/in_effect.yaml
  description: {State} uses this indicator to determine whether {provision} applies under {program}.
  values:
    {start-date}: false    # before provision existed
    {effective-date}: true  # when it took effect
  metadata:
    unit: bool
    period: month
    label: {State} {program} {provision} in effect
    reference:
      - title: {specific regulation}
        href: {url}#page={XX}
  ```
  Companion value parameters go alongside (e.g., high_earnings/rate.yaml, high_earnings/reduction_rate.yaml).

Pattern 2 — regional in_effect (provision that varies by region, then stops):
  Create a boolean parameter controlling the regional/non-regional split:
  ```yaml
  # e.g., payment/regional_in_effect.yaml
  description: {State} uses this indicator to determine whether regional payment standards apply under {program}.
  values:
    {start-date}: true     # regional standards active
    {end-date}: false       # switched to flat statewide standard
  metadata:
    unit: bool
    period: month
    label: {State} {program} regional payment standards in effect
    reference:
      - title: {specific regulation}
        href: {url}#page={XX}
  ```
  Regional value parameters go in subfolder: regional/region_a/amount.yaml, regional/region_b/amount.yaml, etc.
  Flat statewide parameter at: amount.yaml (same level as regional_in_effect.yaml)."
```

### Tier B/C: New Parameters & Formula Changes (if user-approved)

```
subagent_type: "complete:country-models:rules-engineer",
  team_name: "{st}-{prog}-backdate", name: "impl-formulas"
```

The `rules-engineer` agent implements government benefit program rules with zero hard-coded values and complete parameterization. It has the full tool set (Read, Write, Edit, MultiEdit, Grep, Glob, Bash, Skill).

**Instructions:**
```
"Apply formula fixes for {STATE} {PROGRAM} identified in the formula audit.
Load skills: /policyengine-variable-patterns, /policyengine-code-style,
  /policyengine-parameter-patterns, /policyengine-period-patterns, /policyengine-vectorization.
Read formula audit at /tmp/{st}-{prog}-formula-audit.md.

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

REUSE EXISTING VARIABLES AND PARAMETERS:
PolicyEngine-US has hundreds of existing variables for common concepts (fpg, smi,
tanf_fpg, is_tanf_enrolled, ssi, tanf_gross_earned_income, snap_gross_income, etc.).
Before creating ANY non-program-specific variable, Grep the codebase to check if it
already exists. Only create new ones for state-program-specific concepts.

FIXES TO APPLY:
- Create in_effect boolean parameters (replacing zero-sentinels)
- Wire unused parameters into formulas
- Remove redundant logic
- Replace hardcoded numbers in comments with parameter/statute references
- All logic changes are parameter-driven — NEVER use year-checks (period.start.year)

VARIABLE PATTERNS FOR in_effect AND regional_in_effect:
Study existing variables in the same program first to match style.

Pattern 1 — Using in_effect for a provision that starts at a specific date:
  The parameter tree has: high_earnings/in_effect (bool), high_earnings/rate, high_earnings/reduction_rate.
  In the variable formula, use `if p.high_earnings.in_effect:` to gate the logic:
  ```python
  def formula(spm_unit, period, parameters):
      p = parameters(period).gov.states.{st}.{agency}.{prog}.payment
      raw_benefit = ...  # base calculation always runs
      # New provision gated by in_effect boolean
      if p.high_earnings.in_effect:
          threshold = p.high_earnings.rate * some_base
          applies = income >= threshold
          reduction = 1 - p.high_earnings.reduction_rate
          return where(applies, raw_benefit * reduction, raw_benefit)
      return raw_benefit
  ```
  The `if p.in_effect:` branch is NEVER entered for periods before the effective date.
  No year-checks needed — the parameter handles the time logic.

Pattern 2 — Using regional_in_effect for region-based variation:
  The parameter tree has: regional_in_effect (bool), regional/region_a/amount, regional/region_b/amount, amount (flat).
  In the variable formula, use `if p.regional_in_effect:` to switch between regional and flat:
  ```python
  def formula(spm_unit, period, parameters):
      p = parameters(period).gov.states.{st}.{agency}.{prog}.payment
      capped_size = min_(size, p.max_unit_size)
      if p.regional_in_effect:
          region = spm_unit.household('{st}_{prog}_region', period)
          region_a = region == region.possible_values.REGION_A
          region_c = region == region.possible_values.REGION_C
          return select(
              [region_a, region_c],
              [p.regional.region_a.amount[capped_size],
               p.regional.region_c.amount[capped_size]],
              default=p.regional.region_b.amount[capped_size],
          )
      return p.amount[capped_size]
  ```
  When regional_in_effect is false, it falls through to the flat amount.

CRITICAL: These patterns use `if p.some_bool:` (not `where()`). This works because
PolicyEngine parameter booleans are scalar per-period — they don't vary across entities.
Use `where()` for entity-level conditions (income >= threshold), `if p.flag:` for
period-level switches (provision in effect or not)."
```

---

## Phase 4: Tests

After implementation agents complete, spawn TWO test agents in sequence:

### Step 4A: Test Creator

```
subagent_type: "complete:country-models:test-creator",
  team_name: "{st}-{prog}-backdate", name: "test-creator"
```

The `test-creator` agent creates comprehensive integration tests ensuring realistic calculations. It has Read, Write, Edit, MultiEdit, Grep, Glob, Bash, Skill access.

**Instructions:**
```
"Add tests for {STATE} {PROGRAM} backdating.
Load skills: /policyengine-testing-patterns, /policyengine-period-patterns.
Read impl spec at /tmp/{st}-{prog}-impl-spec.md.
Read existing test files listed in /tmp/{st}-{prog}-inventory.md.

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

COVERAGE REQUIREMENTS:
1. Existing untested features: test EVERY parameter, not just newly backdated ones
2. Period transition boundaries: test in the period AFTER every value change date
3. All dimension values: test ALL regions/tiers/filing statuses, not just defaults
4. Integration tests at era boundaries: full pipeline (eligibility → income → benefit)

GOTCHAS:
- absolute_error_margin: 0.1 REQUIRED on every test case
- Test naming: 'Case N, description.' (numbered, comma, period)
- Period: Only YYYY-01 or YYYY (no YYYY-10, no full dates)"
```

### Step 4B: Edge Case Generator

```
subagent_type: "complete:country-models:edge-case-generator",
  team_name: "{st}-{prog}-backdate", name: "edge-case-gen"
```

The `edge-case-generator` analyzes the variables and parameters to automatically generate comprehensive edge case tests (boundary conditions, zero values, maximums).

**Instructions:**
```
"Generate edge case tests for {STATE} {PROGRAM}.
Load skills: /policyengine-testing-patterns, /policyengine-period-patterns.
Analyze variables and parameters in the program folder.

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

Focus on:
- Income just above/below thresholds
- Family size at min/max boundaries
- Zero income, maximum income
- Interaction between features (e.g., housing subsidy + high earner reduction)"
```

---

## Phase 5: Validation & Fix

### Step 5A: Implementation Validator

```
subagent_type: "complete:country-models:implementation-validator"

"Validate {STATE} {PROGRAM} implementation for PolicyEngine standards compliance.
Load skills: /policyengine-variable-patterns, /policyengine-parameter-patterns,
  /policyengine-code-style, /policyengine-period-patterns.
Check naming conventions, folder structure, parameter formatting, variable code style.
Boolean toggle date alignment: when a boolean parameter (in_effect, regional_in_effect,
flat_applies) changes value at date D, verify that ALL parameters it gates have entries
that cover date D. A gap means PolicyEngine backward-extrapolates a later value, which
may be incorrect. Flag as CRITICAL.
Duplicate variable detection: if any new variable was created for a common concept (FPG,
SMI, gross income, enrollment status), Grep the codebase to check if an existing variable
already covers it. PolicyEngine-US has hundreds of reusable variables. Flag duplicates.
Files to validate: parameter and variable files listed in /tmp/{st}-{prog}-inventory.md
Write findings to /tmp/{st}-{prog}-impl-validation.md."
```

### Step 5B: CI Fixer

```
subagent_type: "complete:country-models:ci-fixer"
"Run tests for {STATE} {PROGRAM}, fix failures, iterate until all pass.
After tests pass, run make format as a final step."
```

### Quick Audit (context-safe)

Spawn a `general-purpose` agent (needs Write tool for the report file) to check ci-fixer's work:

```
subagent_type: "general-purpose"
name: "quick-auditor"

"Review git diff of changes. Check for: hard-coded values to pass tests,
year-check conditionals (period.start.year), altered parameter values.
Write SHORT report (max 15 lines) to /tmp/{st}-{prog}-checkpoint.md: PASS/FAIL + issues."
```

Read ONLY the checkpoint file.

### Step 5C: Push to Remote

**Phase 6 requires code on the remote.** `/review-program` reads the PR via `gh pr diff $PR_NUMBER` (GitHub remote API), so local-only commits are invisible. Push all Phase 3-5 work before entering the review-fix loop:

```bash
# Stage only the program's directories — avoid staging unintended files
git add policyengine_us/parameters/gov/states/{st}/ policyengine_us/variables/gov/states/{st}/ policyengine_us/tests/policy/baseline/gov/states/{st}/
git commit -m "Backdate {STATE} {PROGRAM} parameters to {TARGET_YEAR} (ref #{ISSUE_NUMBER})"
git push
```

**Skip this step if `--skip-review`** (Phase 6 won't run).

---

## Phase 6: Review-Fix Loop

**Skip if `--skip-review`.**

This phase runs `/review-program` and fixes critical issues in a loop until zero critical issues remain (or max iterations reached).

### Loop Structure

```
ROUND = 1
MAX_ROUNDS = 3

while ROUND <= MAX_ROUNDS:
    1. Run /review-program --local --full
    2. Read summary → count critical issues
    3. If critical == 0 → EXIT LOOP (success)
    4. If ROUND == MAX_ROUNDS → EXIT LOOP (escalate to user)
    5. If ROUND == 2 → ask user before attempting round 3
    6. Fix critical issues
    7. Run make format + tests
    8. Commit + push fixes (so next round's gh pr diff sees them)
    9. ROUND += 1
```

### Why commit + push is required between rounds

`/review-program` reads the PR code via `gh pr diff $PR_NUMBER`, which fetches the diff from the **GitHub remote API**. Local-only commits are invisible to `gh pr diff`. Step 5C pushes the initial implementation, and each fix round must also **commit AND push** so the next review round sees the updated code.

```
Step 5C push   → implementation commits on remote (commit A)
Round 1 review → gh pr diff sees commit A → reviews implementation
Round 1 fix    → commit B + push (fixes from round 1)
Round 2 review → gh pr diff now includes commit B → reviews the fixed code
Round 2 fix    → commit C + push (fixes from round 2, if any)
Round 3 review → gh pr diff includes commits B+C → final check
Phase 7        → final push (changelog, any remaining changes)
```

### Step 6A: Run /review-program --local --full (Round N)

Invoke the `review-program` skill in local-only mode with `--full`. On **round 1**, this runs the full review:
- **PDF acquisition** (always on): `complete:country-models:document-collector` discovers and renders source PDFs
- **Regulatory accuracy**: `complete:country-models:program-reviewer` researches regulations independently, compares to code
- **Reference quality**: `complete:reference-validator` checks reference completeness and corroboration
- **Code patterns**: `complete:country-models:implementation-validator` checks code patterns
- **Test coverage**: `complete:country-models:edge-case-generator` identifies untested scenarios
- **PDF audit**: 2-5 `general-purpose` agents audit parameter values against PDF screenshots
- **Mismatch verification**: 600 DPI re-render + text cross-reference for every reported mismatch

**Note on round 2+**: The `/review-program` command always runs a full review — it has no "incremental" mode. This is by design: fixes can introduce new issues, and PDF audit agents need to re-verify values that may have changed. The cost of a redundant re-check is low compared to missing a regression.

### Step 6B: Check Results

Read `/tmp/review-program-summary.md` (max 20 lines). Check:
- **Critical issue count** — the number that matters
- **Recommended severity** — APPROVE means zero critical issues

**If critical == 0**: Report to user and exit loop.

**If critical > 0 and ROUND < MAX_ROUNDS**: Proceed to Step 6C.

**If critical > 0 and ROUND == 2**: Use `AskUserQuestion` before round 3:

```
Question: "Review found {N} critical issues after 2 fix rounds. Attempt a 3rd round?"
Options:
  - "Yes, try one more round"
  - "No, stop and show remaining issues"
```

If user says no, exit loop and include remaining issues in the final report.

**If critical > 0 and ROUND == MAX_ROUNDS (3)**: Exit loop. Report remaining issues to user:

```
"After {MAX_ROUNDS} review-fix rounds, {N} critical issues remain:
{one-line summary of each from the summary file}
These will be noted in the final report for manual resolution."
```

### Step 6C: Fix Critical Issues

Spawn a fixer agent to address the critical issues found in this round:

```
subagent_type: "complete:country-models:rules-engineer",
  team_name: "{st}-{prog}-backdate", name: "review-fixer-{ROUND}"

"Fix the critical issues from the /review-program review (round {ROUND}).
Read the full review report at /tmp/review-program-full-report.md.
Focus ONLY on items marked CRITICAL — do not change anything else.
Load skills: /policyengine-variable-patterns, /policyengine-code-style,
  /policyengine-parameter-patterns, /policyengine-period-patterns, /policyengine-vectorization.
Apply fixes. Run make format.

REUSE EXISTING VARIABLES: Before creating any non-program-specific variable, Grep the
codebase first. PolicyEngine-US likely already has it (fpg, smi, tanf_fpg, ssi, etc.).

LEARN FROM PAST SESSIONS (read if they exist — skip if not found):
- {LESSONS_PATH}
- ~/.claude/plugins/marketplaces/policyengine-claude/lessons/agent-lessons.md
These contain real mistakes from past runs. Do NOT repeat them.

LEARN FROM PREVIOUS ROUNDS:
If /tmp/{st}-{prog}-checklist.md exists, read it FIRST. It contains issues
found and fixed in previous rounds. Do NOT reintroduce any of those patterns.

AFTER fixing, APPEND your fixes to /tmp/{st}-{prog}-checklist.md:
Format each line as:
- [ROUND {ROUND}] [{CATEGORY}] {file}:{line} — {what was wrong} → {what you changed}

Categories: HARD-CODED, WRONG-PERIOD, MISSING-REF, BAD-REF, DEDUCTION-ORDER,
UNUSED-PARAM, WRONG-ENTITY, NAMING, FORMULA-LOGIC, TEST-GAP, OTHER"
```

### Step 6D: Verify Fix & Commit

**6D-1: Run tests and fix failures:**

```
subagent_type: "complete:country-models:ci-fixer",
  team_name: "{st}-{prog}-backdate", name: "ci-fixer-{ROUND}"

"Run tests for {STATE} {PROGRAM} after review-fix round {ROUND}.
Fix any test failures introduced by the fixes. Run make format."
```

**6D-2: Commit and push fixes:**

After ci-fixer completes, Main Claude commits and pushes so the next round's `/review-program` (which uses `gh pr diff` from the remote) sees the updated code:

```bash
# Stage only the program's directories — avoid staging unintended files
git add policyengine_us/parameters/gov/states/{st}/ policyengine_us/variables/gov/states/{st}/ policyengine_us/tests/policy/baseline/gov/states/{st}/
git commit -m "Review-fix round {ROUND}: address critical issues from /review-program"
git push
```

**6D-3: Increment ROUND and go back to Step 6A.**

### Loop Summary

| Round | What happens | Exit condition |
|-------|-------------|---------------|
| 1 | Full /review-program → fix criticals → run tests | 0 critical issues |
| 2 | Full /review-program → fix criticals → run tests | 0 critical issues, or user declines round 3 |
| 3 | Full /review-program → report remaining issues | Always exits (max reached) |

**Typical outcome**: Most issues are caught and fixed in round 1. Round 2 catches regressions from round 1 fixes. Round 3 is rare — it's a safety net for complex programs with cascading dependencies.

---

## Phase 7: Finalize

### Step 7A: Push & Changelog

```
subagent_type: "complete:country-models:pr-pusher",
  team_name: "{st}-{prog}-backdate", name: "pusher"
```

The `pr-pusher` agent ensures PRs are properly formatted with changelog, linting, and tests before pushing. It handles:
- Creating changelog fragment in `changelog.d/` (see Changelog section below)
- Running `make format`
- Pushing the branch

**Changelog format (towncrier fragments):**
```bash
echo "Description of change." > changelog.d/<branch-name>.<type>.md
```
Types: `added` (minor bump), `changed` (patch), `fixed` (patch), `removed` (minor), `breaking` (major).
**DO NOT*

…(truncated)
