runward

RW™ · V0.22.0

Docs · Décisions · ADR-0032

ADR-0032.

track the current OSCAL release, and watch dated external facts out-of-band

Date: 2026-07-17 Status: accepted Deciders: Thibault Souris (maintainer) Method: decision-loop — a two-agent investigation (EU/global compliance landscape vs OSCAL; OSCAL versioning + dated-fact surveillance) surfaced that runward had silently fallen two OSCAL generations behind with no test reddening; a follow-up investigation the maintainer explicitly requested then proved the bump trivial (schema validation + real trestle ingestion), which flipped the initial "stay pinned" instinct. Cross-read against the zero-network determinism invariant (ADR-0001) and the versioned-port rule (ADR-0011, ADR-0022).

Context

runward pins facts that live outside the repository and move on their own clock:

  1. The OSCAL schema version. runward emitted an OSCAL component-definition pinned to 1.1.2 (Dec 2024). Since that pin, NIST shipped 1.1.3 (Nov 2025), 1.2.0 (Dec 2025, adds the Control Mapping model), 1.2.1 (Mar 2026) and 1.2.2 (Apr 2026). runward was two generations behind — and no test reddened, because every guard compares repo to doc, never repo to world. A drift guard checks that the string "1.1.2" is present, not that it is still current.

  2. Dated regulatory facts. The regime lenses carry dates that expire independently of the repo: the EU AI Act 2 December 2027 high-risk date, the 2 August 2026 Article 50 / GPAI milestone, FedRAMP OSCAL expectations. regimes/*.json froze them with no re-check date.

The strategic frame (same investigation): there is no European (or other) machine format to migrate to — EUCS/SecNumCloud are cloud-service certifications that do not target a local tool, the EU AI Act technical documentation (Annex IV) has no executable format, and OSCAL is the only mature machine format, with growing non-US uptake (ECSO OSCAL Task Force, CRA). So the question is not which format but how to keep the OSCAL pin and the dated wording honest, and current, over time.

The initial instinct was to pin at 1.1.2 deliberately and justify the lag. The maintainer instead asked to look closer before deciding. That follow-up produced decisive evidence:

  • runward's exact OSCAL output validates against the NIST 1.2.2 component-definition schema (verified with runward's own ajv harness), whether the declared oscal-version reads 1.1.2 or 1.2.2 — the model is unchanged.
  • IBM compliance-trestle 4.2.0 (the CI ingester) loads a 1.2.2-stamped pack without error (verified locally), despite nominally targeting 1.2.1 — oscal-version is a free string with no enum.
  • Schema diff 1.1.2 → 1.2.2: zero definitions removed, root component-definition properties identical; the additions (markup datatypes, control-selection, the orthogonal Control Mapping model) touch nothing runward emits. NIST's versioning policy declares 1.2.x backward-compatible for component-definition.
  • oscal-version does not feed the deterministic UUID seeds, so regenerating the golden is a single-line change (UUIDs stable).

The lag was therefore not protecting anything — it was a near-free gap with only a downside (optics: being "the OSCAL reference one cites" while trailing NIST by 18 months).

Decision

1. Track the current OSCAL release. Bump the pin to 1.2.2 now. Because the bump is proven trivial and risk-free (validates against 1.2.2, ingested by trestle 4.2.0, one-line golden), runward emits OSCAL 1.2.2 and vendors the 1.2.2 schema. A future bump remains a versioned-port decision (ADR-0011, ADR-0022) — re-vendor the schema, regenerate the golden, re-check the trestle proof, update the declared version and the docs — a traced human act, never automatic. Packs previously emitted as 1.1.2 stay valid (backward-compatible); no migration is owed.

2. Watch dated external facts out-of-band, never in the gate. A scheduled, non-blocking workflow (.github/workflows/watch-external-facts.yml) runs weekly, outside the deterministic gate, with network:

  • it compares the pinned OSCAL_VERSION against the latest usnistgov/OSCAL release, and
  • it compares each regimes/*.json reviewBy date against today, and on any drift it opens or refreshes a single tracking issue. It never bumps a pin and never fails a build. Each regime file carries an optional reviewBy (EU AI Act → 2026-08-02, right after the Article 50 milestone; the others → an annual 2027-01-01 checkpoint). This is the durable half of the ADR: the pin is current today, and the watch guarantees the next drift surfaces as a triageable issue rather than an unnoticed lag.

Alternatives discarded

  • Stay pinned at 1.1.2. The initial instinct, discarded once the bump was proven a one-line, fully-tested change: keeping an 18-month lag with no upside is the exact silent-staleness this ADR exists to end.
  • Bump only to 1.2.1 (matching trestle's internal constant exactly). Needlessly conservative: 1.2.2 was empirically ingested by trestle 4.2.0, so targeting 1.2.1 would leave runward one patch behind for no assurance gain.
  • A clock-driven or network test in the gate (redden after a date, or fetch the latest version and assert). Rejected: it puts wall-clock and network into the deterministic, zero-network critical path (ADR-0001) — the golden pins RUNWARD_NOW precisely to forbid this. A test that reddens on a Tuesday with no code change is exactly the non-determinism runward exists to ban.
  • Auto-bump OSCAL via Dependabot. Rejected: the vendored NIST schema is a copied JSON file, outside the dependency tree, and the pin is a decision (a pack must stay re-assemblable against the exact version its auditor saw). Dependabot cannot make that judgement.
  • Add/adopt an EU machine format. Rejected: none exists as an adopted standard; inventing one is the opposite of the audit-ready promise. Keep OSCAL + the regime lenses (regimes/*.json).

Consequences

  • Positive. runward is current with NIST, killing the only real objection to its OSCAL posture. A pinned external fact can no longer fall behind unnoticed: the gap becomes an issue a human triages, while the gate stays deterministic and offline. One surface (the tracking issue) materialises every stale external fact — OSCAL and legal dates alike.
  • Negative, accepted. A weekly scheduled job to maintain; the tracking issue needs human triage (by design — bumping is a decision). reviewBy is metadata on shipped regime files, but it never enters the OSCAL and does not alter the lens content, so it does not violate the "never edit a shipped mapping" rule (ADR-0022): it records when to re-check, not what the mapping says.

Reevaluation trigger (mandatory, dated)

Reopen if (a) NIST publishes an OSCAL release that is not backward-compatible for component-definitions, or that trestle refuses — then the bump stops being a one-line change and needs its own migration note; (b) the EU (via the ECSO OSCAL Task Force / a CRA or AI Act profile) publishes a recognised machine format for AI evidence — then re-evaluate supporting it; or (c) the watch workflow proves too noisy or too silent (false issues, or a known drift it missed).

Trigger set on: 2026-07-17 · Watched via: the watch external facts workflow itself, usnistgov/OSCAL releases, and the ECSO OSCAL Task Force.

References

  • ADR-0001 — the zero-LLM / zero-network gate this keeps determinism-safe.
  • ADR-0011, ADR-0022 — the versioned-port / versioned-data rules a bump follows.
  • ADR-0025, ADR-0031 — the OSCAL-as-citable-spec and regulated-adoption posture this protects.
  • The investigations (2026-07-17): EU/global landscape vs OSCAL; OSCAL versioning + dated-fact surveillance; the bump-safety proof (schema validation + trestle ingestion).
← Tous les ADR