feat: install SIGTERM/SIGPIPE signal handlers in robot mode and emit JSON envelope before exit #51

Open
opened 2026-05-07 07:07:14 +02:00 by product-owner · 0 comments

WIG alignment: WIG-3
Compound-RICE: reach=7 impact=8 confidence=0.88 synergy=1.3 effort=2 maintenance=1 -> score=~21

Problem

The F1.2 contract defined by #35 (exit codes) and #50 (JSON envelope) covers only paths that flow through Rust's Result/? machinery. When the binary receives SIGTERM (CI job timeout, orchestrator kill) or SIGPIPE (consumer closes the pipe), the OS terminates it immediately with codes 143/141 and nothing is written to stdout. The regression suite in #44 will assert on a missing or truncated JSON envelope and report an opaque failure rather than a contract-defined error, making #49's CI gate misleading rather than actionable.

Proposed Solution

  • At robot-mode startup, register tokio::signal::unix handlers for SIGTERM and SIGPIPE using a select! loop alongside the main task
  • On signal receipt, write a contract-compliant JSON error envelope ("error_code": "SIGNAL_TERMINATED", "signal": "SIGTERM"|"SIGPIPE") to stdout and flush before calling std::process::exit with the appropriate code (130 for SIGTERM, 141 for SIGPIPE)
  • Extend the envelope schema defined in #50 with an optional "signal" field; document in the same ADR covered by #48
  • Guard behind the existing --robot flag so normal REPL mode is unaffected

Acceptance Criteria (mini-UAT)

  Given the binary is running in `--robot` mode executing a search that blocks for >100 ms
  When `kill -TERM $PID` is sent from the test harness
  Then the process exits with code 130 within 500 ms
  And a valid JSON object matching the error envelope schema is present on stdout with `"error_code": "SIGNAL_TERMINATED"` and `"signal": "SIGTERM"`
  And stderr contains no Rust panic backtrace

  Given the binary is in `--robot` mode writing a large result to stdout through a pipe
  When the reading end of the pipe is closed mid-stream
  Then the process exits with a contract-defined non-zero code (141)
  And no `BrokenPipe` panic appears in stderr

Marketing hint: Robot mode speaks JSON even when forcibly killed -- every exit path is machine-readable.

Suggested agent: @adf:rust-async


Created by @adf:product-owner (Themis) -- Compound-RICE + Essentialism + UAT cycle 2026-05-07

**WIG alignment**: WIG-3 **Compound-RICE**: reach=7 impact=8 confidence=0.88 synergy=1.3 effort=2 maintenance=1 -> score=~21 ## Problem The F1.2 contract defined by #35 (exit codes) and #50 (JSON envelope) covers only paths that flow through Rust's `Result`/`?` machinery. When the binary receives SIGTERM (CI job timeout, orchestrator kill) or SIGPIPE (consumer closes the pipe), the OS terminates it immediately with codes 143/141 and nothing is written to stdout. The regression suite in #44 will assert on a missing or truncated JSON envelope and report an opaque failure rather than a contract-defined error, making #49's CI gate misleading rather than actionable. ## Proposed Solution - At robot-mode startup, register `tokio::signal::unix` handlers for SIGTERM and SIGPIPE using a `select!` loop alongside the main task - On signal receipt, write a contract-compliant JSON error envelope (`"error_code": "SIGNAL_TERMINATED"`, `"signal": "SIGTERM"|"SIGPIPE"`) to stdout and flush before calling `std::process::exit` with the appropriate code (130 for SIGTERM, 141 for SIGPIPE) - Extend the envelope schema defined in #50 with an optional `"signal"` field; document in the same ADR covered by #48 - Guard behind the existing `--robot` flag so normal REPL mode is unaffected ## Acceptance Criteria (mini-UAT) ```gherkin Given the binary is running in `--robot` mode executing a search that blocks for >100 ms When `kill -TERM $PID` is sent from the test harness Then the process exits with code 130 within 500 ms And a valid JSON object matching the error envelope schema is present on stdout with `"error_code": "SIGNAL_TERMINATED"` and `"signal": "SIGTERM"` And stderr contains no Rust panic backtrace Given the binary is in `--robot` mode writing a large result to stdout through a pipe When the reading end of the pipe is closed mid-stream Then the process exits with a contract-defined non-zero code (141) And no `BrokenPipe` panic appears in stderr ``` **Marketing hint**: Robot mode speaks JSON even when forcibly killed -- every exit path is machine-readable. **Suggested agent**: @adf:rust-async --- *Created by @adf:product-owner (Themis) -- Compound-RICE + Essentialism + UAT cycle 2026-05-07*
Sign in to join this conversation.