Skip to content

Handle SIGTERM and SIGINT as graceful headless cancellation #94

Description

@byapparov

Context

Headless runs have no SIGTERM/SIGINT lifecycle handler. A supervising executor can terminate a running model call before the CLI cancels the session or emits a terminal NDJSON event.

A signal does not itself prove a timeout: SIGTERM may come from an executor timeout, deployment, operator, or process supervisor.

Problem / Goal

On the first termination signal, cancel the active session, emit the most accurate terminal lifecycle available, flush output within a bounded grace period, and exit using conventional signal-derived codes.

Proposed Approach

Register signal handling around the active headless run:

  • SIGINT → interrupted, exit code 130;
  • SIGTERM → terminated, exit code 143;
  • cancel through the existing session cancellation path;
  • allow the event loop to reach idle and emit terminal records;
  • await the NDJSON writer flush;
  • treat a second signal or expired shutdown grace period as immediate hard termination.

The executor remains responsible for recording whether it sent SIGTERM because of its own timeout.

Acceptance Criteria

  • The first SIGINT cancels the active session and exits 130.
  • The first SIGTERM cancels the active session and exits 143.
  • Signals are not classified as timeout without an explicit CLI-owned timeout.
  • JSON mode emits a structured interruption/termination error and session_complete when the grace period permits.
  • Output is flushed before controlled termination.
  • A second signal causes immediate hard termination.
  • Signal handlers are removed when the run completes.
  • Real subprocess tests cover SIGINT, SIGTERM, terminal-event ordering, and exit codes.

Out of Scope

Roadmap Alignment

  • Pillar: EXEC
  • Quarter: Q3 2026
  • Priority: P1
  • Theme fit: Pilot feedback: executor reliability.
  • Decision gate impact: Indirect support for reliable Code Review and Bug-Fix executions.

References

Upstream OpenCode References

  • anomalyco/opencode#24658auto-closed after inactivity, not resolved; SIGTERM bypasses the graceful TUI cleanup path and leaves the process hanging.
  • anomalyco/opencode#32932open; server shutdown leaves sockets and the listening port behind on Windows.

These issues support explicit signal-driven cleanup, but neither defines the headless terminal-event contract or signal-specific exit semantics required here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions