EU AI Act Article 14 covers human oversight for high-risk AI systems. Its date matters: although 2 August 2026 was the Act's general application date, Regulation (EU) 2026/1744 moved Sections 1–3 of Chapter III — including Articles 12, 14, and 26 — to 2 December 2027 for Annex III high-risk use cases and 2 August 2028 for high-risk systems tied to Annex I products. This is therefore an engineering preparation pattern, not a claim that those provisions already apply to your system.
If you're reading this, you may have an agent making consequential tool calls and a legal or compliance team asking, in some form, “how could a human be put in the loop, and what evidence would remain afterward?” This post answers the engineering half of that question, not the legal half.
We're going to do something most vendor content about this regulation skips: read the actual text of Article 14, plus the record-keeping provisions it leans on, and be precise about which specific clauses a policy-plus-receipts implementation like nominee's actually maps to — and which it doesn't touch at all.
What Article 14 actually requires
Article 14(1) sets the general obligation:
"High-risk AI systems shall be designed and developed in such a way, including with appropriate human-machine interface tools, that they can be effectively overseen by natural persons during the period in which they are in use." — Regulation (EU) 2024/1689, Article 14(1)
Article 14(2) says what that oversight is for:
"Human oversight shall aim to prevent or minimise the risks to health, safety or fundamental rights that may emerge when a high-risk AI system is used in accordance with its intended purpose or under conditions of reasonably foreseeable misuse, in particular where such risks persist despite the application of other requirements set out in this Section." — Article 14(2)
Neither paragraph mandates a specific mechanism. That's on purpose — the Act is a systems-level regulation, not an API spec — and it's the first thing worth being honest about: there is no single library, "human oversight" checkbox, or SDK flag that discharges Article 14 on its own. What the text does is set a shape. The rest of the article fills that shape in.
Two places the law lets the control live
Article 14(3) says the oversight measures must be proportionate to risk and autonomy, and implemented one of two ways:
"(a) measures identified and built, when technically feasible, into the high-risk AI system by the provider before it is placed on the market or put into service" — Article 14(3)(a)
"(b) measures identified by the provider before placing the high-risk AI system on the market or putting it into service and that are appropriate to be implemented by the deployer." — Article 14(3)(b)
This is the clause worth sitting with if you're a deployer rather than a provider — i.e. you're the team wiring an agent into a workflow that calls write-capable tools. An ask rule can be part of implementing a measure that the provider identified under 3(b): it is a deployer-side control at the tool-call layer, not something baked into the model. If a deployer invents the rule independently, it may still be a sensible control, but the rule alone is not evidence that Article 14(3)(b)'s provider-identification requirement was met. A policy library also says nothing about model design, technical documentation, risk management, data governance, transparency, or accuracy, robustness, and cybersecurity.
What the person doing the overseeing has to be able to do
Article 14(4) is the part that translates most directly into engineering requirements, because it's written as a list of things the overseeing human must be enabled to do:
"(a) to properly understand the relevant capacities and limitations of the high-risk AI system and be able to duly monitor its operation, including in view of detecting and addressing anomalies, dysfunctions and unexpected performance" — Article 14(4)(a)
"(b) 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), in particular for high-risk AI systems used to provide information or recommendations for decisions to be taken by natural persons" — Article 14(4)(b)
"(c) to correctly interpret the high-risk AI system's output, taking into account, for example, the interpretation tools and methods available" — Article 14(4)(c)
"(d) to decide, in any particular situation, not to use the high-risk AI system or to otherwise disregard, override or reverse the output of the high-risk AI system" — Article 14(4)(d)
"(e) to intervene in the operation of the high-risk AI system or interrupt the system through a 'stop' button or a similar procedure that allows the system to come to a halt in a safe state" — Article 14(4)(e)
Mapped onto a policy-and-approval engine, honestly and without stretching:
- (d), the override, is the closest mapping. An
askrule can pause a tool call and let an authenticated person approve or deny it, making “do not execute this action” a real path rather than a UI dead end. Whether that is an appropriate and proportionate Article 14 measure still depends on the system and its intended use. - (e), the stop procedure, can be supported at one layer. A default-deny fallback or an emergency
denyrule can stop a class of tool actions. It does not necessarily halt the wider AI system or establish that it reaches a safe state, so it is not a complete system-level stop mechanism by itself. - (a) and (c), understanding and interpreting output, are not supplied by receipts. A receipt's human-written policy reason explains why a rule paused; it does not explain the AI system's output, teach its capabilities and limitations, or guarantee that an approval UI presents enough context. Those are application, provider-documentation, training, and UX responsibilities.
- (b), automation bias, is not fixed by a click. Recording a decision does not stop someone from approving reflexively. If stored durably, the record can support later analysis of approval latency and approve/deny patterns, but training, staffing, procedure, and interface design carry the actual control.
The record-keeping half: Articles 12 and 26
Article 14 is about the oversight action itself. The obligation to keep a durable record of what happened sits in Article 12 and Article 26:
"High-risk AI systems shall technically allow for the automatic recording of events (logs) over the lifetime of the system." — Article 12(1)
Article 12(2) ties that logging to, among other things, "monitoring the operation of high-risk AI systems referred to in Article 26(5)" — the deployer's own monitoring duty. Article 26, which sets deployer obligations specifically, adds two clauses that matter directly here:
"Deployers shall assign human oversight to natural persons who have the necessary competence, training and authority, as well as the necessary support." — Article 26(2)
"Deployers of high-risk AI systems shall keep the logs automatically generated by that high-risk AI 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, in particular in Union law on the protection of personal data." — Article 26(6)
These clauses require more than a username and a database row. Article 26(2) calls for competence, training, authority, and support; an approver field can identify who acted but cannot prove any of those qualities. Article 26(6) also makes retention conditional on control of the logs and other applicable law, including data-protection law. nominee's in-memory receipt window retains 1,000 entries by default, which is a development convenience, not a six-month retention plan. A durable receipt store (nominee-postgres, or your own AtomicReceiptStore) can be one implementation component, alongside retention, access, deletion, key-management, and external-checkpoint policies designed for the actual legal context.
A worked example
Here's a policy for an agent that can issue billing credits, with a threshold that routes anything over €1,000 to a named human before it executes:
import { Nominee, allow, ask, deny } from 'nominee'
const nominee = new Nominee({
policy: {
rules: [
allow('invoice.read'),
deny('invoice.issue_credit', {
when: ({ input }) => input.amountCents > 1_000_000,
reason: 'credit over EUR 10,000 is outside delegated authority',
}),
ask('invoice.issue_credit', {
when: ({ input }) => input.amountCents > 100_000,
reason: 'credit over EUR 1,000 requires human oversight under Art. 14(3)(b)',
}),
allow('invoice.issue_credit'),
],
fallback: 'deny',
},
receipts: { key: process.env.NOMINEE_RECEIPT_KEY },
agent: 'billing-agent',
})
An agent tries to credit €4,500 back to a customer. The call plans, the €1,000 rule fires, and the action pauses. Someone in finance — [email protected] — approves it from a Slack workflow, and the durable action resumes and executes:
const creditInput = { invoiceId: 'inv_8841', amountCents: 450_000 }
const prepared = await nominee.prepareAction({
tool: 'invoice.issue_credit',
input: creditInput,
user: 'user_finance_ops',
})
// prepared.status === 'pending_approval'
// ...later, from your approval webhook, with a named human attached:
await nominee.resolveActionApproval(prepared.action.id, {
decision: 'approved',
approver: '[email protected]',
via: 'slack:#billing-approvals',
})
const resumed = await nominee.resumeAction(prepared.action.id)
const result = await nominee.executeCapability(resumed.capability, creditInput, async ({ input }) => {
return issueCredit(input) // your actual billing-system call, run exactly once
})
That sequence — run against the real, unmodified nominee package for this post — produces this receipt chain (formatReceipts(nominee.receipts)):
#0 action.planned invoice.issue_credit 0e1455b451df
#1 policy.decision invoice.issue_credit ask dc13b62f1b20
#2 approval.requested invoice.issue_credit 7d61bf1a8a48
#3 approval.resolved invoice.issue_credit approved b5eaf2d94409
#4 capability.issued invoice.issue_credit 50c4ba53352f
#5 capability.consumed invoice.issue_credit f9ba3f3b6dfe
#6 execution.started invoice.issue_credit 9f4f0a0ec4f0
#7 execution.succeeded invoice.issue_credit succeeded 168171ea7df3
And the two receipts that matter most for oversight — the decision to pause, and the resolution of it — in full, exactly as the chain sealed them:
{
"type": "policy.decision",
"user": "user_finance_ops",
"agent": "billing-agent",
"tool": "invoice.issue_credit",
"actionId": "act_884eaf8a8007de0bd625008aaa7295a63f18",
"effect": "ask",
"rule": "ask:invoice.issue_credit",
"reason": "credit over EUR 1,000 requires human oversight under Art. 14(3)(b)",
"inputHash": "75d9df0ac5311edcd269ae6d82657acf10320c9a448baad32ea594fbc061e149",
"seq": 1, "prev": "0e1455b451df...", "hash": "dc13b62f1b20..."
}
{
"type": "approval.resolved",
"user": "user_finance_ops",
"tool": "invoice.issue_credit",
"decision": "approved",
"approvalId": "apr_884eaf8a8007de0bd625008aaa7295a63f18",
"approver": "[email protected]",
"seq": 3, "prev": "7d61bf1a8a48...", "hash": "b5eaf2d94409..."
}
Now watch what happens if someone — an insider with database access, say — edits that stored chain after the fact, flipping the policy decision from ask to allow to make it look like the credit never needed a human at all:
const tampered = deepClone(nominee.receipts)
tampered[1].effect = 'allow' // quietly rewrite history
verifyReceipts(tampered, { key: process.env.NOMINEE_RECEIPT_KEY })
// { ok: false, checked: 1, brokenAt: 1, reason: 'content does not match hash' }
The edit doesn't just fail silently — verifyReceipts() stops at the very first receipt whose content no longer matches its own sealed hash, and every receipt after it in the chain is now unverifiable too, because each one's hash covers the previous one's hash. That's what "hash-chained" buys you: an edit anywhere breaks verification of everything downstream of it, not just the edited record.
What an auditor could actually verify from this
Given this receipt chain, the receipt signing key, and nothing else, an auditor could confirm:
- That a specific rule (
ask:invoice.issue_credit) evaluated a specific input fingerprint and produced anaskdecision, with a human-readable reason citing why. - That a named individual (
[email protected]) resolved that specific pending approval asapprovedat a specific timestamp. The application suppliedvia: 'slack:#billing-approvals'while resolving it, but the current receipt schema does not seal that channel, so the chain alone cannot prove where the approval happened. - That the tool did not execute before that approval was resolved — the lifecycle order (
policy.decision→approval.requested→approval.resolved→capability.issued→execution.started) is enforced by the chain's sequencing, not just asserted in prose. - That the input the tool actually executed against was the same input the policy evaluated and the approver's decision was bound to —
executeCapability()throws before running the tool if the input's fingerprint changed after approval, and that failure is itself receipted. - That nobody has silently edited, removed from the middle, or reordered a record in the supplied range since it was sealed, provided they re-run
verifyReceipts()themselves rather than trusting a rendered summary. Detecting deletion from the tail also requires comparing the chain with a previously trusted external tail checkpoint; a shortened prefix can remain internally valid.
That's a genuinely useful evidence bundle — see docs/observability.md for the fuller export checklist (sequence range, stream checkpoints, policy version, verification output). It is also a narrower claim than "audit-ready," which is why we're not making that claim.
What receipts do not give you
This is the part worth reading slowly, because it's the part that actually matters if you're taking this to a compliance review:
- It is not a compliance certification. Nothing here is a conformity assessment, a legal opinion, or proof that your specific system satisfies Article 14, Article 12, Article 26, or any other provision. Whether your system counts as "high-risk" under Annex III, and whether your overall control set discharges the Act's obligations, is a legal determination for people qualified to make it — not a property of a software library.
- "Tamper-evident" is not "tamper-proof." A hash chain proves that a record wasn't silently edited without leaving a break in verification. It does not prevent someone who holds both write access to your receipt store and the HMAC signing key from rewriting history and re-sealing a plausible-looking replacement chain from scratch. Verification catches edits to a chain whose earlier hashes you already trust; it can't catch a wholesale forged replacement signed with a leaked key. That's a key-management problem, not a hashing problem — keep the signing key out of the same trust boundary as the people who can write to the receipt store, and rotate it like any other credential.
- The default retention window is not a retention policy.
nominee.receiptskeeps the last 1,000 in-memory entries by default — fine for development, not a six-month log per Article 26(6). Real retention needs a durable receipt store and an explicit export/anchoring cadence, not just leaving the process running. - A receipt in your own database is still evidence you control. A chain that only ever lived in infrastructure you administer is weaker evidence than one anchored somewhere you don't control the write path — a WORM bucket, an external timestamping service, a separate signing authority. "External log anchoring" is doing real work in that sentence, not decoration.
- It doesn't stop prompt injection, and it doesn't stop automation bias. A receipt records that a human clicked approve. It cannot tell you whether they read the request first. Rubber-stamping is a process and staffing problem; the most a receipt chain gives you here is the raw material to go measure it — approval latency, approve/deny ratios per approver — not a fix for it.
- It's scoped to the tool-call layer. Model design, technical documentation, risk management systems, data governance, accuracy/robustness testing — the rest of the Act's high-risk system obligations — are untouched by anything described here.
When this is overkill
If the "high-risk" classification does not apply, if the agent only reads public non-sensitive data, or if a plain application log already satisfies the evidence need your legal team identified, you may not need a hash-chained receipt engine. Add this when tool calls are consequential enough that “we think someone approved that” is not a sufficient engineering record, and when the record must survive process restarts and short-lived approval channels.
See it for yourself
Run the ask-and-receipt proof in 10 seconds.