Skip to main content
LatinFlash Research Paper 2026-09 · Technical Audit Series Public Release Candidate · Version 1.0-RC

Empirical Evaluation of Guardrail Execution Precedence in Asynchronous Agent Runners

An Audit of OpenAI Agents SDK v0.22.3 (`agents.Runner.run()` and `Runner.run_streamed()`) across 1,200 Full-Runner Replication Trials under Asynchronous Parallel and Sequential Modes.

Abstract & Central Claim

Input guardrails are designed to intercept policy-violating inputs before an AI agent executes state-modifying actions. This paper evaluates whether the OpenAI Agents SDK (v0.22.3) provides a structural happens-before guarantee (T1 ≺ T3) ensuring that input guardrail evaluation (T1) strictly precedes tool side-effect commitment (T3).

Central Claim:
In OpenAI Agents SDK v0.22.3, under run_in_parallel=True and within the tested experimental conditions, no structural barrier is observed that guarantees the input guardrail decision precedes tool side-effect commitment. In parallel DENY trials lacking an explicit authorization barrier, 100% of cases produced local state persistence before the runner communicated the tripwire exception (T4); across both execution engines (Runner.run() and Runner.run_streamed()), 99 out of 100 trials per suite additionally committed state before the guardrail verdict was logged (T1).

Conversely, configuring sequential mode (run_in_parallel=False) or introducing an explicit pre-commit authorization barrier yields 0 out of 100 side-effect commits on DENY. We present architectural call-chain analysis, empirical trace breakdowns, and recommended mitigations for agentic control plane developers.

1. Introduction & Problem Statement

Agentic AI frameworks delegate tasks to LLMs via function calling. To maintain safety, control planes implement input guardrails intended to sanitize inputs, detect prompt injections, or enforce policy boundaries before state-modifying actions are committed.

In concurrent systems, safety enforcement requires a formal causal happens-before relation (≺) between guardrail evaluation (T1) and side-effect commitment (T3):

Guardrail Start (T0) ≺ Guardrail Verdict (T1) ≺ Tool Start (T2) ≺ Side Effect Committed (T3)

If chronological telemetry reveals T3 < T1 (side effect committed before guardrail verdict logged), the structural precedence requirement (T1 ≺ T3) is falsified for that execution path. Even if the framework subsequently raises a tripwire exception (T4), the state modification (T3) has already occurred in storage.

Expected Safe Execution (Sequential / Barrier):
  [T0: Guardrail Start] ---> [T1: Guardrail Verdict] ---> [T2: Tool Start] ---> [T3: State Commit]

Observed Parallel Execution Race Window:
  [T0: Guardrail Start] ---+---> (Async Guardrail Task) ---------------------> [T1: Verdict] ---> [T4: Tripwire]
                           |
                           +---> [T2: Tool Start] ---> [T3: State Commit]  <-- CHRONOLOGICAL RACE (T3 < T1)

2. Architectural Call-Chain Analysis (SDK v0.22.3)

Inspection of the OpenAI Agents SDK v0.22.3 source code reveals the structural root cause:

  1. Runner Entry Point: Both Runner.run() and Runner.run_streamed() instantiate agents.run.AgentRunner and delegate turn execution to _run_single_turn().
  2. Asynchronous Task Dispatch: When run_in_parallel=True, _run_single_turn() dispatches input guardrails as background asyncio.Task instances or via asyncio.gather().
  3. Un-Awaited Tool Execution: Concurrently, assistant turn item processing forwards model function call outputs directly to tool_execution.py:execute_tool_call(). execute_tool_call() invokes the tool function without awaiting the completion of the input guardrail tasks.
  4. Post-Commit Exception Raising: The runner gathers input guardrail results at turn completion. If tripwire_triggered == True, the runner raises InputGuardrailTripwireTriggered (T4), but state modification (T3) has already been written to the persistent sink.

3. Experimental Methodology & Harness Architecture

