Skip to content

fix(lifecycle): bound OpenCode server lifecycle cleanup - #119

Merged
Inakitajes merged 5 commits into
mainfrom
fix/fix-opencode-zombie-processes
Sep 15, 2026
Merged

Inakitajes merged 5 commits into
mainfrom
fix/fix-opencode-zombie-processes

Conversation

@Inakitajes

Copy link
Copy Markdown
Owner

Summary

Ships the OpenSpec change fix-opencode-server-lifecycle: Convoy now owns each managed opencode serve child from spawn to confirmed exit, so abandoned servers stop accumulating while deliberately independent lifetimes stay untouched.

Why

Convoy had code-confirmed paths that could leave its OpenCode servers running after their owner was gone — a forced process.exit during abort, and signal handlers retired before final release — while the close primitive only submitted a signal without waiting for or verifying termination. A finished run's terminal hold could also outlive the controller meant to dismiss it. Operators reported accumulating memory-heavy opencode serve processes.

What

  • Owned launch and bounded stop (src/managed-server.ts, src/process-stop.ts): one owned child per managed server with an awaitable, idempotent stop — SIGTERM grace, SIGKILL escalation, and an observed outcome. Confirmed disappearance counts as stopped; a submitted signal never counts as success.
  • Process identity and records (src/process-identity.ts, src/process-records.ts): PID plus kernel birth identity, UID, and executable role, in private versioned records under ~/.convoy/processes/ published before readiness. Unknown or low-resolution identity is never a kill target.
  • Coordinator-wide shutdown ownership (src/runner.ts, src/coordinate.ts): one owner scope spanning boot → execution → terminal hold → release, reserving the last 3s of the existing 15s ceiling for server stop. Repeated abort/deadline force-stops owned servers before exiting instead of skipping cleanup.
  • Controller-aware terminal holds (src/control-server.ts, src/control-progress.ts): a completed run's finish hold resolves on valid /bye or heartbeat-lease expiry, then runs normal cleanup.
  • Conservative orphan reconciliation: attributable run/helper orphans are reconciled at a later managed startup under per-record locks and identity revalidation, optionally stopping a wedged server whose HTTP endpoint no longer answers. Legacy coordinator-PID records stay ineligible.
  • Explicit exclusions: independent authoring services, standalone OpenCode windows, background/paused runs, and pending permission/human gates keep their existing lifetimes. No name/PPID/port pkill-style scans.
  • Fallback proposal helpers close on every early return and must transfer to independent service ownership or reuse that service before invoking an authoring command (src/propose-service.ts).

Planning artifacts live in openspec/changes/fix-opencode-server-lifecycle/.

Known limits

  • Recovery is eventual: it runs at a later managed startup, so a SIGKILL with no subsequent Convoy launch is not contained. No supervisor/daemon is introduced.
  • A SIGKILL between OS spawn and identity publication can leave an unattributable child; uncertain evidence is retained and never auto-killed.
  • Descendant processes that create their own sessions/process groups (MCP, LSP, tool subshells) remain outside this guarantee.

Verification

  • bun run typecheck — clean
  • bun test — 3388 pass, 0 fail (158 files)
  • openspec validate fix-opencode-server-lifecycle --strict — valid

- Track owned server processes and reconcile stale lifecycle records
- Fail closed when process identity probes are unknown
- Share shutdown budgets across coordinator and managed processes
- Add comprehensive process lifecycle and server startup tests
- Give home-tui a hermetic proposeBranchName default so no test reaches
  the real namer and boots an opencode serve child
- Stop servers a test still owns at the end of the run, while the owner is
  alive, and drop their records
- Sync the opencode-server-lifecycle delta into a new main spec under
  openspec/specs/ (8 requirements, 26 scenarios).
- Mark tasks 1.2, 2.4, 2.5, and 6.4 complete with recorded verification.
- Strengthen the Linux identity test to assert birth, uid, and executable
  from a live process, and cover the reboot case.
- Add scripts/identity-smoke.ts, runnable from a compiled binary, proving
  the platform identity probe loads outside the test runtime.
- Refresh the coverage badge.

Linux-only execution is unverified on this macOS host; those suites run on
ubuntu CI.
@Inakitajes
Inakitajes merged commit 436fd30 into main Sep 15, 2026
2 checks passed
@Inakitajes
Inakitajes deleted the fix/fix-opencode-zombie-processes branch September 15, 2026 16:25
Inakitajes added a commit that referenced this pull request Sep 15, 2026
The capability spec was synced in #119; this only moves the completed
change into openspec/changes/archive/2026-09-15-fix-opencode-server-lifecycle/.
All 25 tasks are complete.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant