Pattern · last reviewed 2026-08-14
Sequence-aware authorization: when the call is legal but the trace is not
Sequence-aware authorization makes the allow/deny decision a function of the agent's action history, not of the current call alone. The policy sees the session's prior tool calls and their outputs, and can require an order, require that an argument came from a specific earlier result, or require that a step has not already happened. issue_refund(order_id, amount) is a perfectly legitimate call; it is a different thing entirely when no return was ever verified, or when the amount does not match the quote the agent retrieved thirty seconds ago.
The honest headline is the cost, not the capability: you are trading a stateless, cacheable, independently testable authorization function for a stateful one. That trade is worth making in narrow cases and is a poor default. Most of the ordering invariants people reach for this to enforce belong behind the API the tool calls — where they protect every caller, not only the agent.
Context & problem
Per-call authorization asks one question: may this principal invoke this tool with these arguments? It is stateless by design, and that is most of its value — it is cheap, cacheable, replayable, and you can unit-test it without constructing a world. It is also the model every agent framework ships with by default, because it is the model HTTP APIs already had.
It has a blind spot that agents make load-bearing. An agent chooses its own sequence, and a stateless checker is reading a call rather than a story — so a request that satisfies every rule on its own terms can still be the wrong thing to do next. Delete the working copy before the backup finished. Escalate a ticket to a privileged queue after reading an attacker-controlled document. Transfer an amount that does not correspond to any quote the agent actually fetched. Each call is legal; the trace is not.
AWS shipped a managed implementation on 2026-08-06: temporal policies in Amazon Bedrock AgentCore, which evaluate "each request in the context of an agent's prior actions within a session". The documented conditions are the useful part of the announcement, because they map the shape of the pattern: "enforce workflow sequencing", "require that a tool argument exactly matches the output of a prior call", "require human approval before taking privileged actions", and "enforce data freshness". Note the second one — argument-matching against a prior output is not an ordering rule at all, it is a provenance rule, and it is the condition a stateless checker is furthest from being able to express.
The academic framing arrived first. Beurer-Kellner et al. (2025) set out the constraint the whole area is circling — "once an LLM agent has ingested untrusted input, it must be constrained so that it is impossible for that input to trigger any consequential actions" — and their Plan-Then-Execute pattern fixes the plan before untrusted data is seen, so injected content "cannot inject instructions that make the agent deviate from its plan". They then concede the exact gap this pattern addresses: the injected content can still influence the arguments to the planned calls. Sequence-aware authorization is one way to close that specific hole, by requiring an argument to have come from somewhere.
Forces
- Expressiveness vs. purity. The rules people actually want — "not twice", "not before X", "only with the value you were given" — are all predicates over history. Writing them means giving up the pure function.
- Where the invariant lives. The same rule can be enforced at the agent's gateway, in the tool implementation, or inside the downstream service. Only the last one protects callers that are not this agent.
- False denials are self-defeating. A policy that blocks legitimate work gets loosened by the team that has to ship, until it means nothing. Precision matters more here than in stateless authz, because there is more surface to be imprecise on.
- Statefulness needs a boundary, and the boundary is yours to draw. A session is the obvious scope, but "session" is a decision, not a given — and whatever you pick is a scope an adversary, or an impatient user, can reset by starting a new one.
- Concurrency. "Prior actions" is well defined only if actions are serialized. Parallel tool calls and multi-agent fan-out make it a race.
The pattern
Four decisions, and a runtime rule that follows from them.
- Record a trace, not a flag. Every dispatched tool call and its result gets appended to an ordered, append-only record scoped to the session. The policy reads this; the model does not write it. If the trace is reconstructed from the model's context window you have built a suggestion, because the context is exactly what an injection controls.
- Write the predicates over that trace. Three families cover almost everything real: ordering ("
deployrequires a prior successfulrun_tests"), provenance ("transfer.amountmust equal theamountfield of a priorget_quoteresult"), and exhaustion ("at most oneissue_refundper session"). - Pick the scope deliberately and write down what it does not cover. Session scope is the default and it means the policy is silent about anything an actor can split across two sessions. If your threat model includes a motivated user, session scope is the wrong scope and you need a durable per-subject record instead — which is a materially bigger system.
- Decide the trace store's failure mode up front. If the trace is unavailable, does the call proceed? This is a security decision that did not exist while authorization was stateless, and it will be made by default — badly — if you do not make it explicitly.
The runtime rule: serialize evaluation and recording. Evaluate the candidate call against the committed trace, and commit the call to the trace before its side effect can be observed by a second evaluation. Anything looser and two concurrent calls both read a history that does not contain the other, and a policy reading "at most one refund" pays two.
Reference implementation notes
The pattern is stack-agnostic; what differs is who owns the trace and where the decision point sits.
Managed: AgentCore temporal policies
AWS puts the decision point at the gateway, which is the right place for it if the gateway is genuinely the only path to the tools — and a false sense of coverage if it is not. Two properties are worth reading precisely, and the second is only in the deep-dive rather than the announcement. The state is "within a session", and the session boundary is yours: "You decide what constitutes the beginning and end of a session… whether that is a single user conversation, a multi-step task, or a longer-running workflow." That is more flexible than it first appears and it moves the hard part onto you, because a scope you define is a scope you are responsible for defending. Underneath it sits a ceiling you do not define — "agent trajectories carry a maximum look-back window of 24 hours. Any trajectory events older than that are automatically deleted." So your policy's memory has a TTL. Write an exhaustion rule over a workflow that legitimately spans two days and it stops holding on day two, silently, in the permissive direction. And the same release added rate limiting — "per-user or per-group controls over how much traffic flows to the tools, models, and agents connected to your gateway", with limits on requests, tokens and concurrent connections. Those are different controls that are easy to conflate: a temporal policy constrains what may happen next, a rate limit constrains how much. Neither substitutes for the other, and a rate limit is not an injection defence.
Rolling your own
The engine is not the hard part; a predicate over an append-only list is a weekend. The hard parts are the three things a managed product hides: durable ordered storage for the trace on the request path, a serialization point so concurrent calls cannot both read a stale history, and a denial payload the model can act on. Return the denial as a readable tool result naming the missing precondition — "a verified return is required before a refund; call verify_return first" — rather than throwing. A thrown error ends the turn and teaches the agent nothing; a readable one lets it satisfy the precondition and continue, which is the difference between a control and an outage.
Trade-offs
- Authorization stops being a pure function, and everything downstream of that changes. You lose caching, trivial replay, and the ability to reason about a call in isolation. You gain a new availability dependency on the request path and a consistency model you now have to hold in your head during an incident. The security question you did not previously have — fail open or fail closed when the trace store is unreachable — has no good answer: fail closed and a storage blip becomes an agent outage; fail open and your strongest control is disabled by the cheapest possible attack.
- The policy is a second copy of the workflow, and copies drift. Ordering rules encode the intended process. When the process changes — a new step, a legitimate shortcut, a reordering — the policy does not change with it. It fails in the more dangerous direction of the two: the new step is uncovered and permitted, while the old rule keeps denying the old path. Nothing tells you. This is the same standing-obligation problem the capability latch has with its tool classification, one level more complex.
- Concurrency makes "prior" ambiguous. A single assistant turn can return several tool calls at once, and multi-agent designs fan out by construction. Unless evaluation and commit are serialized, two calls each see a history without the other and an exhaustion rule pays twice. Serializing them is correct and costs you the parallelism you presumably adopted the framework for.
- The session boundary is the adversary's reset button, and it may also expire underneath you. Everything the policy knows is discarded at that boundary, so a sequencing rule constrains order within one context and says nothing about doing the same thing again in a fresh one — and unless you have made the boundary something a user cannot cross, they choose when it moves. Managed implementations add a retention ceiling on top (AgentCore deletes trajectory events older than 24 hours), so a rule over a workflow that legitimately spans days stops holding without failing. If either gap matters, you need durable per-subject state, which is a different and much larger commitment.
- It is a boundary, not a detector — and that is the good news. Hackett et al. (2025) tested six prominent injection and jailbreak protection systems and found evasion reaching "up to 100% evasion success" in some instances. A predicate over a trace cannot be talked out of its answer, which is the property that makes this worth the cost when it is worth it at all.
The failure mode: the retry collision
This is the one that will page you, and it is not in anyone's marketing.
Sequence-aware authorization converts availability incidents into authorization incidents. A tool call times out. The side effect landed; the trace entry did not, or the reverse. Your agent does what you built it to do and retries. The policy is now consulting a history that disagrees with the world — so the retry is either denied as a duplicate when nothing happened, or permitted as a first attempt when it is a second. The exhaustion predicate, the most attractive rule in the family, is precisely the one that breaks: "at most one refund per session" and "retry safely on transient failure" are in direct tension, and the tension is structural, not a bug you can fix in the policy.
The mitigation is the boring one and you should build it before the policy, not after: make the tools idempotent with client-supplied keys, and key the trace on that same idempotency key so a retry is recognisably the same action rather than a new one. If you cannot do that — if the downstream tool has no idempotency story — then an exhaustion predicate over a trace is not a safety control, it is a liveness risk wearing a safety control's clothes, and you should not ship it.
The second-order effect is worse than the first. A denial that looks like a bug gets treated like a bug, and the fix a team under pressure reaches for is to relax the predicate. Two or three of those and the policy permits everything while still costing you a store on the hot path.
When not to use this
When the invariant belongs behind the API. This is the common case and it is the one the vendors will never make for you, because the policy engine is the product. "Never refund more than was charged", "never delete before the backup completes", "never transfer an amount that does not match a quote" are invariants of the resource, not of the agent. Enforced at the agent's gateway they protect exactly one caller — this agent, this session — and leave the ops script, the internal service, the batch job and the second agent free to violate them, while adding a stateful dependency to your request path. If the tool fronts a real API, put the constraint in the API: a state machine on the order, a foreign key, an idempotency key, a check constraint. Then let the temporal policy cover only what genuinely has no downstream owner. A gateway policy is a control over a path; a resource invariant is a control over reality.
When your policy has one bit of state. If the rule is "after X, never Y for the rest of this turn", that is a fuse, and a fuse is a boolean. Do not buy a trace store, a policy language and a serialization point to hold one flag — build a capability latch, which is the degenerate temporal policy: a single predicate over history, latching true, scoped to a turn. Reach for the general mechanism when you have three or four real predicates, not one.
When the agent has no canonical sequence. Research, triage and investigation agents are valuable precisely because they choose an order you did not anticipate. Imposing a sequencing policy on them produces false denials without reducing a threat, and false denials are how a policy gets loosened into decoration. Constrain the consequential actions at the edges instead and leave the exploration alone.
When the action is harmful in isolation too. If a call is unacceptable no matter what the trace holds, per-call authorization already covers it and sequence awareness adds latency plus a second place for the rule to be wrong and to disagree with the first. Delete the tool, or scope the credential. Beurer-Kellner et al. put least privilege, sandboxing and user confirmation among best practices rather than among the patterns, which is the right hierarchy — they are the baseline you do first.
And not on its own. This constrains actions; it does not constrain content. A poisoned document can still shape the words of a reply that triggers no tool at all. It is one layer of a defense in depth, sitting between the coarse bounded loop and an explicit human approval for the things you are not willing to automate at any level of policy sophistication.
What this platform runs instead
We do not run a general temporal policy, and this page carries no as-built evidence — it is in the library because we can say something about the trade-off that the source does not, which is the bar for entry here.
What we do run is the degenerate case, and the comparison is the useful part. The coach's capability latch is a single predicate over the turn's action history — has an untrusted-ingest tool been requested — held in one write-once boolean that is set in src/lib/coach.ts and evaluated in src/lib/tools.ts before dispatch, against a write-tool list that is a constant in the same file. It has no trace store, no serialization point and no ordering ambiguity, because a single bit cannot have any. That is not us falling short of the pattern; it is the pattern sized to the problem. Our coach has three write tools and one ordering concern. A refund workflow has neither of those properties, and would be badly served by a fuse.
The general version is the one we would build if a second predicate ever showed up — and the first thing we would build is not the policy, it is idempotency keys on the write tools, for the reason in the failure-mode section above.
Changelog
- 2026-08-14 — Initial publication. Verified against the AWS AgentCore temporal-policies announcement and its deep-dive (both retrieved 2026-08-14; conditions, caller-defined session boundary and the 24-hour trajectory retention quoted from those pages), Beurer-Kellner et al. (2025), and Hackett et al. (2025).
- Temporal policies evaluating "each request in the context of an agent's prior actions within a session", the four named conditions, and the per-user/per-group rate limiting shipped alongside them: AWS, Amazon Bedrock AgentCore adds temporal policies and rate limiting, announced 6 Aug 2026, retrieved 2026-08-14.
- Caller-defined session boundaries ("You decide what constitutes the beginning and end of a session") and the 24-hour trajectory look-back after which "events older than that are automatically deleted": AWS, Securing AI agents with temporal policies in Amazon Bedrock AgentCore, retrieved 2026-08-14. Neither fact is in the announcement — the retention ceiling in particular is only in the deep-dive, and it is the one that changes what rules you can safely write.
- The guiding constraint, Plan-Then-Execute and its argument-influence caveat, and the best-practices hierarchy: Beurer-Kellner et al. (2025), Design Patterns for Securing LLM Agents against Prompt Injections, arXiv 2506.08837 v3 (27 Jun 2025), checked 2026-07-21.
- Six protection systems tested, "up to 100% evasion success" in some instances: Hackett et al. (2025), Bypassing LLM Guardrails, arXiv 2504.11168 v3, checked 2026-07-21.
- Capability-based policies over extracted control and data flow — the rigorous end of this family, at the cost of an interpreter and a policy engine around the model: Debenedetti et al. (2025), Defeating Prompt Injections by Design (CaMeL), arXiv 2503.18813, checked 2026-07-21.
- Untrusted content belongs in tool results, and least privilege so "a successful injection can do minimal damage": Anthropic, Mitigate jailbreaks and prompt injections — current guidance, retrieved 2026-07-21.
- The one-bit comparison is this platform's own code (
src/lib/coach.ts,src/lib/tools.ts), shipped 2026-07-21. Everything else on this page is an argument, not an implementation report: we do not run a general temporal policy.
The AgentCore feature set is the fact most likely to drift — re-verify the conditions and the session scope against the current AWS documentation rather than this page. Corrections: hello@aiarch.dev.
Learn to design agent permissions, not just agent prompts.
aiArch teaches least-privilege tool design, indirect prompt injection, and the authorization boundaries that survive an agent choosing its own order — by building, for senior engineers moving into AI.
See how aiArch helps senior engineers become AI-native, or compare Professional Membership pricing.
Free sample — no signup · every claim cited · full curriculum is waitlist-only