Most writing about AI regulation describes laws. That does not help much when a product manager asks whether a new feature can ship in Germany and Colorado. To answer that, you need to know what the system does, who you are in relation to it, where it is offered, and which obligations attach to that combination on that date. In 2026 the last part matters more than ever, because several of the most-cited deadlines moved.
This article treats regulation as an input to an engineering system. It explains the handful of facts that decide which rules apply, shows how to keep a dated, versioned register of obligations, and builds a small applicability engine in Python that evaluates every system in your inventory against that register. It then uses this year's changes as a worked example of what happens when the law moves underneath you. The crosswalk between NIST AI RMF, ISO/IEC 42001 and the EU AI Act is in AI Safety Frameworks, in depth, and compute-threshold rules for model training are in the GPU regulatory landscape; this page cites them and spends its words on mechanics. Nothing here is legal advice: the engine tells you which questions to send to counsel, faster and with better facts attached.
Why regulation is an engineering problem
Three properties make AI law awkward to handle with a policy document and an annual review. First, applicability is combinatorial. The same model can be minimal-risk in a writing assistant and high-risk when it ranks job applicants, and the same company can be a provider for one product and a deployer for another. Second, obligations have effective dates, transition periods and grace periods, so the answer to a question depends on when you ask it. Third, the texts change: amendments, delegated acts, guidance, court orders and replacement bills arrive throughout the year.
Those are the properties of any moving configuration, and the same tools work: a source of truth, deterministic evaluation, reviewed diffs and evidence.
The facts that decide applicability
Across the major regimes, a small set of facts about each system decides most of the outcome. Collect them in the system inventory, with an owner and a last-reviewed date for each record.
| Fact | Why it matters | Example values |
|---|---|---|
| Role | Providers, deployers, importers and developers carry different duties | provider, deployer, developer |
| Markets | Laws attach to where a system is placed on the market or used, not where it is built | EU, US-CO, US-CA, CN |
| Use case | Risk tiers are defined by purpose: employment, credit, education, biometrics | hiring_screen, support_chat |
| Decision effect | Consequential decisions about people trigger notice and review duties | advisory, decisive, none |
| Output type | Synthetic media and chat interfaces carry disclosure and labelling duties | text, image, audio, video |
| Training compute | Frontier rules key on operations used in training | estimated operations, or unknown |
| Embedded in a regulated product | Product-safety regimes add conformity routes | medical device, machinery, none |
Two rules make the inventory trustworthy. Every fact may take the value unknown, and unknown is never treated as no. And facts are owned by the team that builds the system, not by the compliance office, because only they notice when a support chatbot quietly gains a tool that issues refunds or scores customers.
A dated obligation register
The register is a list of obligations, each with an identifier, its source instrument and article, a condition over system facts, a start date, an optional grace window, and the controls that satisfy it. Store it as reviewed data in a repository. Because dates and conditions are explicit, a moved deadline is one field change and every downstream answer updates.
The register below encodes a few real obligations as of 1 October 2026. Dates come from published reporting on each instrument and are listed so they can be checked; conditions are deliberately simplified.
from dataclasses import dataclass, field
from datetime import date
from typing import Callable
UNKNOWN = "unknown"
@dataclass
class System:
name: str
role: str # "provider" | "deployer" | "developer"
markets: set[str] # {"EU", "US-CO", "US-CA", "CN"}
use_case: str # "support_chat", "hiring_screen", ...
decision_effect: str # "none" | "advisory" | "decisive" | UNKNOWN
outputs: set[str] # {"text", "image", "audio", "video"}
chat_with_people: bool
training_ops: float | str # estimated operations, or UNKNOWN
@dataclass
class Obligation:
oid: str
source: str
applies: Callable[[System], bool | str] # True, False or UNKNOWN
start: date
controls: list[str] = field(default_factory=list)
note: str = ""
ANNEX_III = {"hiring_screen", "credit_scoring", "exam_proctoring", "biometric_id"}
def tri(*parts):
# Three-valued AND: any False wins, then any UNKNOWN, else True.
if any(x is False for x in parts):
return False
if any(x == UNKNOWN for x in parts):
return UNKNOWN
return True
def decisive(s):
return UNKNOWN if s.decision_effect == UNKNOWN else s.decision_effect in ("advisory", "decisive")
def frontier(s):
return UNKNOWN if s.training_ops == UNKNOWN else s.training_ops > 1e26
REGISTER = [
Obligation("EU-50-1", "EU AI Act Art. 50(1)",
lambda s: tri("EU" in s.markets, s.role == "provider", s.chat_with_people),
date(2026, 8, 2), ["ai_disclosure_banner"]),
Obligation("EU-50-2", "EU AI Act Art. 50(2)",
lambda s: tri("EU" in s.markets, s.role == "provider", bool(s.outputs - {"text"})),
date(2026, 8, 2), ["machine_readable_marking"],
note="grace to 2026-12-02 for systems already on the market"),
Obligation("EU-HR-III", "EU AI Act Annex III high-risk",
lambda s: tri("EU" in s.markets, s.use_case in ANNEX_III),
date(2027, 12, 2), ["risk_mgmt", "data_governance", "logging", "human_oversight"]),
Obligation("CO-189", "Colorado SB 26-189",
lambda s: tri("US-CO" in s.markets, decisive(s)),
date(2027, 1, 1), ["decision_notice", "adverse_outcome_explanation", "human_review"]),
Obligation("CA-53", "California SB 53",
lambda s: tri("US-CA" in s.markets, s.role == "developer", frontier(s)),
date(2026, 1, 1), ["frontier_framework", "incident_reporting"]),
Obligation("CN-LABEL", "CN AI-generated content labelling measures",
lambda s: tri("CN" in s.markets, s.role == "provider"),
date(2025, 9, 1), ["explicit_label", "implicit_label_metadata"]),
]
def evaluate(system: System, on: date):
rows = []
for ob in REGISTER:
hit = ob.applies(system)
if hit is False:
continue
state = "unknown-fact" if hit == UNKNOWN else ("in-force" if on >= ob.start else "upcoming")
rows.append((ob.oid, state, ob.start.isoformat(), ob.controls, ob.note))
return rowsThe three-valued logic is the point. A system whose decision effect is unknown does not silently drop out of the Colorado row; it produces an unknown-fact result that a release gate can block on, which forces the owning team to answer a question rather than letting the default answer it for them.
The 2026 changes, as a worked example
This year is a useful test of the design, because several frequently-cited dates moved. The EU's Digital Omnibus on AI, approved by the European Parliament on 16 June 2026 and in force since 27 July 2026 according to published legal commentary, pushed the high-risk obligations for Annex III systems (employment, credit, education, biometrics, critical infrastructure and similar) from 2 August 2026 to 2 December 2027, and those for AI embedded in Annex I regulated products from 2 August 2027 to 2 August 2028. It did not move the Article 50 transparency duties, which apply from 2 August 2026; commentary reports a grace period to 2 December 2026 for the Article 50(2) machine-readable marking duty on systems already on the market. It also softened the Article 4 AI literacy duty from ensuring a sufficient level to taking measures to support it. Law-firm summaries additionally report a new prohibition on nudification applications from 2 December 2026 and relief extended to small mid-cap companies; confirm both against the Official Journal text before encoding them.
In the United States, Colorado's SB 24-205, already delayed to 30 June 2026, was repealed and replaced by SB 26-189, signed on 14 May 2026 and effective on 1 January 2027. The replacement is narrower and notice-based: developers give deployers documentation on intended uses, training data categories, limitations and human review; deployers give consumers notice at the point of a consequential decision, a plain-language explanation of the system's role within 30 days of an adverse outcome, correction rights and access to human review. The Attorney General enforces it with a 60-day cure period. California's SB 53, signed on 29 September 2025, applies to developers training models with more than 1026 operations, with heavier duties for those above 500 million dollars in annual revenue, including a published frontier framework and reporting of critical safety incidents, with civil penalties of up to one million dollars per violation. China's measures on labelling AI-generated synthetic content have applied since 1 September 2025 and require visible (explicit) labels and embedded (implicit) metadata labels.
Applied to the register, these changes are three small diffs: one start date changes from 2026-08-02 to 2027-12-02, one obligation is replaced with a new identifier and conditions, and one note is added. Re-running the engine shows the effect immediately:
hr = System("cv-ranker", "deployer", {"EU", "US-CO"}, "hiring_screen",
decision_effect="advisory", outputs={"text"},
chat_with_people=False, training_ops=UNKNOWN)
for row in evaluate(hr, date(2026, 10, 1)):
print(row)
# ('EU-HR-III', 'upcoming', '2027-12-02', ['risk_mgmt', ...], '')
# ('CO-189', 'upcoming', '2027-01-01', ['decision_notice', ...], '')Before the changes, the same system would have shown the EU high-risk row in force since 2 August 2026, and a Colorado row from the repealed act. The delay is not a reason to stop work: the controls behind the EU row (logging, human oversight, data governance) are the ones Colorado's notice and review duties need, and they take a long time to build.
Note that the CA-53 row does not fire for this system even though training compute is unknown, because the role condition is false and false wins. That is the behaviour you want: unknowns only block when they could change the answer.
Mapping obligations to controls and evidence
An obligation is satisfied by controls, and a control is satisfied by evidence. Keep the mapping many-to-many: human_review satisfies Colorado's review right and contributes to EU human oversight, while logging feeds EU record-keeping, incident reporting under SB 53 and internal audit. Each control has an owner, a test and an evidence source, ideally produced by the pipeline rather than written by hand. The patterns for producing sampled, auditor-ready evidence are in LLM audit preparation and the logging substrate in LLM audit logging architecture.
Disclosure and labelling controls are the most code-shaped. A disclosure banner is a UI component with a test; machine-readable marking is a step in the media pipeline that writes provenance metadata and a check that verifies it survives your own transcoding. Notice and explanation controls for consequential decisions need a decision record that stores the inputs, the model version, the output and the human action, as described in the AI Bill of Rights article; without that record you cannot explain an adverse outcome 30 days later.
Running it: the change process
- Watch. Assign an owner per jurisdiction who follows the regulator's publications and two or three law-firm trackers; record which source each register field came from.
- Propose. Every register change is a pull request quoting the operative text, with old and new values and counsel's approval.
- Diff the impact. CI runs the engine for every system on today's date and each upcoming start date, before and after the change, and posts which systems gain or lose which obligations, and when.
- Ticket the gaps. Merging creates or updates tickets for each new obligation without a passing control, with a due date set ahead of the start date by your build lead time.
- Gate releases. Deployment checks the system's current facts against the register; unknown facts and in-force obligations with failing controls block.
- Re-review facts. Inventory records expire after a set period, and any change to tools, markets or decision paths forces a re-review.
Failure modes
- Encoding the press release. Provisional agreements, drafts and enacted texts differ. Encode the text that is in force, and keep proposals in a separate branch of the register.
- Unknown defaults to no. The most common silent failure. Three-valued evaluation and blocking on unknowns prevents it.
- Stopping work when a date slips. Teams that paused high-risk work in mid-2026 now face the same controls with less slack. Due dates should track build lead time, not only the legal date.
- Role confusion. Fine-tuning or substantially modifying a third-party model, or putting your name on it, can move you from deployer to provider. Treat role as a fact that changes with the system, not a company-wide constant.
- Over-fitting conditions. Encoding every legal nuance as code makes the register unreadable to counsel. Keep conditions coarse and route edge cases to human review.
Trade-offs
A register-and-engine approach costs a few engineer-weeks and steady legal review time, and it is overkill for one product in one market. Its value rises with systems times jurisdictions. A spreadsheet matrix works until the first amendment, when nobody can say which rows changed for which products. A middle path: start with the inventory and dated register as plain YAML, and add the engine and CI diff past five or so systems or two markets with active AI law.
What to do next
- Build the system inventory with the seven facts above, an owner per record and
unknownallowed everywhere. - Create the obligation register as reviewed data, with source citations and start dates; load the 2026 changes first and have counsel sign each row.
- Implement three-valued evaluation and run it for today and for each upcoming start date.
- Map obligations to controls and controls to automatically produced evidence; reuse controls across regimes.
- Add the CI impact diff on register changes and a release gate that blocks on unknown facts.
- Set ticket due dates from build lead time, and do not pause work because a deadline moved.