Skip to content
CloudSecOps

guide

AI governance for engineers, not lawyers

Translate AI governance into systems you can build: inventory reconciled against runtime telemetry, deployment gates that block, human oversight you can measure, and evidence that survives review. With EU AI Act dates as amended in July 2026.

Setu Parimiupdated 9 August 202677 min read

Governance that is not expressed as a mechanism does not exist. Every governance noun has to reduce to three things you can point at: a system that holds the record, a gate that can block a change, and an artifact a reviewer can diff. This guide performs that translation for AI systems, and spends most of its length on the three hardest cases: inventory, human oversight, and model dependencies.

Scope, and how to read the labels

This is written for the engineer who gets handed "we need AI governance" by someone who then leaves the room. You have a git-based change process, CI, and either a service catalogue or the ability to create one. Your organisation consumes hosted models through APIs — Amazon Bedrock, Microsoft Foundry, Vertex, direct provider APIs — more often than it trains its own. You have no authority to create a committee, and you should not want one.

In scope: inventory, classification, ownership, data lineage, provider dependencies, intended and prohibited use, security testing, human oversight, change management, evaluation, monitoring, incident handling, exceptions, retirement, and the evidence trail that connects them.

Out of scope, deliberately:

  • Compliance procedure. No conformity assessment walkthrough, no notified-body process, no ISO/IEC 42001 certification roadmap.
  • Legal advice. Regulation appears only to fix dates and to show which mechanisms have a legal trigger. Nothing below is a legal opinion, and the sequencing of your obligations depends on facts about your company that this article does not know.
  • Framework exposition. NIST AI RMF, the ISO/IEC 42000 series and the EU AI Act appear as constraints on mechanism design. If a section reads like a summary of a framework rather than an instruction for building something, it has failed.
  • Prompt injection and agent security. Their own engineering problems with their own controls. Governance decides that a threat model exists and that its findings are tracked; it does not replace the threat model.

Because the most damaging thing an engineer can do with a governance document is mistake an internal preference for a legal requirement, every normative statement below carries one of four labels.

LabelMeaningHow to treat it
[Legal]A requirement of a named instrument, with date and articleVerify against the instrument and your own jurisdiction before relying on it
[Standard]A recommendation from NIST, ISO/IEC or OWASP, named explicitlyNot binding; useful as a checklist and as an argument in review
[CloudSecOps]An engineering recommendation from this consultancy, with reasoningDisagree freely; the reasoning is given so you can
[Your risk decision]A threshold or boundary your organisation must setA defensible default is offered and identified as a default

Where a mechanism has a legal floor and an engineering recommendation above it, both labels appear. Log retention is the standing example: [Legal] at least six months for deployers of high-risk systems under Article 26 of the EU AI Act; [CloudSecOps] thirteen months for the systems whose failure would actually hurt you; [Your risk decision] whether prompt and completion content is retained at all, which is a data protection question before it is a governance one.

One further reading instruction, because this guide is full of tables and tables are a persuasive format. Where a number carries [Legal], it comes from a named instrument and the citation is in the references. Every other number here is a starting value chosen for a stated reason, and the reason is the part worth copying. None of them are benchmarks. None are derived from a study or from measured engagement data. Where this guide has no defensible number to offer, it says so instead of supplying one.

The translation table

The rest of this guide is one table expanded. Each row takes a governance noun — the kind of word that appears in a policy PDF — and names the mechanism it has to become, the system that holds its record, the artifact it produces, and the shape of its failure.

Governance nounMechanismSystem of recordEvidence producedFailure signature
InventoryDeclared record in git, reconciled against runtime telemetryService catalogue, sourced from repoai-system YAML per system; reconciliation diffA production system in the invocation logs and not in the catalogue
AccountabilityA named owner, resolved to an on-call rotationCatalogue plus identity provider groupOwner field with a resolvable group; a page that reaches a humanOwner resolves to a disbanded team or someone who left
ClassificationDecision tree run in CI, output written back to the recordRepo, versioned with the codeTier field plus the recorded rationaleEvery system is the same tier, so the tier carries no information
Risk managementThreat model with tracked findings and a dated acceptance recordIssue tracker plus repoFindings with owners and states; acceptance signed by a named personFindings exist, none are closed, no acceptance is recorded
TransparencyDisclosure rendered from the record, not written by handApplication code, sourced from the recordRendered notice; DOM test in CIDisclosure copy and the record disagree about what the system does
Human oversightAn approval or override point that is instrumentedApplication database plus metrics pipelineApproval rate, time-to-decision, modification rate, per-approver spreadApproval rate is 1.00 and nobody noticed
Change managementA required CI check on the record and the model pinCI providerCheck run history; block-rate metricThe check exists and has never failed
EvaluationA frozen test set, executed on defined triggers, with thresholdsEvaluation runner plus evidence storeScored run artifacts tied to a commit and a model versionEvaluations run, no threshold is defined, so no result can fail
MonitoringNamed metrics with alert thresholds and a review cadenceObservability platformAlert definitions; alert history; review notes"We monitor the system" with no metric name attached
Vendor managementA model pin, a deprecation calendar, a notice-to-action pathRepo plus calendar with ownersCalendar entries with EOL dates and migration stateA 410 Gone in production is the first sign of a retirement
Incident responseAI-specific triage in the existing IR process, with reporting clocksIR platformTimeline, classification, reporting decision with timestampsAI incidents are triaged as generic outages and the clock is missed
ExceptionsAn expiring record with a compensating control and a named accepterRepo, enforced by the gate it waivesException YAML with a mandatory expiryExceptions never expire, so the gate is decorative
RetirementA decommission checklist with evidence outliving the systemCatalogue plus evidence storeRetirement record; disposition of keys, data and accessThe system is "gone" but the API key still works

Four of these translations are harder than they look, and they are the ones that get skipped.

Accountability is the easiest to fake. A record with owner: platform-team satisfies a reviewer and pages nobody. The test is mechanical: resolve the owner string to a group in your identity provider, resolve that group to an on-call schedule, and check the schedule has a human in it this week. [CloudSecOps] Make that resolution a CI check rather than a quarterly audit, because ownership decays continuously and audits are discrete.

Transparency fails through drift. A team writes "this response was generated by AI" into a component, changes the system six months later, and nothing forces the copy to change with it. Render the disclosure from the record that drives classification, so that changing the system's purpose changes the notice or breaks a test.

Risk management in the regulatory sense is not a document. Article 9 of the EU AI Act describes "a continuous iterative process planned and run throughout the entire lifecycle" with testing "against prior defined metrics and probabilistic thresholds", ending in a residual risk that is "judged to be acceptable" [Legal]. Read as an engineer: a threat model that gets re-run, tests with thresholds defined before the test, and an acceptance record with a name and a date on it. The acceptance record is the part teams omit, and it is the only part that establishes who decided.

Monitoring degrades into a sentence faster than any other row. The discipline that prevents it is refusing to write the word without naming a metric, a threshold, an alert destination, and a review cadence in the same breath.

What is actually in force, and for whom

This section demonstrates the guide's own thesis before making its argument, because the demonstration is free.

On the verification date, two sources describing the same law disagreed. The Commission's regulatory framework page, last updated four days earlier, states that the high-risk rules for Annex III use cases "have been extended to 2 December 2027". A widely cited third-party implementation timeline, fetched the same day, still shows the original schedule and carries a last-updated date of 1 August 2024. One of those is the regulator. The other is a governance artifact nobody re-verified for two years, which is the failure mode described in every section below.

[Legal] The Digital Omnibus on AI — Regulation (EU) 2026/1744 of 8 July 2026, amending Regulation (EU) 2024/1689 — entered into force on 27 July 2026 and moved the high-risk application dates. If your internal deck says high-risk obligations began on 2 August 2026, it is wrong. That date is real, but it governs general applicability and the Article 50 transparency obligations.

DateWhat appliesWho it bindsStatus on 2026-08-07
2 Feb 2025Prohibited practices (Art 5); AI literacy (Art 4)Providers and deployers in scopeIn force
2 Aug 2025Governance rules; GPAI model obligations; penalties frameworkGPAI model providers; Member StatesIn force
2 Aug 2026General applicability; Art 50 transparency obligations; AI Office and national authorities supervise and enforceBroadIn force
Dec 2026New Art 5 prohibitions on non-consensual intimate imagery and CSAM generation, added by Reg (EU) 2026/1744BroadNot yet
2 Dec 2027Annex III high-risk obligations — biometrics, critical infrastructure, education, employment, migration, asylum, border controlProviders and deployers of those systemsDeferred from 2 Aug 2026
2 Aug 2028Annex I high-risk obligations — AI as a safety component of regulated productsProviders of those productsDeferred from 2 Aug 2027

Three other Omnibus changes have engineering consequences. Annex VIII registration was simplified for systems a provider has assessed as non-high-risk under Article 6(3), while retaining the database requirement for market surveillance. SMEs, startups and small mid-caps may supply technical documentation elements in simplified form and notified bodies must accept it, which gives Article 17(2)'s proportionality clause a concrete expression and grounds the small-team plan later in this guide. And [Legal] the Commission's empowerment to adopt a binding implementing act for the Article 72 post-market monitoring plan template was removed and replaced by an obligation to publish guidance, including a voluntary template, by 2 September 2027. Recital 41 of the amending regulation frames the deletion as offering providers more flexibility rather than as withdrawing the template. Any plan that says "wait for the Commission's monitoring template" needs rewriting: the structure is yours to choose now, and the voluntary template is a later cross-check rather than a thing to block on.

The sequencing most US engineering teams have backwards

For a US-headquartered company with EU exposure, the near-term forcing function is not the EU.

