AI Research Library

Regulation as Requirements Engineering

Rajesh Ranjan Mahapatra

A regulation becomes operational only when the institution preserves legal meaning, authorised applicability and effective time in a typed obligation before it writes controls, tests or closure evidence.

© 2026 Rajesh Ranjan Mahapatra. 31 min read

Regulation as Requirements Engineering

A regulation becomes operational only when the institution preserves legal meaning, authorised applicability and effective time in a typed obligation before it writes controls, tests or closure evidence.

TLDR

  1. A clause is not yet a requirement. Its operational meaning depends on source status, definitions, scope, exceptions, institutional interpretation and the date at which it applies.
  2. The decisive mechanism is an obligation compiler that keeps source text, authorised interpretation, control design and operating evidence separate while maintaining traceable links between them.
  3. Models can find candidate duties, cross-references and affected controls, but legal applicability and control adequacy remain decisions of named human authorities with recorded rationales.
  4. The approach fails when formal fields create false certainty, when the source inventory is incomplete, or when organisations confuse a passing technical predicate with the regulated outcome.
  5. The architecture decision changes from buying a regulatory summariser to building a versioned source registry, obligation ledger, control contracts, failure-injection tests and evidence-backed closure.

The sentence that should trigger nothing

A bank receives two documents from the same authority. One is a consultation. The other is the final rule published six months later. A paragraph appears word for word in both: a firm shall retain a specified record and make it available to the supervisor. A language model extracts the same actor, action and object from each document. A semantic matcher links both to the same retention control. The generated implementation tickets are also identical.

Only one ticket should exist. The consultation may inform planning, but it does not carry the same legal status as the final instrument. Even the final sentence may not yet bind this bank, product or date. A definition in another article may exclude the service. A transition provision may postpone application. A later correction may alter the referenced threshold. The control may already meet the duty, but only for one legal entity. The extraction can be linguistically perfect and operationally wrong.

This is the central problem of regulatory technology. Organisations often treat legal text as an unusually difficult backlog. They search it, summarise it and map phrases to policies or controls. That approach hides the most consequential transformations: who decided the provision applied, which interpretation was accepted, what operational property would satisfy it, who owns that property, and what evidence proves it held for the relevant population and time.

The answer is to treat regulation as requirements engineering, but with an important qualification. Software requirements engineering usually begins after the organisation has authority to choose a system behaviour. Regulatory requirements begin with an external normative source and preserve professional judgement that software cannot silently absorb. The system must compile obligations without pretending to compile law itself.

Part one
A clause is not a control

Legal instruments contain several kinds of sentence. Some impose obligations or prohibitions. Some grant powers or rights. Others define actors, describe purpose, set jurisdiction, create exceptions, establish reporting routes or explain how later provisions should be read. The words that look most executable are often dependent on the words that do not. A requirement extractor that searches for shall can miss an operative definition and promote a recital into a duty.

The practical distinction is between four representations with different owners. First, the authority publishes a source object. Second, an authorised institutional role records an obligation and its applicability. Third, control and engineering owners define how the obligation is realised in the organisation. Fourth, an evidence system records whether the realised mechanism operated as required. Collapsing these layers allows a model, mapper or ticketing workflow to inherit authority it does not possess.

Artifact-based regulatory requirements engineering similarly argues that legal and software perspectives need connected artefacts rather than one flattened requirement list.[9] The four-stage split used here is a narrower practitioner synthesis: it adds explicit authority gates and operating evidence because a trace link alone does not establish who accepted the meaning or whether the realised control worked.

Figure 1. Clause-to-control lineage preserves three changes of authority.Practitioner synthesis. The arrows are not automatic transformations. Each gate records who may accept the next representation and on what evidence.

The source object carries meaning outside the sentence

A reliable intake record includes the publisher, instrument identifier, jurisdiction, legal status, publication and effective dates, language, version relation, retrieval time and cryptographic digest. It preserves the original file, annexes, tables and footnotes. A correction is a new version, not an overwrite. A consolidated text is related to, but not interchangeable with, the individual amending instruments when historical reconstruction matters.

W3C PROV-O supplies a general vocabulary for exchanging provenance across entities, activities and agents.[12] It is useful plumbing for the lineage, but it does not supply legal modality, applicability or decision rights. Those domain semantics still belong in the obligation and authority records.

These details are not clerical decoration. They change whether a sentence may authorise work. A consultation, final regulation, supervisory statement, enforcement notice, speech and industry standard can contain similar language while carrying different force. A voluntary framework may become mandatory only because a contract, internal policy or regulator incorporates it. NIST describes AI RMF 1.0 as voluntary, so its presence in a source library does not itself create an applicable legal duty.[15] Source status is an input to applicability, not a label added after extraction.

Figure 2. Identical words can require four different treatments.Illustrative decision surface. Source lifecycle and effective time are varied while the sentence remains unchanged.

Thought experiment: the perfect parser

Imagine a parser that never misreads grammar. It identifies every modal verb, actor, action and object. Now place an exception in a definition two articles away: the duty applies to an “operator”, but that definition excludes a particular outsourced service. The parser still extracts the local sentence perfectly. It cannot determine the bank's operating model, contractual role or accepted legal interpretation from the clause alone.

