v0.6.4
Fix: Plugin init deadlock causing lead to hang during teammate spawning
When spawning a teammate, OpenCode creates a new instance for the worktree directory and loads all plugins — including ours. Our plugin's init ran recovery routines that called session.abort back to the server. The server couldn't process the abort because it was still handling the session.create that triggered the plugin load. This deadlock caused a 120-second timeout, after which the lead's prompt loop died silently.
The root cause was identified through structured logging added to the plugin. The [ensemble] log lines traced the exact sequence: spawn:session:start → server bootstraps instance → plugin loads → recovery calls session.abort → deadlock.
- Skip recovery for worktree instances —
isWorktreeInstance()detects teammate directories and bypassesrecoverStaleMembers,recoverUndeliveredMessages, andrecoverOrphanedWorktreesduring init - Deliver lead-bound messages via system prompt transform instead of
promptAsynccontent injection — messages are stored in the DB and included inline in the lead's system prompt on its next turn - Wake the lead from
team_messagewith a minimalpromptAsyncnotification ([System: New team message from X]) — the system prompt transform delivers the actual content - Fire-and-forget all
promptAsynccalls with error logging instead of silent swallowing - Clean up
withTimeouttimer on success to prevent unhandled rejections - Remove dead
flushPendingMessagescode and skip lead-bound messages in recovery
Also in this release
- Structured logging via SDK
app.log()for all critical paths (spawn lifecycle, message delivery, recovery, wake-ups) - Biome lint step added to CI workflow
- 14 integration tests covering full team lifecycle, parallel spawn races, deadlock prevention, spawn rollback with lead notification, message delivery pipeline, concurrent messaging, and edge cases
- 332 total tests (up from 302)
Full Changelog: v0.6.3...v0.6.4