[Legal] Colorado SB24-205, the Consumer Protections for Artificial Intelligence act, was signed on 17 May 2024 with an original effective date of 1 February 2026. SB25B-004, signed 28 August 2025 and itself effective 25 November 2025, moved that to 30 June 2026. SB 26-189, signed 14 May 2026, repealed and reenacted it before that date arrived, replacing the high-risk AI system regime with a narrower one keyed on automated decision-making technology that materially influences a consequential decision — education, employment, housing, financial services, insurance, health care, government benefits. It applies to decisions made on or after 1 January 2027.

What survives is transparency and process. Developers supply technical documentation to deployers. Deployers give notice before the interaction, and an explanation within 30 days of an adverse outcome. Consumers can request correction of inaccurate personal data and meaningful human review of an adverse decision. What was removed is the part most governance decks were sized against: the duty of reasonable care against algorithmic discrimination, the risk management programme, the impact assessments, and the notification to the Attorney General on discovering algorithmic discrimination. Attorney General rulemaking on disclosure content and sector-specific guidance is required by 1 January 2027 and had not been formally proposed on the verification date; pre-rulemaking input closed 13 July 2026. Enforcement is separately stayed: in xAI v. Weiser the court granted a joint motion on 27 April 2026 suspending enforcement pending a preliminary injunction ruling, and the stay reaches the legislation replacing SB24-205 as well as SB24-205 itself. [CloudSecOps] Build to the notice, correction and human-review duties on the 1 January 2027 date; treat the litigation as a reason to watch rather than a reason to wait.

[Legal] Texas HB 149, the Texas Responsible Artificial Intelligence Governance Act, took effect 1 January 2026 per the enrolled bill text. It is intent-based rather than disparate-impact-based: it prohibits intentional unlawful discrimination, manipulation toward self-harm or crime, biometric capture without consent, and CSAM generation. Those prohibitions bind private entities with a Texas nexus as well as government. The AI-interaction disclosure duty does not. Section 552.051(b) puts it on a governmental agency that makes an AI system available to interact with consumers, and the only private-sector disclosure duty in the enacted bill is the separate one at §552.051(f), covering AI used in health care services. The general private-sector consumer-notification and impact-assessment requirements that appeared in earlier drafts did not survive into the enrolled text. [CloudSecOps] Disclosing that a user is talking to a model is usually the right product decision anyway, but if your disclosure control is justified in a control narrative by TRAIGA and you are neither a governmental agency nor a health care provider, the justification is wrong even where the control is not.

The state clocks still run ahead of the EU one. Texas has been in force since 1 January 2026, and Colorado's replacement duties attach eleven months before the EU Annex III date. [CloudSecOps] If you are building a roadmap around 2 December 2027 and you have Colorado or Texas users, the roadmap is ordered wrong.

Three caveats, stated because leaving them out would repeat the stale tracker's error. The Colorado paragraph above is itself a correction: at an earlier check the state Attorney General's AI resources page returned 404 on two attempts and the 2026-session amendment could not be verified, which is precisely how a guide ends up describing a repealed regime as though it were in force. The page resolves now and documents SB 26-189. What remains genuinely unresolved is the rulemaking and the outcome of the xAI litigation. CEN-CENELEC harmonised standards, which give a presumption of conformity for high-risk systems and whose availability was a stated driver of the Omnibus delay, were not checked at all. And this covers three jurisdictions out of the many now legislating on AI. Verify all of it against primary sources before relying on the sequencing above.

Two things engineers can use directly rather than through a third party: the AI Act Service Desk run by the European AI Office, which hosts an AI Act Explorer, a Compliance Checker and guidance on high-risk classification; and the Official Journal text itself. Neither produces an authoritative determination for your system, and both beat a tracker that stopped updating in 2024.

The governance control plane

Governance needs the same architectural treatment as any other cross-cutting concern: name the authoritative system for each record type, name the write path, and name the mechanism that keeps it fresh. A record with no freshness mechanism is a snapshot, and every snapshot is wrong by the time someone reads it.

Figure 1. The governance control plane. Alt text: declared state lives in git as system records, exception records and policy; CI and the evaluation runner act as control points and write to an evidence store; runtime telemetry, provider invocation logs and billing feed a reconciliation job whose output flows back into the catalogue and raises tickets. The reconciliation edge from observed state back to declared state is the only thing preventing inventory rot.

declared state lives in git as system records, exception records and policy; CI and the evaluation runner act as control points and write to an evidence store; runtime telemetry, provider invocation logs and billing feed a reconciliation job whose output flows back into the catalogue and raises tick

Record typeAuthoritative systemWrite pathFreshness mechanismRetention
System declarationGit repositoryPull request, reviewedCI fails on review.next_due in the pastLife of system plus retention period
System identity and ownershipService catalogueIngested from gitOwner group resolved against IdP in CILife of system
Classification and tierGit, alongside the codePR, with recorded rationaleRe-run of the decision tree on any spec changeLife of system plus retention
Model pinGit, in application configPRDeprecation calendar due date; provider noticeLife of deployment
Evaluation resultsEvidence storeWritten by evaluation runnerTied to commit SHA and model version[CloudSecOps] 13 months
Decision logsObservability platform or object storageWritten by the applicationContinuous[Legal] at least 6 months for high-risk deployers; longer is a privacy decision
ExceptionsGitPR with named accepterGate fails on expiry date passedLife of system plus retention
Approvals and overridesApplication databaseWritten by the oversight UIContinuousMatch the decision-log retention

[CloudSecOps] On the thirteen months: the number is not a benchmark, it is one annual cycle plus a month, so that any annual review can always reach the equivalent period in the prior year without a gap at the boundary. If your review cycle is quarterly, four months plus a month is the same argument with different arithmetic.

Reconciliation, or how you find the system nobody told you about

Every inventory rots. The question is whether it rots silently. Declared inventory only captures systems whose owners chose to declare them, which excludes the two categories you most want: the feature a team shipped without knowing it counted as an AI system, and the shadow deployment.

The mechanism is a scheduled job that derives observed inventory from runtime signals and diffs it against the declared set. Three signal sources, in decreasing order of fidelity:

  1. Provider invocation logs. On Bedrock these give you modelId and identity.arn per invocation, which is close to ground truth. One catch that has caught many teams: Bedrock model invocation logging is disabled by default. Nothing is being recorded until someone enables it. The console configuration selects modalities — Text, Image, Embedding, Video — and logs data for all models supporting the chosen modalities. Delivery is to CloudWatch Logs, Amazon S3 or both, and only destinations in the same account and Region are supported, so a multi-account estate needs the job to run per account.
  2. Application telemetry. OpenTelemetry GenAI spans carry gen_ai.provider.name and gen_ai.operation.name as Required attributes and gen_ai.request.model as Conditionally Required. This covers providers whose own logging you do not control, but only for services that are instrumented, so absence of a span is not absence of a system.
  3. Billing and network egress. The lowest-fidelity and highest-coverage signal. A cost line item or a TLS connection to a model provider from a service with no declared AI dependency is a lead, not a finding.

Illustrative — CloudWatch Logs Insights query over Amazon Bedrock model invocation logs. Not executed against a live log group during writing. Validate the log group name and field paths against your own delivery configuration; the log schema is ModelInvocationLog schemaVersion 1.0, and S3-delivered logs are queried with Athena rather than Logs Insights.

fields @timestamp, modelId, identity.arn as principal, operation
| filter ispresent(modelId)
| stats count(*) as invocations,
        min(@timestamp) as first_seen,
        max(@timestamp) as last_seen
  by modelId, principal, operation
| sort invocations desc
| limit 200

The output is a set of (principal, modelId) pairs. Map each principal to a service through your existing role-to-service convention, then diff the service set against declared records. [CloudSecOps] Treat three outcomes differently:

  • Observed, not declared. A ticket against the owning team with a deadline, not a block. Blocking production traffic to punish a missing YAML file makes governance an adversary.
  • Declared, not observed, for a period longer than the system's normal idle gap. A candidate for retirement. This is how you find the systems still holding credentials after the experiment ended. Pick the period from your own traffic patterns; a seasonal batch job and a customer-facing endpoint do not share a threshold.
  • Declared model differs from observed modelId. Investigate immediately. Either the pin is not being honoured, or the provider changed something.

The reconciliation job is a small amount of code with an outsized effect, because it converts inventory from a thing people maintain into a thing the environment maintains. [CloudSecOps] Run it weekly and alert on a growing "observed, not declared" count rather than on any single instance. One undeclared system is a Tuesday; a trend is a broken intake process.

What reconciliation cannot see

The job's coverage is exactly the coverage of its inputs, and three blind spots are worth naming before you present its output as an inventory.

An account with logging disabled contributes nothing and looks identical to an account with no AI systems. Across more than a handful of accounts, enablement has to be checked as its own control: a Config rule or equivalent sweep asserting that invocation logging is configured in every account and Region where a model provider is reachable. Without it, the job's clean output is unfalsifiable.

Direct provider APIs bypass the cloud control plane. A service calling a model vendor over the internet with a personal API key produces no invocation log you can read, no billing line under your organisation, and a span only if someone instrumented it. Egress inspection is the remaining signal, and it degrades to "a TLS connection to a known provider domain": enough to open a conversation, not to close a finding.

Reconciliation identifies systems, not risk. It tells you a principal called a model, not whether that call reaches a customer, moves money, or feeds a decision about a person. That is the classification step, and it needs a human for the first pass on every system the job surfaces.

The AI system record

One file per AI system, in the repository that holds the system's code, reviewed through the same pull request process as everything else. Putting it in a GRC platform, a wiki or a spreadsheet creates a parallel registry, and a parallel registry is where inventory rot starts, because nothing forces it to change when the code changes.

