Coding agents are becoming increasingly capable of implementing individual software tasks. Give an agent a repository, a clear issue, and enough context, and it can often inspect the codebase, modify files, write tests, and produce a working implementation.
The harder problem starts one level above that.
What happens when we need to implement an entire feature consisting of ten related tasks? Some can run in parallel, some depend on others, some require architectural decisions, and some touch areas where autonomous changes should not be allowed.
At that point, the challenge is no longer simply:
Can an AI agent write the code?
The more useful question becomes:
How do we transform a software initiative into units of work that agents can execute, validate, review, and integrate safely?
This article proposes an end-to-end workflow for implementing an Epic using AI agents while minimizing human intervention without removing the controls required by the risk of the changes.
The core architecture looks like this:
┌─────────────────────┐
│ Epic │
│ intent + constraints│
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Task dependency │
│ graph │
└──────────┬──────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Task A │ │ Task B │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Planner │ │ Planner │
└────┬────┘ └────┬────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Builder │ │ Builder │
└────┬────┘ └────┬────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│Reviewer │ │Reviewer │
└────┬────┘ └────┬────┘
│ │
└───────────────┬───────────────┘
▼
┌─────────────────────┐
│ Epic integration PR │
│ + CI │
└──────────┬──────────┘
│
human approval
│
▼
main
Enter fullscreen mode Exit fullscreen mode
The specific tools are interchangeable. The important part is the workflow.
The Epic as the source of intent
An Epic is useful because it gives the agents a shared description of what the system is supposed to accomplish.
It should contain at least:
- the objective;
- the problem or product context;
- scope and explicit non-goals;
- requirements;
- tasks or user stories;
- acceptance criteria;
- dependencies and risks;
- success metrics.
I would avoid treating the Epic as the absolute “source of truth.”
It is better understood as the central source of intent, requirements, and constraints for the initiative.
The repository still contains the technical reality of the system. Existing APIs, schemas, architectural decisions, infrastructure, tests, and implementation constraints may reveal information that the Epic does not contain.
This distinction becomes important once agents start making decisions.
Imagine that a task only says:
Add retry support to payment processing.
Enter fullscreen mode Exit fullscreen mode
An agent might reasonably ask:
- Which failures are retryable?
- How many retries are allowed?
- Should retries be synchronous or asynchronous?
- What happens to idempotency?
- Can the payment provider receive the same request twice?
- Is retrying outside the scope of a specific payment method?
The task itself may not answer those questions.
The Epic can provide the product and architectural boundaries required to answer them without duplicating the entire context in every issue.
A practical implementation is to represent the Epic as a parent GitHub Issue and its tasks as sub-issues. This keeps the planning artifacts close to the code and allows issues, pull requests, commits, diagrams, files, and technical decisions to reference each other.
Task granularity matters more than prompt count
One of the easiest mistakes when building agentic development workflows is to hand a very large objective directly to a coding agent:
Implement the entire billing Epic.
Enter fullscreen mode Exit fullscreen mode
A sufficiently capable model may still make progress, but the execution becomes difficult to reason about.
The agent must simultaneously:
- discover the architecture;
- interpret requirements;
- make design decisions;
- modify multiple domains;
- keep dependencies consistent;
- validate behavior;
- understand what is in and out of scope.
The problem is not simply context-window size.
The problem is the number of decisions that must remain coherent throughout the execution.
A better workflow reduces the complexity of each execution.
When is a task granular enough?
A useful rule is:
A task is sufficiently granular when it represents one coherent delivery, can be implemented and validated independently, and can produce a pull request that can be understood, tested, and reverted without relying on undeclared changes.
Task size should therefore not be measured primarily by lines of code or number of files.
The more important property is cohesion.
For example, adding a field to an API may require modifying:
database schema
↓
domain entity
↓
service
↓
API endpoint
↓
tests
Enter fullscreen mode Exit fullscreen mode
That can still be one coherent task.
Several layers are affected, but they all implement the same vertical capability.
By contrast, a change touching only three files may still be too broad if it combines:
authentication
+ billing rules
+ event processing
+ infrastructure changes
Enter fullscreen mode Exit fullscreen mode
The useful questions are therefore:
- Does the task have one observable result?
- Does it represent one coherent capability?
- Can it be validated independently?
- Are major architectural decisions already resolved?
- Can the diff be reviewed as one logical unit?
- Can the change be reverted independently?
The last question is particularly useful:
Can a reviewer understand and validate this diff as a single logical change?
If the answer is no, the task probably needs further decomposition.
Separate investigation from implementation
Tasks become especially dangerous when uncertainty and implementation are mixed together.
Consider:
Choose an asynchronous processing architecture and implement it.
Enter fullscreen mode Exit fullscreen mode
This contains at least two fundamentally different types of work:
- deciding what architecture should exist;
- implementing that architecture.
A better decomposition could be:
Task 1 — Investigate asynchronous processing alternatives
Task 2 — Record the architectural decision
Task 3 — Implement the event producer
Task 4 — Implement the event consumer
Enter fullscreen mode Exit fullscreen mode
The first tasks reduce uncertainty.
The later tasks execute against a decision that already exists.
This distinction also makes agent behavior easier to control. We can allow an agent to investigate broadly without implicitly granting it permission to modify the architecture.
A readiness check before implementation
Before a task reaches a Builder, the workflow should verify that it is actually ready to be implemented.
A practical checklist is:
- [ ] There is one clearly defined outcome.
- [ ] Scope and non-goals are explicit.
- [ ] Acceptance criteria are verifiable.
- [ ] Dependencies are declared.
- [ ] No major architectural decision remains unresolved.
- [ ] The change represents a coherent capability.
- [ ] There is an objective validation strategy.
- [ ] The change can produce an independent pull request.
- [ ] The change can be reverted without removing unrelated work.
- [ ] The expected diff is reasonably bounded or its size is justified.
These do not need to become rigid numerical rules.
A 2,000-line generated schema migration may be simpler than a 100-line authentication change.
Cohesion, independence, and verifiability matter more than raw size.
One task, one isolated execution environment
Once tasks can run concurrently, filesystem isolation becomes necessary.
A simple strategy is:
Epic
│
├── integration/epic-payments
│
├── task/payment-retry
│ └── worktree A
│
├── task/payment-webhook
│ └── worktree B
│
└── task/payment-events
└── worktree C
Enter fullscreen mode Exit fullscreen mode
Each Builder receives:
- its own Git branch;
- its own Git worktree or container;
- the context package for the current task;
- the relevant validation commands.
This prevents two agents from directly modifying the same working directory.
It does not, however, eliminate integration conflicts.
Two isolated agents can still independently modify the same API, data model, or subsystem. Their worktrees are isolated operationally, but their changes may conflict semantically when integrated.
The orchestrator must therefore understand task dependencies and integration order.
Model task dependencies explicitly
An Epic should not be treated as a flat task list.
It is better represented as a dependency graph.
For example:
┌───────────────┐
│ Add DB schema │
└───────┬───────┘
│
┌─────────┴─────────┐
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Write producer │ │ Create API │
└───────┬────────┘ └───────┬────────┘
│ │
▼ │
┌────────────────┐ │
│ Write consumer │ │
└───────┬────────┘ │
└──────────┬─────────┘
▼
┌────────────────┐
│ Integration │
│ validation │
└────────────────┘
Enter fullscreen mode Exit fullscreen mode
A task can then have explicit metadata such as:
id: payment-consumer
blocked_by:
- payment-schema
- payment-producer
Enter fullscreen mode Exit fullscreen mode
The orchestrator can execute independent nodes concurrently while waiting for their dependencies.
This is significantly safer than telling several agents to work through the Epic and hoping they discover the correct order themselves.
Planner, Builder, and Reviewer
The workflow uses three main roles.
Planner
The Planner investigates before implementation.
Its responsibilities include:
- inspecting the relevant codebase;
- identifying affected components;
- checking dependencies;
- identifying risks;
- proposing an implementation approach;
- defining validation steps;
- detecting whether the task should be split.
The Planner should not modify production files during this phase.
Its output should be an execution plan, not an implementation.
A typical result might look like:
Affected modules:
- payments/service.ts
- payments/repository.ts
- payments/service.test.ts
Implementation:
1. Add retry classification for transient provider errors.
2. Add bounded exponential retry behavior.
3. Preserve idempotency key across attempts.
4. Add tests for retryable and non-retryable failures.
Validation:
- unit test suite
- payment integration tests
- lint
- typecheck
Risk:
- ensure declined payments are never retried
Enter fullscreen mode Exit fullscreen mode
That output becomes part of the Builder’s context.
Builder
The Builder executes the approved task.
Its responsibilities are intentionally narrower:
- implement the planned change;
- update or create tests;
- run the required validations;
- commit the changes;
- open or update the pull request.
The Builder should not silently redefine acceptance criteria or expand scope because it discovered something interesting during implementation.
If implementation reveals a significant architectural issue, the correct action is usually to escalate the finding back to the orchestrator.
Reviewer
The Reviewer evaluates the result independently.
It should inspect:
- the actual diff;
- the acceptance criteria;
- tests;
- validation output;
- architectural constraints;
- possible regressions.
The review should be based on the expected behavior, not merely on the Builder’s explanation of what it implemented.
That distinction matters because the Builder and Reviewer may otherwise share the same incorrect assumption.
The Reviewer should return concrete findings such as:
BLOCKING
Retry logic also retries PaymentDeclinedError.
Acceptance criterion:
Only transient provider failures may be retried.
payments/service.ts:87
Enter fullscreen mode Exit fullscreen mode
Instead of:
The implementation doesn't look quite right.
Enter fullscreen mode Exit fullscreen mode
Objective findings make automated correction loops possible.
Internal and external orchestration
There are two broad ways to coordinate the agents.
Internal orchestration
A primary agent delegates work through a native multi-agent runtime.
Conceptually:
main agent
│
├── planner agent
├── builder agent
└── reviewer agent
Enter fullscreen mode Exit fullscreen mode
The runtime manages the child executions and returns their results to the parent.
This is useful when delegation is closely tied to the reasoning process of the primary agent.
External orchestration
A separate process controls independent agent executions.
For example:
orchestrator
│
├── agent process -- task A
├── agent process -- task B
└── agent process -- review A
Enter fullscreen mode Exit fullscreen mode
The executions communicate through structured output, files, Git, APIs, or another durable mechanism.
External orchestration is particularly useful when we need:
- deterministic workflows;
- separate worktrees or containers;
- explicit concurrency;
- retries;
- timeouts;
- task queues;
- persistent execution state;
- provider-independent agents.
The architecture described in this article favors external orchestration for the main workflow while still allowing individual agents to use internal subagents when useful.
Give each task only the context it needs
The Epic contains global information.
That does not mean every agent should receive the entire Epic, every previous conversation, and every implementation log.
Instead, the orchestrator should build a task context package.
For example:
epic:
objective: Add asynchronous invoice processing
constraints:
- existing synchronous API must remain compatible
task:
id: invoice-event-producer
objective: Publish an event after invoice creation
acceptance_criteria:
- exactly one event is emitted after a successful transaction
- failed transactions must not emit events
dependencies:
completed:
- invoice-event-schema
architecture:
- ADR-014-event-bus.md
relevant_files:
- src/invoices/service.ts
- src/events/publisher.ts
validation:
- npm test -- invoices
- npm run typecheck
instructions:
- AGENTS.md
Enter fullscreen mode Exit fullscreen mode
The pipeline becomes:
Epic
↓
context selection
↓
task-specific context
↓
isolated execution
↓
validated result
↓
Epic integration
Enter fullscreen mode Exit fullscreen mode
Long context windows are useful, but they should be treated as available capacity rather than a target to fill.
More context is not automatically better context.
Excessive context can introduce:
- obsolete information;
- conflicting instructions;
- irrelevant implementation details;
- old architectural assumptions;
- competing objectives.
Context engineering is therefore part of orchestration.
The question is not:
How much information can the model receive?
It is:
What is the minimum sufficient context required to make this decision correctly?
Define autonomy as a risk policy
Reducing human intervention does not mean giving agents unrestricted permissions.
The workflow should define which actions are safe to perform automatically and which require approval.
A possible policy is:
Autonomous
Agents may perform these operations inside their isolated task environment:
- edit application code;
- add or update tests;
- run tests, linters, and static analysis;
- create commits;
- open pull requests;
- apply Reviewer-requested fixes that remain within the approved scope.
Autonomous with additional validation
Changes in this category may proceed automatically only when specific validation rules succeed:
- dependency updates;
- non-destructive database migrations;
- API contract changes;
- shared configuration changes;
- changes affecting multiple domains or services.
Human approval required
Examples include:
- production deployments;
- destructive migrations;
- secret access or modification;
- infrastructure mutations;
- authentication or authorization changes;
- billing operations;
- irreversible data transformations;
- final integration into the main branch.
These categories should not be universal constants.
A migration adding a nullable column may be routine in one system and dangerous in another with billions of rows.
The correct abstraction is therefore not a hardcoded list of actions.
It is a risk policy.
The complete implementation cycle
We can now put the pieces together.
1. Create the Epic
The human or product process defines:
objective
problem
scope
non-goals
requirements
acceptance criteria
risks
success metrics
Enter fullscreen mode Exit fullscreen mode
At this stage, the emphasis is on what needs to be achieved, not exactly how every part will be implemented.
2. Decompose the Epic
A planning process converts the Epic into tasks.
Each task is checked for:
cohesion
independence
testability
reversibility
architectural uncertainty
Enter fullscreen mode Exit fullscreen mode
If major design uncertainty exists, investigation tasks are created before implementation tasks.
3. Build the dependency graph
Dependencies between tasks are declared explicitly.
For example:
A ──► C ──► E
│
└──► D ──► E
B ───────► E
Enter fullscreen mode Exit fullscreen mode
Tasks A and B can begin immediately.
C and D wait for A.
E waits for all upstream work.
The orchestrator now has enough information to determine safe parallelism.
4. Create the Epic integration branch
A branch is created from the current target branch:
main
│
└── epic/invoice-processing
Enter fullscreen mode Exit fullscreen mode
Individual task branches are based on an appropriate integration state.
Long-running Epics should periodically synchronize with the target branch to avoid allowing the integration branch to drift too far from main.
5. Build a context package for the next task
The orchestrator selects only the information required for the task:
Epic summary
+ task description
+ acceptance criteria
+ architecture decisions
+ completed dependencies
+ relevant files
+ repository instructions
+ validation commands
+ risk constraints
Enter fullscreen mode Exit fullscreen mode
This becomes the Planner’s initial input.
6. Run the Planner
The Planner inspects the repository and produces a plan.
Possible outcomes are:
READY
Enter fullscreen mode Exit fullscreen mode
NEEDS_SPLIT
Enter fullscreen mode Exit fullscreen mode
BLOCKED_BY_ARCHITECTURE
Enter fullscreen mode Exit fullscreen mode
BLOCKED_BY_DEPENDENCY
Enter fullscreen mode Exit fullscreen mode
Only READY tasks proceed automatically.
This step acts as an important boundary between project planning and code generation.
7. Create an isolated Builder environment
The orchestrator creates:
task branch
+
Git worktree or container
+
task-specific context
Enter fullscreen mode Exit fullscreen mode
For example:
worktrees/
├── payment-retry/
├── payment-webhook/
└── invoice-events/
Enter fullscreen mode Exit fullscreen mode
Independent Builders can now execute concurrently without sharing the same filesystem.
8. Execute the Builder
The Builder receives:
task context
+
Planner result
+
repository instructions
Enter fullscreen mode Exit fullscreen mode
It implements the change and runs the required validations.
The result should include structured information such as:
status: completed
commit: a814ed3
validation:
unit_tests: passed
integration_tests: passed
lint: passed
typecheck: passed
files_changed:
- src/payments/service.ts
- src/payments/service.test.ts
notes:
- preserved existing idempotency behavior
Enter fullscreen mode Exit fullscreen mode
The exact schema is not important.
Structured output is.
An orchestrator should not need to parse an essay to determine whether tests passed.
9. Open the task pull request
Each task produces a pull request targeting the Epic integration branch:
task/payment-retry
│
▼
epic/payment-improvements
│
▼
main
Enter fullscreen mode Exit fullscreen mode
The task PR should remain independently reviewable.
CI runs again outside the Builder’s local environment.
This gives us two independent validation layers:
Builder validation
+
CI validation
Enter fullscreen mode Exit fullscreen mode
10. Run the Reviewer
The Reviewer receives:
task requirements
+
acceptance criteria
+
diff
+
test results
+
relevant architecture constraints
Enter fullscreen mode Exit fullscreen mode
It returns structured findings.
For example:
status: changes_requested
findings:
- severity: blocking
file: src/payments/service.ts
line: 87
reason: declined payments are being retried
criterion: only transient failures may be retried
Enter fullscreen mode Exit fullscreen mode
Or:
status: approved
findings: []
Enter fullscreen mode Exit fullscreen mode
11. Run the correction loop
If the Reviewer finds a blocking problem:
Reviewer
↓
Builder
↓
validation
↓
Reviewer
Enter fullscreen mode Exit fullscreen mode
The loop continues within predefined limits.
An important operational detail is that retries should not be infinite.
After a certain number of unsuccessful correction cycles, the task should be escalated.
For example:
attempt 1 → failed review
attempt 2 → failed review
attempt 3 → failed review
↓
human escalation
Enter fullscreen mode Exit fullscreen mode
Repeated failure is itself useful information. It may indicate that the task is poorly specified, incorrectly decomposed, or hiding an unresolved architectural problem.
12. Merge into the Epic integration branch
Once:
Builder validation = passed
CI = passed
Reviewer = approved
Enter fullscreen mode Exit fullscreen mode
the task can be merged into the Epic branch according to the project’s autonomy policy.
This may unblock downstream tasks in the dependency graph.
The orchestrator then schedules the newly available work.
13. Run Epic-level validation
Passing every task independently does not prove that the Epic works as a whole.
Once all required tasks are integrated, the workflow runs broader validation:
full test suite
integration tests
end-to-end tests
contract tests
migration checks
security checks
performance checks
Enter fullscreen mode Exit fullscreen mode
The exact set depends on the project.
This stage catches problems that task-level validation cannot detect.
For example, two individually correct tasks may still implement incompatible assumptions.
14. Validate the Epic acceptance criteria
The final Reviewer evaluates the integrated result against the original Epic rather than individual tasks.
The question changes from:
Did we implement Task 7 correctly?
to:
Does the system now satisfy the outcome defined by the Epic?
This distinction is important.
A workflow can successfully complete every task and still fail to achieve the intended product behavior if the decomposition itself was incomplete.
15. Human approval and final merge
If the final integration satisfies the Epic criteria, the workflow produces an Epic pull request:
epic/payment-improvements
│
▼
main
Enter fullscreen mode Exit fullscreen mode
This is an appropriate place for a human approval gate.
The human is no longer expected to manually implement or review every small code change.
Instead, human attention is concentrated where it has the highest value:
requirements
architecture
risk
exceptions
final integration
Enter fullscreen mode Exit fullscreen mode
That is a more realistic interpretation of “human-in-the-loop” development than requiring a person to supervise every tool call made by an agent.
Choosing models by role
There is no requirement that every stage use the same model.
Different roles have different computational requirements.
The Planner may benefit from stronger reasoning because it needs to understand architecture and dependencies.
The Reviewer may need similar capability because it must identify subtle inconsistencies.
A Builder executing a very constrained change may not require the same model.
Conceptually:
Orchestrator ── high reasoning capability
Planner ── high reasoning capability
Reviewer ── high reasoning capability
Builder ── selected according to task complexity
Enter fullscreen mode Exit fullscreen mode
This also creates room for provider-independent workflows.
An orchestration layer could use OpenCode, Codex, Claude, or other coding-agent runtimes without fundamentally changing the architecture described here.
The model becomes an execution component rather than the workflow itself.
Measure the workflow
Once the process is structured, it can be measured.
Useful metrics include:
- tokens consumed per task;
- tokens consumed per agent role;
- execution time per task;
- total Epic execution time;
- number of retries;
- number of failed Builder attempts;
- Reviewer findings;
- CI failures;
- tasks completed without human intervention;
- human escalations;
- cost per pull request;
- cost per Epic.
These metrics should not be used only to minimize token usage.
They can help answer more useful questions.
For example:
Does adding a Planner reduce failed implementations?
Does a stronger Reviewer reduce integration defects?
Are certain task types consistently escalated?
At what task size does autonomous completion become unreliable?
Is parallel execution actually reducing lead time?
At that point, decisions about agents and models can be based on workflow performance rather than intuition.
The workflow as a state machine
Once everything above is explicit, the orchestration problem becomes surprisingly mechanical.
A task can move through states such as:
PENDING
↓
READY
↓
PLANNING
↓
BUILDING
↓
VALIDATING
↓
REVIEWING
│
├── changes requested ──► BUILDING
│
├── blocked ────────────► ESCALATED
│
└── approved
↓
MERGED
Enter fullscreen mode Exit fullscreen mode
The Epic has its own lifecycle:
PLANNING
↓
EXECUTING
↓
INTEGRATING
↓
VALIDATING
↓
AWAITING_APPROVAL
↓
COMPLETED
Enter fullscreen mode Exit fullscreen mode
This is the point where AI-assisted software development starts looking less like a chat interface and more like a distributed software-delivery system.
And that is probably the more useful abstraction.
Final thoughts
The most interesting problem in AI-assisted software development is increasingly not code generation itself.
It is orchestration.
An autonomous development workflow needs to answer questions such as:
- What should the agent work on?
- What context should it receive?
- Which work can happen in parallel?
- Which decisions have already been made?
- How should the implementation be validated?
- Who reviews the result?
- What happens when the Reviewer disagrees?
- Which actions can happen automatically?
- Where is human approval required?
- How do independent changes become one coherent feature?
Better models will make individual executions more capable.
They will not eliminate the need to answer those questions.
A robust agentic development workflow therefore should not be designed around the assumption that a sufficiently powerful model can receive an entire project and simply “figure it out.”
Instead, the system should reduce ambiguity before execution.
That means:
clear intent
+ coherent tasks
+ explicit dependencies
+ minimal relevant context
+ isolated execution
+ objective validation
+ independent review
+ risk-based autonomy
+ controlled integration
Enter fullscreen mode Exit fullscreen mode
The objective is not to remove humans from software engineering.
It is to move human attention away from supervising routine implementation and toward the decisions where judgment, product context, architecture, and risk actually matter.
Once those boundaries are explicit, AI agents stop being isolated coding assistants and become components of a software delivery pipeline.