Audit: Hardcoded Policy Values That Should Move to jurisdiction.toml / JDM Rulesets (2026-04-20)

On this page

Status

Audit only — no code changes. Findings below drive follow-up plan tickets.

Audit Area Scope Status

SNAP

services/canopy-snap, SNAP reporting, SNAP-related shared crates

Done (2026-04-20)

TANF

services/canopy-tanf, TANF reporting

Done (2026-04-20)

Medicaid + CHIP

services/canopy-medicaid, T-MSIS / CMS-64 / CMS-416 reporting

Done (2026-04-20)

CAPS + WIC

services/canopy-caps, services/canopy-wic

Done (2026-04-20)

Shared + cross-program

crates/canopy-*, orchestrator, applications, persons, verification, enrollment, renewals, notices, appeals, web

Done (2026-04-20)

Context

ADR-011 (Policy-to-rules traceability) requires every policy value — thresholds, percentages, durations, dollar amounts — to trace to an authoritative source via citations.toml and live in jurisdiction.toml (for parameters) or under rulesets/ (for eligibility logic). Rust source should contain none of these values except as transient injection points.

This audit was prompted during canopy-web-persons-wiring MR work on 2026-04-20, when the author introduced four hardcoded percentages in UI display strings (90% TANF disregard, 50% / 85% CAPS SMI, 185% WIC FPL). Those were caught and removed before commit, but the incident surfaced a broader concern: how many other hardcoded policy values are already in the codebase?

Aggregate findings

Five subagents returned ~90 distinct findings across the codebase. The most severe pattern is silent unwrap_or(<federal_value>) — a jurisdiction.toml section loads, but when a key is missing, code falls back to a hardcoded Rust default rather than erroring. This defeats ADR-011 traceability because a broken TOML silently returns federally-accurate values without a citation trail.

Highest-impact items (cross-cutting)

  1. crates/canopy-reference/src/cross_program.rs duplicates rulesets/federal/cross-program-2026.json. TMA_COVERAGE_MONTHS=12, TMA_QRF_DUE_MONTHS=[4,7,10], EXPRESS_LANE_MEDICAID_FPL_PCT=235, EXPRESS_LANE_PEACHCARE_FPL_PCT=247, EXPRESS_LANE_MAX_AGE=19, TSNAP_CERTIFICATION_MONTHS=5, TSNAP_TRIGGER_REASONS. File’s own docstring admits the duplication. Downstream consumers (tma.rs, express_lane.rs, TSNAP subscriber) bypass the policy pipeline entirely. Recommendation: load the JSON into a shared parameter table at startup, delete the `pub const`s.

  2. Federal budgeting-factor pipeline is half-wired for SNAP. rulesets/federal/snap-budgeting-factors.json correctly captures the 20% earned-income deduction, 30% allotment contribution, 50% shelter test, and pay-period conversions as data, but nothing loads it. Same constants appear as magic numbers in canopy-snap/src/deductions.rs:82/122/181, canopy-snap/src/determine.rs:75-76/103-104, and rulesets/georgia/snap-eligibility.json:157/165/228. Changing the federal rate in one place leaves two stale.

  3. canopy-applications/src/api/mod.rs:382-389 hardcodes every program’s processing deadline. SNAP 30/7, TANF 30, Medicaid 45, CHIP 45 — zero jurisdiction.toml entries for application_processing. The worker portal (canopy-web/api/applications.rs:93) independently recomputes the SNAP expedited deadline as +7 days, duplicating policy across the BFF.

  4. TANF JDM rulesets bake age/hour/duration literals directly into rule expressions. rulesets/georgia/tanf-work-requirements.json embeds every threshold (ages 18/59, 12-month infant cutoff, 72-month under-6 cutoff, and the full 20/30/35 hour matrix). jurisdiction.toml [tanf.wpr] already houses most of these values; the ruleset should consume them via context.thresholds.* inputs.

  5. Medicaid JDM rulesets bake age thresholds directly into JDM expressions. Every age (1, 6, 18, 19, 21, 26, 45, 65, 30-day hospital LOS) is a literal in medicaid-magi.json / medicaid-non-magi.json / chip-eligibility.json. Each traces to a specific federal/state regulation and should be injected as a named context input.

  6. canopy-reporting/src/reporting/medicaid.rs contains large policy surfaces as Rust match-arms. T-MSIS coverage-group map (50+ mappings), CMS-416 age bands [(0,1),(1,2),(3,5),(6,9),(10,14),(15,18),(19,20)], disability-COA allowlist (16 COAs), dual-eligible-COA allowlist — all effectively federal reporting policy with no citation trace. Should move to rulesets/federal/tmsis-*.json / cms-416-2026.json.

  7. GRG payment constants are in Rust, not jurisdiction.toml. canopy-tanf/src/api/grg_handlers.rs:41,44$100 MSP amount and × 4 CRISP multiplier (PAMMS 1210) are literals.

  8. CAPS age gate is a federal parameter living in Rust. canopy-caps/src/determine.rs:90,9219 (special-needs) / 13 (standard) per 45 CFR 98.20(a)(1)(i). No citations.toml entry.

  9. WIC food-package assignment is eligibility logic in Rust. canopy-wic/src/params.rs:157-178 inlines the 7 CFR 246.10 Table 4 decision tree as a Rust match, including the federal 6-month infant cutoff. Per ADR-003 this belongs in a JDM ruleset.

  10. ABAWD time-limit constants live only in Rust. canopy-snap/src/abawd.rs:107/211/218/224/237 — 36-month rolling window, 3-month countable limit, 3 consecutive qualifying months. jurisdiction.toml [snap.abawd] exists with other values but these are absent.