The envelope is deliberately Backstage-compatible: apiVersion, kind, metadata, spec, with metadata.name inside the 1–63 character constraint and custom keys under a domain-prefixed annotation namespace, since the backstage.io/ prefix is reserved. If you use a different catalogue, keep the shape and change the envelope. If you use no catalogue, this file is your catalogue. The vocabulary borrows from SageMaker Model Cards rather than inventing a new one — intended uses paired with explicitly non-recommended uses, and a four-value risk rating — so the record can be generated from or exported to a model card without a translation layer.

Illustrative — AI system record. A shape to adapt, not a schema to adopt unchanged; field names must match whatever your policy gate and catalogue expect. Values are hypothetical.

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: claims-triage-assistant
  description: Ranks inbound claims by likely complexity to order a human queue
  annotations:
    example.com/ai-system: "true"
    example.com/ai-record-version: "3"
  tags: [ai-system, tier-2]
spec:
  type: ai-system
  lifecycle: production
  owner: group:default/claims-platform      # must resolve in the IdP
  system: claims

  ai:
    # --- Classification: two independent outputs, never collapsed ---
    classification:
      internal_tier: T2                      # your control tier
      internal_tier_rationale: >-
        Output orders a human work queue and is visible to staff. No automated
        adverse action. Failure degrades throughput, does not deny a claim.
      legal_trigger:
        eu_ai_act:
          annex_iii_area: null               # null = not in an Annex III area
          assessment: not-high-risk
          basis: "Art 6(3)(d) preparatory task; no profiling of natural persons"
          assessed_on: 2026-07-14
          assessed_by: user:default/a.rivera
          documented_at: docs/ai/claims-triage-6-3-assessment.md
        us_state:
          colorado_sb26_189: admt-consequential-decision-review-pending
          texas_hb149_disclosure_required: false   # §552.051(b) binds gov agencies

    # --- Intended and prohibited use ---
    intended_use:
      - Ordering the human claims queue by predicted handling complexity
    non_recommended_use:
      - Any automated decision to approve, deny or price a claim
      - Any use where the ranking is shown to the claimant
    risk_rating: Medium                      # Low | Medium | High | Unknown

    # --- Model and provider dependency ---
    model:
      provider: aws-bedrock
      model_id: <pinned-dated-snapshot-id>   # pinned, never an alias, at T2+
      deployment_type: on-demand
      auto_upgrade: false
      provider_eol_date: 2027-03-31          # tracked in the deprecation calendar
      region_scope: [us-east-1, eu-west-1]

    # --- Data lineage ---
    data:
      inputs:
        - name: claim-summary-text
          classification: confidential
          contains_personal_data: true
          lawful_basis_ref: privacy/claims-triage-dpia.md
      retrieval_sources:
        - name: policy-handbook-index
          store: opensearch://handbook-v4
      training_or_tuning: none
      egress:
        crosses_org_boundary: true
        provider_data_retention: "see provider terms; zero-retention not assumed"

    # --- Controls asserted, each with a verifiable reference ---
    controls:
      threat_model_ref: security/threat-models/claims-triage.md
      threat_model_reviewed: 2026-06-02
      output_handling: "rendered as text, never as HTML; no tool invocation"
      access_control: "service role claims-triage-invoke; no human API keys"
      human_oversight:
        type: human-on-the-loop
        instrumented: true
        metrics_dashboard: obs://dashboards/claims-triage-oversight

    # --- Evaluation ---
    evaluation:
      suite_ref: evals/claims-triage/
      frozen_test_set: s3://evidence/evals/claims-triage/testset-v4.jsonl
      thresholds:
        ranking_correlation_min: 0.72
        refusal_rate_max: 0.03
        pii_leak_findings_max: 0
      triggers: [pr-to-main, model-version-change, weekly-schedule]
      last_run: s3://evidence/evals/claims-triage/2026-08-04T0312Z/

    # --- Review cadence: the anti-rot field ---
    review:
      last_reviewed: 2026-07-14
      next_due: 2026-10-14
      cadence_days: 92

The values in evaluation.thresholds are placeholders. There is no correct ranking correlation for your system; there is only the number your suite produced on a model you accepted, written down before the next run.

Five fields do most of the work. non_recommended_use is more operationally valuable than intended_use, because intended use is what the team will tell you anyway and non-recommended use is the sentence someone points at when a product manager proposes the thing you were worried about. model_id pinned to a dated snapshot rather than an alias makes a version change a reviewable event. data.egress.crosses_org_boundary determines whether a privacy review is a formality or the long pole. evaluation.thresholds exists because an evaluation without a pre-declared threshold cannot fail, and an evaluation that cannot fail is a report. review.next_due is what the CI gate reads to make staleness a build failure rather than an observation.

Note what is absent: no free-text risk narrative, no maturity score, no control-framework mapping table. Those are outputs you generate from this record when someone asks; carrying them in the record means maintaining them by hand, and hand-maintained fields go stale first.

Classification that means something

Most classification schemes fail the same way: every system ends up in the middle tier, so the tier conveys nothing and its controls are applied uniformly, which is the same as having no tiers.

The structural fix is to stop asking one question. There are two independent determinations, and collapsing them is what makes tiering useless.

The legal trigger asks whether a named instrument imposes duties on this system. It is a jurisdictional fact that changes when legislators change it, as it did on 27 July 2026 when the Annex III high-risk application date moved by sixteen months. Nothing about the engineering risk of any system changed that day.

The internal control tier asks how much control this system's failure justifies. It is an engineering judgment about blast radius, reversibility and the presence of a human between output and consequence. It should be stable across regulatory churn.

[CloudSecOps] Keep them as two fields and never derive one from the other. A team that sets internal_tier: T3 because the legal trigger came back "not high-risk" has made a category error that will produce exactly one incident, and it will be the interesting kind.

Figure 2. Two determinations from one walk. Alt text: the left branch derives an internal control tier from whether output reaches an external person, whether harm is irreversible, and whether the system acts autonomously, producing tiers T0 through T3. The right branch derives an EU AI Act legal trigger by testing Annex III membership, the significant-risk threshold and the four Article 6(3) conditions, and the profiling carve-out, producing either a high-risk determination or a documented non-high-risk assessment that still carries registration duties under Article 49(2).

the left branch derives an internal control tier from whether output reaches an external person, whether harm is irreversible, and whether the system acts autonomously, producing tiers T0 through T3. The right branch derives an EU AI Act legal trigger by testing Annex III membership, the significant

The Article 6(3) trap

[Legal] Article 6(3) has a threshold clause that engineers routinely skip, and skipping it inverts the test. An Annex III system is not high-risk only "where it does not pose a significant risk of harm to the health, safety or fundamental rights of natural persons, including by not materially influencing the outcome of decision making" — and where one of four conditions is met: a narrow procedural task; improving the result of a previously completed human activity; detecting decision-making patterns or deviations from them without being meant to replace or influence the previous human assessment "without proper human review"; or a preparatory task to an assessment relevant to an Annex III use case. Meeting condition (a) does not by itself put you outside high-risk. The significant-risk test comes first.

[Legal] There is then one carve-out that swallows a lot of proposed derogations: "an AI system referred to in Annex III shall always be considered to be high-risk where the AI system performs profiling of natural persons."

Two consequences engineers consistently get backwards.

[Legal] Article 6(4): "A provider who considers that an AI system referred to in Annex III is not high-risk shall document its assessment before that system is placed on the market or put into service." Deciding you are out of scope is itself a documented decision, produced before deployment, not reconstructed afterwards.

[Legal] The same paragraph continues: "Such provider shall be subject to the registration obligation set out in Article 49(2)." Regulation (EU) 2026/1744 simplified the Annex VIII content for those systems but retained the database requirement for market surveillance. Concluding you are out of scope does not remove you from the register.

The engineering implication is small and specific: the classification decision needs a persisted artifact with a date and an author, produced by a gate that runs before first deployment, whichever way the decision goes. That artifact is legal_trigger.documented_at in the record above.

Internal tiers, and what attaches to them

TierTriggerRequired controlsGate behaviourReview cadence
T0Output can cause irreversible or adverse outcome to a personThreat model, adversarial evaluation, instrumented oversight, decision logging, pinned model, named accepter for residual riskBlocking on all gates; second approver required90 days
T1Autonomous action: tool use, writes, money movement, outbound messagesThreat model, evaluation suite with thresholds, scoped credentials, decision logging, pinned modelBlocking on all gates90 days
T2Output reaches an external person, reversible, human between output and consequenceRecord complete, evaluation suite, pinned model, disclosure where requiredBlocking on record and pin; advisory on evaluation delta180 days
T3Internal or experimental; output does not leave the building teamRecord with owner and expiry; no production data unless declaredAdvisory only; blocks on expiryExpires unless promoted

[Your risk decision] The cadences above are arbitrary intervals attached to a non-arbitrary idea: a review that lands inside a planning cycle someone already runs gets done, and one that lands between cycles does not. Ninety and 180 days suit an organisation planning quarterly. If yours plans annually, both numbers are wrong.

[Your risk decision] The tier boundaries are questions, not quotas. If your distribution puts most systems in one tier, the questions are wrong for your estate rather than the systems being uniform. Recalibrate the questions, not the count.

[CloudSecOps] Make T3 expire. An experimental tier without an expiry becomes the tier everyone declares, and six months later your highest-risk system is labelled an experiment. The specific interval matters less than the property: it must be shorter than the interval at which someone would otherwise notice.

Deployment gates that block

A gate is a check that runs at a defined point in the change process, evaluates declared state against policy, and takes an action on failure. The action is what makes it a gate. A check that reports and proceeds is a linter, which is a fine thing to be, and it should not appear in a control narrative as though it prevents anything.