To isolate SDK task loop scheduling mechanics from network I/O jitter, the experiment uses:

  • Offline Scripted Model: An offline ScriptedModel emitting deterministic ResponseFunctionToolCall and ResponseOutputMessage turn items.
  • Persistent Local Sink: A local SQLite database (effects.db for non-streaming, stream_effects.db for streaming) recording committed_effects and telemetry_events.
  • High-Resolution Monotonic Telemetry: Monotonic nanosecond timers (time.perf_counter_ns()) logging lifecycle events (T0 to T4).
  • Latency Control: Synthetic latency parameterization: 50 ms for slow DENY guardrails, 5 ms for fast tool execution.

4. Empirical Findings Across 1,200 Trials

We executed 1,200 trials (N=100 per condition across 6 non-streaming and 6 streaming conditions):

Condition Runner Engine Latency Spec Barrier Mode Chronological Race (T3 < T1) SQLite Commits Tripwire (T4) Empirical Outcome
R2-A Runner.run() G=50ms, T=5ms None 99.0% (99/100) 100/100 100/100 Chronological Race (T3 < T1)
R2-B Runner.run() G=50ms, T=5ms None (Sequential) 0.0% (0/100) 0/100 100/100 Precedence Maintained (0 commits)
R2-C Runner.run() G=5ms, T=50ms None 0.0% (0/100) 100/100 0/100 Coincidence (T1 < T3)
R2-D1 Runner.run() G=50ms, T=5ms Barrier (ALLOW) 0.0% (0/100) 100/100 0/100 Authorized Commit Permitted
R2-D2 Runner.run() G=50ms, T=5ms Barrier (DENY) 0.0% (0/100) 0/100 100/100 Barrier Enforced (0 commits)
R2-E Runner.run() G=50ms, Error None 0.0% (0/100) 0/100 100/100 Error Propagated Cleanly
S-A Runner.run_streamed() G=50ms, T=5ms None 99.0% (99/100) 100/100 100/100 Chronological Race (Identical to R2-A)
S-B Runner.run_streamed() G=50ms, T=5ms None (Sequential) 0.0% (0/100) 0/100 100/100 Precedence Maintained (Identical to R2-B)
S-C Runner.run_streamed() G=5ms, T=50ms None 0.0% (0/100) 100/100 0/100 Coincidence (Identical to R2-C)
S-D1 Runner.run_streamed() G=50ms, T=5ms Barrier (ALLOW) 0.0% (0/100) 100/100 0/100 Authorized Commit (Identical to R2-D1)
S-D2 Runner.run_streamed() G=50ms, T=5ms Barrier (DENY) 0.0% (0/100) 0/100 100/100 Barrier Enforced (Identical to R2-D2)
S-E Runner.run_streamed() G=50ms, Error None 0.0% (0/100) 0/100 100/100 Error Propagated (Identical to R2-E)

5. Four-Way Categorization of Execution Outcomes

To maintain strict epistemological clarity, execution outcomes are partitioned into four distinct categories:

  1. Category 1 — Chronological Precedence Race (T3 < T1): Side effect committed (T3) before guardrail decision logged (T1). Observed in 99.0% of parallel DENY trials (99/100 in R2-A and 99/100 in S-A).
  2. Category 2 — Post-Verdict Commit Before Tripwire Exception (T1 < T3 < T4): Observed in 1 trial per parallel DENY suite (R2-A Trial 31, Δ = +13.35 ms; S-A Trial 1, &Delta = +38.02 ms). Python asyncio event-loop task scheduling jitter is a plausible contributing factor for delaying T1 logging; however, tool dispatch T2 was still executed and committed state before T4. The SDK runner did not block tool dispatch upon T1 completion.
  3. Category 3 — Side-Effect Persistence Prior to Tripwire Exception: In 100% of parallel DENY trials lacking an authorization barrier (100/100 in R2-A and 100/100 in S-A), local database state was committed (T3) prior to the runner raising the tripwire exception (T4).
  4. Category 4 — Complete Side-Effect Suppression Under Authorization Barrier: Introducing an explicit application-level authorization gate (R2-D2 and S-D2) yielded 0/100 commits on DENY across 200 trials.

