· Antonio Leiva · ai · 12 min read
What Is Loop Engineering? A Practical Codex Example

People are starting to talk more and more about Loop Engineering.
As always when a new label appears, part of it is hype. But I do think there is something useful here: once something has a name, we can discuss it more clearly.
For me, Loop Engineering is not about leaving an AI agent alone and hoping it magically builds the right thing.
That usually ends badly.
The interesting part is designing the whole development loop around the agent:
- where the agent reads the task from
- where it writes code
- how it validates the result
- how feedback gets back into the system
- and when a human should step in
I recorded a practical walkthrough with Codex here:
https://www.youtube.com/watch?v=jkJaXP-hYx8
But this article is meant to stand on its own. The goal is to show the architecture behind the loop, so you can reproduce it in a real repository.
What a loop actually is
When we first start programming with AI, the workflow is usually very direct.
We open a chat, ask for a function, copy the result, run it, paste the error back, and keep going.
That is already a loop, but it is a manual one. It depends completely on you watching the chat and guiding every step.
To build a more autonomous loop, you need a bit more structure:
- A source of state: a backlog, an issue, a Markdown spec, or a JSON file where the agent can read what needs to be done.
- An isolated workspace: a place where the agent can make changes without stepping on another agent’s work.
- A verification phase: tests, lint, typecheck, build, or any other objective signal that says whether the code still works.
- A feedback mechanism: a way to send failures or review comments back to the right agent.
- Human control points: the moments where a person must approve, decide, or stop the loop.
This changes the problem.
You stop obsessing over the perfect prompt and start designing the complete workflow around the model.
The minimal architecture
Before launching multiple agents in parallel, you need to define the pieces of the system and what each one is responsible for.
Otherwise you end up with several chats editing the same repo, duplicating work, losing state, and creating a mess that is harder to fix than the original task.
A minimal setup can work with four pieces:
- A state file, such as
features.mdorfeature_list.json, with tasks, status, dependencies, and evidence. - A Manager thread, which reads that state and decides what can run next.
- Worker threads, each responsible for one concrete task.
- A PR Reviewer thread, which reviews Pull Requests and produces feedback.
The state file does not need to be fancy. It only needs to be readable by the Manager:
[
{
"id": "feature-auth",
"title": "Add email login",
"status": "pending",
"depends_on": [],
"evidence": []
},
{
"id": "feature-profile",
"title": "Create profile screen",
"status": "blocked",
"depends_on": ["feature-auth"],
"evidence": []
}
]
You do not need a database, a queue, or a custom platform to start. For a first version, a versioned file in the repo is enough.
A Manager and several Workers
In Codex, you can create independent threads with their own context. That makes role separation very natural.
The main thread acts as the Manager.
Its job is not to write code. Its job is to read the task list, understand dependencies, decide what can run, and coordinate other threads.
The Workers are the ones that implement each task. Each worker receives a narrow piece of work, makes changes in its own workspace, validates the result, and opens a Pull Request when it is done.

This sounds simple, but it changes the whole shape of the system.
If a single thread has to understand the whole project, implement code, review it, fix failures, manage dependencies, and remember the overall state, its context becomes noisy very quickly.
When you split the roles, each thread has a smaller job:
- the Manager keeps the global picture
- the Workers implement specific tasks
- the Reviewer checks the resulting code
A good initial prompt for the Manager should not be “implement the app”.
It should be something more operational:
You are the Manager for this repository. Your job is to read feature_list.json,
detect which tasks are pending and have no blocking dependencies, and launch
one worker thread for each executable task.
Each worker must use the feature-flow skill, work in its own Git Worktree,
and create a Pull Request when it finishes.
Do not implement features directly. Maintain project state, coordinate workers,
check dependencies, and ask for human input when there is a conflict you cannot
resolve safely.
That last part matters.
The Manager should not become another worker. The moment it starts implementing features, it loses the clean perspective it needs to coordinate the system.
Why Git Worktrees matter
The first practical problem when you parallelize agent work is the workspace.
If you let three agents edit the same local folder, they will collide. One changes a component, another edits the same file, dependencies drift, and suddenly you are debugging the agents instead of the product.
This is where Git Worktrees become useful.
A worktree lets you have multiple branches of the same repository checked out in different folders at the same time. They share the same Git history, but each folder works independently.
For agent work, this is exactly what you want:
- Worker A can edit its branch without touching Worker B.
- Worker B can install dependencies, build, and test in a separate directory.
- Both can open Pull Requests independently.
- Conflicts show up where they should: in the normal Git and PR flow.
In Codex, you do not need to manually create worktrees with Git commands. You can ask the Manager to launch each worker thread in its own Git Worktree, and Codex prepares the isolated workspace.
The important part is the contract you give to the worker:
Launch a new thread in a Git Worktree for task feature-auth.
That thread must implement only this task and work in isolation.
Run tests, lint, and build before finishing.
When validated, create a Pull Request and leave the link in feature_list.json.
This removes a lot of local chaos.
It does not magically eliminate merge conflicts, of course. But it moves them into the normal development process: branches, Pull Requests, and merges.
The PR Reviewer as a loop inside the loop
The next piece is the PR Reviewer.
This is a dedicated thread whose only job is to review open Pull Requests.
It runs in the background. Every few minutes it checks whether there are new PRs, reads the diff, analyzes the code, and leaves comments. If it finds problems, it says so in the PR. If it does not see anything relevant, it leaves a clear signal that the Manager can understand.