GateWhat it checksBlocks or advisesEvidence emittedOwner
Record presenceAn ai-system record exists for any service invoking a model providerBlocks at T0–T2; advises at T3Check run; record pathPlatform
Record validitySchema valid; owner resolves in IdP; review.next_due in the futureBlocksCheck run with failure reasonPlatform
Classification freshnessDecision tree re-run when spec.ai changes; tier rationale presentBlocks on missing rationaleClassification artifact with dateService owner
Model pinmodel_id is a dated snapshot, not an alias, at T2 and above; auto_upgrade: falseBlocks at T2+Diff of pin across commitsService owner
Approved model listDeclared model appears on the organisation's reviewed listBlocksList version and match resultSecurity
Evaluation thresholdSuite ran on this commit and model version; all thresholds metBlocks at T0–T1; advises at T2Scored run artifact in evidence storeService owner
Threat model currencythreat_model_reviewed within tier cadenceAdvises, then blocks after grace periodDate comparison resultSecurity
Exception validityAny exception referenced is unexpired and has a compensating controlBlocksException record and expirySecurity

Writing the policy so it fails safely

The gate is a policy evaluation over the record. Rego with Conftest is the common shape; the important part is not the language but the failure semantics.

Illustrative — Rego for OPA 1.x, evaluated by Conftest against the AI system record. Written against OPA v1.17.0 (released 28 May 2026) but not executed during writing: run opa check and the negative fixtures below before adopting. Field names must match your record schema.

package aisystem.gate

import rego.v1

# `import rego.v1` is a no-op under OPA 1.x and keeps the file loadable by the
# 0.x toolchains some CI images still pin.

tier := object.get(input, ["spec", "ai", "classification", "internal_tier"], "UNSET")

known_tiers := {"T0", "T1", "T2", "T3"}

blocking_tiers := {"T0", "T1", "T2"}

date_shape := `^\d{4}-\d{2}-\d{2}$`

# A dated snapshot carries YYYYMMDD or YYYY-MM-DD somewhere in the identifier.
snapshot_date := `\d{8}|\d{4}-\d{2}-\d{2}`

# --- Ownership must resolve, not merely exist ---
deny contains msg if {
	not valid_owner
	msg := "spec.owner must be group:<namespace>/<name> with a non-empty name"
}

valid_owner if {
	is_string(input.spec.owner)
	startswith(input.spec.owner, "group:")
	count(trim_space(substring(input.spec.owner, 6, -1))) > 0
}

# --- Shape is checked before the value is parsed ---
deny contains msg if {
	not has_review_date
	msg := "spec.ai.review.next_due is required and must be YYYY-MM-DD"
}

has_review_date if {
	next_due := input.spec.ai.review.next_due
	is_string(next_due)
	regex.match(date_shape, next_due)
}

# --- Staleness is a build failure, not an observation ---
deny contains msg if {
	has_review_date
	next_due := input.spec.ai.review.next_due
	time.parse_rfc3339_ns(sprintf("%sT00:00:00Z", [next_due])) < time.now_ns()
	msg := sprintf("record review overdue: next_due %s is in the past", [next_due])
}

# --- An absent or unrecognised tier denies; it never defaults open ---
deny contains msg if {
	not tier in known_tiers
	msg := sprintf("spec.ai.classification.internal_tier must be one of T0-T3, got %v", [tier])
}

# --- Model pinning at T2 and above ---
deny contains msg if {
	tier in blocking_tiers
	not has_pinned_model
	msg := sprintf("tier %s requires a pinned dated model snapshot in spec.ai.model.model_id", [tier])
}

has_pinned_model if {
	id := input.spec.ai.model.model_id
	is_string(id)
	id != ""
	regex.match(snapshot_date, id)
	not endswith(id, "-latest")
	input.spec.ai.model.auto_upgrade == false
}

# --- Evaluation thresholds must exist before they can be met ---
deny contains msg if {
	tier in {"T0", "T1"}
	count(object.get(input, ["spec", "ai", "evaluation", "thresholds"], {})) == 0
	msg := sprintf("tier %s requires at least one declared evaluation threshold", [tier])
}

Four properties of this policy are the reason to copy it rather than the rules themselves.

Every not X is paired with a positively-defined X that type-checks its inputs. Writing not input.spec.ai.model.model_id looks equivalent to not has_pinned_model and is not: it is satisfied when the field is undefined, when the path above it is undefined, and when the value is false, and it silently passes when the field contains an empty string. [CloudSecOps] In a governance gate, "undefined" and "explicitly set to a bad value" must produce the same outcome, and only an explicit presence-and-type check gives you that.

The same reasoning is why the tier is denied outright when it is unrecognised. Reading it with object.get and a default of "UNSET" keeps the policy from erroring on a malformed record, but "UNSET" is in neither blocking_tiers nor {"T0", "T1"}, so without the known_tiers denial a record that simply omits internal_tier would skip every tier-gated rule and pass. Deleting a field is the cheapest way to escape a gate keyed on it, so the gate has to treat a missing key as the most severe case rather than the most lenient one.

A pin is tested by asserting a date, not by rejecting a suffix. not endswith(id, "-latest") rejects exactly one naming convention and passes every other alias, and most provider aliases — undated family names, cross-region inference profile identifiers — carry no -latest suffix at all. A gate written that way blocks close to nothing while reading in a control narrative as though it enforces pinning. snapshot_date is deliberately coarse and will accept any identifier containing eight consecutive digits; the approved-model-list gate in the table above is the precise version of the same control, and where you have that list, matching against it is the better test.

Shape validation runs before parsing. time.parse_rfc3339_ns on a value that is not an RFC 3339 timestamp raises a built-in error, and OPA suppresses built-in errors by default: the surrounding expression becomes undefined, which in a deny rule means the record passes. Separating the shape check from the comparison makes a malformed date produce its own denial. The residual case is a calendar-invalid value like 2026-13-45, which matches the shape and still errors; add a range check, or evaluate with opa eval --strict-builtin-errors, if that matters to you.

Version-dependent negation. OPA v1.17.0 introduced an opt-in future.keywords.not import, described in its release notes as making the negated body unsatisfiable when any sub-expression is undefined, so that not succeeds. The policy above does not import it and does not need to, because the positively-defined helper pattern behaves identically under both semantics. If you inherit a policy that does import it, re-read every negation in that file.

Test against deliberately broken fixtures: missing owner; owner set to group: with nothing after it; next_due set to "soon"; next_due in the past; a record with no internal_tier at all, and one with a tier of "T4"; model_id ending in -latest; model_id set to an undated alias with no -latest suffix, such as claude-sonnet-4-5 or gpt-4o; auto_upgrade: true; a T0 record with an empty thresholds object. A governance gate with no negative test cases is an untested control.

Illustrative — GitHub Actions fragment showing blocking versus advisory wiring. Pin action versions to a commit SHA in production use.

name: ai-governance-gate
on: [pull_request]

jobs:
  record-policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate AI system records
        run: |
          conftest test catalog-info.yaml \
            --policy policy/aisystem/ \
            --all-namespaces \
            --output github

  evaluation:
    runs-on: ubuntu-latest
    needs: record-policy
    steps:
      - uses: actions/checkout@v4
      - name: Read tier
        id: tier
        run: |
          TIER=$(yq '.spec.ai.classification.internal_tier' catalog-info.yaml)
          echo "tier=$TIER" >> "$GITHUB_OUTPUT"
      - name: Run evaluation suite
        # Advisory at T2 and below; the step still runs and still writes evidence,
        # but only a T0/T1 failure fails the job.
        continue-on-error: ${{ steps.tier.outputs.tier != 'T0' && steps.tier.outputs.tier != 'T1' }}
        run: promptfoo eval -c evals/claims-triage/promptfooconfig.yaml --fail-on-error
      - name: Upload evidence
        if: always()
        run: aws s3 cp ./output/ "s3://evidence/evals/${GITHUB_REPOSITORY##*/}/${GITHUB_SHA}/" --recursive

The evidence upload runs if: always(). A failed evaluation is more valuable evidence than a passing one, and a gate that only records its successes produces an audit trail that lies by omission.

Figure 3. A model version bump through the gates, including the exception path. Alt text: an engineer's pull request passes policy evaluation but fails the evaluation threshold, blocking the merge; the engineer files an exception record with a compensating control and expiry, which the policy engine validates and routes to a named approver, whose approval is written to the evidence store before the check passes.

an engineer's pull request passes policy evaluation but fails the evaluation threshold, blocking the merge; the engineer files an exception record with a compensating control and expiry, which the policy engine validates and routes to a named approver, whose approval is written to the evidence store

The gate itself is an attack surface

A gate is only as strong as the configuration that makes it required, and that configuration is usually invisible in the evidence trail. Three weaknesses, none exotic, all applying to CI-based controls generally.

A required check is a repository setting, not a property of the policy file. Anyone who can edit branch protection can make the gate advisory in a click, and the resulting merge looks identical to one where the gate passed. [CloudSecOps] Snapshot the branch protection and required-check configuration into the evidence store on a schedule and diff it. Without that, you can prove the policy existed and cannot prove it was enforced.

Whoever can merge to the policy repository can write their own exemption. The policy bundle needs at least the review requirements of the systems it governs, and ideally a different set of approvers, because the person requesting an exception should not be able to change the rule instead.

Evidence written by the pipeline is trusted because the pipeline wrote it. A compromised runner, or a workflow modified in the same pull request it evaluates, can emit a passing artifact for a record that never passed. Use a pinned, non-modifiable workflow for the gate job where your CI supports it, restrict which identity can write to the evidence prefix, and prefer object-lock or versioned storage so an artifact cannot be quietly replaced.

This is why a control narrative should say "the gate blocked this change on this date, and here is the check run" rather than "we have a gate".