Now remove the exception and make the rule apply to every operator. The parser becomes much more useful. We varied one causal feature, dependence on external scope, while keeping linguistic competence constant. The result shows why extraction quality and regulatory correctness are different constructs. A perfect sentence parser can support the first; it does not settle the second.

Published evidence A 2024 systematic mapping study screened 6,914 papers and retained 280 primary studies on regulatory requirements engineering. Only 13.6 per cent considered both software engineers and legal experts, and 20.7 per cent connected requirements engineering to other lifecycle areas. The gap is not merely better text extraction; it is coordination across artefacts, roles and lifecycle stages.[10]
The argument in one passChart · flow
flowchart TD
  A["The sentence that should<br/>trigger nothing"] --> B["On this page"]
  B --> C["Part one A clause is not a control"]
  C --> D["Part two The obligation compiler"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#5B5CF6,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through Regulation as Requirements Engineering, from The sentence that should trigger nothing to Sources. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part two
The obligation compiler

A compiler does not ask a processor to execute prose directly. It preserves meaning through intermediate representations, checks types and rejects programmes that cannot be safely lowered. The analogy is useful if its limit is explicit: legal interpretation is not a deterministic programming language. The institution still needs authorised judgement. The compiler's job is to make that judgement structured, attributable, time-bounded and testable.

The core intermediate representation is a typed obligation. One compact form is:

O = ⟨S, M, A, D, C, X, T, R, E, V⟩

Here S is the exact source span and source status; M is modality such as obligation, prohibition or permission; A is the regulated actor; D is the demanded action and object; C contains conditions and scope; X contains exceptions and defeaters; T is the valid and effective interval; R records decision rights and owners; E states the expected evidence; and V states how implementation can be verified.

LegalRuleML offers a richer standard vocabulary for deontic operators, temporality, jurisdiction, provenance, defeasibility and multiple semantic annotations.[13] The tuple above is deliberately smaller. It is an operational intermediate representation for one institution, not a claim to encode every legal reasoning construct.

No field should be silently inferred at closure. Unknown actor, unresolved exception, missing effective date or pending applicability is a first-class state. It blocks downstream authority while allowing investigation to continue. This is more valuable than a smooth summary because it shows exactly what the institution does not yet know.

Figure 3. The obligation is an intermediate representation, not a paraphrase.Practitioner synthesis. The anatomy makes omissions visible before a control or ticket inherits them.

Six compiler passes

The first pass verifies source identity. The second identifies candidate normative content and resolves structural dependencies such as definitions and cross-references. The third records an authorised applicability decision for a named entity, activity, product, jurisdiction and time. The fourth decomposes the accepted obligation into operational properties. The fifth binds those properties to controls and tests. The sixth binds test results and observed outcomes to closure.

Each pass can return more than pass or fail. Candidate obligations may be accepted, rejected, partially applicable, pending information, superseded or adopted as policy beyond the legal minimum. Control mapping may be proposed, approved, contested or stale. Evidence can pass, fail, remain unknown or conflict across sources. Unknown is a result, not an empty cell that becomes green in a dashboard.

Minimal worked example

Worked scenario, synthetic clause “Where an automated decision rejects an application, the operator shall retain the decision identifier, model version, rule version and reviewer outcome for 180 days, unless a legal hold requires longer retention.”

A weak translation creates one ticket: “retain rejection records for 180 days”. A compiled obligation records the operator role, the triggering condition of automated rejection, four mandatory evidence fields, the base retention interval and the legal-hold exception. It also records which entity and product are in scope, who approved that conclusion and from what date it applies.

The control contract can then state a population: all automated rejections in the daily case manifest. Its predicate checks that each case contains the required fields and that expiry respects any legal hold. Its evidence receipt records the manifest hash, test version, population count, passed, failed and unknown counts, and protected references to the underlying records. A ticket marked complete proves none of those properties. A reproducible receipt can.

The negative case is revealing. Suppose the test checks only that a record exists. Every case passes, yet the rule version is absent and four records cannot be read because of a permissions fault. The narrow predicate passed on the visible subset, but the obligation did not. The compiler must reject closure because population coverage and evidence fields are part of the control contract.

What models may do

Language models are useful in the candidate stages. They can classify likely requirement-bearing clauses, extract actors and actions, identify cross-references, explain a provision in plain language, retrieve related controls and propose requirement wording. Their outputs should carry model version, prompt or configuration, source spans and uncertainty. They should never overwrite the accepted obligation or approved mapping.

Recent results support this bounded role. Reg2Req evaluated full clause sets from the GDPR and EU AI Act. Its requirement-bearing classifier achieved macro F1 scores of 0.815 and 0.779. Human reviewers rated completeness highly, but requirement correctness was the weakest dimension, especially where exceptions, conditions and unresolved cross-references mattered.[8] Another legal traceability study reported an engineered prompt with roughly 84 per cent recall but about 30 per cent precision, useful for recommendation yet unsuitable for automatic acceptance.[11]

Those are promising research results, not a case for delegated interpretation. High recall reduces search effort. Low precision creates review work. More importantly, neither metric proves that the institution's entity, product, process and time facts were correct. The model can propose a trace link; the organisation must own the obligation.

Part three
Scope, time and authority

Regulatory applicability is a function, not a document tag. It depends on the instrument, legal entity, jurisdiction, regulated activity, product, customer or subject, thresholds, role in the value chain, transition arrangement and evaluation date. The answer may also change because the institution voluntarily applies a higher standard. The obligation ledger must separate “legally applicable” from “policy adopted” so that future challenge does not erase the difference.

A useful decision record contains the question, source spans, material facts, alternatives considered, accepted interpretation, approver, rationale, valid interval and review triggers. A prior decision is not a global answer. It becomes a candidate precedent only when jurisdiction, entity, activity, product and time are comparable.

Applicability is an argument, not a Boolean

A useful applicability record keeps the conclusion separate from the premises that support it. The conclusion may be applicable, not applicable, partly applicable, pending facts, contested, superseded or adopted as internal policy. Its premises identify the regulated role, entity, activity, product, customer class, location, thresholds, exclusions and relevant date. Every material fact needs provenance and a validity interval. A corporate registry record, product catalogue, contract, process map and legal opinion can disagree or change at different speeds. Reducing that structure to true or false destroys the path by which the decision can be challenged.

The decision should also preserve alternatives. An outsourced service may make the bank a deployer under one reading and a provider under another. A threshold may be calculated per transaction, per customer or across related activity. Recording only the accepted answer makes later review look like inconsistency; recording the rejected alternatives shows which factual or interpretive premise would have to change. Disagreement is a governed state when its grounds, authority and consequences remain visible.

This representation creates a practical test. Vary one applicability fact while holding the source and control design constant. Change the legal entity, customer type, product purpose or effective date. The decision should remain stable when the fact is immaterial and change when it is decisive. If a supposed scope rule cannot survive these paired cases, it is a label rather than a reproducible argument. The test does not prove the legal conclusion. It proves that the institution can state and consistently apply the reasoning it authorised.

Thought experiment: the midnight deployment

Consider the same bank, same credit system, same model, same customers and same control environment at 23:59 and 00:01. Only the legal effective time changes. Before the relevant application date, a high-risk-system obligation may be an approved future requirement. After the date, the same missing control becomes a current compliance gap. A system that stores only “applicable: true” cannot reconstruct either state.

The current EU AI Act provides a concrete illustration. It entered into force on 1 August 2024 and became generally applicable on 2 August 2026. Following the 2026 AI Omnibus, high-risk rules for Annex III systems apply from 2 December 2027, while Annex I product-related systems have a later date of 2 August 2028.[2][3] These dates are not website metadata. They alter which obligation version authorises which control decision.

EU AI Act application timeline A horizontal timeline from August 2024 to August 2028 showing entry into force, early obligations, general application, Annex III high-risk rules and Annex I product rules. 1 Aug 2024entry into force 2 Feb 2025prohibitions, literacy 2 Aug 2026general application 2 Dec 2027Annex III high-risk 2 Aug 2028Annex I high-risk 2026 AI Omnibus extended the high-risk dates
Figure 4. Effective time is part of the requirement.Sourced timeline from the European Commission and Regulation (EU) 2026/1744. Dates are legal milestones, not measured system results.[2][3]

Use bitemporal records

Two clocks matter. Valid time states when the obligation or interpretation applies in the world. Transaction time states when the organisation learned, recorded or changed it. Suppose a correction published on 10 March states that a provision had legal effect from 1 January. The institution needs to know both the corrected historical position and that its operational record changed only in March.

Bitemporal storage prevents a later interpretation from silently rewriting the past. It allows a reviewer to reconstruct what source, decision and control evidence were available when an action occurred. It also supports impact analysis: a changed provision can identify every applicability decision, control mapping, test and closure receipt whose validity may need challenge.

Authority should narrow, not diffuse

Legal or compliance roles accept interpretation and applicability. Business owners accept operational impact and residual exposure. Control owners define and maintain the mechanism. Engineering implements systems and tests. Independent assurance challenges design and evidence. The platform team owns source ingestion, workflow, lineage and access control. A model may assist every role, but it should not merge them into one synthetic “compliance agent”.

This separation also clarifies abstention. A missing entity fact routes to the entity owner. An unresolved definition routes to legal interpretation. A control predicate that cannot observe the full population routes to control design and platform owners. An ambiguous deployment result remains unknown until readback succeeds. Escalation becomes a typed dependency, not a generic request for human review.

Practitioner synthesis An obligation should be accepted only by a role authorised for that legal or policy object. A process owner may challenge operational impact but should not approve legal applicability. A compliance reviewer may accept the obligation but should not attest that code was deployed without an effect receipt.

Part four
Controls, evidence and closure

After applicability, the problem changes. The organisation must choose a mechanism that makes the demanded property true. This is control design, not text classification. A control contract states the risk or outcome addressed, objective, authoritative population, predicate or procedure, cadence, tolerance, exceptions, owner, escalation, evidence and retention. Where the property remains partly semantic, the contract specifies the human judgement and sampling design rather than disguising it as code.

Direct sentence-to-control matching is brittle because the relation is many to many. One obligation may require policy, data, workflow, training, system and oversight controls. One platform control, such as immutable decision logging, may support several obligations. Reuse is valuable only when each edge states which part of the obligation the control covers, for which population and during which interval.

Figure 5. Control coverage is a graph, not a phrase match.Illustrative topology. Edges require approved coverage statements; their number is not a measure of compliance.

Control coverage needs a falsifiable property

“Policy updated” is a deliverable, not a control result. “Training completed” may show attendance, not competent behaviour. “Logs enabled” may show configuration, not that every in-scope decision produced a complete, retrievable record. A useful test can fail because it names the population, observation and threshold. It also preserves unknowns caused by permissions, outages or incompatible source states.

The distinction is visible in current supervisory material. DORA, applicable from 17 January 2025, establishes an EU-wide framework for ICT risk management and resilience in financial entities.[4][5] A cloud scanner can contribute evidence, but a passing configuration rule does not establish governance, resilience testing, incident handling or third-party risk across the regulated scope. The control contract must state which obligation and population the technical predicate supports.

For UK banks, the current version of PRA SS1/23 sets supervisory expectations for model risk management, including models using AI or machine learning; the April 2026 amendment clarified existing policy rather than replacing it.[6] Some model inventory, validation and governance controls may be reusable, but SS1/23, DORA and the AI Act remain distinct source objects with different scopes and authorities.

The FCA's 2026 outcomes-monitoring guidance makes a parallel point for the Consumer Duty: collecting data, listing metrics or reporting management information does not by itself show that customers receive good outcomes.[7] Outcome regulation resists reduction to a single configuration test. It needs linked evidence across journeys, customer groups, interventions and accountable decisions.

Control fieldQuestion it must answerFailure if omitted
ObjectiveWhich obligation property or regulated outcome does this mechanism support?A familiar activity is labelled a control without causal relevance.
PopulationWhich entities, cases, systems, regions and time windows are in scope?A clean subset is reported as complete coverage.
Predicate or procedureWhat observation would pass, fail or remain unknown?Reviewers cannot reproduce the conclusion.
Tolerance and exceptionWhich deviations are authorised, by whom and until when?Every exception looks like failure, or every failure becomes an exception.
EvidenceWhat proves the population, test version, result and observed effect?A screenshot or ticket substitutes for operating state.
Owner and escalationWho maintains the control and who decides ambiguous cases?Unknowns age without an accountable decision.

Evidence should strengthen along the causal chain

An organisation often closes a change with the weakest artefact available: a project ticket, meeting note or policy document. Those artefacts prove that work was discussed or declared complete. They do not prove the operating property. Stronger evidence binds the approved obligation and control version to the evaluated population, observation time, test code, exceptions and result. The strongest practical evidence adds an observed business or customer outcome where the regulation is outcome-based.

A test proves only its declared claim

Control evidence should distinguish design, implementation, operation and outcome. A design review can show that a proposed logging control covers the fields named in the obligation. A deployment receipt can show that the approved configuration reached the intended environments. A population test can show that every in-scope event produced a complete record during an interval. A retrieval test can show that authorised reviewers could obtain those records within the required conditions. None of these observations alone proves that the wider regulated outcome was achieved.

The evidence receipt therefore needs more than a pass flag. It identifies the obligation and control versions, evaluator, test code or procedure, authoritative population query, observation interval, denominator, passed, failed and unknown counts, authorised exclusions, source-system versions and protected links to supporting artefacts. It also records the claim level supported: design adequacy, implementation completion, operating effectiveness or observed outcome. A stronger sounding conclusion must require stronger and more causally relevant evidence.

This discipline prevents metric laundering. A team cannot turn ninety-eight per cent log completeness into “compliant” without an approved tolerance, an account of the missing two per cent and evidence that the log is the property the obligation actually requires. Nor can it hide an inaccessible population by removing it from the denominator. Unknown and conflicting evidence remain visible until an authorised role accepts a bounded exception or the underlying state is repaired.

Figure 6. Closure evidence should follow the claimed causal distance.Illustrative ladder. Higher steps do not eliminate judgement; they better support stronger claims.

Production-shaped example: creditworthiness AI

Worked scenario A fictional EU retail bank plans to use an AI system to help evaluate the creditworthiness of natural persons. The scenario is synthetic. It illustrates an operating model and does not state a legal conclusion for any real firm or system.

The source layer contains the current consolidated EU AI Act, the 2026 AI Omnibus amendment, relevant Commission guidance when issued, and other applicable instruments. The legal team first determines the bank's role, whether the use falls within the high-risk category, which entity deploys it, and the applicable date. Creditworthiness assessment appears in Annex III, while deployer duties and a fundamental-rights impact assessment are addressed in Articles 26 and 27.[1] The obligation ledger records the accepted reading and any unresolved dependencies rather than generating a generic “AI Act compliant” label.

Suppose the authorised interpretation yields five implementation properties: use within approved instructions; assigned and competent human oversight; monitoring with incident and risk escalation; retained, retrievable logs under the bank's control; and an approved impact assessment before deployment. These are still not controls. Each must be bound to the bank's process and system boundary.

The deployment gate verifies that the approved model, instructions, intended purpose, data interfaces and oversight design match the released configuration. The oversight control checks named roles, authority, training evidence and whether reviewers can understand, challenge and override the system in the actual workflow. The logging control checks every in-scope decision for required fields, versions and retention treatment. The monitoring control combines technical drift, subgroup outcomes, complaints, overrides and material incidents. The impact-assessment workflow records approval, scope and review triggers.

Now add DORA and the Consumer Duty. The same logging and monitoring platform may support ICT resilience, incident reconstruction and customer-outcome analysis, but the obligations remain distinct. A resilient system can still produce poor customer outcomes. A fair aggregate outcome can still conceal an unrecoverable technology dependency. The graph allows reuse without pretending that one passing dashboard proves every duty.

Closure occurs only when each accepted obligation has an approved implementation mapping, the required controls are effective for the right population, test receipts show no unresolved failures or unknowns beyond authorised tolerance, and an accountable owner accepts the residual exposure. A later source correction, model change, product expansion or evidence defect can reopen the chain. Compliance is maintained by invalidation and re-evaluation, not by a permanent green status.

Figure 7. The lifecycle includes reopening as a normal state.Practitioner synthesis. Reopening preserves the earlier decision and evidence while creating a new validity interval.
How the claim changes under pressureChart · state
stateDiagram-v2
  [*] --> Claim
  state "The source object carries<br/>meaning outside the sentence" as Claim
  state "Thought experiment, the<br/>perfect parser" as Test
  state "Six compiler passes" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with The source object carries meaning… and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Part five
Evaluation and the operating boundary

A regulatory system should be evaluated as a chain, not as one question-answering model. Source tests verify document identity, version relations and missing expected feeds. Structural tests cover renumbering, moved clauses, tables, footnotes and corrected publications. Extraction tests measure span accuracy, modality, actors, exceptions and cross-references. Applicability support tests whether the right facts and authorities were assembled, not whether a model agreed with one reviewer.

Mapping tests ask whether affected policies, processes, systems and controls were retrieved without unbounded false expansion. Control tests verify population, predicate, tolerance and evidence. Closure tests inspect whether an accepted obligation has effective controls and reproducible receipts. Historical replay reconstructs the source, interpretation, control and evidence that existed at the decision time.

Failure injection is the primary evaluation. Create paired cases that differ in one causal feature: consultation versus final rule, old versus corrected clause, included versus excluded entity, before versus after effective date, complete versus missing cross-reference, approved versus expired exception, full versus partial population, known versus unknown external effect. The system should change state only when the injected feature should matter.

LayerPositive caseNegative controlRelease evidence
SourceFinal instrument with verified digestConsultation with identical clauseCorrect lifecycle treatment and no binding task from draft
DependencyException and definition resolvedSame clause with hidden cross-referenceBlocked acceptance until dependency is handled
ApplicabilityIn-scope entity and productExcluded legal entityDifferent authorised decisions with cited facts
TimeAfter effective dateOne minute before effectivenessFuture requirement does not appear as current breach
ControlFull population and predicateClean subset onlyUnknown or coverage failure, never silent pass
EvidenceComplete receipt and readbackTicket done, effect unobservedClosure blocked until operating state is known

Executable artefact: a small obligation compiler

The accompanying Python programme does not interpret law. It validates an already-authorised obligation against typed source status, applicability, effective time, control coverage and evidence receipts. The positive synthetic case closes. The negative case uses a consultation source, pending applicability, an unhandled exception, four unknown records and a missing evidence field. It must remain blocked.

Run it with python obligation_compiler.py. It uses only the Python standard library. The report digest is a compact integrity identifier for the supplied obligation, controls and receipts, not a digital signature.


positive case: VERIFIED
{
  "state": "VERIFIED",
  "obligation_id": "OBL-42-2",
  "as_of": "2027-12-05",
  "covered_by": [
    "CTRL-RET-07"
  ],
  "issues": [],
  "digest": "65387309c957f88f"
}

negative case: BLOCKED
{
  "state": "BLOCKED",
  "obligation_id": "OBL-42-2",
  "as_of": "2027-12-05",
  "covered_by": [
    "CTRL-RET-07"
  ],
  "issues": [
    "source is not operative: consultation",
    "applicability decision is not approved",
    "applicability state cannot be closed: pending",
    "CTRL-RET-07: unhandled exceptions: legal_hold_extends_retention",
    "CTRL-RET-07: 4 item(s) have unknown outcome",
    "CTRL-RET-07: missing evidence fields: rule_version"
  ],
  "digest": "3d7db4a08292621f"
}
Open the complete runnable Python artefact
#!/usr/bin/env python3
"""A small, dependency-free validator for regulation-to-control lineage.

The examples are synthetic. The program does not interpret law. It checks that an
already-authorised obligation has enough typed information, control coverage and
operating evidence to support a closure decision.
"""

from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import date
import hashlib
import json
from typing import Iterable


ALLOWED_MODALITIES = {"obligation", "prohibition", "permission"}
OPERATIVE_SOURCE_STATES = {"final", "in_force", "binding_guidance"}
CLOSABLE_APPLICABILITY_STATES = {"applicable", "partly_applicable", "policy_adopted"}


@dataclass(frozen=True)
class SourceRef:
    authority: str
    identifier: str
    version: str
    status: str
    source_span: str
    retrieved_on: date
    checksum: str


@dataclass(frozen=True)
class ApplicabilityDecision:
    state: str
    jurisdiction: str
    entity: str
    product: str
    valid_from: date
    valid_to: date | None
    approver: str
    approved: bool
    rationale: str


@dataclass(frozen=True)
class Obligation:
    obligation_id: str
    modality: str
    actor: str
    action: str
    object: str
    conditions: tuple[str, ...]
    exceptions: tuple[str, ...]
    effective_from: date
    effective_to: date | None
    owner: str
    evidence_expectations: tuple[str, ...]
    source: SourceRef
    applicability: ApplicabilityDecision


@dataclass(frozen=True)
class ControlContract:
    control_id: str
    obligation_ids: tuple[str, ...]
    objective: str
    population: str
    predicate: str
    cadence: str
    owner: str
    handled_conditions: tuple[str, ...]
    handled_exceptions: tuple[str, ...]
    evidence_fields: tuple[str, ...]
    effective_from: date
    effective_to: date | None


@dataclass(frozen=True)
class EvidenceReceipt:
    control_id: str
    observed_on: date
    test_version: str
    population_count: int
    tested_count: int
    passed_count: int
    failed_count: int
    unknown_count: int
    fields_present: tuple[str, ...]


@dataclass(frozen=True)
class CompileReport:
    state: str
    obligation_id: str
    as_of: date
    covered_by: tuple[str, ...]
    issues: tuple[str, ...]
    digest: str


def active_on(start: date, end: date | None, when: date) -> bool:
    return start <= when and (end is None or when <= end)


def digest_payload(*objects: object) -> str:
    payload = json.dumps(
        [asdict(obj) for obj in objects],
        default=str,
        sort_keys=True,
        separators=(",", ":"),
    ).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()[:16]


def compile_obligation(
    obligation: Obligation,
    controls: Iterable[ControlContract],
    receipts: Iterable[EvidenceReceipt],
    *,
    as_of: date,
) -> CompileReport:
    """Validate lineage and evidence. It deliberately does not decide legal meaning."""

    issues: list[str] = []
    controls = tuple(controls)
    receipt_by_control = {receipt.control_id: receipt for receipt in receipts}

    if obligation.modality not in ALLOWED_MODALITIES:
        issues.append(f"unsupported modality: {obligation.modality}")
    if obligation.source.status not in OPERATIVE_SOURCE_STATES:
        issues.append(f"source is not operative: {obligation.source.status}")
    if not obligation.source.source_span.strip():
        issues.append("source span is missing")
    if not obligation.source.checksum.strip():
        issues.append("source checksum is missing")

    app = obligation.applicability
    if not app.approved:
        issues.append("applicability decision is not approved")
    if app.state not in CLOSABLE_APPLICABILITY_STATES:
        issues.append(f"applicability state cannot be closed: {app.state}")
    if not active_on(app.valid_from, app.valid_to, as_of):
        issues.append("applicability decision is not valid on the evaluation date")
    if not active_on(obligation.effective_from, obligation.effective_to, as_of):
        issues.append("obligation is not effective on the evaluation date")

    covering = tuple(
        control for control in controls if obligation.obligation_id in control.obligation_ids
    )
    if not covering:
        issues.append("no control contract covers the obligation")

    for control in covering:
        prefix = f"{control.control_id}: "
        if not active_on(control.effective_from, control.effective_to, as_of):
            issues.append(prefix + "control is not effective on the evaluation date")
        if not control.objective.strip() or not control.predicate.strip():
            issues.append(prefix + "objective or test predicate is missing")
        if not control.population.strip():
            issues.append(prefix + "test population is missing")
        if not control.owner.strip():
            issues.append(prefix + "control owner is missing")

        missing_conditions = set(obligation.conditions) - set(control.handled_conditions)
        missing_exceptions = set(obligation.exceptions) - set(control.handled_exceptions)
        if missing_conditions:
            issues.append(prefix + "unhandled conditions: " + ", ".join(sorted(missing_conditions)))
        if missing_exceptions:
            issues.append(prefix + "unhandled exceptions: " + ", ".join(sorted(missing_exceptions)))

        receipt = receipt_by_control.get(control.control_id)
        if receipt is None:
            issues.append(prefix + "evidence receipt is missing")
            continue
        if receipt.observed_on > as_of:
            issues.append(prefix + "evidence receipt is from the future")
        if receipt.tested_count + receipt.unknown_count != receipt.population_count:
            issues.append(prefix + "tested plus unknown does not equal the population")
        if receipt.passed_count + receipt.failed_count != receipt.tested_count:
            issues.append(prefix + "pass plus fail does not equal tested")
        if receipt.failed_count:
            issues.append(prefix + f"{receipt.failed_count} tested item(s) failed")
        if receipt.unknown_count:
            issues.append(prefix + f"{receipt.unknown_count} item(s) have unknown outcome")

        missing_evidence = set(control.evidence_fields) - set(receipt.fields_present)
        if missing_evidence:
            issues.append(prefix + "missing evidence fields: " + ", ".join(sorted(missing_evidence)))

    state = "VERIFIED" if not issues else "BLOCKED"
    return CompileReport(
        state=state,
        obligation_id=obligation.obligation_id,
        as_of=as_of,
        covered_by=tuple(control.control_id for control in covering),
        issues=tuple(issues),
        digest=digest_payload(obligation, *covering, *receipt_by_control.values()),
    )


def print_report(name: str, report: CompileReport) -> None:
    print(f"\n{name}: {report.state}")
    print(json.dumps(asdict(report), default=str, indent=2))


def examples() -> tuple[CompileReport, CompileReport]:
    source = SourceRef(
        authority="Synthetic Authority",
        identifier="SYN-42",
        version="1.0",
        status="in_force",
        source_span="section 4(2)",
        retrieved_on=date(2027, 11, 1),
        checksum="sha256:synthetic-final-v1",
    )
    applicable = ApplicabilityDecision(
        state="applicable",
        jurisdiction="Exampleland",
        entity="Example Bank plc",
        product="automated lending",
        valid_from=date(2027, 12, 1),
        valid_to=None,
        approver="Head of Regulatory Interpretation",
        approved=True,
        rationale="The entity and product fall within the stated scope.",
    )
    obligation = Obligation(
        obligation_id="OBL-42-2",
        modality="obligation",
        actor="operator",
        action="retain",
        object="decision record",
        conditions=("automated_rejection",),
        exceptions=("legal_hold_extends_retention",),
        effective_from=date(2027, 12, 1),
        effective_to=None,
        owner="Head of Lending Operations",
        evidence_expectations=("decision_id", "model_version", "rule_version", "review_outcome"),
        source=source,
        applicability=applicable,
    )
    control = ControlContract(
        control_id="CTRL-RET-07",
        obligation_ids=("OBL-42-2",),
        objective="Retain a complete, replayable record for every automated rejection.",
        population="all automated lending rejections in the daily case manifest",
        predicate="required fields exist and retention expiry respects any legal hold",
        cadence="daily",
        owner="Lending Controls",
        handled_conditions=("automated_rejection",),
        handled_exceptions=("legal_hold_extends_retention",),
        evidence_fields=("decision_id", "model_version", "rule_version", "review_outcome"),
        effective_from=date(2027, 12, 1),
        effective_to=None,
    )
    good_receipt = EvidenceReceipt(
        control_id="CTRL-RET-07",
        observed_on=date(2027, 12, 5),
        test_version="retention-test@3.1.0",
        population_count=100,
        tested_count=100,
        passed_count=100,
        failed_count=0,
        unknown_count=0,
        fields_present=("decision_id", "model_version", "rule_version", "review_outcome"),
    )

    bad_source = SourceRef(
        authority="Synthetic Authority",
        identifier="SYN-42-draft",
        version="0.9",
        status="consultation",
        source_span="section 4(2)",
        retrieved_on=date(2027, 10, 1),
        checksum="sha256:synthetic-draft-v09",
    )
    pending = ApplicabilityDecision(
        state="pending",
        jurisdiction="Exampleland",
        entity="Example Bank plc",
        product="automated lending",
        valid_from=date(2027, 12, 1),
        valid_to=None,
        approver="unassigned",
        approved=False,
        rationale="Scope exception has not been resolved.",
    )
    bad_obligation = Obligation(**{
        **asdict(obligation),
        "source": bad_source,
        "applicability": pending,
    })
    bad_control = ControlContract(**{
        **asdict(control),
        "handled_exceptions": (),
    })
    bad_receipt = EvidenceReceipt(
        control_id="CTRL-RET-07",
        observed_on=date(2027, 12, 5),
        test_version="retention-test@3.1.0",
        population_count=100,
        tested_count=96,
        passed_count=96,
        failed_count=0,
        unknown_count=4,
        fields_present=("decision_id", "model_version", "review_outcome"),
    )

    when = date(2027, 12, 5)
    return (
        compile_obligation(obligation, (control,), (good_receipt,), as_of=when),
        compile_obligation(bad_obligation, (bad_control,), (bad_receipt,), as_of=when),
    )


if __name__ == "__main__":
    positive, negative = examples()
    print_report("positive case", positive)
    print_report("negative case", negative)

The failure surface

The degree of safe automation depends on two dimensions. The first is interpretive ambiguity: whether actor, action, scope, exception and test property are explicit. The second is consequence: whether a wrong transformation is cheap and reversible or materially affects rights, obligations, customers or supervisory exposure. Automation may execute deterministic checks in the low-ambiguity region. It should shift to proposal, review and abstention as either dimension rises.

Automation boundary across interpretive ambiguity and consequence A square field with ambiguity increasing left to right and consequence increasing bottom to top. Zones move from automated checks to reviewed mapping, proposal only and stop for authoritative human decision. Automate exact checks Review mappings Proposal only Authoritative decision Interpretive ambiguity → Consequence → authority boundary
Figure 8. Authority narrows as ambiguity or consequence increases.Illustrative failure surface. The dashed boundary should be calibrated by domain evidence and decision rights, not copied as a universal threshold.

A serious negative control

The preferred architecture can be excessive. Consider a local rule written in a formally constrained language: every payment above a fixed threshold requires two distinct approvals, the actor and threshold are explicit, no exceptions or cross-references exist, the rule is already accepted as applicable, and the payment engine exposes an authoritative event log. A direct rules-as-code implementation with property tests may be sufficient. A large semantic graph could add cost without improving the decision.

This negative control identifies the boundary. The obligation compiler earns its keep when meaning, scope, time, ownership or evidence must survive change across several systems and professional roles. When a norm is already formal, local, stable and mechanically observable, use the smaller mechanism. OECD work on Rules as Code shows the value of machine-consumable rules, but official code does not eliminate the need to distinguish legal source, implementation and institutional context.[14]

Decision instrument: choose the next transformation

Figure 9. The next step follows semantic ambiguity and effect, not model confidence alone.Practical decision instrument. Model confidence is evidence about a model output, not authority to change an institutional obligation.

Before approving a regulatory-technology design, ask ten questions. Can every obligation open its exact source span? Is the source final, corrected or superseded? Who accepted applicability, for which entity, product and interval? Are conditions and exceptions explicit? Does each control state objective, population and falsifiable property? Can unknown coverage be distinguished from pass? Does closure rely on operating evidence rather than task status? Which changes invalidate the decision? Can the historical state be replayed? Can a model failure degrade to inventory and workflow without corrupting accepted judgements?

Operational metrics should follow the chain: expected-source coverage, verified intake latency, material-change recall, unresolved dependency age, applicability decision time, mapping acceptance and override, orphan obligations, control population coverage, unknown outcomes, evidence rejection, reopened closures and defects found after implementation. Counting generated summaries or extracted duties encourages volume, not correctness.

Open hypothesis The most useful next research benchmark is not “Can a model read a regulation?” It is a paired, longitudinal benchmark that measures whether source changes propagate to the correct obligations, controls, tests and evidence while preserving legitimate disagreement and historical validity.

Compile the duty, preserve the judgement

Regulation becomes operational through a chain of transformations. The authority publishes text. The institution accepts an interpretation for a defined scope and time. Control owners choose mechanisms. Engineering implements them. Assurance decides whether the evidence supports closure. A useful system connects those acts without pretending they are one act.

The architecture decision is therefore clear: build an obligation compiler, not an automatic legal oracle. Preserve authoritative sources and corrections. Use a typed obligation as the intermediate representation. Keep applicability and policy adoption distinct. Bind obligations to falsifiable control contracts. Produce evidence receipts over explicit populations. Reopen decisions when source, scope, system or evidence changes. Let models search, structure, explain and propose, while named authorities retain interpretation, acceptance and assurance.

The resulting system will not make regulation simple. It will make complexity inspectable. A reviewer can see which words came from the authority, which judgement came from the institution, which mechanism claims to satisfy it, and which observation supports that claim. That is the difference between circulating regulatory summaries and engineering a defensible response.

The evidence conversationChart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The sentence that should trigger nothing
  M->>E: The source object carries meaning outside the sentence
  E-->>M: compare rival explanations
  E->>D: Sources
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in Regulation as Requirements Engineering. Reading use: follow the relation, then test it against the surrounding evidence and limits.

Sources

  1. European Union. Regulation (EU) 2024/1689 laying down harmonised rules on artificial intelligence, consolidated text as amended on 27 July 2026. EUR-Lex consolidated text.
  2. European Commission. “AI Act”, application timeline and implementation overview. Shaping Europe’s digital future.
  3. European Union. Regulation (EU) 2026/1744 amending the AI Act as regards simplification of implementation. EUR-Lex official text.
  4. European Union. Regulation (EU) 2022/2554 on digital operational resilience for the financial sector. EUR-Lex official text.
  5. European Banking Authority. DORA application and register-of-information preparation resources. EBA DORA resources.
  6. Prudential Regulation Authority. SS1/23, Model risk management principles for banks, including the April 2026 clarification. Bank of England.
  7. Financial Conduct Authority. “Outcomes monitoring: good practice and areas for improvement”, 27 July 2026. FCA.
  8. Nair, P. P. M., and Anish, P. R. “From Regulation to Requirements: An Automated Requirement Derivation and Explanation Pipeline”, 2026. arXiv:2607.04448.
  9. Kosenkov, O., et al. “On Developing an Artifact-based Approach to Regulatory Requirements Engineering”, 2024. arXiv:2405.00415.
  10. Kosenkov, O., et al. “Systematic Mapping Study on Requirements Engineering for Regulatory Compliance of Software Systems”, 2024. arXiv:2411.01940.
  11. Etezadi, R., Abualhaija, S., Arora, C., and Briand, L. “Classifier or Prompt: A Case Study on Legal Requirements Traceability”, Empirical Software Engineering, 2026. Author preprint.
  12. W3C. PROV-O: The PROV Ontology, W3C Recommendation. Specification.
  13. OASIS Open. LegalRuleML Core Specification Version 1.0. Specification.
  14. OECD. Cracking the Code: Rulemaking for Humans and Machines, 2020. OECD publication.
  15. NIST. Artificial Intelligence Risk Management Framework 1.0 and Core resources. The framework is voluntary and should not be confused with an applicable legal obligation. NIST AI RMF Core.

The banking and creditworthiness examples are synthetic composites used to expose the mechanism. They are not legal advice, deployment claims or descriptions of a named institution.