Pattern #1: Silent unwrap_or(<federal_value>)

Found in: canopy-applications/params.rs:25,29, canopy-renewals/params.rs:34,38,42,46,61, canopy-enrollment/main.rs:47,51,55, canopy-appeals/config.rs:22,38-42, canopy-caps/params.rs:74,78,82,86, canopy-wic/params.rs:93,103,57,146, canopy-snap/src/params.rs:170,176.

All these services read jurisdiction.toml but fall through to a hardcoded Rust default if the key is missing. ADR-011 says policy values must have citations; silent fallbacks bypass that contract because the fallback value isn’t cited anywhere.

Recommendation: audit every unwrap_or(N) where N is a policy value; replace with .context()? so missing config fails loudly at startup. canopy-notices (strict toml::from_str) and canopy-snap/canopy-tanf (explicit context errors for some keys) are the right pattern.

Pattern #2: Ruleset literals that should be context inputs

TANF rulesets (tanf-work-requirements.json, tanf-eligibility.json) and Medicaid rulesets (medicaid-magi.json, medicaid-non-magi.json, chip-eligibility.json) embed policy thresholds directly as JDM literals instead of reading context.thresholds.* — despite those thresholds already being in jurisdiction.toml. A ruleset-parameter plumbing pass would unify this.

Pattern #3: Missing citations.toml entries for values already in jurisdiction.toml

  • CAPS: copayment_tiers, default_provider_rate_cents_per_hour

  • WIC: food_packages, certification_periods_months (under rulesets/federal/wic-food-packages-2026.json)

  • Medicaid: T-MSIS / CMS-64 / CMS-416 reporting constants

cargo xtask policy audit should be rejecting these — either it is not enforcing the rule for these sections, or these values are ingested through a path the audit doesn’t walk.

Pattern #4: Duplicate policy windows across timing domains

Expungement advance notice (30 days), renewal advance notice (30 days), worker-portal dashboard lookahead (30 days), renewals-API default lookahead (90 days) — all related but each lives as a separate literal. A consolidated [shared.timing] section would eliminate the drift risk.

Notable pre-existing gaps (already tracked)

These surfaced in the audit but are already in plan errata / known Tier-7 items — not new findings:

  • reporting/tanf.rs WPR targets (50/90) hardcoded as fallbacks (documented in tanf-federal-reporting.adoc errata)

  • SelfEmploymentNet disregard applied to net not gross (tanf-pamms-alignment plan)

  • 20-hour child-under-6 reduced WPR threshold (45 CFR 261.32(b)) not implemented

  • Jurisdiction-parameterized notice timing (Tier 7 roadmap item)

  • chip_lower_pct_fpl was hardcoded as 134 — resolved 2026-04-12

Per-area detailed findings

Each subagent’s full output is preserved below.

SNAP (19 findings)