A gate that has never blocked anything is not a gate

[CloudSecOps] Measure your gates. For each one, compute the block rate — the fraction of triggering changes it failed — over a rolling quarter.

A block rate of zero has exactly three explanations, and you need to know which one applies. The gate is misconfigured and never evaluates anything meaningful, which is common and easy to check by feeding it a deliberately broken record. Or the condition it checks genuinely never occurs, in which case the gate costs CI minutes and reviewer attention for no return and should be removed or made advisory. Or engineers have learned to route around it, which shows up as a rising exception count against that specific gate.

A block rate high enough that engineers experience blocking as the normal outcome is a different problem: the gate is catching what the development process should have prevented earlier, and the fix is upstream — a template, a scaffold, a pre-commit hook — rather than a stricter gate. [Your risk decision] This guide has no defensible number for "high enough" and does not offer one. The readable signal is the exception count per gate, which rises before anyone files a complaint.

Human oversight you can measure

"A human reviews the output before it is used" is the most common governance claim and the least frequently instrumented. It is also the claim regulation is least willing to take on trust.

[Legal] Article 14(4)(b) of the EU AI Act requires that oversight enable the assigned person "to remain aware of the possible tendency of automatically relying or over-relying on the output produced by a high-risk AI system (automation bias)". Subparagraphs (d) and (e) require the ability "to decide, in any particular situation, not to use the high-risk AI system or to otherwise disregard, override or reverse the output", and to interrupt the system through a stop button or similar procedure. [Legal] Article 26(2) requires deployers to assign oversight to natural persons "who have the necessary competence, training and authority, as well as the necessary support", which means the reviewer must be able to say no without escalating.

Regulation names the failure mode. It does not tell you how to detect it. That translation is engineering work, and it starts by treating oversight as a control with a measurable operating characteristic, like a rate limiter.

The instrumentation

Every approval or override point writes a row. Minimum fields: decision id, system id, model version, approver identity, decision (approved / rejected / modified), timestamp of presentation, timestamp of decision, and — the field everyone omits — a reference to the outcome, populated later.

Illustrative — derived oversight metrics over an approvals table, in PostgreSQL dialect. Adapt column names; the shape matters more than the dialect.

SELECT
  approver_id,
  count(*)                                        AS decisions,
  avg(CASE WHEN decision = 'approved' THEN 1.0 ELSE 0 END)  AS approval_rate,
  avg(CASE WHEN decision = 'modified' THEN 1.0 ELSE 0 END)  AS modification_rate,
  percentile_cont(0.5) WITHIN GROUP (
    ORDER BY extract(epoch FROM decided_at - presented_at)
  )                                               AS median_seconds_to_decision,
  min(extract(epoch FROM decided_at - presented_at)) AS fastest_decision_seconds
FROM ai_oversight_decisions
WHERE system_id = 'claims-triage-assistant'
  AND decided_at > now() - interval '30 days'
GROUP BY approver_id
HAVING count(*) >= 20
ORDER BY approval_rate DESC;
MetricDefinitionWhat a reading provesSignal the control is absent
Approval rateApproved / total decisionsNothing on its own; only movement against your own baseline1.00 sustained across a large sample: the step distinguishes nothing
Median time to decisionMedian seconds from presentation to decisionWhether the reviewer had time to read the itemMedian below a timed read of a representative item
Modification rateShare of decisions where the human changed the outputThat the interface permits modification and someone uses itExactly zero across all approvers
Per-approver spreadRange of approval rates across approversThat approvers exercise individual judgmentAll approvers statistically indistinguishable
Seeded-case detection rateShare of deliberately bad items rejectedDirect evidence the control operatesNear zero, whatever the approval rate says

[Your risk decision] There is no published healthy range for any of these and this guide does not invent one. Two readings are decisive by logic rather than by data: an approval rate of 1.00 across a large sample means the step separates nothing, and a median decision time below the time needed to read the item means the item was not read. Everything else is a baseline problem. Measure for your first 30 days, record the observed distribution in the system record next to the thresholds, and alert on deviation from your own numbers. Any band you take from an article — including this one — describes a stranger's system.

[CloudSecOps] A 100 percent approval rate is not evidence the model is good. It is evidence the approval step carries no information, and a control that carries no information is not a control regardless of how a policy document describes it.

When the numbers say the human is not helping

Three responses, in the order to consider them.

Sample rather than review everything. A reviewer facing 500 items a day will approve nearly all of them, because that is what the throughput requires. A reviewer facing a sampled subset with time to consider each produces a usable signal about the population. [CloudSecOps] For T1 and T2 systems, statistical sampling with an escalation path beats exhaustive review that has degraded into clicking. Size the sample from the defect rate you need to detect, not from what feels thorough.

Inject adversarial cases. Seed a small known fraction of items that should be rejected: degraded outputs, outputs violating a stated constraint, outputs from an older model version. The detection rate on seeded cases directly measures whether the control operates, and unlike approval rate it cannot be explained away by "the model really is that good". Two conditions make this safe. Seeded items must be handled so they never reach a real consequence and must be distinguishable in your data store so they never contaminate quality metrics. And the practice must be disclosed to reviewers in advance, without telling them which items are seeded, because covertly testing employees has employment-law and works-council implications in several jurisdictions.

Remove the human where they add nothing, and say so. If measured outcomes show the reviewer performs at or below chance, the honest move is to delete the step and reallocate the control budget: a stricter output filter, a narrower action scope, a rollback mechanism. Keeping a human in a loop where they demonstrably add no signal is worse than removing them, because it produces an unearned claim of oversight that other controls are then sized against.

That third option has a legal boundary. [Legal] Where a system falls under Annex III high-risk obligations, Article 14 requires human oversight measures, and removing the human is not available in the same way. The measurement still matters: the requirement is for oversight that is effective, and evidence that it is not is a finding, not a licence to switch it off.

Model and provider dependencies

An AI system has a dependency that behaves like no other in your stack: it can change without a deploy, without a version bump in your lockfile, and without any event in your change management system. This is documented provider behaviour, not speculation.

Microsoft Foundry / Azure OpenAI is the clearest documented case. Deployments of type Global Standard, Data Zone Standard and Standard are auto-upgraded by Microsoft when a model version is retired, "scheduled on a rolling, region-by-region basis", with the schedule published in advance. Global Provisioned, Provisioned and Data Zone Provisioned deployments are not auto-upgraded and must be migrated manually. The behaviour is controlled by a versionUpgradeOption property with three values: OnceNewDefaultVersionAvailable, OnceCurrentVersionExpired, and NoAutoUpgrade — the last meaning the deployment stops working at retirement rather than silently changing. Notice is at least 60 days for GA models and 30 days for preview. After retirement, inference returns 410 Gone.

The consequence of "rolling, region-by-region" deserves stating plainly: on a default deployment type, two regions can serve different model versions of the same logical deployment at the same time, and nothing in your application code changed. That is a change management event with no entry in your change management system.

Amazon Bedrock uses three statuses — Active, Legacy, EOL — with published minimums: a model remains available for at least 12 months after launch before its EOL date, and stays in Legacy for at least 6 months before EOL. New customers cannot start on a Legacy model, existing customers may lose access after 15 days of inactivity, and no new Provisioned Throughput is issued for Legacy models. For models with EOL dates after 1 February 2026, after a minimum of three months in Legacy the model enters a public extended access period at higher, provider-set pricing. After EOL, requests fail. AWS states plainly that "migration will not happen automatically".

Anthropic commits to at least 60 days' notice before retiring publicly released models, emailed to customers with active deployments, with a console usage page that exports usage as CSV broken down by API key and model — directly useful as a reconciliation input. Model IDs are dated, so pinning is the default shape rather than something to opt into.

ProviderMinimum noticeDefault upgrade behaviourAt retirementVerified
Microsoft Foundry / Azure OpenAI60 days GA, 30 days previewAuto-upgrade on Standard tiers; manual on provisioned410 GoneYes, provider docs
Amazon Bedrock12 months on platform; 6 months in Legacy before EOLNo auto-migration; code must changeRequests fail; extended access at higher price for post-Feb-2026 EOLsYes, provider docs
Anthropic60 days for public modelsNo auto-migration; dated model IDsRetired IDs stop servingYes, provider docs
Google Vertex / GeminiNot verifiedDocuments lifecycle stages and migration paths; distinguishes stable versions from auto-updated aliasesNot verifiedPartial — page relocated; figures not retrievable on two fetches
OpenAINot verifiedNot verifiedNot verifiedNo — deprecations page returned HTTP 403 on two attempts

The blank cells are deliberate. Filling them from memory would make the table more useful and less true. [CloudSecOps] Check every provider you actually use against its own current documentation and record the numbers in your deprecation calendar rather than trusting any table, including this one.

The durable rule, which survives the product churn: notice periods are measured in weeks to months, default upgrade behaviour differs by deployment type, and at least one major platform will change the model version for you unless you opt out.

Pinning policy

[CloudSecOps] Pin to a dated snapshot at T2 and above. Aliases and auto-updating deployment types are acceptable at T3, where the blast radius is a broken experiment. Above T3, the documented rolling regional upgrade behaviour means an alias converts a reviewable change into an unobservable one, and the premise of a change gate collapses.

The counter-argument is real. Pinning means sitting on a model with known weaknesses until someone does the migration work, and unpinned deployments get quality and safety improvements automatically. [Your risk decision] The trade is between unreviewed improvement and reviewed staleness. The tier boundary above is where CloudSecOps places it; a team with a strong regression suite and a fast migration cadence can defensibly place it higher, and a team with neither should place it lower.

Pinning is worthless without the second half: a deprecation calendar with a named owner per entry.