6. Transparency, Reproducibility & Editorial Precautions

6.1 Verification & Reproducibility Notice

All empirical statistics reported in this paper were verified locally by LatinFlash Research using frozen execution traces (r2_replication_results.json, stream_replication_results.json) and local SQLite database instances (effects.db, stream_effects.db). Researchers can independently re-run the replication harness using:

python experiments/a02-07-guardrail-happens-before/r2/r2_repro_harness.py 100 --out-dir experiments/a02-07-guardrail-happens-before/r2-repro
python experiments/a02-07-guardrail-happens-before/r2-stream/stream_repro_harness.py 100 --out-dir experiments/a02-07-guardrail-happens-before/r2-stream-repro

6.2 Status of JEV Automated Review

Evaluations generated by JEV (jev-1.13.0) represent automated LLM-based rubric assessments of report text and source excerpts. JEV scoring does not constitute external academic peer-review or independent physical validation of SQLite disk transactions. JEV results are preserved strictly as supplementary audit metadata.

6.3 Telemetry & Timezone Disclosure

Telemetry intervals (Δ = T3 − T1) are calculated using Python's monotonic high-resolution clock (time.perf_counter_ns()), unaffected by system clock adjustments. Wall-clock run metadata timestamps recorded in JSON headers and SQLite metadata tables are stored in standard UTC ISO-8601 format (e.g. 2026-09-24T10:56:00Z). Local workspace log timestamps correspond to CEST (UTC+2, e.g. 2026-09-24T12:56:00+02:00).

7. Methodological Scope & Boundaries

  • Latency Parameterization: The 99.0% race rate is parameter-dependent (50 ms guardrail vs 5 ms tool execution). It is not asserted as a universal empirical probability for all production workloads.
  • SQLite Local Sink: Local SQLite database commits demonstrate persistent state writes in an offline harness; they are not asserted as universally irreversible operations or equivalent to live external financial API transactions.
  • SDK Version Scope: Conclusions apply specifically to OpenAI Agents SDK v0.22.3.

9. Provenance & Hashes

Verified Dependencies & Environment:
--------------------------------------------------------------------------------
Python Version:     3.11.15
SDK Package:        openai-agents v0.22.3
OpenAI Client:      openai v1.65.5
Pydantic:           pydantic v2.10.6
Database Driver:    sqlite3 v3.45.3
Operating System:   Windows-10-10.0.19045-SP0

Corpus SHA-256 Hashes:
--------------------------------------------------------------------------------
r1-snapshot/harness.py:
  ff5de1f3e98f06003717eaab49c0f3c6652c2d39e490960ff5d4803703885216

r2/r2_harness.py:
  25212b883bc9d79e893544c7f6fea0f4851780969de91dba330dfc701bb1d527
r2/effects.db:
  7a70f1279b827bb1749255846982a4b7fc22077a50142be0efe1055281cab95d
r2/raw_traces/r2_replication_results.json:
  a2074faabb0d4296702af301225cc16bed1b5768aab8401641c642e4937dbb04

r2-stream/stream_harness.py:
  e94e87ca2b21db97e007b7bc3f3a05e64d551d3c6579c81eee271b4d27097d45
r2-stream/stream_effects.db:
  1834285fdd2153e66275a90dfe3394204dd50913f4889b4aa2c2546e6ad4c197
r2-stream/raw_traces/stream_replication_results.json:
  16f0280c95e04ba30c9bed942a37de80da15652893ada9d0e30d4b83ad446d65
--------------------------------------------------------------------------------
Publication Manifest Verification: 57 / 57 Binary SHA-256 PASS (0 Mismatches)