# Getting parallel work back together in Sprowt Harness

By Horia Jurcut · 5 October 2026

Sprowt Harness now runs up to two Codex workers on a codemod, the name I use for a feature or fix. I tried it on a small to-do app, with one worker adding a health endpoint and another improving the existing interface’s CSS. The two workers ran at the same time because neither change needed the other.

In [the previous post](https://minimumeffort.dev/blog/sprowt-harness-can-now-keep-working-on-a-pull-request), I had one worker making changes and coming back for another round of edits on the same PR. Adding a second worker also meant working through how their changes would come together, and what should happen when another PR merges while they are working.

## Choosing work that can run together

My first two-task plan still ran sequentially. The planner had made the interface depend on the new API, so the second task had to wait. Having two workers available doesn’t remove that dependency.

For the next test, I asked for `GET /health` to return `{"status":"ok"}` without accessing the database, alongside CSS changes for keyboard focus, contrast and narrow screens. The endpoint had its own source and test files. The CSS task worked on the existing app and didn’t need the endpoint.

Rust follows those dependencies when assigning work. It can start two ready tasks together, but a task that needs another task’s result waits for it to pass its checks and be combined. Tasks that share files must be ordered too. I’ve kept the planner’s instructions explicit about this: don’t split a small task just to use both workers.

## Separate files, shared VM

I gave each task a Git worktree inside the codemod’s Linux VM. Each has its own branch and working folder. A worker can write to its assigned folder and its own runtime directory; the other task’s files and the combined source are read-only.

Both workers share one VM, with separate Codex conversations and runtime directories. One controller owns the VM and handles package setup, checkpoints and Git integration one at a time. The worker turns and task commands can overlap.

When a worker finishes, it returns commands for checking its task. Rust runs them again before combining the change. After all tasks finish, it puts the combined source into each task’s folder and reruns the checks there, using the dependencies that task installed.

That last check matters even when Git can combine the files cleanly. A passing test in one worker’s checkout says something about that version of the project. It hasn’t yet checked the other worker’s changes.

The to-do run confirmed that the workers overlapped, but the CSS worker’s browser checks hit a sandbox restriction. Chromium needed local sockets that the VM policy denied. I enabled that access and checked browser startup through both workers and the verifier, along with the file and network boundaries. The initial run had still stopped short of combined verification.

## When another PR lands

There’s a similar problem between codemods. If I build A and B in parallel, then merge A, B’s earlier checks were run without A’s changes. Even if Git shows no conflict, the combined behavior still needs checking.

While the harness is open, it checks the target branch every 30 seconds. If a newer target is detected, an open PR is marked as a draft. Running workers finish before their source changes. A finished codemod then saves a checkpoint, brings in the updated target and runs an integration task in its existing VM.

Codex gets the original request, the incoming commit messages and, for text conflicts, the conflicting versions. It resolves the code and checks the combined behavior. Rust reruns those checks before the update is ready for review. A clean merge goes through verification too.

The worker is instructed to ask me when the changes imply conflicting product decisions. Binary conflicts need manual reconciliation. Paused or failed work keeps its checkpoint and waits for a retry or an edit request.

After the checks pass, I can review the new diff and publish an update to the same PR. Publication checks the target again, so a merge that arrives during verification triggers another update. I still merge the PRs on GitHub. The harness doesn’t enforce their merge order, and GitHub’s branch protection or merge queue is still needed to require current checks at merge time.

## Keeping the project current

I also found a gap after publishing a PR, deleting its codemod and then merging it. Local `main` wouldn’t update until I started another codemod. The project’s sync had been tied too closely to the work that produced the PR.

Project sync now runs independently, on startup and every 30 seconds while the harness is open, including when there are no codemods left. It can fast-forward the checked-out branch while preserving local edits and staging. Conflicting edits or divergent history stop the update for me to resolve.

That updates my project folder. Bringing merged work into an ongoing codemod remains a separate operation because its branch and VM contain work that hasn’t landed yet.

The current limit is two Codex executors per codemod. Muse, direct worker-to-worker messaging and an independent review worker are still to come. I’ve checked concurrency and recovery in live VM tests, along with the automated scheduling and integration tests. Those tests cover concurrency and recovery. They don’t tell me how much time two workers save on a complete change.
