It was late, and I was expanding a new batch of ukiyo-e wallpaper sources into eight derivative sets, one per combination of orientation and size. It was material for the wallpaper apps I run as an indie developer, and with only my own two hands available I had given each set its own sub-agent and let a parent agent coordinate them.
Seven of them were fine. The one handling the portrait set had misread the source dimensions and was upscaling small originals. Reading the log, my hands stopped over the keyboard — I wanted to halt that one child and let the other seven keep going.
If there is one thing I'd like to pass on from that night, it's this: the hard part was not stopping the child. It was what to do with the files it had already written. Below is how the three tools I use differ in what a stop reaches, and the output layout I've settled on so that the answer no longer depends on the tool.
What a "stop" stops depends on the tool
On my machine, Antigravity's Manager view shows each sub-agent as its own card, and I can stop one card without touching the others. Since CLI 1.2.9, typing @ followed by a child's name sends a message to that child alone. Telling the portrait agent "keep originals at native size, no upscaling" never required interrupting the parent.
Try the same move elsewhere and the reach of a stop changes. In Claude Code, there is an open report that pressing Stop halts unrelated children as well (Issue #82617), and another that a transient 529 error ends a child mid-run and sends the parent into a rebuild (Issue #82605). Gemini CLI is still at the stage where coordinated parallel children are a feature request (Issue #19430).
| Tool | Stopping a single child | Re-instructing the stopped child | My understanding (as of Sept 2026) |
|---|---|---|---|
| Antigravity IDE / CLI | Per-card stop | Direct via @ in CLI 1.2.9 | In daily use here |
| Claude Code | Stop reported to reach unrelated children | Through the parent | #82617 and #82605 still open |
| Gemini CLI | Parallel coordination is a request | — | Watching #19430 |
I may be missing details in the two tools I use less, and the open issues may close by the time you read this. But building that table showed me the one thing that stays constant no matter which column you pick. A child that is stopped halfway leaves its half-written files exactly where they are. And the parent's merge step has no idea those files are incomplete.
The stopped child's output was already mixed into the other seven
My mistake that night was that all eight children wrote into the same out/ directory. By the time I stopped the portrait agent, a dozen or so upscaled images were sitting next to the correct sets. The parent counted "files for eight sets present" and was one step away from adding them to the delivery list.
I noticed the next morning, opening the staging list. Only the portrait set had dimensions a digit off.
Looking back, it was never a problem of how to stop. I simply hadn't placed each child's work in a unit I could throw away in one go. The tool gives me a way to stop a child. What the tool does not give me is a boundary that separates the stopped child's output from everyone else's.
Stopping a child is the tool's job; giving each child a unit I can discard whole is mine. Once I drew that line, the next morning looked the same regardless of which tool had run the night before.
One directory per child, and one last line
The layout is plain. Each child gets one directory. After the child passes its own check, it writes an empty file named DONE inside that directory, as the very last thing it does. The parent merges only directories that contain DONE. A child that was stopped never got to write it, so its partial output can't leak in.
Every child's instructions carry the same three fixed lines:
Write only inside out/<set-name>/.
After writing all files, verify the dimensions and file count yourself.
If the check passes, create an empty out/<set-name>/DONE as the final step (do not create it if the check fails).The parent's merge script looks at nothing but DONE. The problem it solves is letting the parent tell apart a stopped child, a still-running child, and a finished child using file state alone.
#!/usr/bin/env bash
# merge_done_sets.sh — collect only the sets that have a DONE marker into the delivery directory
set -euo pipefail
OUT_ROOT="${1:-out}" # root of the sub-agents' output
DIST="${2:-dist}" # merge destination
mkdir -p "$DIST"
merged=0; skipped=0
for set_dir in "$OUT_ROOT"/*/; do
name="$(basename "$set_dir")"
if [ ! -f "$set_dir/DONE" ]; then
# stopped children and still-running children land here; count them, don't touch them
echo "skip $name (no DONE)"; skipped=$((skipped+1)); continue
fi
# any file newer than DONE means something was written after the marker; keep it out
if find "$set_dir" -type f -newer "$set_dir/DONE" ! -name DONE | grep -q .; then
echo "skip $name (modified after DONE)"; skipped=$((skipped+1)); continue
fi
mkdir -p "$DIST/$name"
rsync -a --exclude DONE "$set_dir" "$DIST/$name/"
echo "merge $name"; merged=$((merged+1))
done
echo "merged=$merged skipped=$skipped"
# if anything was skipped, the parent's next stage (building the delivery list) must not run
[ "$skipped" -eq 0 ]DONE goes last so that whenever the stop arrives, the marker doesn't exist yet. The -newer line catches the case where a child keeps writing after the marker — which is exactly what happens when it receives a correction and resumes. The final [ "$skipped" -eq 0 ] keeps the parent from moving on if even one set is missing.
The stopped portrait set is gone with a single rm -rf out/portrait. Because the unit of disposal was a directory from the start, nothing next to it is at risk.
Two small choices made the script easier to trust over time. I name each directory after the set exactly as it appears in the parent's list of children, so the merge log and the Manager cards read the same way and I never have to translate between them. And I kept the script free of anything specific to Antigravity — no card IDs, no session names — because the same file has to run on the nights I hand a batch to Claude Code instead. A merge step that only understands one tool would have become a second thing to migrate.
Unattended, a child that never stops keeps the parent waiting
Everything above happened while I was sitting in front of the screen. Running unattended adds one more wrinkle.
In CLI 1.2.9, a headless -p run now waits for background work up to --print-timeout (the ceiling is 30 minutes). So a child that hangs without finishing holds the parent until that ceiling. At the keyboard I'd stop its card; unattended, that hand isn't there.
My answer was one more line in each child's instructions: a time budget. "If more than N minutes have passed since you started, finish without creating DONE." With no marker, the merge script above rejects the set, and a timed-out child falls naturally onto the discard side. How the timeout semantics shifted between versions is something I wrote up in Antigravity CLI 1.1.28 Made a print-timeout Overrun Exit Successfully — Rechecking How I Judge Unattended Runs.
When a child won't finish because of an unapproved artifact on the parent's side, there is a place to check before reaching for stop at all; the order is in When a Sub-Agent Never Finishes, Check the Parent's Unapproved Artifacts First. And when you'd rather resume a stopped child's work than throw it away, the steps in Stopping an Antigravity Agent Mid-Run: How to Pause and Resume Without Losing Work apply directly, one directory at a time.
Start by splitting the output directories
I expect the reach of a stop to keep shifting with every release, in every one of these tools. What carried over unchanged, each time I switched, was the habit of one directory per child and a DONE written last. That small agreement is what sits between the three tools and keeps them interchangeable for me.
If you're running sub-agents in parallel right now, I'd start with nothing more than giving each one its own output directory. Adding the DONE marker can wait until the next run.
That's how I began, too — just the directories. The morning I discarded a broken child with one rm -rf and nothing else moved, I felt my shoulders drop, and I still remember it.