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).
This commit is contained in:
@@ -19,6 +19,7 @@ import { Specification, parseSpecification } from "../types/specification.js";
|
|||||||
import { loadConfig, saveConfig, isCIAgentInitialized, initCIAgent } from "../core/config.js";
|
import { loadConfig, saveConfig, isCIAgentInitialized, initCIAgent } from "../core/config.js";
|
||||||
import { getAgent } from "./index.js";
|
import { getAgent } from "./index.js";
|
||||||
import { IntelligenceBackend, BackendUnavailableError } from "../backends/types.js";
|
import { IntelligenceBackend, BackendUnavailableError } from "../backends/types.js";
|
||||||
|
import { registerEscalationProtocol } from "../cli/index.js";
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
export interface GitAgentContext extends AgentContext {
|
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.decisionEngine = new DecisionEngine(this.config, context.project_path, this.currentMilestone);
|
||||||
this.escalationProtocol = new EscalationProtocol(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) {
|
while (this.pipelineState.current_phase <= this.totalPhases) {
|
||||||
this.log(`Processing phase ${this.pipelineState.current_phase} of ${this.totalPhases}`);
|
this.log(`Processing phase ${this.pipelineState.current_phase} of ${this.totalPhases}`);
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ import {
|
|||||||
createProjectsCommand,
|
createProjectsCommand,
|
||||||
} from "./commands.js";
|
} 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();
|
const program = new Command();
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|||||||
Reference in New Issue
Block a user