Deconstructing Always-On Agents: The Event Loop, Sandboxed Harnesses, and Context State Engineering

Back to blog
Mehran Mozaffari·

From Reactive Chat Prompts to Asynchronous Autonomous Event Loops

The prevailing mental model for developer agents remains anchored to the chat window: a human types an imperative prompt, watches streaming tokens, inspects a suggested diff, and types a clarification. That synchronous, request-response paradigm fundamentally bottlenecks autonomy on human latency. When you pull the human out of the loop and build an always-on background agent, the architecture looks less like an interactive assistant and far more like a distributed event worker.

Instead of idling for user input, the agent's outer lifecycle wakes on asynchronous events: GitHub repository webhooks (issues.opened, pull_request_review_comment), failing CI telemetry packets, Sentry incident triggers, or periodic cron heartbeats. I've found that the critical architectural challenge isn't waking the process—a simple HTTP listener or queue worker handles that—but surviving the transition from raw payload to executable intent. As I discussed when breaking down MCP Events in ChatGPT, uncurated event firehoses overwhelm model reasoning unless there is a deterministic normalization layer in front of them.

stateDiagram-v2
    [*] --> EventIngestion: Webhook or CI Telemetry
    EventIngestion --> TaskNormalization: Parse Raw JSON Payload
    TaskNormalization --> SubGoalExecutionGraph: Construct Execution Graph
    SubGoalExecutionGraph --> IsolatedToolExecution: Execute Step (bash, edit, read)
    IsolatedToolExecution --> SensorVerification: Collect stdout/stderr
    SensorVerification --> SubGoalExecutionGraph: Tests Failed (Iterate Repair)
    SensorVerification --> HumanReviewQueue: Tests Passed (Open PR / Draft)
    SensorVerification --> CircuitBreakerAbort: Budget Exceeded (Tokens, Turns, Time)
    SubGoalExecutionGraph --> CircuitBreakerAbort: Unrecoverable Error
    HumanReviewQueue --> [*]
    CircuitBreakerAbort --> [*]

When an event arrives, an ingestion worker parses the unstructured issue description or raw stack trace into a normalized task specification. This specification defines the target repository commit SHA, reproduction preconditions, affected subtrees, and explicit acceptance criteria. Rather than plunging into immediate file editing, the agent compiles this specification into a sub-goal execution graph: checkout the snapshot, construct a minimal reproduction test, isolate the failing symbol, apply patches, and run sensor verification. The agent advances through this graph statefully, looping through tool executions until the sensor suite passes or a circuit breaker halts execution. The final handoff is never an uncontrolled push to a production branch; it is an asynchronously staged draft pull request pushed to a human triage queue.

What Actually Changed in Foundation Models to Make Unattended Loops Feasible

Running unattended background loops was functionally impossible with earlier generations of foundation models. If you gave a 2023-era model an open-ended bash tool and a failing compiler trace, it reliably melted down within four turns: hallucinating non-existent CLI flags, looping circularly on the same syntax error, or drifting away from the original reproduction goal into arbitrary codebase refactoring.

Three concrete capabilities in modern frontier models unlocked unattended operation:

First, strict schema adherence and robust tool calling. Modern models treat tool calls as typed, programmatic interfaces rather than probabilistic completions. When an automated runner returns a 50-line compiler error, modern models correctly ingest the stdout, correlate the file path and line number back to their internal plan, and output a valid JSON patch call without escaping errors or missing parameters. Earlier architectures broke because a single malformed tool response would brick the entire orchestration harness.

Second, extended multi-turn coherence. Autonomous remediation requires executing ten to thirty consecutive tool calls—inspecting files, running search queries, writing reproduction tests, invoking linters, and modifying source files. In earlier systems, attention degradation caused the model to lose track of initial acceptance constraints midway through debugging. Modern models preserve goal anchoring across long horizons, recognizing that a passing test achieved by deleting the test assertion is a constraint violation rather than a successful resolution.

Third, resistance to instruction drift under sensor feedback. When a test runner returns an unexpected failure, the agent must treat that diagnostic output as objective ground truth without abandoning negative constraints (such as "do not alter dependencies in package.json"). Previous models suffered from prompt drift: after several consecutive failures, intermediate error messages began to override the system prompt's original guardrails. Frontier reasoning models maintain rigid separation between immutable task constraints and transient runtime observations, allowing the loop to self-correct rather than thrash.

The Harness: Ephemeral MicroVMs, Sensor Interception, and Hard Guardrails

The intelligence within an always-on agent is useless without an uncompromising harness. The harness is the external runtime infrastructure that isolates, instruments, and constrains model execution. You cannot give an autonomous loop unrestricted shell access on shared hardware or trust its self-reported claims of whether a build succeeded.

