Plan: Policy Currency & Federal Source Consumption (epic &59)
On this page
Implements ADR-031 §1 for epic &59 (parent &58). Grounding below is code-verified (2026-06-09). Issues are cut from the Status rows per ADR-013 once this plan lands.
Status
| MR | Description | Status |
|---|---|---|
MR1 (federal audit) |
Bring |
Done (2026-06-09) — |
MR2 (source pinning) |
Add source pinning to the citation schema: optional |
Done (2026-06-09) — |
MR3 (drift tool) |
|
Done (2026-06-09) — |
MR4 (reverse completeness) |
Extend |
Done (2026-06-09) — |
MR5 (annual indexing model) |
Model the federal indexing cycle explicitly: a small |
Done (2026-06-09) — |
MR6 (PolicySource trait + docs) |
Code the |
Done (2026-06-09) — |
Design — grounded current state (code-verified)
-
Citation schema (
crates/canopy-policy/src/citation.rs:36-60):value,authority,source_ref(PAMMS file path, e.g.dfcs-snap/modules/snap/pages/3617.adoc),section,manual_transmittal,effective_date,federal_citation,state_citation,verified_date,verified_by,notes. No hash/pin/effective-until fields. -
Validation (
citation.rs:172-229): forward completeness, value consistency, staleness (warning ≥365d,is_error()false only forStale), schema non-empty. Orphaned citations are silently ignored. -
xtask policyAction enum (xtask/src/cmd/policy.rs:26-48):Audit,AuditUnwraps,AuditLiterals,SyncCache. NoDrift.sync_cachereads[policy_source].reposfrom jurisdiction.toml, clones--depth 1intorulesets/{jurisdiction}/.policy-cache/— no commit pin, no hash, no last-synced marker. -
Federal data (
rulesets/federal/):fpl-2026.json(Jan 1),snap-allotments-2026.json/snap-deductions-2026.json/snap-income-limits-2026.json/cms-416-2026.json(Oct 1),smi-2026.json(Jul 1),snap-budgeting-factors.json(static 1977-88),wic-food-packages-2026.json,cross-program-2026.json. Each carries_source/_effective_dateheaders.rulesets/federal/citations.tomlexists with entries (FNS COLA memo, FNS Handbook 501, CMS SHO letters as `source_ref`s) but no audit consumes it — the epic’s "federal aren’t citation-traced" is stale; the true gap is audit + currency coverage. -
Staged-enforcement precedent:
adr-011-policy-auditCI job went advisory → blocking after the burn-down ("205/201 keys cited — clean"); same.rust-basejob pattern for the new gates.
Design — MR1 findings (2026-06-09)
-
The federal manifest could not even parse before MR1:
Citation.valuewas a required field, but 12 of the federal entries are file-level citations (key = data-file stem, no single value). The schema change is additive —value: Option<toml::Value>— with strictness preserved per family: jurisdiction citations and federal leaf citations without avalueareSCHEMAerrors. -
Three citation granularities, discovered from the live data: file-level (key = stem, e.g.
"fpl-2026"), subtree (dotted key resolving to a JSON object, e.g."snap-budgeting-factors.pay_periods"— cites a group of values, no singlevalue), and leaf (dotted key resolving to a scalar/array —valuerequired + consistency-checked). The first audit run flaggedpay_periodsbefore the subtree rule existed; the rule was added rather than forcing a fake aggregate value. -
The audit found 5 genuinely uncited data files (
cross-program-2026,ele-grant-2026,ele-lapse-2026,ele-renewal-2026,wic-food-packages-2026) — backfilled in the same MR with citations transcribed from the files' own_comment/_citationheaders and the plans that authored them (epic &55 Plan 2 MR4, wic-eligibility plan). -
CI job lands blocking from birth (deviation from "advisory first"): because the findings were fixed in the same MR, there was nothing to burn down — the degenerate case of the staged pattern.
adr-011-policy-audit(default--source all) also covers the federal family;adr-031-federal-auditexists so a federal failure is named in the pipeline. -
Unresolvable dotted paths are still skipped (e.g.
snap-alien-eligibility.qualified_alien_five_year_bar, whose value lives inside JDM rule content with no addressable key) — that is MR4 orphan territory, by design.
Design — MR2 findings (2026-06-09)
-
114 distinct
source_ref`s in the Georgia manifest; 46 resolve in the cache.The rest are CFR/USC/memo references used as `source_ref by non-PAMMS authorities — they stay unpinned by design (no upstream file to hash; the federal family’s currency assurance is MR5’s indexing-window check). -
Pinning is format-preserving (
toml_edit, already in-tree viatoml0.8 — no new duplicate dep):--pinappends/updates onesource_sha256line per citation block; the file’s 55 comment lines survive. Pinning is idempotent. -
The initial back-fill pins the cache content at pin time, not at
verified_date. A value that drifted upstream between the last human verification (2026-04-07 full audit) and the first pin would be captured as-is; that residual risk is bounded by the staleness check and closed permanently by the MR3 drift→re-verify loop going forward. Re-pinning after re-verification is the documented loop. -
A
.gitguard protects the HEAD pin:git rev-parsewalks up the tree, so a non-clone directory in the cache would otherwise silently pin canopy’s own HEAD.
Design — MR4 findings (2026-06-09)
-
The reverse check found real rot on its first run: 7 jurisdiction citations targeted nothing. Four were stale duplicates of the
tanf.work_requirement_*keys left behind when those keys moved under[tanf.wpr]— correctly-keyed citations already existed, so the orphans were silently double-counting the audit’s "citations" total (218 vs 211 keys). Deleted. -
The one legitimate orphan class is structural: a citation documenting a value embedded in JDM rule content or service code (TANF sanction/PR wire codes in
tanf-eligibility.json, the PAMMS 1540 itemized self-employment method in canopy-tanf, the PRWORA 5-year bar insnap-alien-eligibility.json). These go incompliance/adr-031-citation-orphan-allowlist.tomlwith a written reason naming where the value actually lives — thecompliance/*.tomlpattern, same as the ADR-011 allowlists. -
Validate signatures gained an
orphan_allowlistparameter rather than post-filtering in xtask, so the exemption logic is uniform across both families and the library’s tests pin it.
Design — MR5 findings (2026-06-09)
-
grace_daysmodels real publication lag (deviation from a naive hard cutover): HHS publishes the FPL guidelines weeks after Jan 1, so a hard Jan-1 error would put main’s CI red for weeks every year through no fault of the repo. Inside a family’s grace window an out-of-window table is a warning (visible in the audit output); past it, an error. SNAP COLA getsgrace_days = 0— FNS publishes in August, so there is no excuse on Oct 1. -
Two of the year-suffixed files are static, not indexed:
cross-program-2026.json(TSNAP/TMA/ELE regulatory constants per 7 CFR 273.26 / 42 CFR 435.112 / 42 CFR 435.1102) andwic-food-packages-2026.json(7 CFR 246.10 — reg-driven, not annually indexed). The-2026suffix reflects authorship vintage, not an indexing cadence; they live in thestaticfamily alongside the JDM rulesets and the budgeting factors. -
Coverage is closed-world: a new data file dropped into
rulesets/federal/without an indexing.toml assignment fails the audit (UNINDEXED), so the model can’t silently rot as files are added; overlapping patterns areAMBIGUOUSerrors.
Design — decisions
-
Hash unit = whole source file (
source_ref), not thesectionanchor. Section-granular hashing requires AsciiDoc structural parsing for marginal benefit; a file-level hash with thesectionfield for human navigation is robust and cheap. A drifted file with 12 citations reports 12 findings grouped under one file — correct, since each needs re-verification. -
Federal upstream is not parsed. FNS/CMS/ACF publish memos and PDFs, not machine-readable tables; building parsers is fragile and out of scope. The federal JSON files in-repo ARE the verified snapshot; currency assurance for federal is the indexing-window check (MR5) + audit of the citations file (MR1), not upstream hash drift. PAMMS (git-backed AsciiDoc) gets true drift detection.
-
Drift stays advisory permanently (
allow_failure: true): it compares against a live upstream, so it can turn red on a Tuesday with no repo change — blocking unrelated MRs on that would train people to ignore it. It is surfaced in CI + a scheduled pipeline instead. The audits (MR1/MR4/MR5) are deterministic against the repo and follow advisory→blocking. -
Pin data is gitignored (sync-manifest lives in
.policy-cache/), but the per-citationsource_sha256is committed in citations.toml — the pin travels with the citation; the manifest is just the comparison cache.
Verification
-
Unit tests in
canopy-policyfor the new validate checks (orphan jurisdiction/federal, schema with/without hash) + fixture-tree tests fordrift(pinned hash vs mutated cache file → finding; unpinned citation → "unpinned" notice, not error). -
Live:
cargo xtask policy sync-cache --jurisdiction georgia && cargo xtask policy drift --jurisdiction georgiaagainst the real PAMMS cache;policy auditgreen on both source families before the blocking flip. -
Each MR through the standard gate (validate + D1-D8 + force-merge squash=false); CI jobs land
allow_failure: trueand flip per the burn-down.