Audit: Hardcoded Policy Values That Should Move to jurisdiction.toml / JDM Rulesets (2026-04-20)
On this page
- Status
- Context
- Aggregate findings
- Highest-impact items (cross-cutting)
- Pattern #1: Silent
unwrap_or(<federal_value>) - Pattern #2: Ruleset literals that should be context inputs
- Pattern #3: Missing
citations.tomlentries for values already injurisdiction.toml - Pattern #4: Duplicate policy windows across timing domains
- Notable pre-existing gaps (already tracked)
- Per-area detailed findings
- Recommended follow-up plans
- Verification suggestions
- Errata
Status
Audit only — no code changes. Findings below drive follow-up plan tickets.
| Audit Area | Scope | Status |
|---|---|---|
SNAP |
|
Done (2026-04-20) |
TANF |
|
Done (2026-04-20) |
Medicaid + CHIP |
|
Done (2026-04-20) |
CAPS + WIC |
|
Done (2026-04-20) |
Shared + cross-program |
|
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)
-
crates/canopy-reference/src/cross_program.rsduplicatesrulesets/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. -
Federal budgeting-factor pipeline is half-wired for SNAP.
rulesets/federal/snap-budgeting-factors.jsoncorrectly 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 incanopy-snap/src/deductions.rs:82/122/181,canopy-snap/src/determine.rs:75-76/103-104, andrulesets/georgia/snap-eligibility.json:157/165/228. Changing the federal rate in one place leaves two stale. -
canopy-applications/src/api/mod.rs:382-389hardcodes every program’s processing deadline. SNAP 30/7, TANF 30, Medicaid 45, CHIP 45 — zero jurisdiction.toml entries forapplication_processing. The worker portal (canopy-web/api/applications.rs:93) independently recomputes the SNAP expedited deadline as+7 days, duplicating policy across the BFF. -
TANF JDM rulesets bake age/hour/duration literals directly into rule expressions.
rulesets/georgia/tanf-work-requirements.jsonembeds 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 viacontext.thresholds.*inputs. -
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. -
canopy-reporting/src/reporting/medicaid.rscontains 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 torulesets/federal/tmsis-*.json/cms-416-2026.json. -
GRG payment constants are in Rust, not jurisdiction.toml.
canopy-tanf/src/api/grg_handlers.rs:41,44—$100MSP amount and× 4CRISP multiplier (PAMMS 1210) are literals. -
CAPS age gate is a federal parameter living in Rust.
canopy-caps/src/determine.rs:90,92—19(special-needs) /13(standard) per 45 CFR 98.20(a)(1)(i). Nocitations.tomlentry. -
WIC food-package assignment is eligibility logic in Rust.
canopy-wic/src/params.rs:157-178inlines the 7 CFR 246.10 Table 4 decision tree as a Rustmatch, including the federal 6-month infant cutoff. Per ADR-003 this belongs in a JDM ruleset. -
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(underrulesets/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.rsWPR targets (50/90) hardcoded as fallbacks (documented intanf-federal-reporting.adocerrata) -
SelfEmploymentNetdisregard applied to net not gross (tanf-pamms-alignmentplan) -
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_fplwas 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.
Recommended follow-up plans
Based on the aggregate findings, the natural follow-up plans are:
| Plan | Scope | Priority |
|---|---|---|
Remove silent |
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 |
Load |
High — single highest-impact item |
Inject age thresholds as context inputs into TANF + Medicaid + CHIP JDM rulesets |
Pass named |
Medium — substantial ruleset-author + parameter-plumbing work |
Externalize T-MSIS / CMS-64 / CMS-416 reporting maps |
Create |
Medium — improves T-MSIS audit traceability |
Externalize SNAP budgeting factors |
Load |
Medium |
Externalize ABAWD time-limit constants |
Add |
Medium |
Externalize application-processing deadlines |
Add |
Medium |
Move WIC food-package assignment to a JDM ruleset |
New |
Medium — aligns with ADR-003 |
Externalize CAPS age gates to jurisdiction.toml |
Add |
Low — single file fix |
Externalize GRG payment amounts |
Add |
Low — single file fix |
Add missing citations for |
Add |
Low — policy-audit hygiene |
Verification suggestions
-
Extend
cargo xtask policy auditto walk everyunwrap_or(…)in*/params.rsand flag any whose argument is a numeric literal — forces future additions to fail loud rather than silently fall back. -
Grep guard in CI for
\bdec!(0\.\d+)\bandDecimal::from\([0-9]+\)inservices//src//*.rsoutside of tests and params.rs, flagging new hardcodes at review time. -
Ruleset-input lint that parses every
.jdm.jsoninrulesets/and flags numeric literals inside expressions that aren’t prefixed withcontext.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).