Illustrative — deprecation calendar entry, stored in git next to the system records so the same gate can read it.

- model_id: <pinned-dated-snapshot-id>
  provider: aws-bedrock
  used_by: [claims-triage-assistant, agent-support-summariser]
  status: legacy                     # active | legacy | extended-access | eol
  eol_date: 2027-03-31
  legacy_since: 2026-09-30
  notice_received: 2026-08-01
  owner: group:default/claims-platform
  migration:
    target_model_id: <replacement-snapshot-id>
    regression_suite: evals/claims-triage/
    status: not-started              # not-started | in-progress | validated | done
    must_start_by: 2026-12-31        # eol_date minus a full evaluation cycle

The must_start_by field is the one that does work. An EOL date twelve months out generates no urgency; a start-by date computed backwards from it, owned by a named group, and checked by the same CI job that checks record freshness, generates a build warning at the right time. Compute it from your own measured migration duration, not from a number in a guide.

Detecting a change you were not told about

Two detectors, both cheap.

The first is divergence between the model you asked for and the model that answered. OpenTelemetry's GenAI semantic conventions carry gen_ai.request.model and gen_ai.response.model as separate attributes precisely because they can differ. Alert when they diverge, and alert when the distinct set of gen_ai.response.model values for a service changes against a rolling baseline.

Illustrative — divergence detector expressed as SQL over a flattened span export. Most tracing backends store OTel attributes in a map rather than as columns, so the column names below are an assumption about your export, not a schema. Attribute names are at "Development" stability and may change.

-- Any span where the served model differs from the requested model
SELECT service_name,
       gen_ai_request_model,
       gen_ai_response_model,
       count(*) AS spans,
       min(start_time) AS first_seen
FROM otel_spans
WHERE start_time > now() - interval '24 hours'
  AND gen_ai_response_model IS NOT NULL
  AND gen_ai_request_model IS NOT NULL
  AND gen_ai_response_model <> gen_ai_request_model
GROUP BY 1, 2, 3
ORDER BY spans DESC;

-- New response-model values not present in the prior 7 days.
-- The IS NOT NULL in the subquery is load-bearing: a single NULL in a
-- NOT IN subquery makes the whole predicate return no rows, and the
-- detector fails silently.
SELECT service_name, gen_ai_response_model, count(*) AS spans
FROM otel_spans
WHERE start_time > now() - interval '24 hours'
  AND gen_ai_response_model IS NOT NULL
  AND gen_ai_response_model NOT IN (
    SELECT DISTINCT gen_ai_response_model
    FROM otel_spans
    WHERE start_time BETWEEN now() - interval '8 days' AND now() - interval '1 day'
      AND gen_ai_response_model IS NOT NULL
  )
GROUP BY 1, 2;

A stability caveat that matters for anyone building a control on this: nearly every gen_ai.* attribute is marked at "Development" stability, and the GenAI conventions have already moved out of the main OpenTelemetry semantic-conventions repository into a dedicated one. gen_ai.operation.name and gen_ai.provider.name are Required; gen_ai.request.model is Conditionally Required; gen_ai.response.model is only Recommended, which means it may be absent depending on your instrumentation — and a detector keyed on an absent attribute reports silence, not safety. Build it, and expect to maintain it.

The second detector is the Bedrock invocation log's modelId, diffed against the declared pin. This is ground truth for the AWS path and needs no application instrumentation, at the cost of requiring invocation logging to be enabled, which it is not by default.

Figure 4. Model version change response loop. Alt text: four trigger sources — provider notice, calendar due date, telemetry divergence, and invocation-log drift — route into a regression evaluation against a frozen test set; passing leads to a pinned update through normal gates, failing leads either to holding the current pin with migration work opened, or, if the pin is no longer available, to an exception with a compensating control and hard expiry, or to disabling the feature.

four trigger sources — provider notice, calendar due date, telemetry divergence, and invocation-log drift — route into a regression evaluation against a frozen test set; passing leads to a pinned update through normal gates, failing leads either to holding the current pin with migration work opened,

The point of the loop is that a provider notice produces a test run rather than a meeting. [CloudSecOps] If the response to "the provider is retiring our model" is a calendar invite, the organisation does not have a regression suite, and building one is more urgent than the migration.

An honest limitation: no primary source consulted during research confirms or refutes whether any provider changes the behaviour of a pinned, dated snapshot in place. Treat in-place behavioural drift on a pinned model as an engineering assumption to test rather than a fact. The scheduled regression evaluation is justified as cheap insurance whether or not the drift occurs.

Self-hosted and open-weight models

If you run weights yourself, this section changes shape rather than disappearing, and the substitutions are mechanical.

The pin becomes a content digest rather than a dated model ID, which is strictly better because you can verify it. The deprecation calendar becomes a maintenance calendar keyed on the serving stack rather than the model — inference server, CUDA and driver stack, tokenizer library — because that is where CVEs and breaking changes now arrive, and nobody sends a 60-day notice for them. Reconciliation loses the provider invocation log and gains better signals: model-server request metrics, GPU scheduling records, and the artifact registry holding the weights. The evidence trail gains the provenance of the weights themselves, for which a CycloneDX ML-BOM is the machine-readable form.

What you lose is the provider's evaluation and safety work, which shifts onto your own suite. What you gain is that nothing changes underneath you without a deploy. [CloudSecOps] For a T0 or T1 system, that property alone is often worth the operational cost.

Evaluation, monitoring, and the evidence trail

Evaluation with a threshold declared first

The rule that makes evaluation useful is procedural rather than technical: the threshold is declared in the record before the run, and the run is scored against the declared threshold. An evaluation whose pass criterion is decided after seeing the result is a report.

Four triggers, at minimum: any pull request touching the prompt, the retrieval configuration or the model pin; any model version change; a scheduled run against a frozen test set to detect drift; and any incident, as part of the postmortem.

For threat coverage, map test cases against two sources rather than one, because they are organised differently. [Standard] NIST AI 600-1, the Generative AI Profile published July 2024, enumerates twelve risk categories organised by harm, including confabulation, data privacy, harmful bias or homogenization, human-AI configuration, information integrity and value chain integration. [Standard] OWASP's Top 10 for LLM Applications 2025 is organised by attack surface: prompt injection, sensitive information disclosure, supply chain, data and model poisoning, improper output handling, excessive agency, system prompt leakage, vector and embedding weaknesses, misinformation, unbounded consumption. A suite derived from only one leaves a visible gap. [Standard] NIST SP 800-218A, finalised 26 July 2024, is the SSDF community profile for generative AI, and is the right document to map existing secure-development practices onto rather than inventing an AI-specific SDLC.

Microsoft's documented evaluation approach is a useful reference shape because it is vendor-documented rather than theoretical: benchmark-based model selection, pre-production evaluation with curated datasets and edge cases plus red teaming built on PyRIT, then post-production continuous evaluation of sampled production traffic with scheduled drift runs against a test dataset. [Standard] The evaluator families it names — general quality, retrieval groundedness and relevance, safety and security, and agent measures like tool call accuracy and task completion — are a reasonable checklist whichever tooling you use. Inspect, from the UK AI Security Institute, and promptfoo, a declarative CLI with CI integration, are both usable as the thing a deployment gate calls; neither page displayed a version number during research, so they are named by capability.

Monitoring that names its metrics

[CloudSecOps] Refuse to use the word "monitoring" in a governance artifact without naming, in the same sentence, a metric, a threshold, an alert destination, and a review cadence. This one editorial rule eliminates most governance theatre in the monitoring row.

A minimum set for a T1 or T2 system: refusal rate; output length distribution; retrieval hit rate and groundedness score where retrieval is used; tool call error rate for agentic systems; token consumption per request against the declared rate limit; the model version divergence detector above; and the oversight metrics from the previous section. Each with a threshold, each alerting somewhere a human is paged or a ticket is created, each reviewed on the tier's cadence.

[Legal] Article 72 requires providers of high-risk systems to establish a post-market monitoring plan. Note the change: Regulation (EU) 2026/1744 removed the Commission's empowerment to adopt an implementing act specifying the template for that plan, and replaced it with an obligation to publish guidance, including a voluntary template, by 2 September 2027. The plan is yours to structure, and nothing in it waits on the Commission.

Decision logging and the retention question

[Legal] Article 12 requires that high-risk AI systems "technically allow for the automatic recording of events (logs) over the lifetime of the system", to enable identification of risk situations and to facilitate post-market monitoring. [Legal] Article 26(6) requires deployers of high-risk systems to keep logs automatically generated by that system, "to the extent such logs are under their control, for a period appropriate to the intended purpose of the high-risk AI system, of at least six months, unless provided otherwise in applicable Union or national law".

Two qualifiers in that sentence do engineering work. "Under their control" means that if the provider holds the invocation record and you do not, the obligation reaches only what you hold, which is an argument for enabling your own logging rather than a reason to skip it. "Unless provided otherwise" means sector rules can override the floor in either direction; regulated financial deployers in particular meet this through existing documentation obligations.

What to log is harder than how long. OpenTelemetry's guidance states that model instructions, user messages and model outputs "are considered sensitive and are often large in size", and that instrumentations should not capture them by default but should offer opt-in. That is the correct default. [Your risk decision] Retaining prompts and completions is a data protection decision before it is a governance one: you are creating a durable store of whatever users typed, which for many applications includes personal data they were not asked to provide.

[CloudSecOps] A workable middle position: always log the metadata — request id, timestamp, principal, gen_ai.request.model, gen_ai.response.model, token counts, finish reason, latency, and a content hash. Log content selectively, for T0 and T1 systems or on a sampled basis or only on error and override paths, with shorter retention than the metadata and a documented lawful basis. On Bedrock, invocation logging captures input.inputBodyJson and output.outputBodyJson up to 100 KB inline, with larger or binary bodies written to S3 under a data prefix. Turning it on is a data-retention decision as much as an observability one.