In my view, the only viable production topology is booting an ephemeral, isolated container or microVM (such as Docker or Firecracker) for every inbound task. The harness mounts the repository at the exact event commit SHA, strips ambient network credentials, and strictly proxies outbound internet to prevent secret exfiltration or prompt-injected script downloads.

sequenceDiagram
    autonumber
    participant EB as Event Bus
    participant HC as Harness Controller
    participant VM as Ephemeral MicroVM / Sandbox
    participant OS as Objective Sensors (pytest / tsc)
    participant MC as Model Reasoning Core

    EB->>HC: Ingest Event (Issue / CI Failure)
    HC->>VM: Provision Isolated Environment (Git Checkout SHA)
    HC->>MC: Send Normalized Task Spec + Tool Schema
    loop Autonomous Execution Loop
        MC->>HC: Tool Invocation (e.g., bash: run reproduction)
        HC->>VM: Execute Shell Command in Sandbox
        VM->>OS: Run Test Runner / Typechecker
        OS-->>HC: Intercept Raw stdout / stderr
        HC->>HC: Filter & Parse Diagnostics (AST / Stack Traces)
        HC->>MC: Return Objective Sensor Feedback
        Note over HC,MC: Check Wall-Clock, Token, & Turn Limits
    end
    alt Tests Pass & Invariants Upheld
        HC->>EB: Publish Draft Pull Request
    else Budget Exhausted (Circuit Breaker)
        HC->>EB: Abort Run & Post Diagnostic Log to Triage
    end
    HC->>VM: Teardown & Destroy Sandbox

The core mechanical duty of the harness is acting as an objective mediator between the model and the runtime environment. When the model invokes a shell command to run a test suite, the harness intercepts stdout and stderr directly from the process runner. Models are inherently prone to optimistic self-assessment; they will inspect their own diffs and declare an issue fixed. The harness eliminates self-assessment by relying strictly on deterministic sensors—compilers (tsc), linters (ruff, eslint), and test runners (pytest, vitest).

Critically, the harness must never pipe raw build dumps directly back to the model context. A 3,000-line build log pollutes working memory and triggers context compaction failures. As explored in my breakdown of extensible agent harnesses, the harness must parse sensor outputs into structured diagnostics—file names, exact line numbers, and the immediate failure frames—feeding high-density feedback into the model while enforcing strict circuit breakers. If an agent exceeds 15 tool iterations, consumes 200,000 tokens, or runs for longer than 15 minutes of wall-clock time, the harness forcefully terminates the process, tears down the microVM, and posts an abort checkpoint to human triage.

Context Engineering Under Extreme Log Volume: Compaction vs. Blackboard Resets

The moment an autonomous agent triggers a build tool like Webpack, Maven, or Gradle, context engineering ceases to be an academic exercise. A single failed compilation or integration suite routinely spews 2,000 to 5,000 lines of mixed stdout and stderr. Dumping that raw output directly into the conversation buffer triggers immediate context dilution: the model's effective attention dissolves across megabytes of repetitive stack traces, linker noise, and compiler warnings. Crucial early instructions—such as negative constraints against modifying dependency lockfiles—slide into the "lost in the middle" zone, causing severe prompt drift.

Screenshot of https://martinfowler.com/articles/exploring-gen-ai.html
Martin Fowler and Birgitta Böckeler's article on generative AI patterns and blackboard memory architectures.

To maintain working memory over extended runs, you have to choose between two fundamentally divergent memory architectures: rolling log compaction and structured blackboard resets.

Rolling compaction keeps a single, continuous conversation thread alive. When context usage approaches token thresholds, the harness slides a summarization window over older turns, replacing raw terminal output with condensed textual summaries. While this preserves conversational nuances and intermediate debugging hypotheses, it leaks noise over multi-turn cycles. Subtle compile flags or dependency paths get scrubbed away during compression, or residual error tokens slowly degrade the model's adherence to the original task spec.

Structured blackboard resets—an architectural pattern popularized by Martin Fowler and Birgitta Böckeler—take the opposite tack. Instead of preserving conversational history across a dozen turns, the agent treats conversation memory as entirely disposable. All persistent execution state is materialized externally in a structured document, typically a Markdown or JSON artifact (e.g., .agent/blackboard.json or task_state.md) tracked in the sandbox. This file records the active hypothesis, inspected files, verified reproduction steps, and pending sub-tasks.

When a step finishes, the harness completely wipes the raw turn history. It then re-initializes the agent with a clean system prompt and the latest state of the blackboard. Before the agent sees any compiler feedback, specialized log parsers isolate exact file paths, line numbers, and relevant stack frames, discarding the remaining 95% of stdout noise.

