A roundtable run that is interrupted anywhere during the fan-out discards all completed lane answers, including paid http lanes that have already billed for tokens. The user-visible symptom is a run that "randomly dies" with zero output and no transcript.
Cause
main() collected the fan-out with results = list(pool.map(...)), which blocks until the last lane returns, and only then wrote the transcript:
with cf.ThreadPoolExecutor(max_workers=max(len(lanes), 1)) as pool:
results = list(pool.map(lambda l: ask(...), lanes))
...
if not a.no_transcript:
path.write_text(json.dumps({"brief": prompt, "results": results, ...}))
Every answer is therefore held in memory for the whole run. A SIGKILL, a closed terminal, a harness/task timeout, or a Ctrl-C at any point before the last lane returns throws away everything already collected. Nothing hits disk.
This is invisible in normal use — a run that completes looks fine — and it is worst exactly when it costs the most: a long brief with many lanes, where the slowest lane decides whether the other eleven survive.
Repro
roundtable --lanes JARVIS,HAL9000,Wintermute "..."
kill -9 the process once at least one lane has answered
- Before the fix: no transcript is written at all; stdout is empty. The completed answers are gone.
Observed tonight on a 7-lane, 92 KB brief: the run was stopped mid-flight and left a 0-byte output and no transcript, after dispatching every lane.
Fix
Claim the transcript path before the fan-out and persist after every lane completes, via as_completed with results written into the slot each lane was dispatched from, so ordering is preserved and a partial transcript reads exactly like a complete one (null for lanes still out). Writes are atomic (write-to-.tmp, then replace), so a kill during the write leaves the previous good transcript rather than a truncated one. Partial transcripts carry "partial": true and a note.
Verified: kill -9 mid-run now leaves a transcript containing the two lanes that had answered, with the third slot null. Full suite green (119 passed, 29 subtests).
🤖 Generated with Claude Code
https://claude.ai/code/session_01VmauK5UCYGRYyXoQ36FQ3S
A roundtable run that is interrupted anywhere during the fan-out discards all completed lane answers, including paid
httplanes that have already billed for tokens. The user-visible symptom is a run that "randomly dies" with zero output and no transcript.Cause
main()collected the fan-out withresults = list(pool.map(...)), which blocks until the last lane returns, and only then wrote the transcript:Every answer is therefore held in memory for the whole run. A SIGKILL, a closed terminal, a harness/task timeout, or a Ctrl-C at any point before the last lane returns throws away everything already collected. Nothing hits disk.
This is invisible in normal use — a run that completes looks fine — and it is worst exactly when it costs the most: a long brief with many lanes, where the slowest lane decides whether the other eleven survive.
Repro
roundtable --lanes JARVIS,HAL9000,Wintermute "..."kill -9the process once at least one lane has answeredObserved tonight on a 7-lane, 92 KB brief: the run was stopped mid-flight and left a 0-byte output and no transcript, after dispatching every lane.
Fix
Claim the transcript path before the fan-out and persist after every lane completes, via
as_completedwith results written into the slot each lane was dispatched from, so ordering is preserved and a partial transcript reads exactly like a complete one (nullfor lanes still out). Writes are atomic (write-to-.tmp, thenreplace), so a kill during the write leaves the previous good transcript rather than a truncated one. Partial transcripts carry"partial": trueand a note.Verified:
kill -9mid-run now leaves a transcript containing the two lanes that had answered, with the third slotnull. Full suite green (119 passed, 29 subtests).🤖 Generated with Claude Code
https://claude.ai/code/session_01VmauK5UCYGRYyXoQ36FQ3S