[hardening] upload-trace still runs curl through Bun's spawnSync, which can wedge the poll loop
Affected NanoClaw version or commit
main at 65b69ba1 (unchanged by #3841).
Host platform
Agent container (Bun 1.4.0). Host Node is unaffected.
What happened?
#3839 traced the six-hour CI hang to Bun's spawnSync losing the child's exit and spinning forever at 100% CPU (oven-sh/bun#34069, reproduced on Bun 1.4.0; incidence scales with spawn volume next to sqlite, sockets and timers). #3841 removed spawnSync from the OpenCode turn path.
One call site remains in the runner: container/agent-runner/src/upload-trace.ts:38 runs every curl for /upload-trace through spawnSync from inside poll-loop.ts (line ~192). A wedge there is a synchronous spin on the poll loop: no heartbeat touch, no later messages, no timer can fire, until the host's stale-session sweep kills the container.
The path is admin-gated and rare, so exposure is low. It is the last synchronous spawn in the runner, though, and the mechanism is the same one that cost six hours of CI.
What did you expect?
curl runs through async spawn with an explicit deadline (the pattern now in opencode-memory.ts: runHookCommand), or through fetch so no child process is involved at all. uploadTrace becomes async and poll-loop.ts awaits it.
How can we reproduce it?
Not on demand (see oven-sh/bun#34069: distilled repros do not fire). Verification is structural: grep -rn spawnSync container/agent-runner/src --include='*.ts' | grep -v test.ts returns nothing after the change, and upload-trace.test.ts still passes.
Source: nanocoai/nanoclaw