Lifecycle stageArtifactWhere storedMinimum retention
DesignThreat model; data lineage; classification rationaleGit, alongside codeLife of system plus 3 years [CloudSecOps]
Pre-deploymentClassification assessment with date and author; evaluation baseline; residual risk acceptanceGit plus evidence storeSame
DeploymentGate check runs; branch protection snapshots; approvals; exception recordsCI history plus evidence store13 months [CloudSecOps]
OperationDecision logs; oversight metrics; alert historyObservability platform, object storage[Legal] at least 6 months, high-risk deployer; content shorter [Your risk decision]
ChangeModel version change records; regression run artifactsEvidence store13 months [CloudSecOps]
IncidentTimeline; classification; reporting decision with timestampsIR platformPer existing IR policy
RetirementDecommission record; data and key dispositionEvidence storeLongest applicable retention of the data it processed

[Standard] Annex IV lists the technical documentation for high-risk systems: general description and intended purpose, development process and architecture, human oversight measures, predetermined changes, validation and testing procedures, capabilities and limitations, foreseeable risks, performance metrics, lifecycle changes made by the provider, standards applied, and the post-market monitoring plan. Read as an evidence checklist rather than a document outline, every item maps to something in the table above. If you produce the artifacts as a by-product of engineering work, the technical documentation is an assembly job. If you do not, it is a writing project, and writing projects produce documents that describe a system nobody built.

Incidents and exceptions

Incident clocks are a paging requirement

[Legal] Article 73 sets reporting clocks for serious incidents: 15 days generally; 2 days where the incident involves widespread infringement or disruption of critical infrastructure; and 10 days where a person has died. Each period runs from when the provider — "or, where applicable, the deployer" — becomes aware of the incident.

Read the addressee carefully, because it changes who is on the clock. The Article 73 filing obligation sits on providers of high-risk systems placed on the Union market. If you are a deployer, your direct duty under Article 26(5) is to inform the provider and the relevant authorities without undue delay, which means your clock is shorter than the provider's and is set by how fast you can reach them. Neither role gets to discover the incident slowly.

A two-day clock is not a policy sentence. It is a paging requirement: someone has to classify an incident as reportable within hours, at 3am, without a lawyer available. [CloudSecOps] That translation has three parts — a triage question in the incident runbook that fires on AI system involvement, a pre-identified escalation contact who can make the reporting call, and a timestamped record of when the incident was known, because every clock runs from awareness and reconstructing awareness afterwards is both hard and unconvincing.

AI incidents differ from ordinary security incidents in a way existing triage does not capture. The system may be behaving exactly as designed and still producing the harm: no compromise, no unauthorised access, no vulnerability to patch, just an output that caused a consequence. A severity matrix keyed on confidentiality, integrity and availability has no row for "correct system, wrong answer, real damage". Add one, and add a triage step that captures the model version in use at the time, because a postmortem that cannot identify which model produced the output cannot conclude anything.

Exceptions with teeth

Every gate needs an exception path, because a gate with no exception path gets disabled the first time it blocks something urgent. The exception is a record, subject to the same gate it waives.

Illustrative — exception record. Expiry, compensating control and named accepter are the mandatory fields; everything else is convenience. Values are hypothetical.

apiVersion: example.com/v1
kind: GovernanceException
metadata:
  name: exc-2026-0143
spec:
  system_ref: component:default/claims-triage-assistant
  control_waived: evaluation-threshold-ranking-correlation
  reason: >-
    Replacement model scores 0.64 against a 0.72 threshold on the frozen test
    set. Current pinned model reaches extended-access pricing on 2026-11-30 and
    the cost delta is material. Deploying the replacement with reduced scope.
  compensating_control: >-
    Ranking confidence below 0.8 routes to unranked FIFO queue rather than
    displaying a low-confidence ranking. Sampling review raised from 5% to 25%
    for the exception period.
  compensating_control_verified_by: user:default/s.okafor
  compensating_control_evidence: s3://evidence/exceptions/exc-2026-0143/
  accepted_by: user:default/m.chen        # named human, not a group
  accepted_role: director-of-engineering
  accepted_on: 2026-08-04
  expires_on: 2026-11-04                  # hard stop, enforced by the gate
  review_before_expiry: 2026-10-21

Three properties make this real rather than decorative.

Expiry is enforced by the same gate. The policy that checks the exception's validity fails once expires_on passes, so an unrenewed exception blocks the next deployment. Without that, exceptions accumulate and the gate becomes advisory by attrition.

The accepter is a named individual. accepted_by: group:default/security accepts nothing. The purpose of the field is to put a person's name next to a decision they will be asked about.

The compensating control has evidence. "We increased monitoring" is not a compensating control; "sampling review raised from 5 to 25 percent, dashboard link, verified by name" is. [CloudSecOps] Require that the compensating control be verified by someone other than the requester, and that the verification produce an artifact.

Track exception count and median exception age per gate. [CloudSecOps] A rising exception count against one specific gate is the clearest available signal that the gate is miscalibrated: it is catching things the organisation has repeatedly decided it is willing to accept, which means the policy should change rather than the exceptions continuing.

Who owns what

[CloudSecOps] Do not start with a review board. A board is the default recommendation in most governance material and it is usually the wrong first move for an engineering organisation, for a structural reason: a board is a batch process with a queue, and it becomes the bottleneck teams route around, which produces the worst outcome — a control that delays honest teams and does not catch dishonest ones.

Start with named service owners, automated gates, and a narrow escalation path. Add a board only when you have a class of decision that genuinely cannot be resolved by a gate and an owner, which in practice means novel harms, cross-organisational trade-offs, and residual risk acceptance for T0 systems.

ActivityService ownerSecurityLegal and privacyEscalation
System record and internal tierAccountableReviews at T0–T1Platform lead
Legal trigger assessmentContributes factsConsultedAccountableExternal counsel
Threat modelAccountableAccountable jointlySecurity lead
Evaluation suite and thresholdsAccountableReviews thresholds at T0–T1Security lead
Model pin and deprecation migrationAccountableInformedPlatform lead
Human oversight design and instrumentationAccountableConsultedConsulted at T0Security lead
Exception requestRequestsVerifies compensating controlConsulted if legal triggerNamed accepter, director level or above
Residual risk acceptanceProposesReviewsReviews at T0Named accepter, recorded
Incident classification and reporting decisionContributes factsAccountableAccountable for reporting callIncident commander
Retirement and data dispositionAccountableVerifies key and access revocationConfirms retention obligationsPlatform lead

The smallest set of roles that works: a service owner per system who is a real on-call group, one security engineer who owns the policy bundle and the gates, and one named individual with authority to accept residual risk. Below that, you have governance that depends on one person's memory. Above that, before you have systems to govern, you have a process looking for work.

How this fails

Each of these is common, each is detectable with a number you can compute this week, and each has a fix smaller than the problem suggests.

Failure modeDetection signalFix
Governance theatre: a policy document, a spreadsheet, no CI check referencing eitherCount of gates with a nonzero block rate: zeroOne blocking gate on record presence; accept that everything else waits
Inventory rot: the catalogue was accurate at the time of the auditObserved-not-declared count, trending upReconcile weekly; ticket the owner, do not block
Unfalsifiable inventory: reconciliation returns nothing because its inputs are offAccounts and Regions where invocation logging is not enabledMake logging enablement its own config rule
Rubber-stamp oversight: "a human reviews every output" with no dashboardApproval rate at 1.00 over a large sample; median decision time below reading timeSeed adversarial cases; move to sampling; measure detection rate
Gate erosion: exceptions have quietly become the normal pathException count per gate per quarter, trending up; median age exceeding expiryChange the policy the exceptions are all against; enforce expiry
Unenforced gate: the check exists but is not a required checkDiff of branch protection configuration over timeSnapshot that configuration into the evidence store on a schedule
Tier collapse: one tier holds most of the estateDistribution of tiers across recordsRecalibrate the decision tree questions; make T3 expire
Stale legal claims: an internal deck cites deadlines that movedDate of last verification, versus the regulator's pageDate-stamp every regulatory claim; cite the regulator, never a tracker
Silent model change: behaviour shifts, the postmortem stallsgen_ai.response.model values absent from the prior baselinePin at T2+; build the divergence detector; alert on it
Undetected retirement: 410 Gone in production is the first signPinned models with no deprecation calendar entryEvery pin gets a calendar entry with a must_start_by date

Three more belong on the list without needing a row of their own: write-only threat models, detected by the median age of threat_model_reviewed across the estate; orphaned ownership, detected by the share of records whose spec.owner resolves to a group with a live on-call schedule; and evidence that only records successes, detected by looking for a single failed check run in the store.

Every detection signal here is a query against data you already have, except the reconciliation job, which is a scheduled script. The fixes are single mechanisms rather than programmes, which matters: a governance improvement that requires a programme will not happen at a company that does not already have one.

Minimum viable governance

[Legal] Article 17(2) states that implementation of the quality management system "shall be proportionate to the size of the provider's organisation", and Regulation (EU) 2026/1744 extended that principle by allowing SMEs, startups and small mid-caps to supply technical documentation elements in simplified form on a Commission-established form which notified bodies must accept. Proportionality is written into the instrument. Use it.

Here is what a twenty-engineer company should build in thirty days, and — the more valuable half — what it should deliberately skip.

Week 1: find out what you have. Enable provider invocation logging where it is off, which on Bedrock is everywhere by default. Write the reconciliation query. Produce a list of every service that has talked to a model provider in the last 90 days. This is the only week that produces a surprise, and the surprise is the point.

