Two AI harnesses, one project: why the work kept stopping
What two attempts to coordinate Claude and Codex taught me about messages, ownership, token costs and the human work behind autonomous development.
9 min read
I kept coming back to the same question: why had the work stopped again?
There were open tasks in Epos of Ashes, several AI sessions available, and enough separate areas of the game for work to continue. Yet I would return to a checkpoint, an unanswered handover or a session waiting for my next message. Asking about progress often restarted the process. Leaving it alone did not reliably keep it moving.
In my previous article, I described what comes after a first playable result. Coordination became one of those problems. As a solo developer working with Claude and Codex, I was carrying information between sessions, checking responsibilities and deciding what should happen next. I wanted to move some of that recurring work into a shared system.
That led to two applications: harnes-sync, followed by Harnes Sync Next. Both produced useful capabilities. Neither gave me the dependable, continuous collaboration I had set out to achieve. This is the course of that attempt, up to 18 October 2026.
The idea was easy to explain
I wanted one coordinating session to maintain the goals, priorities and open questions. Worker sessions in the two harnesses would take suitable tasks, report their results and become available for the next piece of work. If they needed feedback, the coordinator would receive the question while other independent work continued.
The distinction between a harness and a model matters here. Claude and Codex were running inside their own applications, with their own conversations, tools and ways of continuing a session. A shared project folder did not automatically give them shared working memory or a common execution lifecycle.
The game also imposed practical limits. Two sessions could work on separate source files, but they could not safely make competing changes to the same terrain generator or rebuild the same Unity world. Test copies provided additional capacity; they still needed ownership and current inputs. A free conversation was not necessarily a free test environment.
My goal was therefore more specific than getting two models to exchange messages: keep useful work moving, within session and resource limits, without making me the person who had to notice every unfinished handover.
First, we gave the harnesses a mailbox
The first application grew around a local service and persistent project mailboxes. Messages could live in a Git-backed history or in the existing project folder. Both harnesses could read and write through MCP, the tool interface connecting them to the service. An event would signal that something needed attention, and the receiving session would collect the details.
That separation was deliberate. The mailbox held the information; the wake-up was only supposed to be a bell. Sessions should not spend model calls repeatedly asking whether anything had arrived.
In practice, we initially blurred those roles. An evaluation on 1 October recorded a queue reaching its limit of 100 submissions. Individual events had created individual wake-ups, failed deliveries were retried, and notices intended for the human were treated as though an unreachable harness would eventually collect them. The delivery machinery could accumulate work about work.
We changed it to combine notifications, keep the actual content in the mailbox and make failed delivery visible. A compact briefing gathered messages, questions and current tasks without launching an extra model to summarise them.
That fixed a concrete transport problem. It did not answer whether the intended session had actually resumed.
A successful send was not a working colleague
One of the most consequential findings was that a command could return successfully while a dormant desktop chat remained dormant. In our tested Codex setup, queue acceptance did not establish that the target conversation had started reading or doing anything.
Other gaps appeared earlier in the chain. A session running outside the code project's folder did not inherit its project-local configuration. Hooks we expected to report session activity did not always run in the environment we were using. A tool call could prove contact with a harness without identifying a conversation that the service could wake.
The interface needed to distinguish these situations. A message being stored, a wake-up being accepted, a session reading its briefing and a worker claiming the task were different events. Calling all of them “delivered” made the display easier to read and the system harder to trust.
We introduced more explicit registration and session-specific delivery. A readiness handshake tied a request to the intended session and its actual lifecycle. Later, a desktop-specific adapter successfully activated an unloaded conversation and the worker claimed its assigned task. That was real progress, but it proved one route through the system, not uninterrupted collaboration across the project.
Then the mailbox became a task system
Messages could explain what had happened, but they were a poor way to establish what remained to be done. An open issue might be mentioned in a chat, a roadmap, a playtest note and a handover, each with a slightly different status.
We added persistent tasks with priorities, dependencies, acceptance criteria and explicit ownership. Claims prevented two workers from taking the same task. Path reservations protected shared files. A diagnosis could lead to two independent changes, with a later review waiting for both.
The difficult cases were ordinary working situations. A worker waiting for feedback might be available for another task while still owning a correction. A conversation could finish its response while a test process continued. An expired reservation was not proof that a writer had stopped. Automatically handing that work to another session risked duplicate edits; retaining every reservation indefinitely risked blocking the project.
Our rules also changed as the problem became clearer. Early instructions required human authority for individual work. Later, I explicitly wanted autonomous task distribution within the approved project. Adding that permission did not automatically remove older instructions, retained claims or assumptions embedded in existing sessions.
The accumulating protocol could explain more cases, but it also created more conditions that had to agree before work would move. Some waiting was legitimate. Some came from stale state or a handover nobody had completed. From the outside, both looked like another quiet worker.
The second attempt narrowed the contract
Harnes Sync Next was an attempt to make that model easier to reason about. Tasks had three visible states: open, running and done. Individual execution attempts were recorded separately, so an interrupted run did not have to be mistaken for either a completed task or a fresh opportunity to start it again.
The new core used transactional SQLite storage. Assignment, questions and results could be recorded together. Sessions were explicitly admitted to a project, messages had exact recipients, and a worker could finish or yield with a concrete question. Answering that question made the task eligible again without interrupting unrelated work already underway.
A model-free worker process waited for suitable work and invoked the harness adapter when needed. The coordinator was still responsible for deciding which work was useful and ready. The service could distribute an executable queue without asking a model to approve every transition.
We also had to respect how the applications were already using their sessions. A second server could not simply take ownership of an open Codex desktop conversation. Claude continuation needed to address the intended existing session rather than create a parallel one. Later repairs dealt with stale desktop contacts and result delivery; machine-readable results were bound to the particular execution attempt instead of relying only on a conversational final answer.
These were improvements to the execution contract. They did not remove the need to prepare valid tasks or resolve the project's technical dependencies.
The small loop worked before the larger one did
There were genuine successes. On 17 October, tests used two Codex and two Claude sessions. They completed eight tasks involving feedback and resumption, followed by another eight through service-managed workers. Those controlled tests excluded file operations and disabled Claude customisations. They established useful behaviour within those conditions.
Product work later showed automatic follow-up assignments too. Recorded checkpoints include a processing-station task followed by another accepted and completed task, and a later world-rendering follow-up. It would be inaccurate to say the applications never coordinated anything.
But the broader operating pattern still needed repair and intervention. Contacts became stale. A service became unreachable. An update had to be installed before a fix reached the running application. At one checkpoint, an interactive Claude coordinator still lacked an automatic wake adapter, leaving autonomous replanning unproven even though workers could process available tasks.
Sometimes the queue was correctly empty: the next source change needed a diagnosis, another owner's handover or a player decision. Creating filler tasks would only have disguised that dependency. The harder requirement was to recognise the missing step, assign it appropriately and continue once it was resolved. That remained unreliable enough for me to keep stepping in.
Communication had a cost of its own
Part of the motivation was economic. I wanted expensive model time spent on implementation and judgment, with cheaper models or ordinary code handling bounded supporting work. Frequent status checks, repeated briefings and acknowledgement rounds could consume the same limited allowance needed to fix the game.
More agents also meant more conversations needing context. A short message could require a recipient to recover why a task existed, what had changed and which restrictions still applied. A handover that omitted those details saved tokens at the sender and moved the cost into investigation or rework at the receiver.
We tried compact briefings, event-driven wake-ups, limited retries, quieter monitoring and fewer coordination rounds. The service could wait without invoking a model. It could not make the next model turn, its history or its reasoning free. A cap on concurrent sessions was also not a guaranteed cap on token consumption.
Usage limits added another kind of interruption. Knowing that a quota was exhausted, knowing when it reset and successfully continuing the right work afterwards were separate problems. Starting more sessions did not necessarily create more available allowance.
I cannot turn these observations into an exact price for coordination or a measured saving. What I can describe is the additional work: diagnosing delivery, repairing state, preparing handovers and then returning to the original game problem. The tool meant to reduce management became another project I had to manage.
I remained part of the control loop
My intervention was not limited to granting access. I repeatedly clarified that independent work could continue during a long test, corrected interpretations of whether a session was busy, asked why an owner had not taken the next task and checked whether a reported completion had actually entered the system.
Some of that judgment belongs with the developer. Choosing how difficult a boss should be or accepting the appearance of a landscape cannot be reduced to a delivery receipt. Other interventions were exactly the routine coordination I had hoped to automate.
That distinction matters when describing the outcome. A person supervising creative and technical decisions is expected. A person repeatedly restoring the link between completion and the next executable task means the continuity goal has not yet been met.
What the attempt changed for me
The two applications made messages, responsibilities and failures more visible. They demonstrated that targeted communication and bounded automatic handovers were possible in our setup. They also exposed how much sits between those capabilities and a development process that keeps moving reliably.
For another attempt, I would make the smallest complete operating loop the acceptance criterion much earlier: an existing session receives useful work, produces a recoverable result, yields for feedback, continues with the right context and hands off to the next eligible task. Then test that loop through a restart, a quota pause and a genuinely shared resource. A larger backlog and more parallel workers would come after that evidence.
The lesson is not that multiple AI sessions cannot collaborate. Our own results would contradict that. It is that successful collaboration in a controlled sequence did not become dependable continuity in the game project. Planning, delivery, execution, feedback and acceptance each needed their own state and evidence, while the cost of connecting them kept growing.
I wanted to spend less time carrying the project between conversations. By the end of this attempt, I had built better ways to see where the handovers broke, but I was still the person returning to close them.