See services/canopy-snap/src/*, rulesets//snap-*.json, canopy-reference/cross_program.rs.

Top hits: deductions.rs (20% / 30% / 50% factors), abawd.rs (36/3/3 month windows), determine.rs (pay-period conversions, silent certification defaults), params.rs (silent $23 minimum-benefit fallback), verification.rs ($100 IEVS threshold).

TANF (19 findings)

See services/canopy-tanf/src/*, rulesets//tanf-*.json, canopy-reporting/reporting/tanf.rs.

Top hits: GRG MSP/CRISP amounts, 6-month certification period, federal 60-month time limit literal in denial strings, JDM rulesets embedding every age/hour/duration threshold as literals instead of context inputs, WPR SQL using literal >= 30 / >= 20 / >= 35.

Medicaid + CHIP (24 findings)

See services/canopy-medicaid/src/*, rulesets//medicaid-.json, rulesets//chip-*.json, canopy-reporting/reporting/medicaid.rs.

Top hits: cross_program.rs constant duplication with cross-program-2026.json, TMA Phase-2 205% literal in determine.rs:423, Chafee 18-21 / Pathways 19-64 / WHM 18-64 / P4HB 18-44 / FFCM <26 age ranges baked into rulesets, hospital LOS 30-day threshold, T-MSIS coverage-group map and CMS-416 age bands as Rust match-arms.

CAPS + WIC (21 findings)

See services/canopy-caps/src/, services/canopy-wic/src/, rulesets//caps-.json, rulesets//wic-.json.

Top hits: CAPS age gates (13 / 19) in determine.rs, every CAPS params.rs lookup wrapped with unwrap_or(Georgia value), WIC food-package assignment as a Rust match decision tree with embedded 6-month infant cutoff, WIC silent fallbacks to 185% FPL / 12-month certification, missing citations for copayment_tiers, default_provider_rate, food_packages, certification_periods_months.

Shared + cross-program (17 findings)

See crates/canopy-*, services/canopy-eligibility, canopy-applications, canopy-enrollment, canopy-renewals, canopy-appeals, canopy-web.

Top hits: application processing deadlines hardcoded in canopy-applications/api/mod.rs, unwrap_or(<federal default>) across every service’s params loader, age >= 60 elderly threshold in orchestrator with no jurisdiction.toml entry, expungement/renewal window drift across 4+ files, FTI retention * 365 day-per-year approximation.

Based on the aggregate findings, the natural follow-up plans are:

Plan Scope Priority

Remove silent unwrap_or(<federal>) fallbacks across params loaders

All services — convert silent fallbacks to hard errors; add missing jurisdiction.toml keys; add citations

High — closes the largest class of ADR-011 violations in one pass

Consolidate cross_program.rs constants into a parameter table

Load cross-program-2026.json at startup; delete `pub const`s; update TMA + Express Lane + TSNAP subscribers

High — single highest-impact item

Inject age thresholds as context inputs into TANF + Medicaid + CHIP JDM rulesets

Pass named context.thresholds.*_age values from parameter tables; replace JDM literals with references

Medium — substantial ruleset-author + parameter-plumbing work

Externalize T-MSIS / CMS-64 / CMS-416 reporting maps

Create rulesets/federal/tmsis-coverage-group-map.json, cms-416-2026.json; consume from canopy-reporting; add citations

Medium — improves T-MSIS audit traceability

Externalize SNAP budgeting factors

Load snap-budgeting-factors.json via SnapParameterTable; inject into deductions.rs and rulesets/georgia/snap-eligibility.json; remove duplicates

Medium

Externalize ABAWD time-limit constants

Add [snap.abawd] rolling_window_months / time_limit_months / regaining_consecutive_months; consume from abawd.rs

Medium

Externalize application-processing deadlines

Add [shared.application_processing] section; consume from canopy-applications and canopy-web; remove BFF-side recomputation

Medium

Move WIC food-package assignment to a JDM ruleset

New rulesets/federal/wic-food-package-assignment.json; replace Rust match decision tree

Medium — aligns with ADR-003

Externalize CAPS age gates to jurisdiction.toml

Add [caps] standard_age_limit / special_needs_age_limit; cite 45 CFR 98.20

Low — single file fix

Externalize GRG payment amounts

Add [tanf.grg] msp_amount_cents / crisp_fm_multiplier; cite PAMMS 1210

Low — single file fix

Add missing citations for copayment_tiers, default_provider_rate_cents_per_hour, food_packages, certification_periods_months

Add [citations.*] entries; verify cargo xtask policy audit flags them if removed

Low — policy-audit hygiene

Verification suggestions

  1. Extend cargo xtask policy audit to walk every unwrap_or(…​) in */params.rs and flag any whose argument is a numeric literal — forces future additions to fail loud rather than silently fall back.

  2. Grep guard in CI for \bdec!(0\.\d+)\b and Decimal::from\([0-9]+\) in services//src//*.rs outside of tests and params.rs, flagging new hardcodes at review time.

  3. Ruleset-input lint that parses every .jdm.json in rulesets/ and flags numeric literals inside expressions that aren’t prefixed with context.thresholds. (or equivalent).

Errata

This report was generated by 5 parallel contextless subagents on 2026-04-20. Each was scoped to a non-overlapping slice of the codebase. Findings are the raw agent output consolidated by the author; line numbers are from commit c3102c0 (main, 2026-04-20).

Edit this page · default