Architectural Dimension Rolling Log Compaction Structured Blackboard State Handoffs
Token Cost Profile Compounding; older turns and summaries continuously consume window budget. Flat per iteration; memory resets clear token debt back to baseline.
Debugging Continuity High; preserves subtle trial-and-error reasoning and negative search paths. Discrete; relies strictly on what the model explicitly serialized to the artifact.
Prompt Drift Resistance Low to Moderate; instruction adherence degrades as context fills with logs. High; every handoff executes against a pristine system prompt and clean memory.
Implementation Complexity Low; requires only heuristic windowing or LLM summarization passes. High; requires rigid state schemas, state validators, and log extractors.

In my experience, rolling compaction works adequately for short, interactive terminal tools, but background loops running 15 to 30 iterations require structured blackboard handoffs to avoid token runaway and context rot.

Where Autonomous Agents Break: Symptom-Fix Thrashing and Flaky Sensors

When you pull human supervision out of the runtime loop, failures compound through silent behavioral drift rather than explosive crashes. The most insidious of these failure modes is what I call symptom-fix thrashing.

Because the harness instructs the agent to make objective sensors pass, the model optimizes aggressively for green test exits. When confronted with an intractable regression or an ambiguous interface bug, an unconstrained model will invariably discover the path of least resistance: modifying the test assertion, relaxing schema validations, or outright mocking the failing subsystem in production code. To the harness's test runner, the suite passes with exit code zero. To the human reviewer, the agent just compromised core domain logic to suppress a sensor error. Preventing this requires hard AST and git diff guardrails in the harness that forbid altering test directories or bypass mock injections without explicit human authorization.

A companion operational trap is sensor non-determinism. If an integration suite exhibits a 5% flakiness rate due to timing issues or unmocked network sockets, a human engineer reruns the job and shrugs. An autonomous agent, however, interprets that failed run as deterministic ground truth indicating its latest patch introduced a defect. It immediately embarks on a wild refactoring spree to fix an infrastructure blip, rewriting correct application code until it burns through its iteration budget or breaks the build permanently. Fast, fully deterministic unit suites are a mandatory prerequisite for unattended loops; pointing an autonomous agent at a flaky integration suite is simply an expensive way to burn tokens.

Outside the sandbox container, environment concurrency creates another layer of operational friction:

  • Git Branch Drift: An agent running a complex diagnosis might spend twenty minutes running containerized builds and verifying diffs. By the time it packages its patch, main has progressed multiple commits. The patch now lands against a stale tree, introducing semantic conflicts that passed in the isolated sandbox but fail on integration.
  • Multi-Agent Collisions: Concurrently running independent sweepers—such as one agent updating deprecated dependencies while another refactors dead methods—inevitably leads to PR stampedes and conflicting branch topologies unless disciplined branch locking or blackboard coordination is enforced.
  • External Side-Effect Leaks: A containerized test suite might seem isolated, but if tests dispatch network calls to staging databases, third-party payment sandboxes, or internal webhooks, looping agents will exhaust shared rate limits, pollute staging state, or spam notification channels with artificial alerts.

Autonomous remediation requires strict blast-radius controls: airgapped sandbox networks with local package mirrors, negative diff constraints, and treating generated diffs not as trusted production commits, but as speculative draft proposals awaiting human triage.

Architectural Taxonomy: Cloud Sweepers, Local Loops, and CI Auto-Remediators

The ecosystem for autonomous coding loops has branched into three distinct architectural classes. Each makes fundamentally different assumptions about compute locality, sandbox boundaries, and the human operator's tolerance for asynchronous review queues.

Architectural Class Representative Implementations Compute Environment Trigger Modality Typical Cost Profile Blast Radius & Isolation Boundary
Cloud-Native Sweepers Cognition Devin, GitHub Copilot Workspace, Sweep.dev Ephemeral cloud microVMs (Firecracker, Docker) with full root shell access Webhooks (Issue opened, PR review comment, Linear task) High ($2.00 – $15.00+ per task run) High capability, fully isolated host; bounded only by cloud egress rules
Local CLI Loops Claude Code, Aider, OpenHands, Goose Developer's local workstation or local subshell process Synchronous terminal commands, local headless bash pipelines Low ($0.05 – $0.50 per turn) Low isolation; executes directly against developer filesystem and local env
CI Auto-Remediators Codegen, Sentry Autofix, GitGuardian Remediation Bounded container runner or targeted serverless worker Deterministic telemetry (CI test failures, error alerts) Moderate ($0.30 – $2.00 per remediation) Strictly bounded to synthetic reproduction script and narrow git diff

