Coding Agents as Execution Systems
A technical walk through how a coding agent explores, plans, edits, verifies, and recovers while fixing a bug across UI, API, and persistence.
Consider a requirement that does not sound extraordinary:
An asynchronous task has completed and its result is visible on the current page. After a refresh, the result disappears. Fix the issue and add regression coverage.
It asks for neither an architectural rewrite nor a difficult algorithm. It is still enough to separate a model that generates code from a coding agent that completes engineering work.
The system must discover whether “disappears” belongs to UI state, caching, an API response, or persistence. It must find the real call path, modify its owner without breaking existing behavior, and produce evidence that the result survives a refresh.
The model does not have this information in its first turn. It can only reduce uncertainty through the repository, tools, and feedback.
This leads me to a more useful definition:
A coding agent is a closed-loop execution system that continuously turns an engineering goal into a verifiable code change.
Code generation is one step. Delivery depends on how the execution system preserves the goal, selects context, constrains modifications, gathers evidence, and continues after interruption.
Turn the request into a success contract
The user gives the agent a natural-language goal, not an executable specification. Its first responsibility is not to search immediately for async, but to translate the goal into conditions that can be checked.
For this example, a minimum success contract could be:
Given: an asynchronous task completed and produced an artifact
When: the page refreshes and reloads the task from the server
Then: the artifact remains visible
And: existing behavior for old, running, and failed tasks is unchanged
And: a regression test fails before the fix and passes after it
The contract does three things. It turns a visual symptom into a state-restoration problem. It limits scope to the reload path rather than inviting a rewrite of the task system. And it defines the evidence required for completion.
A success contract does not always need to become a formal specification file. For a small change it may live in task state or a short plan. What matters is that the agent and verifier share the same conditions. Otherwise the model optimizes for “looks fixed” while the test proves something else.
We can express completion as a conjunction of predicates:
done(task) =
root_cause_explained
∧ minimal_change_applied
∧ regression_test_passed
∧ relevant_existing_checks_passed
∧ diff_within_scope
If any predicate lacks evidence, a polished summary should not terminate the task.
Repository discovery is not reading everything
The next job is to build a local map of the system.
Reading every file from the repository root is expensive and rarely reduces the most important uncertainty. Better discovery is question-driven: each read should test a hypothesis.
At the beginning, the symptom may have four competing or overlapping explanations:
- The result exists only in client memory and is never persisted.
- The server persists it but omits it from the query response.
- The API returns it but the client drops the field during hydration.
- The data exists but refresh uses a different task ID or cache key.
The agent can trace both the write and read paths:
Task completes
→ completion handler
→ persistence write
→ task record / artifact reference
Page refreshes
→ route loader
→ task query
→ response mapper
→ client store
→ artifact renderer
Repository instructions are part of this map. Build commands, directory scope, protected files, test conventions, and the current worktree all change what the agent is allowed to do. AGENTS.md and similar project guidance are not decorative background; they are policy and context inputs to execution.
Before editing, a mature coding agent should know at least:
- the current branch and uncommitted changes, so it does not overwrite user work;
- the files that actually participate in the call path;
- the smallest reproduction and existing test entry points;
- the project’s required verification commands;
- whether the fix implies a migration, external resource, or another high-risk action.
Discovery stops when the agent has evidence that can distinguish the leading hypotheses, not when it understands the entire repository.
Planning is a mutable hypothesis, not a fixed workflow
Many coding agents expose a plan or task list. This can suggest that a separate planner must run first and that execution then follows a fixed sequence.
A more useful interpretation is that the plan externalizes the best current hypothesis.
Initial plan
1. Trace completion write path
2. Trace refresh read path
3. Reproduce the mismatch
4. Patch the owning boundary
5. Add regression coverage
6. Run targeted verification
If step two shows that the backend returns the artifact correctly while a client mapper drops unknown fields, the remaining work should narrow around that mapper and its type contract. If no persistence write exists, the agent must instead reconsider the data model, idempotency, and compatibility with existing task records.
The plan must change with evidence. It is useful because it:
- keeps remaining work visible during a long task;
- lets the user catch a misunderstanding of scope;
- preserves intent across interruption or compaction;
- distinguishes investigation from completion;
- prevents a local success from displacing the regression test.
Not every task needs a dedicated planner component, and not every change deserves a specification document. A plan can remain an implicit model policy or become explicit through a task list. Complexity should follow uncertainty, duration, and risk rather than forcing every request through the same workflow.
Context engineering: admit evidence into the working set
A repository can contain millions of lines while the model context remains finite. A central capability of a coding agent is not memorizing the codebase but selecting the right slice for the current decision.
For this fix, the initial working context might contain only:
Goal and success contract
Repository instructions
Relevant route and component
Task API schema
Completion persistence code
Existing tests around task restoration
Current hypothesis and unresolved questions
Search results rarely deserve permanent residence. They are indexes that lead to evidence. After reading a file, the agent should preserve the interfaces, invariants, and call relationships that affect the decision rather than keeping hundreds of source lines in context indefinitely.
This requires three different information classes:
- Working context contains code and evidence required for the next judgment.
- Task state contains the active hypothesis, modified files, test results, and blockers.
- Durable memory contains project conventions and verified knowledge useful to future work.
When the context approaches its limit, compaction should preserve execution continuity rather than merely summarize the conversation. A useful compacted state includes:
Goal and non-negotiable constraints
Confirmed root cause
Decisions and why they were made
Files read and files modified
Failed approaches and their evidence
Tests already run and exact outcomes
Open questions / blockers
Next safest action
Explicit not-to-do items
If compaction retains only “we are fixing an artifact that disappears after refresh,” the agent can continue speaking but can no longer continue working reliably.
The execution loop: use action to remove uncertainty
Discovery, planning, and modification are not three strictly separated phases. Real execution resembles an evidence-driven loop:
Observe → Hypothesize → Choose action → Execute
↑ ↓
└──── Update state ← Interpret result ─┘
A plausible trajectory for the example is:
1. Search for the refresh loader
2. Read the task response type
3. Trace where completed artifacts are written
4. Compare the persisted record with the response mapper
5. Reproduce the missing field in an existing test harness
6. Patch the mapper or owning contract
7. Add a regression assertion
8. Run the narrow test
9. Run relevant typecheck / broader suite
10. Inspect the final diff against scope
Every step should reduce at least one uncertainty. Repeating the same search without producing evidence means the agent needs another strategy. A failed test with an error unrelated to the hypothesis calls for explaining the environment, not making more blind edits.
Search, read, edit, and execute are different capabilities
Calling all four operations tool calls hides important differences.
- Search expands the candidate space, but its results are not usually facts.
- Read acquires evidence, but the evidence may be truncated or lack runtime context.
- Edit changes the worktree and must preserve existing user modifications and control the diff.
- Execute may produce build artifacts, network requests, or external side effects; its risk depends on the command and environment.
The editing primitive also affects reliability. Full-file rewriting can disturb unrelated formatting. A contextual patch is easier to review but fails when its context has drifted. AST editing is strong for well-structured mechanical transformations but does not fit every language or semantic change. No primitive is always best; the harness should let the agent choose by change type and verify the result with the diff.
A minimal change is a control strategy
“Change only the boundary that owns the root cause” is more than code aesthetics. It controls agent risk. As a diff grows, the verifier must cover a larger state space and the chance of colliding with user work increases.
If the refresh bug comes from one omitted field in an API mapper, simultaneously refactoring the client store, renaming the artifact type, and migrating the database turns a verifiable fix into a multivariable experiment.
The agent can report adjacent problems as follow-up work. It should not silently expand current scope.
Sandboxes and permissions: execution needs boundaries
A coding agent reads a partially untrusted environment. Repository text, dependency scripts, test output, web pages, and even issue descriptions can influence the model. A system prompt telling the model not to run dangerous commands is not a sufficient security model.
The execution system must independently control:
- readable and writable directories;
- network availability;
- commands that may run automatically;
- actions that require one-time or persistent approval;
- whether secrets enter child processes;
- access to Git state, dependencies, databases, and external systems.
A permission request should carry intent: why the capability is needed, what it affects, and whether a narrower grant exists. The user approves a concrete action, not an abstract trust in the agent.
This makes a sandbox more than a security add-on. It shapes the action space of the agent and allows autonomy to remain inside an acceptable blast radius.
Verification: do not trust “fixed”
After the edit, the agent can gather at least five levels of evidence:
- Static evidence: types, lint, schemas, or compilation succeed.
- Local behavioral evidence: the new regression test passes.
- Adjacent regression evidence: the relevant suite remains intact.
- Change evidence: the final diff stays within scope and preserves user changes.
- Runtime evidence: the artifact reloads through a real refresh path or a representative integration environment.
Different tasks require different highest levels. A pure function can end with unit tests. A restoration bug crossing UI, API, and persistence usually needs more than a typecheck. For production writes, payments, permissions, or external messages, code tests cannot replace an independent read from the system of record.
Independent evidence matters. Asking the same logic that produced the patch to verify it only by rereading the code is weak. Tests, type systems, database constraints, runtime probes, and user acceptance provide feedback from different sources.
The verifier itself can be wrong. A test that asserts the artifact appears before refresh does not cover the original goal. Verification quality depends on fidelity to the success contract, not the number of tests.
Durable state: the task should outlive a model call
A real fix may cross context compaction, process exit, user pause, permission wait, or external CI. Resuming the conversation is not automatically the same as recovering the task.
Reliable recovery needs a minimum checkpoint:
Task checkpoint
├── goal_version
├── success_contract
├── current_status
├── confirmed_facts
├── active_hypothesis
├── completed_actions
├── modified_files + worktree identity
├── verification_evidence
├── pending_approvals / blockers
├── next_action
└── not_to_do
Authority matters more than preserving every sentence. After recovery, the agent must know which worktree contains the edit, which tests actually ran, whether an external action remains in an unknown state, and whether a later user instruction replaced the original goal.
A transcript explains the past. A checkpoint allows execution to continue.
Public design choices in three implementations
The following is not a capability ranking and does not infer private implementation details. It is a dated view of publicly documented harness designs as of August 2026.
| Dimension | Claude Code | Codex | Pi |
|---|---|---|---|
| Task continuity | Saves local sessions with --continue / --resume; worktrees isolate parallel work |
App Server exposes thread / turn, resume, and compact lifecycles; the CLI also resumes sessions |
Open-source core manages sessions and agent state, with automatic compaction and branch summaries |
| Project context | CLAUDE.md, rules, skills, and on-demand nested instructions; AGENTS.md can be imported |
Hierarchical AGENTS.md combines with developer instructions, skills, and tool context |
Loads project context files and skills while keeping the default prompt and tool set small |
| Planning | Plan Mode is a permission-constrained read-only phase before approved edits | Plans can be explicit task and collaboration state without requiring a fixed planner for every request | Plan mode can be implemented as an extension, illustrating a small-core, extensible-policy design |
| Execution boundary | Read-only by default; permissions, sandboxing, and working-directory boundaries control edits and commands | Sandbox policy, writable roots, and approval policy are explicit runtime inputs | Defaults to read, bash, edit, and write; extensions can alter remote execution and behavior |
| Editing and verification | File tools, shell, and reviewable changes; project rules define task-specific checks | apply_patch, shell, review, and project verification commands form an evidence path |
Direct edit tool; extensions can add Git checkpoints, tool wrappers, and custom event handling |
| Extension surface | Skills, hooks, MCP, subagents, and plugins | Skills, MCP, apps, subagents, and the App Server protocol | Extensions register or replace tools, observe events, and customize compaction |
All three provide loops, tools, context, and some session continuity, but they place complexity in different locations.
Claude Code’s public documentation emphasizes project instructions, permission modes, sessions, and multiple extension surfaces. Sessions are saved locally and can be resumed, while mutating actions are governed by permissions and sandboxing.
Codex exposes its execution lifecycle in a more protocol-shaped form. The public App Server represents client-runtime interaction through threads, turns, items, approvals, and compaction. Hierarchical AGENTS.md files supply project constraints, while sandbox and permission policies bound execution.
Pi illustrates a small-core route. Its public system prompt organizes the default agent around read, bash, edit, and write. Extensions can replace tools, observe lifecycle events, implement plan mode, or add Git checkpoints. Compaction preserves structured summaries and file activity.
The useful conclusion is not that one has more features. It is that there is no single canonical component diagram for a coding agent, but reliable designs make context, execution boundaries, task continuity, and completion evidence explicit.
The complete sequence of one task
Returning to the refresh bug, the execution can be represented as:
User
│ goal
↓
Goal Contract ────────────────┐
│ │
↓ │
Repository Discovery │
│ instructions + evidence │
↓ │
Working Hypothesis │
│ │ update on new evidence
↓ │
Context Builder ← Task State ─┘
│
↓
Model Decision
│
├─ search / read ─→ observation ─┐
├─ edit ─────────→ diff ─────────┤
└─ execute ──────→ result ───────┤
↓
State Update
│
incomplete?├──→ next loop
│
↓
Verification
│
failed evidence├──→ revise hypothesis
│
↓
Evidence Bundle + Done
Done should carry an evidence bundle, not only a summary: the root cause, changed files, checks run, checks not run, remaining risks, and a path for the user to verify the result.
The final answer becomes a readable projection of task state rather than the task state itself.
The real product boundary of a coding agent
Models are increasingly good at producing locally correct code. That matters. The hard parts of software engineering, however, rarely exist only inside one function. They live in incomplete requirements, hidden call paths, dirty worktrees, irreversible side effects, cross-session state, and the question of how we know the system is actually fixed.
A reliable coding agent combines two kinds of capability:
- It uses a model to handle semantic uncertainty: understand the goal, form hypotheses, and choose the next action.
- It uses engineering systems to control execution: permissions, state, idempotency, scope, tests, and recovery.
The first lets it advance through unfamiliar problems. The second prevents every clever judgment from becoming an uncontrolled action.
A coding agent is therefore neither a more intelligent autocomplete nor a model wrapped in while tool_calls. It is an execution system around an engineering goal: it continuously turns unknowns into evidence, evidence into changes, and changes into verifiable results.
When it says “done,” the important questions are not how confident the sentence sounds. They are where the change is, why it belongs there, what proves it works, and whether the task can resume in the same factual world if execution stops now.


Discussion
Comments
Questions, disagreements, and useful additions are all welcome.