ADR-0022.
Regime mappings as versioned data, selected by --regime-version
Date: 2026-07-16 Status: accepted Deciders: Thibault Souris (maintainer) Method: decision-loop — factual audit of the compliance layer (mappings hardcoded in TypeScript), cross-read against ADR-0015/0016 provenance guardrails, challenge on over-engineering, durable position
Context
The audit confirmed: the regime structure runward compliance renders — ISO/IEC 42001 clause references, the EU AI Act Annex IV point table, the NIST AI RMF crosswalk, each regime's "required from the operator" list — lives as string literals inside src/lib/compliance.ts. Evolving a mapping when a regulatory text moves (an ISO revision, an Annex IV amendment, an AI RMF update) is a code patch and a release, when it is in truth an edit to data about a dated external text. ADR-0015 already frames a regime as a dated lens; ADR-0016 already stamps the drafts as dated engineering framing. What is missing is the mechanical consequence: the dated part extracted into dated files, and a way for the operator to pin which version of the lens a pack was assembled with.
Decision
- Extract the regime structure into versioned data files, shipped with the package:
regimes/<regime>@<version>.json(e.g.iso-42001@2023.json,eu-ai-act@2024-1689.json,nist-ai-rmf@1.0.json). Each file carries the regime label, its version identifier, its dated notes (e.g. the Annex III applicability date), the clause/point mappings, and the operator-required list. The render functions consume the data; the narrative glue — stable prose that does not track the regulatory text — stays code. No template DSL. runward compliance <regime> --regime-version <v>selects the lens version; the default is the highest shipped version. The selected version is stamped into the draft header and the OSCAL props — a dated lens must say its date (anti-overclaim, ADR-0015/0016). Old versions stay shipped: a pack assembled last quarter must be re-assemblable against the lens its auditor saw.- A regulatory change becomes a data-file addition (a new
<regime>@<newversion>.json), reviewed like any mapping change, never a silent edit of an existing version. - The OSCAL component-definition stays regime-neutral (unchanged): the ASI grammar is the universal layer (ADR-0009); regimes are lenses over it.
- Ingestion proof posture. The shipped, offline proof remains schema validation against the vendored NIST OSCAL schema with negative controls. A real-ingest test enters CI only if a credible, maintained OSS OSCAL ingester runs offline after install; a flaky or abandoned dependency in the gate's test path is worse than an honest documented boundary. Either way, the manual ingest procedure (how to feed the pack to a GRC tool) is documented.
Alternatives discarded
- A full document-template DSL (every sentence in data). Over-engineering: the prose glue is stable product voice; only the regulatory mapping tracks an external text. Extracting the moving part is the whole point; extracting the stable part is ceremony.
- Mission-level regime files (operators editing the mapping per mission). A silently edited regulatory mapping with the package's name on it is a provenance leak (ADR-0016 explicitly guards pack provenance). The package versions the lens; the operator picks the version.
- Fetching regime updates remotely. Violates zero-network; versions ship with releases, deterministically.
Consequences
- Positive. A regulation moves → a data file is added; the pack says which lens version produced it; past packs stay reproducible. The compliance layer's honesty claim gains a mechanical backbone.
- Negative, accepted. A loader, a version-selection surface and a data schema to maintain; two sources of truth (code glue + data) instead of one file — accepted because they change on different clocks.
- On other boundaries.
compliance.tsgains a regime loader;cli.tsgains--regime-version;package.jsonshipsregimes/; the readiness drafts and OSCAL stamp the lens version; unit tests pin loader determinism and version selection.
Reevaluation trigger (mandatory, dated)
Reopen if a regime's structure stops fitting the shared data shape (a regime needing logic, not data — that is a code change by nature), or if shipped versions accumulate beyond usefulness (then define a retention rule, still never breaking a published pack's reproducibility).
Trigger set on: 2026-07-16 · Watched via: each regime-mapping release review.