From d6ba76e660b330a72770025fde529673655cc043 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Fri, 29 May 2026 20:05:48 +0000 Subject: [PATCH] fix(P01): add SIGTERM/SIGINT signal handlers for graceful shutdown ---ci--- project: ci phase: 1 milestone: v0.8 status: in_progress decisions: - id: D-026 decision: Graceful drain on SIGTERM/SIGINT: dispose timers then exit rationale: Prevents orphaned setTimeout timers from leaking when process is killed confidence: 0.88 requirements: covered: [FIX-07] ---/ci--- FIX-07: cli/index.ts registers SIGTERM/SIGINT handlers that call escalationProtocol.dispose() before process.exit. OrchestratorAgent registers its EscalationProtocol instance via registerEscalationProtocol(). SIGINT exits with code 130, SIGTERM with 143 (standard signal+128 convention). --- src/agents/orchestrator.ts | 2 ++ src/cli/index.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/agents/orchestrator.ts b/src/agents/orchestrator.ts index 77ab775..a7166c7 100644 --- a/src/agents/orchestrator.ts +++ b/src/agents/orchestrator.ts @@ -19,6 +19,7 @@ import { Specification, parseSpecification } from "../types/specification.js"; import { loadConfig, saveConfig, isCIAgentInitialized, initCIAgent } from "../core/config.js"; import { getAgent } from "./index.js"; import { IntelligenceBackend, BackendUnavailableError } from "../backends/types.js"; +import { registerEscalationProtocol } from "../cli/index.js"; import { execSync } from "node:child_process"; export interface GitAgentContext extends AgentContext { @@ -87,6 +88,7 @@ export class OrchestratorAgent extends BaseAgent { this.decisionEngine = new DecisionEngine(this.config, context.project_path, this.currentMilestone); this.escalationProtocol = new EscalationProtocol(this.config, context.project_path, this.currentMilestone); + registerEscalationProtocol(this.escalationProtocol); while (this.pipelineState.current_phase <= this.totalPhases) { this.log(`Processing phase ${this.pipelineState.current_phase} of ${this.totalPhases}`); diff --git a/src/cli/index.ts b/src/cli/index.ts index c0d05a1..448e979 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -19,6 +19,25 @@ import { createProjectsCommand, } from "./commands.js"; +let activeEscalationProtocol: { dispose(): void } | null = null; + +export function registerEscalationProtocol(protocol: { dispose(): void }): void { + activeEscalationProtocol = protocol; +} + +function gracefulShutdown(signal: string): void { + if (activeEscalationProtocol) { + try { + activeEscalationProtocol.dispose(); + } catch {} + activeEscalationProtocol = null; + } + process.exit(signal === "SIGINT" ? 130 : 143); +} + +process.on("SIGINT", () => gracefulShutdown("SIGINT")); +process.on("SIGTERM", () => gracefulShutdown("SIGTERM")); + const program = new Command(); program