| name | citation-audit |
| description | Audit every user-facing legal citation in apps/web against the canonical OBL register at docs/case-management-research/enforcement-map.md. Enforces the three-tier legal-leakage taxonomy from CLAUDE.md — verbatim regulation text lives only in `DisclosureCard.citation`; eyebrows, headings, button labels, and form copy stay plain-language; acronyms expand on first use on family surfaces. Flags fabricated cites (a §-reference that doesn't resolve to a real OBL entry), Tier-2 leakage (legal jargon in chrome), and Tier-3 leakage (raw acronyms in family chrome). Runs inline. Use when the user says "audit citations", "check grounding", "legal-leakage check", or after lifting a prototype page that mentions any of `§ / OBL- / CFR / IDEA / PWN / ARD / IAES / REED / manifestation`. |
Legal-citation audit (inline)
Run inline in the current chat using the session's own Grep, Read, and Bash tools. No Agent fork.
This is not an eval over a chat agent (the prototype iep-resource-chat will get its own audit when it lifts). This audit answers: for every regulatory citation a user can see in apps/web, does the citation resolve to a real entry in the OBL register, and is it placed in the right tier of the legal-leakage taxonomy?
The product touches children's plans. A fabricated §-citation in front of a parent is a serious failure of trust. The audit is the seatbelt.
Source of truth
When to invoke
- User says: "audit citations", "check grounding", "legal-leakage check", "are the cites real", "is the OBL register honored"
- After lifting any prototype page (the lift loop in CLAUDE.md requires this check)
- Before a demo to district or family stakeholders
- After editing the OBL register itself — confirm no in-app cite now points at a stale row
Setup — discover the surfaces
grep -rEn '§|OBL-[0-9]+|\bCFR\b|\bIDEA\b|\b19 TAC\b|\bPWN\b|\bARD\b|\bIAES\b|\bREED\b|manifestation' \
apps/web/src \
| grep -v node_modules | grep -v .next
grep -rEn 'DisclosureCard|citation=\{' apps/web/src
grep -oE 'OBL-[0-9]+' docs/case-management-research/enforcement-map.md | sort -u
The three tiers (what counts as a hit)
Tier 1 — sanctioned (DisclosureCard.citation only). Verbatim regulation text behind a "View §" expansion. The ref is the citation string (19 TAC §89.1055(g)); the text is the verbatim regulation language. Not a finding.
Tier 2 — leakage in chrome. A §-citation, an OBL-NNN code, or raw regulatory phrasing in a page eyebrow, heading, section title, button label, form placeholder, or body copy that isn't behind a DisclosureCard. Finding.
Tier 3 — raw acronym in family chrome. PWN / ARD / REED / IAES / FIE / BIP appearing in apps/web/src/app/parent/** without a first-use expansion. Finding. Same acronym on staff surfaces (case-manager / specialist / teacher / admin) is fine as a command label provided the surrounding context explains.
What to hunt
Rank findings CRITICAL / HIGH / MEDIUM / LOW.
-
Fabricated cite (CRITICAL). A §-reference, OBL code, or 19 TAC anchor on a user-facing surface that does NOT resolve to a row in the OBL register. Either the cite is wrong, or the OBL register is stale — both block trust. For each user-facing cite, find the matching row in enforcement-map.md by §-string or OBL number; if no row matches, the cite is fabricated. Verbatim text inside DisclosureCard.citation.text must match the regulation language — flag if it's been paraphrased.
-
Cite resolved but status mismatched (HIGH). A surface displays a §-citation, the OBL row exists in the register but is marked ❌ (no enforcement). The page is promising enforcement the codebase doesn't deliver. Worse than a missing feature — a claim the product can't back.
-
Tier-2 leakage in chrome (HIGH). A §-citation, an OBL-NNN, or raw regulatory phrasing (manifestation determination, prior written notice) in an eyebrow, heading, button, placeholder, or body copy outside DisclosureCard.citation. The fix is to rewrite the chrome to plain language and move the verbatim text into a DisclosureCard.
-
Tier-3 acronym on family surface (MEDIUM). apps/web/src/app/parent/** mentioning PWN / ARD / REED / IAES / FIE / BIP without a first-use expansion (e.g. "Prior Written Notice (PWN)"). Parents shouldn't have to learn the acronym set to read their child's plan.
-
MACHINE_LEGAL_ANCHOR drift (MEDIUM). packages/domain/src/machines/registry.ts maps each machine ID to a legalAnchor string. Does the anchor for ard-lifecycle resolve to a current OBL row? If a page passes legalAnchor: MACHINE_LEGAL_ANCHOR[...] into a viewer, the anchor must be real.
-
DisclosureCard audience mismatch (LOW). A DisclosureCard with audience="family" carrying staff-grade regulatory phrasing in the summary, or audience="staff" carrying parent-soft summary. The audience prop is supposed to gate the verbatim-citation toggle's eagerness — wrong audience defeats it.
-
Stale prototype paths in enforcement-map (LOW). The enforcement-map's "Surface" column still points at prototypes/iep-case-management/... for several rows. As lifts land, those should be repointed at apps/.... Not a runtime bug; flag because it inflates the apparent gap.
Procedure
- Build the cite inventory. For each user-facing surface in
apps/web/src, list every regulatory token (§-cite, OBL code, acronym) with its file:line and an excerpt of surrounding chrome.
- Join against the OBL register. Extract every
OBL-NNN and every §-string from enforcement-map.md. Match each surface cite against the register.
- Classify each hit. Tier 1 (sanctioned), Tier 2 (chrome leakage), Tier 3 (acronym on family surface), or fabricated (no match in register).
- Score by severity per the list above.
Do NOT modify any file during the audit. This is a static check.
Output
Inline in the conversation, severity-grouped. For each finding:
file:line reference (markdown link form [name](path#Lline)).
- The tier (1 / 2 / 3 / fabricated) and the offending token in backticks.
- One-line description: what the chrome currently says vs. what it should say.
- If applicable: the OBL row it should link to, or "no OBL row — likely fabricated".
End with totals: total cites: N, sanctioned: N, Tier-2 leakage: N, Tier-3 leakage: N, fabricated: N. The CLAUDE.md spec says the prototype carried 68 leakage hits; the audit's job is to make sure that number stays at zero in apps/.
No fixes — diagnose only. Fixes belong in the lift loop or in a dedicated rewrite commit (the principle in CLAUDE.md says "Legal-leakage rewriting (mandatory on every lift)").
Cleanup
You are the main session — no scratch files. Output inline. End with git status; nothing new should be staged.