mcpField
E
execution-lifecycle-manager

execution-lifecycle-manager

github.com/curiositechエージェントワークフロー25771 回インストール2 日前に更新
リポジトリ

execution-lifecycle-manager について

DAG 実行のライフサイクル管理を実装する開発者向けのスキルです。状態追跡、キャンセルの伝播、グレースフルシャットダウン、孤児プロセスのクリーンアップ、中断時のコスト記録を扱います。

主な機能

  1. 01実行中の処理と状態の追跡
  2. 02AbortSignal による子実行へのキャンセル伝播
  3. 03SIGTERM を試してから SIGKILL に移行
  4. 04終了時の実行停止とクリーンアップ
  5. 05中断処理での最終コスト記録

活用シーン

  1. 01DAG 実行に開始・停止・一時停止・再開を追加
  2. 02サーバー終了時に実行中の処理を停止
  3. 03タイムアウトやユーザー中断後に子プロセスを整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/curiositech/some_claude_skills --skill execution-lifecycle-manager

インストール後、Claude Code で/execution-lifecycle-managerを入力して読み込まれたか確認します。

同梱ファイルreferences/process-signals.md

詳細

実行ライフサイクルマネージャー

実行中の DAG 実行を一元的に状態管理し、グレースフルシャットダウンのパターンを提供します。

使用する場面

✅ 以下の場合に使用します:

  • 実行の開始/停止/一時停止/再開の制御を実装する
  • プロセスをグレースフルに終了する(SIGTERM → SIGKILL)
  • システム全体でアクティブな実行を追跡する
  • 孤立したプロセスをクリーンアップする
  • コスト追跡機能付きの中止ハンドラーを実装する

❌ 以下には使用しません:

  • コストの見積もりや料金の計算(cost-accrual-tracker を使用します)
  • DAG 構造の構築や変更
  • スキルの照合や選択
  • プロセスの起動(executor を直接使用します)

主なパターン

1. グレースフルシャットダウンパターン

必ず最初に SIGTERM を使用し、その後必要に応じて SIGKILL に切り替えます:

// CORRECT: Two-phase shutdown
const GRACEFUL_TIMEOUT_MS = 2000;

async function terminateProcess(proc: ChildProcess): Promise<void> {
  proc.kill('SIGTERM');

  const forceKillTimer = setTimeout(() => {
    if (!proc.killed) {
      proc.kill('SIGKILL');
    }
  }, GRACEFUL_TIMEOUT_MS);

  await waitForExit(proc);
  clearTimeout(forceKillTimer);
}
2. AbortController パターン

キャンセルの伝播には AbortController を使用します:

// Parent (DAGExecutor)
const abortController = new AbortController();

// Pass signal to child executors
await executor.execute({
  ...request,
  abortSignal: abortController.signal,
});

// To abort all children:
abortController.abort();
3. 実行レジストリパターン

監視とクリーンアップのために、アクティブな実行を追跡します:

interface ActiveExecution {
  executionId: string;
  abortController: AbortController;
  status: 'running' | 'stopping' | 'stopped' | 'completed' | 'failed';
  startedAt: number;
  stoppedAt?: number;
}

class ExecutionManager {
  private executions: Map<string, ActiveExecution> = new Map();

  create(id: string): ActiveExecution { /* ... */ }
  stop(id: string, reason: string): Promise<StopResult> { /* ... */ }
  listActive(): ActiveExecution[] { /* ... */ }
}

アンチパターン

SIGTERM を使わずに SIGKILL を送信する

初心者の考え方:「すぐに強制終了すればよい」

実際の問題:SIGKILL ではクリーンアップを実行できません。プロセスは以下を行えません:

  • バッファーをディスクに書き出す
  • ネットワーク接続をグレースフルに閉じる
  • ロックを解放する
  • 途中までの進捗を保存する

タイムライン:

  • 常に:SIGTERM でグレースフルシャットダウンを実行する
  • 2〜5 秒経っても停止しない場合:SIGKILL を使用する

正しい方法:必ず最初に SIGTERM を使用し、SIGKILL はフォールバックとして使用します。

Abort シグナルの伝播が不足している

初心者の考え方:「最上位の実行だけを追跡すればよい」

実際の問題:シグナルが伝播しないと、子プロセスが孤立します:

  • 親プロセスが終了しても、子プロセスは実行を続ける
  • リソースがリークする
  • コストが発生し続ける

正しい方法:実行ツリー全体に AbortSignal を渡します。

同期的な停止ハンドラー

初心者の考え方:「停止処理はすぐに戻るべきだ」

実際の問題:停止処理は非同期です。プロセスの終了には時間が必要です:

  • ネットワークリクエストのタイムアウトを待つ必要がある
  • ファイルハンドルを閉じる必要がある
  • コストの最終計算が必要である

正しい方法:クリーンアップの完了後に最終状態を返す Promise を返します。

状態マシン

         ┌──────────┐
         │  idle    │
         └────┬─────┘
              │ start()
              ▼
         ┌──────────┐
    ┌───►│ running  │◄───┐
    │    └────┬─────┘    │
    │         │          │ resume()
    │         │ pause()  │
    │         ▼          │
    │    ┌──────────┐    │
    │    │ paused   │────┘
    │    └────┬─────┘
    │         │ stop()
    │         ▼
    │    ┌──────────┐
    └────│ stopping │ (transitional - 2-10s)
         └────┬─────┘
              │
     ┌────────┴────────┐
     ▼                 ▼
┌──────────┐     ┌──────────┐
│ stopped  │     │  failed  │
└──────────┘     └──────────┘

API 設計

停止エンドポイントのレスポンス
interface StopResponse {
  status: 'stopped';
  executionId: string;
  reason: string;  // 'user_abort' | 'timeout' | 'error'
  finalCostUsd: number;
  stoppedAt: number;
  summary: {
    nodesCompleted: number;
    nodesFailed: number;
    nodesTotal: number;
    durationMs: number;
  };
}
サーバーシャットダウン時のクリーンアップ
// In server.ts
process.on('SIGINT', async () => {
  console.log('Shutting down...');

  // Stop all active executions gracefully
  const active = executionManager.listActive();
  await Promise.all(
    active.map(e => executionManager.stop(e.executionId, 'server_shutdown'))
  );

  server.close();
});

統合ポイント

コンポーネント責務
ExecutionManager実行を追跡し、停止を調整する
DAGExecutorAbortController を所有し、ウェーブを統括する
ProcessExecutorプロセスを起動し、SIGTERM/SIGKILL を処理する
/api/execute/stop停止リクエスト用の HTTP インターフェース

参考資料

Unix シグナルの処理について詳しくは、/references/process-signals.md を参照してください。

コメント