Week 2: one record per system. Write the ai-system YAML for each service on the list, in that service's repository. Fill in owner, intended use, non-recommended use, model pin, and a review date. Leave the rest empty. Classify each system with the four-question internal tier tree; skip the legal trigger branch unless you have EU users or a Colorado or Texas nexus, in which case do that branch first for the systems touching consequential decisions.

Week 3: two gates. Gate one: a record exists and is schema-valid, with a resolvable owner and a future review date. Gate two: the model is pinned to a dated snapshot at T2 and above. Both blocking. Nothing else. Two gates that block are worth more than eight that advise.

Week 4: one measurement and one calendar. Instrument the human oversight point on your highest-tier system — approval rate and time to decision, nothing more. Create the deprecation calendar with one entry per pinned model, each with an owner and a must_start_by date. Schedule the reconciliation job weekly.

CapabilityWhat the enterprise addsWhat the small team skips, and why
InventoryFederation across accounts, plus a logging-enablement check per accountNothing — this is load-bearing at any size
ClassificationBoth branches for every system; counsel review of the legal branchFormal impact assessments; they come after tiering
GatesThe full gate table, tier-differentiated, with branch-protection evidenceEvaluation gates in month one; you need a suite before you can gate on it
EvaluationSuites per system, scheduled drift runs, red teamingComprehensive coverage; a real suite on one system beats a stub on ten
OversightEvery approval point instrumented, with seeded adversarial casesSeeding, initially; add it in month two
EvidenceVersioned store with retention per classAn evidence management platform; a bucket and a naming convention suffice
RolesThe full matrix, plus a board for T0 residual risk onlyThe review board; most commonly recommended, most commonly bypassed

What both columns share is the reconciliation job, the pinned models, and at least one gate that has actually blocked something. [CloudSecOps] If a governance programme at any size cannot point to a specific change a gate stopped, it has not started.

Below about five engineers the plan compresses further, and honestly: run the reconciliation job, pin the models, and accept that everything else is a person paying attention. A three-person team writing its own records, evaluations and approvals has separation of duties in form only, and a plan that pretends otherwise is worse than one that names the gap.

Retirement, and what you keep

Retirement is the least-implemented lifecycle stage in every governance programme, because nothing forces it. A system that has stopped being used raises no alert, and the reconciliation job's "declared but not observed" output is usually the only signal you get.

Figure 5. AI system lifecycle states. Alt text: a system moves from proposed to approved to production through gates; model version changes loop within production via the regression gate; gate failures route through an exception-granted state that either returns to production or, on expiry without renewal, moves to retired; systems reach legacy through supersession or provider end-of-life and then retired via a decommission checklist, with evidence retained beyond the system's life.

a system moves from proposed to approved to production through gates; model version changes loop within production via the regression gate; gate failures route through an exception-granted state that either returns to production or, on expiry without renewal, moves to retired; systems reach legacy t

The decommission checklist, in the order things get forgotten:

  1. Credentials. Revoke the service role, provider API keys and long-lived tokens. Verify by attempting an invocation, not by reading an IAM policy. This item fails most often, and a working key on a retired system is a credential with no owner and no monitoring.
  2. Data. Delete or archive retrieval indexes, embedding stores, fine-tuning datasets and cached completions per the retention obligations of the underlying data, not per the system's own lifecycle. Embedding stores are the commonly missed one: they hold a lossy but real representation of whatever was indexed.
  3. Egress. Remove the network path and any provider account entitlement, so a redeployment of old code fails loudly rather than silently working.
  4. Evidence. Move evaluation artifacts, gate history, approvals and exceptions into their retention location. Their clock runs from the data the system processed and any applicable reporting obligation, not from the shutdown date.
  5. The record. Set lifecycle: retired rather than deleting the file. A deleted record makes the reconciliation job's memory shorter than the incident timeline you may later need to reconstruct.
  6. Downstream consumers. Check who reads the system's outputs. A retired ranker whose stale scores are still cached in a downstream table has stopped being maintained and has not stopped having effects.

Limits of this approach

Mechanism-first governance is the right default for an engineering organisation and it under-serves in five identifiable places. Stating them is part of the recommendation.

Novel harms have no metric yet. Everything above assumes you can name what you are measuring. The failures that will matter most in the next two years are the ones nobody has a threshold for, and a system built entirely from thresholds is structurally blind to them. The partial compensation is qualitative: a red team exercise with no success criteria, a scheduled session where someone describes how the system could hurt somebody in a way the test suite would not catch, and a route for that finding to become a tracked item. Weaker than a metric, and not nothing.

No gate fixes an organisational decision. If a product leader has decided the feature ships, a blocking gate produces an exception rather than a different outcome. The mechanism gives you attribution, not prevention: a named accepter, a date, and a compensating control. That is a smaller claim than "governance prevents bad deployments" and it is the honest one.

At small scale, the gate and the builder are the same person. A three-engineer team where one person writes the system, the record, the evaluation and the approval has separation of duties in form only. The realistic mitigation is external: periodic review by someone outside the team, or a peer from another team as the named accepter for T0 and T1 systems.

This assumes a git-and-CI shape. An organisation whose AI systems are built in a low-code platform, a SaaS workflow tool or a vendor's own console has no pull request to gate and no repository to hold the record. The record still works as an artifact; the enforcement does not. What remains is periodic attestation plus whatever signal the platform emits, which is materially weaker and should be described that way rather than dressed up as the same control.

Provider opacity bounds everything. When a hosted model changes in ways the provider does not document, your regression suite detects the effect and not the cause, your incident review terminates at "the provider changed something", and your evidence trail records an unexplained shift. The mechanisms above make that visible rather than invisible, which is a genuine improvement and is not the same as control.

One limit specific to this guide: the labelling convention is a discipline, not a guarantee. Every [Legal] statement was checked against a primary source on the verification date, and every one will need re-checking. The most useful habit to take from this guide is the one demonstrated in its own third section — when two sources disagree about the law, check which one is the regulator, and check when it was last updated.

References

Regulation and official guidance

Standards and frameworks

Provider documentation

Tooling and specifications

Validity and revision

Verification date: 2026-08-09. Every regulatory date, provider commitment, standard designation and version number in this guide was checked against the primary source linked above. Most were checked on 2026-08-07, two days after the 2026-08-06 publication date in the frontmatter. The US state law section and the Article 72 monitoring template were re-checked on 2026-08-09 and corrected: the Colorado paragraph had described a regime that SB 26-189 repealed and reenacted on 14 May 2026, the Texas paragraph had attributed a governmental-agency disclosure duty to private entities, and the Article 72 note had recorded the removal of the mandatory monitoring template without the guidance obligation that replaced it. The corrections are in the text above; the failure is the one this guide's third section is about.

What was not verified, and is marked as such in the text: OpenAI's deprecation notice period (page returned HTTP 403 on two attempts); Google's notice period in days or months (page relocated and returned truncated content on two attempts); Colorado Attorney General rulemaking under SB 26-189, which had not been formally proposed on the verification date, and the outcome of the enforcement stay in xAI v. Weiser; CEN-CENELEC harmonised standards status; and whether any provider alters the behaviour of a pinned, dated snapshot in place.

What is version-dependent or short-lived:

  • EU AI Act dates. The high-risk application dates of 2 December 2027 (Annex III) and 2 August 2028 (Annex I) are the post-Omnibus values set by Regulation (EU) 2026/1744, in force from 27 July 2026. They have already moved once. The December 2026 prohibition on non-consensual intimate imagery and CSAM generation is not yet in force, and the Commission's Article 72 guidance with its voluntary post-market monitoring template is not due until 2 September 2027.
  • US state law. Colorado's regime has moved three times: SB24-205's 1 February 2026 date, SB25B-004's 30 June 2026 date, and now SB 26-189, which repeals and reenacts SB24-205 and applies to decisions made on or after 1 January 2027, with enforcement stayed in xAI v. Weiser. Texas HB 149's 1 January 2026 effective date is taken from the enrolled bill text, and its AI-interaction disclosure duty binds governmental agencies rather than private deployers. State AI legislation is the fastest-moving item in this guide.
  • NIST AI RMF remains at 1.0 (AI 100-1, 26 January 2023) and is under revision with no published target date. There is no 2.0. NIST SP 800-218 Rev. 1 is an initial public draft and should not be cited as settled.
  • Provider lifecycle terms. Microsoft's 60/30-day notice, Bedrock's 12-month and 6-month minimums, and Anthropic's 60-day commitment are current provider policy and change without a versioned artifact.
  • OpenTelemetry gen_ai.* attributes are at "Development" stability and the conventions have relocated to a dedicated repository. Any control built on these attribute names will need maintenance.
  • Tool versions. OPA v1.17.0 (28 May 2026), whose opt-in future.keywords.not import changes negation semantics. Inspect and promptfoo are referenced by capability because neither page displayed a version.
  • Product names. During research, Anthropic's developer docs moved to platform.claude.com, Google's Vertex model-versioning docs redirected to a Gemini Enterprise Agent Platform path, and Microsoft's docs render the product as "Microsoft Foundry" while the URL path remains azure/ai-foundry/. Any governance artifact that hardcodes these links is already decaying.

Recommended review date: 2027-02-09, and immediately if the EU AI Act is amended again, if the NIST AI RMF revision is published, or if a provider you depend on changes its deprecation policy.

  • ai-governance
  • ai-security
  • eu-ai-act
  • nist-ai-rmf
  • iso-42001
  • policy-as-code
  • model-risk
  • mlops

The service behind this work

Cloud and AI security training

Instructor-led private cohorts for your engineers and your security team — cloud security, AI and agent security, threat modelling, detection engineering — taught against real misconfigurations, with architecture review and AI security programme design when the gaps turn out to be structural.