The review loop looks like this:
- The worker implements the task.
- The worker opens a PR.
- The reviewer checks the PR.
- If there are comments, the Manager sends that feedback back to the right worker.
- The worker fixes the issue and updates the PR.
- The reviewer checks again.
There is no magic here.
It is just state, clear signals, and separated responsibilities.
In Codex, the simplest way to do this is to create a dedicated reviewer thread and give it a heartbeat. You do not need a new conversation for each PR. The heartbeat runs in the same thread, so the reviewer remembers which PRs it has already checked.
The prompt can be very direct:
Create a heartbeat that every five minutes checks whether there is any open
Pull Request in this repository.
If there is one, review it. If you see things to improve, leave them as comments
in the PR. If you do not see anything relevant, comment exactly:
"ready to integrate".
You can make this more sophisticated later: ask it to check out the project in another worktree, run tests, review architecture, or follow a specific checklist.
But the base idea is the same: one persistent reviewer thread that checks PRs in a loop.
The important thing is to use recognizable signals:
needs changes: feedback must go back to the worker.blocked: missing information or human decision required.ready to integrate: the reviewer does not see relevant issues.
The reviewer does not need to merge anything. Its job is to produce reliable feedback that the Manager can act on.
The hard case: conflicts and dependencies
Things get interesting when real dependencies appear.
Imagine the Manager launches three worker threads:
- Worker 1 finishes its feature, opens a PR, and the reviewer marks it as
ready to integrate. - Worker 2 also opens a PR, but it was based on an older version of
main. - Task 3 depends on task 2 being integrated, so it should not start yet.
This is where toy automation breaks.
Writing code in an ideal environment is the easy part. Reacting correctly when the repository changes halfway through is the harder part.
In the demo, several PRs are created in parallel. The reviewer leaves comments. Workers update their PRs. Then one PR is integrated into main, and another PR can no longer merge cleanly because its base is now outdated.
At that point, the Manager should not write code. It should coordinate:
- detect that a PR is ready and integrate it, if you chose to automate that step
- notice that another PR now has conflicts
- send that context to the correct worker thread
- wait for the worker to update its worktree and push the fixed PR
- launch newly unblocked tasks when dependencies are integrated
The Manager also needs its own heartbeat.
In the video, I use it in two phases.
First, to forward PR Reviewer feedback to the right worker:
Every 10 minutes, check whether there are new comments from the PR Reviewer
in the open Pull Requests.
If there are comments requesting changes, send that information to the
corresponding worker thread so it can update its worktree and push the PR again.
Then, if you want to test the full loop, you can add integration:
If a Pull Request is marked as "ready to integrate", integrate that change
into main, close the corresponding thread, remove the worktree, and leave
traceability of what you did.
In a real project, I would probably keep that final merge behind human approval.
But if you want to test the architecture end to end, it is useful to see the Manager integrate only when the signals are green.
With that information, the Manager can update feature_list.json:
- if a PR is
ready to integrate, mark the task as ready - if a PR
needs changes, send feedback to the worker that created it - if integrating one PR creates a conflict for another, ask that worker to update against
main - if a task was blocked by a dependency that is now integrated, launch a new worker thread in a worktree
The full loop becomes:
- The Manager reads the state.
- It launches workers only for unblocked tasks.
- Each worker works in its own worktree and opens a PR.
- The reviewer checks the PR and leaves a signal.
- The Manager reacts to that signal.
- The worker fixes issues if needed.
- The Manager integrates the change if you automated that step, or asks for human approval before merging.
- When a dependency is resolved, the Manager launches the next unblocked tasks.
That is the loop.
Where humans still belong
The temptation with these tools is to automate everything: let the agent write, review, merge, and deploy without anyone looking.
Technically, you can push in that direction.
In practice, I do not think it is the right default.
Humans still matter in the parts where judgment matters:
- choosing the solution or architecture
- giving final approval before code reaches the main branch
- deciding whether a detected issue is actually critical
- validating whether the feature makes sense for users, beyond passing tests
The point is not to remove the developer from the process.
The point is to remove the repetitive coordination work, so the developer can spend more time on design, review, and decisions.
The limits are still there
This does not make agents magically reliable.
They keep the same limitations, just inside a more structured environment.
If you are not careful, the failure modes are obvious:
- If the task is poorly defined, the agent may implement the wrong thing very efficiently.
- If tests are weak, the system will accept weak solutions.
- If dependencies are poorly modeled, the Manager will parallelize tasks that should not run in parallel.
- If you do not observe what is happening, agents can burn tokens without making real progress.
- If contexts are not separated, each thread becomes noisy very quickly.
That is why I prefer thinking about this as workflow design, not agent autonomy.
Full autonomy sounds nice in a demo. In real development work, control and observability matter much more.
A minimal checklist
If you want to build a loop like this, start small:
- Define a simple source of tasks: an issue, a backlog, a Markdown file, or a JSON file.
- Split roles into different Codex threads: one Manager and the Workers you need.
- Ask Codex to launch each worker in a Git Worktree.
- Make every worker deliver through a Pull Request, never direct commits to
main. - Create a PR Reviewer thread with a heartbeat that reviews open PRs and leaves clear signals.
- Create a Manager heartbeat that forwards reviewer comments to the correct worker.
- Use explicit states for tasks:
pending,in_progress,review,blocked,done. - Use automated checks before accepting work: tests, lint, typecheck, build.
- Decide which steps require your approval.
With that structure, you are no longer using AI as just a chat box.
You are building a system that can start, fail, receive feedback, recover, and continue.
The point is control
Programming with AI is not about finding a magic prompt.
It is about designing the system around the tool.
Current models can already generate useful code. The real difference is how you organize the work around them: isolated workspaces, review loops, dependency management, validation, and human control points.
That is why I think Loop Engineering is a useful label.
Not because it is a revolutionary idea invented yesterday, but because it points at the part that actually matters:
how to take a task from an initial idea to integrated code in a way that is controlled, observable, and repeatable.
And honestly, we are still very early there.



