State Law Compliance Frameworks: Statutory Rules as Executable Code
Within Core Architecture & Compliance Mapping, the state-law compliance engine is the deterministic rule layer that converts legislative text into the executable controls every disclosure decision is measured against. It sits downstream of classification and upstream of records retrieval: once a request is classified it carries a statutory clock, an exemption posture, and a jurisdiction, and this engine is what computes the binding deadline, flags the applicable exemptions, and writes the audit record that an oversight body will later read. Each state’s open-records act diverges on response windows, tolling triggers, fee schedules, and exemption categories — California’s Public Records Act, the Texas Public Information Act, and New York’s Freedom of Information Law all start and stop the clock differently — so the framework must treat every rule as a versioned artifact bound to an effective date, never as a hard-coded constant. This guide builds that engine as immutable, cryptographically fingerprinted Python objects whose every evaluation is reproducible years after the fact.
Problem Framing & Statutory Requirement
Public-records statutes are deadline machinery. The federal baseline under 5 U.S.C. § 552(a)(6)(A)(i) is a 20-business-day response window, but state acts override it in both directions: the Texas Public Information Act runs on a “promptly, and no later than 10 business days” prompt-cost-estimate trigger, the California Public Records Act sets a 10-calendar-day determination window with a 14-day extension for unusual circumstances, and New York’s FOIL requires acknowledgement within 5 business days. A framework that encodes any of these as a literal timedelta in application code guarantees eventual statutory misalignment the moment a legislature amends the window — and an agency that miscomputes a single deadline has, by definition, violated the statute.
Three failure classes drive the requirement. First, clock errors: a deadline computed against the wrong convention (calendar vs. business days, wrong tolling rule, missing state holiday) produces a missed statutory deadline that the agency believed it had met. Second, disclosure errors: an exemption that is evaluated by ad-hoc human judgment rather than against a closed rule matrix lets privileged or personal material slip past redaction, or conversely withholds releasable material and invites an appeal. Third, audit errors: if the exact rule version, input, and verdict are not captured at evaluation time, the agency cannot reconstruct why a decision was made when it is challenged on administrative appeal or in litigation.
The compliance engine therefore consumes the validated record emitted by FOIA request taxonomy design, evaluates it against the statute in effect on the date of receipt, and hands its verdict to records retrieval only after writing an immutable audit entry. The requirement is a rule layer that is closed (exemptions drawn from a controlled set), versioned (every rule stamped with an effective and supersession date), and deterministic (the same request and rule snapshot always yield the same verdict).
Prerequisites & Environment Setup
The engine is pure Python with the standard library only; deadline arithmetic and hashing need no third-party runtime dependency, which keeps it safe to run inline in the request path.
- Python 3.11+ — required for
datetime.UTC,StrEnum, and thedataclass(frozen=True, slots=True)features used below. - Standard library:
dataclasses,datetime,enum,hashlib,json,logging,uuid— no other runtime dependencies. workalendar>=17.0(optional, recommended in production) — supplies per-state business-day calendars including statutory holidays; install withpip install workalendar. The snippets below isolate the calendar behind a single function so it can be swapped without touching rule logic.pytest>=8.0(dev only) — for the regression suite in the verification section.- Access controls: the compliance service needs read access to the versioned rule registry and write access only to the append-only audit log; it must hold no credentials for the records store itself, since evaluation completes before retrieval. That separation is enforced through security boundary configuration at this hop.
Treat the statutory rules themselves as a configuration artifact under version control — one rule object per statute per effective period — not as constants buried in application code. Each rule change is tagged with a semantic version and an effective date so that a request received last quarter is always re-evaluated against the statute that was authoritative when it arrived.
Architecture Overview
The engine is a deterministic transform between two pipeline stages. A normalized request enters, the active rule for its jurisdiction and receipt date is selected, the deadline and exemption posture are computed, and a verdict plus an audit hash leave. Retention windows are cross-referenced against records retention scheduling so that a request for records already past lawful disposition is handled distinctly from one for live records, and the scoping rules from the security boundary constrain the evaluation set to authorized jurisdictional domains.
This layering prevents over-disclosure, enforces least-privilege access, and guarantees that every compliance decision is traceable to a specific statutory provision and rule version. The verdict object produced here is the contract handed downstream, so its shape — rule applied, exemption flagged, computed deadline, redaction flag, audit hash — is effectively the API between compliance and retrieval.
Step-by-Step Implementation
1. Compile statutes into immutable, versioned rule objects
Model each statute as a frozen dataclass so a compiled rule cannot be mutated at runtime, and stamp it with effective and supersession dates so the registry can select the rule that was authoritative on any given receipt date. The frozen=True setting is the structural guarantee that statutory rules behave as constants once loaded.
from __future__ import annotations
import hashlib
import json
import logging
import uuid
from dataclasses import dataclass
from datetime import date, datetime, timedelta, UTC
from enum import StrEnum
# --- Structured JSON audit logging (NIST SP 800-53 AU-3: content of audit records) ---
class JSONLogFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
payload = {
"timestamp": datetime.now(UTC).isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
for key in ("request_id", "rule_id", "verdict_hash", "error_details"):
if hasattr(record, key):
payload[key] = getattr(record, key)
return json.dumps(payload)
logger = logging.getLogger("state_compliance_engine")
logger.setLevel(logging.INFO)
_handler = logging.StreamHandler()
_handler.setFormatter(JSONLogFormatter())
logger.addHandler(_handler)
class ExemptionCategory(StrEnum):
PRIVACY_PII = "privacy_pii"
LAW_ENFORCEMENT = "law_enforcement"
TRADE_SECRETS = "trade_secrets"
DELIBERATIVE_PROCESS = "deliberative_process"
NONE = "none"
@dataclass(frozen=True, slots=True)
class ComplianceRule:
"""Immutable representation of a state statutory compliance rule."""
rule_id: str
state_code: str # e.g. "CA", "TX", "NY"
statute_reference: str # e.g. "Cal. Gov. Code Sec. 7922.535"
exemption_type: ExemptionCategory
response_calendar_days: int
response_business_days: int
use_business_days: bool # CA PRA: calendar; TX PIA / NY FOIL: business
effective_date: date
superseded_date: date | None = None
def is_active_on(self, on: date) -> bool:
"""A rule applies only within its effective window."""
if on < self.effective_date:
return False
return self.superseded_date is None or on < self.superseded_date
def generate_audit_hash(self) -> str:
"""Cryptographic fingerprint binding a verdict to the exact rule version."""
payload = f"{self.rule_id}:{self.statute_reference}:{self.effective_date.isoformat()}"
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
Expected behavior: two ComplianceRule instances built from the same statute and effective date produce identical generate_audit_hash() values, while any amendment that ships a new effective_date produces a different fingerprint — so an audit record can be matched back to exactly one rule version.
2. Compute statutory deadlines deterministically
Deadline arithmetic is where most compliance bugs live. Calendar-day statutes (California’s PRA determination window) and business-day statutes (Texas PIA, New York FOIL) must not share a code path, and business-day math must skip weekends and the jurisdiction’s statutory holidays. Isolate the calendar behind one function so the rule logic stays pure and the holiday source can be swapped.
def add_business_days(start: date, n: int, state_code: str) -> date:
"""Skip weekends and per-state statutory holidays.
In production, back this with workalendar's per-state calendar so that, e.g.,
Cesar Chavez Day (CA) or Emancipation Day (TX) toll the clock correctly.
"""
try:
from workalendar.registry import registry
cal = registry.get(f"US-{state_code}")() if registry.get(f"US-{state_code}") else None
except Exception: # pragma: no cover - calendar lookup is environment-specific
cal = None
current, remaining = start, n
while remaining > 0:
current += timedelta(days=1)
is_weekend = current.weekday() >= 5
is_holiday = bool(cal) and not cal.is_working_day(current)
if not is_weekend and not is_holiday:
remaining -= 1
return current
def compute_deadline(rule: ComplianceRule, received: date) -> date:
"""Deterministic statutory deadline aligned with the governing convention."""
if rule.use_business_days:
# TX Gov. Code Sec. 552.221 / NY Pub. Off. Law Sec. 89(3): business-day clock.
return add_business_days(received, rule.response_business_days, rule.state_code)
# Cal. Gov. Code Sec. 7922.535: 10 calendar days to determine, plus extensions.
return received + timedelta(days=rule.response_calendar_days)
Expected output: a California rule (use_business_days=False, response_calendar_days=10) evaluated against a request received 2026-06-01 returns 2026-06-11; a Texas rule (use_business_days=True, response_business_days=10) against the same date skips two weekends and returns 2026-06-15.
3. Evaluate the request and emit a structured verdict
The evaluation function selects the rule active on the receipt date, computes the deadline, derives the exemption posture, and returns a verdict object rather than a bare boolean — downstream redaction and fee logic need the structure. It raises rather than guessing when no rule covers the jurisdiction, because a silent default is itself a compliance failure.
@dataclass(frozen=True, slots=True)
class ComplianceVerdict:
request_id: str
rule_applied: str
exemption_flagged: ExemptionCategory
response_deadline: date
requires_redaction: bool
audit_hash: str
trace_id: str
def select_active_rule(rules: list[ComplianceRule], state_code: str, received: date) -> ComplianceRule:
"""Pick the single rule for this jurisdiction that was authoritative at receipt."""
candidates = [r for r in rules if r.state_code == state_code and r.is_active_on(received)]
if not candidates:
# Fail closed: never evaluate against an absent or wrong-era statute.
raise ValueError(f"No active compliance rule for {state_code} on {received.isoformat()}")
# Most recent effective_date wins if windows overlap during a transition.
return max(candidates, key=lambda r: r.effective_date)
def evaluate_request_compliance(
request_payload: dict,
rules: list[ComplianceRule],
trace_id: str,
) -> ComplianceVerdict:
"""Evaluate a normalized request against the statute in effect at receipt."""
received = date.fromisoformat(request_payload["received_date"])
rule = select_active_rule(rules, request_payload["state_code"], received)
deadline = compute_deadline(rule, received)
requires_redaction = rule.exemption_type != ExemptionCategory.NONE
verdict = ComplianceVerdict(
request_id=request_payload["request_id"],
rule_applied=rule.rule_id,
exemption_flagged=rule.exemption_type,
response_deadline=deadline,
requires_redaction=requires_redaction,
audit_hash=rule.generate_audit_hash(),
trace_id=trace_id,
)
logger.info(
"Compliance evaluated",
extra={"request_id": verdict.request_id, "rule_id": rule.rule_id},
)
return verdict
The exemption posture here is intentionally coarse — a single flag — because the per-code matching logic lives in the exemption mapping detailed in how to map state-specific FOIA exemptions to Python dictionaries. This engine decides which statute governs and when the clock expires; the mapping decides which clause applies to which field.
4. Write an immutable, hash-chained audit entry
Every verdict must be recorded with the exact rule version, the hash of the input, the verdict, and a timestamp, so any disclosure or denial can withstand review. Chaining each entry’s hash over the previous entry makes silent tampering detectable.
def log_compliance_audit(
verdict: ComplianceVerdict,
request_payload: dict,
previous_hash: str,
) -> str:
"""Append an immutable audit record and return its chaining hash.
NIST SP 800-53 AU-9 (protection of audit information) / AU-10 (non-repudiation):
the verdict_hash binds prior_hash + input + verdict so tampering breaks the chain.
"""
request_hash = hashlib.sha256(
json.dumps(request_payload, sort_keys=True).encode("utf-8")
).hexdigest()
verdict_hash = hashlib.sha256(
f"{previous_hash}:{verdict.request_id}:{verdict.audit_hash}:"
f"{verdict.response_deadline.isoformat()}".encode("utf-8")
).hexdigest()
entry = {
"timestamp": datetime.now(UTC).isoformat(),
"trace_id": verdict.trace_id,
"request_hash": request_hash,
"rule_id": verdict.rule_applied,
"exemption_applied": verdict.exemption_flagged.value,
"deadline": verdict.response_deadline.isoformat(),
"prior_hash": previous_hash,
"verdict_hash": verdict_hash,
}
logger.info(
"Compliance audit recorded",
extra={"request_id": verdict.request_id, "verdict_hash": verdict_hash},
)
# In production, append `entry` to write-once storage or an append-only ledger.
return verdict_hash
In production, route these entries to write-once storage or an append-only ledger and forward them to a SIEM; the returned verdict_hash becomes the previous_hash for the next entry, forming a tamper-evident chain.
Validation & Verification
A compliance engine is only trustworthy if its determinism and its fail-closed behavior are asserted on every change. The highest-value tests catch a deadline-convention drift or a rule-selection error before it ships.
import pytest
CA_RULE = ComplianceRule(
rule_id="CA-PRA-2024", state_code="CA",
statute_reference="Cal. Gov. Code Sec. 7922.535",
exemption_type=ExemptionCategory.PRIVACY_PII,
response_calendar_days=10, response_business_days=0,
use_business_days=False, effective_date=date(2024, 1, 1),
)
TX_RULE = ComplianceRule(
rule_id="TX-PIA-2024", state_code="TX",
statute_reference="Tex. Gov. Code Sec. 552.221",
exemption_type=ExemptionCategory.NONE,
response_calendar_days=0, response_business_days=10,
use_business_days=True, effective_date=date(2024, 1, 1),
)
REQ = {"request_id": "r-1", "state_code": "CA", "received_date": "2026-06-01"}
def test_evaluation_is_deterministic():
a = evaluate_request_compliance(dict(REQ), [CA_RULE], "t-1")
b = evaluate_request_compliance(dict(REQ), [CA_RULE], "t-2")
# Same request + same rule snapshot -> identical deadline and audit hash.
assert a.response_deadline == b.response_deadline
assert a.audit_hash == b.audit_hash
def test_calendar_vs_business_day_conventions_differ():
ca = evaluate_request_compliance(dict(REQ), [CA_RULE], "t")
tx = evaluate_request_compliance(dict(REQ, state_code="TX"), [TX_RULE], "t")
assert ca.response_deadline == date(2026, 6, 11) # 10 calendar days
assert tx.response_deadline >= date(2026, 6, 12) # 10 business days, skips weekends
def test_missing_rule_fails_closed():
with pytest.raises(ValueError):
evaluate_request_compliance(dict(REQ, state_code="ZZ"), [CA_RULE], "t")
def test_audit_chain_detects_tampering():
v = evaluate_request_compliance(dict(REQ), [CA_RULE], "t")
h1 = log_compliance_audit(v, dict(REQ), previous_hash="genesis")
h2 = log_compliance_audit(v, dict(REQ, received_date="2026-06-02"), previous_hash=h1)
assert h1 != h2 # any input change yields a different chaining hash
To assert the audit trail itself, capture log records with pytest’s caplog fixture and confirm that every evaluation emits exactly one Compliance evaluated record carrying a request_id and rule_id. Because both ComplianceRule and ComplianceVerdict are frozen=True, replaying the same request during recovery yields an equal verdict and never starts a second statutory clock — the idempotency guarantee an auditor expects to see exercised.
Troubleshooting & Edge Cases
-
Requests spanning a statutory amendment. A request arrives the week a state shortens its response window. Diagnosis:
select_active_rulereturns the new rule for an older request because effective-date filtering is wrong. Fix: always select on the receipt date viais_active_on, keep the superseded rule in the registry, and never mutate or delete an old rule — historical requests must re-evaluate against the statute that governed them. -
Tolling and clock-stop events ignored. A fee estimate is sent or a clarification is requested, which under the Texas PIA and many state acts pauses the clock, but the deadline keeps counting. Diagnosis: the deadline appears earlier than the agency’s records show. Fix: model tolling explicitly as freeze intervals subtracted from elapsed business days, rather than computing a single deadline at receipt and treating it as immutable.
-
Holiday calendar gaps. A business-day deadline lands on a state holiday because the calendar source lacks that jurisdiction’s observances (e.g., Cesar Chavez Day in California, Emancipation Day in Texas). Diagnosis: a deadline one day earlier than the agency calendar. Fix: back
add_business_dayswith the per-stateworkalendarcalendar and assert holiday coverage in tests; never hard-code only federal holidays. -
Duplicate submissions producing two clocks. A requester emails and then re-submits via the portal, generating two
request_idvalues and two deadlines for one logical request. Diagnosis: two audit entries with near-identicalrequest_hashvalues within a short window. Fix: deduplicate upstream in the taxonomy stage before this engine assigns a clock, and reconcile against a deterministic dedup key rather than evaluating each channel independently. -
Litigation-hold conflict with disposition. The engine flags records as releasable while they sit under an active legal hold. Diagnosis: a disclosure verdict for held material. Fix: treat hold status as a gate evaluated after this verdict but before any release or destruction action, deferring to the records retention scheduling engine, which refuses disposition while a hold flag is set.
Compliance Verification Checklist
Related
- Core Architecture & Compliance Mapping — the parent architecture this rule layer enforces
- FOIA Request Taxonomy Design — produces the classified record this engine evaluates
- Records Retention Scheduling — computes lawful disposition dates and gates holds against this verdict
- Security Boundary Configuration — enforces least-privilege separation at the evaluation hop
- How to map state-specific FOIA exemptions to Python dictionaries — the per-clause exemption matching behind the verdict’s exemption flag
- Automating records retention schedule updates with cron jobs — keeps the disposition schedules this engine cross-references current