Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: c7b0191a904142ccac1611e93a33ff4f
Branch: dev/5-late-spawn-release
Base: 3db12f0
At: 2026-11-10T06:20:44Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
Bot: FlyBot Product
What happens
boundedSpawn (plugins/workflow/src/service.js:423-443, added for #2748) bounds the wait for a host spawn. When the host answers after the deadline with success, it releases the Bots that answer created:
Promise.resolve(pending)
.then(ids => releaseAll(Object.values(ids ?? {}), release, warn))
.catch(error => warn(`flybot-workflow: a late start could not release its Bots: ${error?.message ?? error}`))A late answer that fails does not release anything: the .then handler above only runs on success, and the .catch merely warns. spawnAll (plugins/workflow/src/ephemeral.js:46-62) already put the ids it had made onto that error (:61 error.spawned = made), so the rejection carries exactly the Bots that now leak:
} catch (error) {
error.spawned = made
throw error
}The early-answer path does not leak: startLive's catch (service.js:1299) calls releaseAll(error.spawned ?? [], …) on the same error. Same error object, released when it arrives before the deadline, dropped when it arrives after it.
The leak is permanent, not deferred:
- the start is refused before
beginRun(), so no run ever holds those ids in itsephemeralmap; reap()only walkslane.active/lane.last(service.js:1403-1470), andorphanIds()only readsstate.orphans(:2421); this path writes neither.
So Bots created at hidden: true, ephemeral: true live until the process exits — while changelog.d/2748-workflow-stuck-run.fixed.md and docs/subsystems/workflow.md both state that a late spawn's Bots are released.
Reproduction
At b89ea5ecef80a996bc912f4e90c538d0f0ff2cbb (base here), with a spawned host whose create never answers inside SPAWN_DEADLINE_MS and then rejects with error.spawned set:
- late failure branch:
released: []plusflybot-workflow: a late start could not release its Bots: …— fails; - the same partial failure answered before the deadline:
released: ["eph-plan"]— passes.
# tests 2 / # pass 1 / # fail 1, failing line not ok 1 - a late spawn failure still releases the Bots it made.
Expected
A start refused at the deadline releases every Bot the pending spawn had already created, whether the host's late answer is a success or a failure. The refusal itself (error.code === 'spawn-timeout') does not change, and the existing warn stays so a failed release is still visible.
Fix shape (verified, 3 lines)
In the deadline branch, release late.spawned when the late answer rejects, then warn:
.catch(late => releaseAll(Array.isArray(late?.spawned) ? late.spawned : [], release, warn)
.then(() => warn(`flybot-workflow: a late start was refused: ${late?.message ?? late}`)))The sentence changes from could not release its Bots to was refused, because after this fix the release has already happened when that line prints; releaseAll keeps warning once per Bot it really could not remove, so a failed release stays visible.
Verified at the same base: 2/2 green, plugins/workflow full suite 515/515.
Scope
plugins/workflow/src/service.jsonly (the deadline branch ofboundedSpawn), plus a regression test.- No
core/, no client, nopatches/ds-botmirror ⇒ nogenerate, so this does not take the single generate lane. - Risk tier T2:
plugins/**is a T2 row of.github/agent/risk-rules.json, and this change adds nodiffPatternshit (noDSH_HOME, nochild_process, no fenced-prose line), so nothing bumps it.
Dedup
Read live main at the base above, no-cache, and searched issues and PRs for spawn-timeout, SPAWN_DEADLINE, spawned, 2748: #2735 is the "refused start puts the card back in the queue" half, #2752 (open) is "a failed run does not drain", #2753/#2750 are Stop release retry limits. No open issue or PR covers a late failure leaking the Bots it made.
Activity
fix(workflow): release the Bots a late spawn failure made
merge main into dev/5-late-spawn-release (ThinkFlowLab#2785)
fix(workflow): release the Bots a late spawn failure made (ThinkFlowL…
fix(workflow): drain the lane's queue when a takeover reap ends a run (…