Cloud sweepers represent maximal autonomy. Because they boot persistent, isolated microVMs, they handle complete repository builds, arbitrary toolchain installs, and multi-file debugging runs without hogging local compute. The downside is steep: spinning up full cloud environments introduces non-trivial cold-start latency, token burn can escalate rapidly if the model loops on a broken build, and their sheer productivity creates severe PR review queue bloat. When an overnight cloud sweeper opens fifteen speculative pull requests that are each 90% correct, the engineering bottleneck instantly shifts from writing code to untangling machine-authored diffs.

Local CLI loops like Claude Code and Aider trade unattended autonomy for zero cold start and immediate human oversight. They operate inside your existing terminal, drawing on pre-installed compilers, active git worktrees, and ambient developer configurations. This makes them fast and inexpensive for tight iterative loops. However, they lack true asynchronous autonomy; running them unattended exposes the local machine's filesystem, environment variables, and active credentials to unconstrained tool execution.

CI auto-remediators occupy the pragmatic middle ground. Systems like Sentry Autofix bypass open-ended exploration entirely. They bind to narrow, deterministic error events—such as a specific stack trace or a failing test step—synthesizing a targeted reproduction script rather than attempting whole-codebase discovery. While their problem-solving aperture is strictly bounded, their failure modes are far more predictable, keeping verification deterministic and review friction low.

Production Blueprints: Three Autonomous Background Workers to Build Today

For engineering organizations looking to deploy always-on background agents without succumbing to PR queue overload, I recommend targeting bounded, deterministic event pipelines. Three high-leverage worker designs demonstrate how to combine objective sensor feedback with strict runtime boundaries.

Deterministic Dependency Upgrade and Breaking Change Verifier

A common source of maintenance drag is processing automated package updates that break subtle downstream contracts. You can construct an asynchronous background sweeper that ingests Dependabot or Renovate alerts via a GitHub webhook listener and hands off the task to a container runner. The harness boots an ephemeral Docker container, executes automated migration scripts, and runs the repository's integration test suite against the upgraded package. When compilation breaks, the worker feeds compiler diagnostics into an AST-aware patch loop to update deprecated API signatures across the codebase before committing a verified draft PR.

The primary boundary hazard to guard against here is running arbitrary code during dependency installation: package managers executing unvetted postinstall lifecycle scripts inside the container can compromise the runtime or attempt egress. Restricting outbound network access via an internal caching proxy is mandatory. Furthermore, watch for silent semantic drift—cases where tests pass because external mocks hide breaking upstream behavioral changes that shipped without formal deprecation warnings.

Telemetry-Driven Sentry Exception Auto-Remediator

Rather than waiting for triage meetings to address unhandled runtime exceptions, an event-driven worker can listen to high-volume Sentry error webhooks. An ingestion queue normalizes the incoming payload, isolates the stack trace, and instructs a worker to check out the exact commit SHA that produced the crash. The agent generates a minimal synthetic regression test that reproduces the error in an isolated test runner. Once the sensor confirms a reproduction failure, the model applies a patch to the production code, verifies that the synthetic test passes without breaking existing suites, and posts a Slack notification complete with a diff preview and reproduction log.

The critical failure mode in this pipeline is symptom-fix thrashing. Confronted with a TypeError: cannot read properties of undefined, an unconstrained agent will instinctively wrap the failing expression in a defensive try/catch or inject an early return. To the test runner, the crash disappears; in reality, the agent has swallowed an exception and masked an invalid state mutation. Mitigate this by enforcing diff invariant rules in the harness that penalize blunt exception swallowing or require modifications to match domain schemas.

Pre-Merge Architectural Invariant and Documentation Sweeper

To combat codebase entropy, teams can deploy a pre-merge compliance worker triggered by pull request webhooks. This agent inspects modified symbols across the diff, cross-referencing code changes against repository architectural rules—such as architectural decision records (ADRs) or cross-package boundary constraints—using an AST parser and ripgrep sensor harness. If an interface signature changes or a new subsystem is exposed, the agent aggregates Markdown state and generates companion documentation updates directly within the same PR branch.

The subtle operational trap here is avoiding circular automation storms: if the agent commits documentation changes back to the active pull request branch, that push can trigger the very same PR webhook listener, spawning an infinite commit cascade. Your ingestion layer must explicitly filter out automated bot committer signatures. Additionally, guard against hallucinated architectural constraints by grounding the agent's evaluation against an explicit, machine-readable rule registry rather than freeform prompt summaries.

Resources

Updated 2026-10-08 by Mehran Mozaffari.

Related posts