While preparing the 1.2.1 update for Cloud Media Shrinker, I had six separate agent threads open across three providers. Two of them had nearly identical titles, one bug had already been fixed in a different chat, and I was spending too much time copying test results between windows.
I turned one thread into the project coordinator and built a small local orchestration skill around it. Now I talk to the coordinator, and it routes discovery, implementation, and independent QA across Claude Code, Codex, and Google Antigravity.
Separating discovery, editing, and verification
When the same model writes a fix and writes the test for that fix, both often share the same blind spot. Giving those jobs to different models with different permissions caught bugs that single-session reviews missed.
Discovery and code edit
Proposes and patches
Independent release QA
Tries to break the patch
Each provider gets the role that fits both its strengths and my remaining plan limits:
- Discovery and open-ended audits: Claude runs in read-only mode to inspect UX flows, performance hot paths, and existing tests. During the 1.2.1 review, it found a cancellation test that could pass even when the job finished normally and an index file that was rewriting 2.26 MB to disk on idle checks.
- Scoped implementation: Well-defined fixes with a clear reproduction go to Google Antigravity when I have spare Google allowance, or stay with the active Claude editor when a multi-step repair already has context in flight.
- Independent QA: A separate Codex session at extra-high effort receives a frozen snapshot of the candidate and tries to disprove the fix before anything is installed on my Mac or test phone.
Why independent QA mattered on a deletion check
Cloud Media Shrinker lets you compress a large video and then move the original to the Trash once the smaller copy is verified. A bug in that check can cost someone a video they care about, so I wanted the QA pass to be adversarial.
- First patch checks file sizeThe editor updates the deletion guard so a replaced smaller copy with a different byte count is refused.
- QA tests a same-size replacementThe independent tester swaps in a different file with the exact same byte count and shows it still reaches the intercepted Trash step.
- QA upgrades the fixture to a playable movieTo rule out a check that only catches corrupt files, the tester builds a playable red video matching the black test video's exact byte size.
- Second patch samples 16 byte blocksThe editor hashes 16 slices across the file. The tester constructs two playable videos that match in all 16 sampled blocks but differ in between.
- Release stays on hold until full identity passesEvery test intercepts the Trash request before any file moves, and the coordinator holds device installs until the full check passes.
If the editor had written its own verification test, the first size check would have passed and shipped. Having a second model try to construct a counterfeit file that satisfied the guard turned a plausible patch into a real guarantee.
Keeping worker sessions resumable and out of the way
I didn't want orchestration to mean five terminal windows popping up on my screen or stealing keyboard focus while I work. Together with running visible GUI tests in a separate macOS account, the local launcher script keeps background provider chats headless and locked to their workspace:
python3 provider_chat.py start --provider google --model gemini-4-argon-high --effort high \
--mode edit --workspace "$workspace" --record "$records/google-fix.json" < "$records/fix-brief.md"
python3 provider_chat.py start --provider claude --model claude-fable-5-2 --effort high \
--mode review --workspace "$workspace" --record "$records/claude-review.json" < "$records/review-brief.md"
python3 provider_chat.py resume --record "$records/google-fix.json" < "$records/retest-brief.md"
While building the launcher, I ran into four practical traps:
- One-shot print mode can lose context. When I tested resuming a Google session after the first process exited, ordinary print mode returned the same session ID but forgot a value supplied in the first turn. Switching the Google adapter to Antigravity's JSON-RPC Agent Client Protocol (
agy acpwithsession/newandsession/load) preserved the conversation across turns. - Requested models need receipt verification. Passing a model flag on the command line doesn't prove the provider used it. The launcher inspects the turn receipt and token usage table after every run to confirm the exact model and thinking level that answered.
- Failed turns leave partial edits behind. The launcher holds an
fcntl.flocklock on a sibling.lockfile and writes receipts atomically. If a worker is interrupted mid-turn, the record enters aninspectstate and refuses a blind retry until the coordinator checks the transcript and working tree. - Workers stay caged to file tools. In
reviewmode, a worker only gets read, glob, and grep tools. Ineditmode, it can modify regular files inside the assigned workspace, while shell commands, network fetches,.gitmutations, and browser launches are denied.
Where it goes next
The current bottleneck is that a single uncommitted working tree forces editors to take turns when their files overlap. The next upgrade to the launcher adds APFS copy-on-write workspace snapshots (cp -cR) and a host-side --verify-cmd flag, so an editor can patch an isolated copy of the repo and auto-repair its own compiler or unit-test failures before handing a clean diff back to the coordinator.
Having one coordinator chat hasn't made the models infallible. It just means when one model makes an optimistic assumption, another one is already waiting to test it.