For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション
Theme
Codex

Codex use case

インシデントの調査と修正の検証

インシデントの調査資料をもとに不具合を再現し、必要な箇所だけを修正して、レビュー用の引き継ぎ資料をまとめます。

Difficulty Intermediate
Time horizon Long-running

機密情報を取り除いたインシデントの調査資料と、テストを実行できる環境を Codex Cloud に渡します。Codex Cloud は資料を調べ、証拠がコードの不具合を示していれば、その不具合を再現します。必要な箇所だけを修正したパッチに、回帰テストの実行結果と、デプロイおよびロールバックの引き継ぎ資料を添えて返します。

Best for

  • 影響が落ち着いたインシデントを調査する小規模な開発チーム
  • 機密情報を除去した調査資料があり、手元の入力データを使って安全に再現を試せるアプリケーションの不具合
  • 稼働中の隔離されたテスト環境で、対象を絞った修正をレビューするチーム

Contents

    ← All use cases

    インシデントの調査と修正の検証

    インシデントの調査資料をもとに不具合を再現し、必要な箇所だけを修正して、レビュー用の引き継ぎ資料をまとめます。

    機密情報を取り除いたインシデントの調査資料と、テストを実行できる環境を Codex Cloud に渡します。Codex Cloud は資料を調べ、証拠がコードの不具合を示していれば、その不具合を再現します。必要な箇所だけを修正したパッチに、回帰テストの実行結果と、デプロイおよびロールバックの引き継ぎ資料を添えて返します。

    Intermediate
    Long-running

    機密情報を取り除いたインシデントの調査資料と、テストを実行できる環境を Codex Cloud に渡します。Codex Cloud は資料を調べ、証拠がコードの不具合を示していれば、その不具合を再現します。必要な箇所だけを修正したパッチに、回帰テストの実行結果と、デプロイおよびロールバックの引き継ぎ資料を添えて返します。

    Intermediate
    Long-running

    Related links

    Best for

    • 影響が落ち着いたインシデントを調査する小規模な開発チーム
    • 機密情報を除去した調査資料があり、手元の入力データを使って安全に再現を試せるアプリケーションの不具合
    • 稼働中の隔離されたテスト環境で、対象を絞った修正をレビューするチーム

    Starter prompt

    [path to sanitized incident packet] にある調査資料を使って、[repository/service] のインシデント [incident ID] を調査してください。このタスク用に選択された公開済みのクラウド環境で作業してください。 調査資料には、インシデントが発生した時間帯、影響を受けたリリースの SHA、期待される動作と実際に観測された動作、関連するログとトレースの抜粋、安全に再現を試すための入力データが記載されています。まず AGENTS.md と、リポジトリのセットアップ手順およびテスト手順を読んでください。チェックアウトしたリビジョンが影響を受けたリリースと一致するか確認し、異なる場合は結論を出す前にその違いを説明してください。 経緯を簡潔に時系列でまとめ、各項目に調査資料とソースコードの該当ファイル名と行番号を示してください。観測した事実、仮説、不足している証拠を区別し、最も有力な仮説を隔離されたテスト環境で検証してください。障害を再現できない場合や決め手となる証拠がない場合は、調査結果に次の診断手順を添えて返してください。根本原因を特定したと断言したり、推測で修正を加えたりしないでください。 コードの不具合を裏付ける証拠がある場合は、回帰テストを追加し、修正前のコードで実行して、そのテストが失敗することを示してください。必要最小限の修正を加え、同じテストを再実行してください。その後、このリポジトリで実行できる、より広い範囲を対象とした関連テスト、lint、型チェックを実行してください。実行したコマンドと終了コード、実行できなかったチェックを記録してください。 ブランチの差分、Pull Request の説明文の下書き、test-evidence.md、デプロイとロールバックの引き継ぎ資料を用意してください。証拠で裏付けられた原因、まだ確定できていない点、修正によって変わった動作を明記してください。判定基準ごとに検証記録を 1 行にまとめてください。各行には、リビジョンと環境のバージョン、修正前と修正後それぞれのコマンド・終了コード・ログ、実際の結果、判定(合格、失敗、阻害要因により実行不能、未実行)、検証上の制約を記載してください。Pull Request の説明文は、レビュー用の下書きとしてローカルに保存してください。 今回の Webhook 重複配信の例では、コミット前とコミット後のそれぞれについて、どの時点で障害が起きるかを明示してください。独立したデータベース接続を使い、同じイベントが重複して届くと処理が競合することを実証してください。トランザクションの分離、受信記録の保持、新旧バージョンが混在するロールアウトについても、前提条件を明記してください。 この例で保留中の配信の再開を提案する前に、旧バージョンのコンシューマーが残りの処理をすべて終えた時点で、旧バージョンで作成された注文のうち、対応する受信記録がないものを特定してください。既存のキューまたは隔離手順を使い、該当するイベントキーの処理を保留してください。受信記録のない注文と、それに対応する再試行待ちのイベントを初期データとするテストを追加し、保留中は同じ注文がもう一度作成されることも、成功応答が返されることもないと実証してください。照合には、信頼できる不変のイベント識別情報、正規のペイロード、注文がコミットされたことを示す証拠だけを使ってください。証拠が不足している場合や食い違う場合は、担当者が確認できるよう該当イベントの隔離を続けてください。フィンガープリントを推測で作成したり、受信記録を削除したり、未解決の旧バージョンのイベントを再実行したりしないでください。 私が別の承認済み情報源を指定しない限り、提供された調査資料とリポジトリだけを使ってください。監視データへのアクセスは、明示的に承認された読み取り専用の監視データソースが提供された場合に限り、そのソースだけを対象としてください。コードとタスクの成果物はこのリポジトリ内にだけ書き込み、データベースはこのリポジトリの隔離されたテスト用データベースだけを使ってください。本番のアプリケーションホストやデータベースホストに直接接続したり、本番システムを変更したりしないでください。本番イベントの再実行、稼働中のデータや監視設定の変更、外部向けの状況報告の作成や投稿、マージ、デプロイは行わないでください。
    [path to sanitized incident packet] にある調査資料を使って、[repository/service] のインシデント [incident ID] を調査してください。このタスク用に選択された公開済みのクラウド環境で作業してください。 調査資料には、インシデントが発生した時間帯、影響を受けたリリースの SHA、期待される動作と実際に観測された動作、関連するログとトレースの抜粋、安全に再現を試すための入力データが記載されています。まず AGENTS.md と、リポジトリのセットアップ手順およびテスト手順を読んでください。チェックアウトしたリビジョンが影響を受けたリリースと一致するか確認し、異なる場合は結論を出す前にその違いを説明してください。 経緯を簡潔に時系列でまとめ、各項目に調査資料とソースコードの該当ファイル名と行番号を示してください。観測した事実、仮説、不足している証拠を区別し、最も有力な仮説を隔離されたテスト環境で検証してください。障害を再現できない場合や決め手となる証拠がない場合は、調査結果に次の診断手順を添えて返してください。根本原因を特定したと断言したり、推測で修正を加えたりしないでください。 コードの不具合を裏付ける証拠がある場合は、回帰テストを追加し、修正前のコードで実行して、そのテストが失敗することを示してください。必要最小限の修正を加え、同じテストを再実行してください。その後、このリポジトリで実行できる、より広い範囲を対象とした関連テスト、lint、型チェックを実行してください。実行したコマンドと終了コード、実行できなかったチェックを記録してください。 ブランチの差分、Pull Request の説明文の下書き、test-evidence.md、デプロイとロールバックの引き継ぎ資料を用意してください。証拠で裏付けられた原因、まだ確定できていない点、修正によって変わった動作を明記してください。判定基準ごとに検証記録を 1 行にまとめてください。各行には、リビジョンと環境のバージョン、修正前と修正後それぞれのコマンド・終了コード・ログ、実際の結果、判定(合格、失敗、阻害要因により実行不能、未実行)、検証上の制約を記載してください。Pull Request の説明文は、レビュー用の下書きとしてローカルに保存してください。 今回の Webhook 重複配信の例では、コミット前とコミット後のそれぞれについて、どの時点で障害が起きるかを明示してください。独立したデータベース接続を使い、同じイベントが重複して届くと処理が競合することを実証してください。トランザクションの分離、受信記録の保持、新旧バージョンが混在するロールアウトについても、前提条件を明記してください。 この例で保留中の配信の再開を提案する前に、旧バージョンのコンシューマーが残りの処理をすべて終えた時点で、旧バージョンで作成された注文のうち、対応する受信記録がないものを特定してください。既存のキューまたは隔離手順を使い、該当するイベントキーの処理を保留してください。受信記録のない注文と、それに対応する再試行待ちのイベントを初期データとするテストを追加し、保留中は同じ注文がもう一度作成されることも、成功応答が返されることもないと実証してください。照合には、信頼できる不変のイベント識別情報、正規のペイロード、注文がコミットされたことを示す証拠だけを使ってください。証拠が不足している場合や食い違う場合は、担当者が確認できるよう該当イベントの隔離を続けてください。フィンガープリントを推測で作成したり、受信記録を削除したり、未解決の旧バージョンのイベントを再実行したりしないでください。 私が別の承認済み情報源を指定しない限り、提供された調査資料とリポジトリだけを使ってください。監視データへのアクセスは、明示的に承認された読み取り専用の監視データソースが提供された場合に限り、そのソースだけを対象としてください。コードとタスクの成果物はこのリポジトリ内にだけ書き込み、データベースはこのリポジトリの隔離されたテスト用データベースだけを使ってください。本番のアプリケーションホストやデータベースホストに直接接続したり、本番システムを変更したりしないでください。本番イベントの再実行、稼働中のデータや監視設定の変更、外部向けの状況報告の作成や投稿、マージ、デプロイは行わないでください。

    準備したクラウド環境で、範囲を絞ったインシデントを 1 件、Codex に調査してもらいます。目標は、エンジニアがレビューできる形で、根拠のある説明、修正前に失敗する回帰テスト、不具合に絞ったパッチ、実行したテストの結果をそろえることです。

    この進め方は、インシデントへの初動対応でサービスが安定し、残る作業がアプリケーションの不具合の解明と修正になった段階に適しています。手元の証拠で原因を特定できない場合は、何を調査したか、次に何を確認すべきかを明確にすることが成果になります。

    環境とインシデント調査資料の準備

    1. Web アプリまたはデスクトップアプリで、作業場所 > クラウド の順に選び、公開済みの環境を選択します。

    2. 環境を新規作成するには、環境を作成を選び、対象の GitHub リポジトリを選択します。ランタイム、依存関係、隔離されたテスト用サービスの準備を Codex に依頼します。

    3. セットアップレポートを確認し、失敗したチェックの原因を解消してから、公開を選び、新しいタスクを開始します。

    依存関係やサービスのセットアップが変わった場合は、再利用する環境を編集し、テストしてから再公開します。その後、新しいタスクで更新後の環境を検証します。

    リポジトリを更新しても、インストールコマンドや起動コマンドは再実行されません。既存のタスクはそれぞれの状態を保持します。

    必要なネットワーク接続先とテスト用の認証情報は個別に設定してください。接続先を許可しても、サービスへのアクセス権は付与されません。

    新しいタスクで、チェックアウトした対象のコード、依存関係、各種チェック、使い捨てのテスト用データベースが機能することを確認します。調査開始時のコミットとテストのベースラインを記録してください。

    初期調査は、用意した資料だけで進められるようにします。機密情報を除いた資料一式をリポジトリに置くか、タスクから各ファイルを参照できるようにします。開始前にエンジニアが次の資料をまとめ、内容を確認してください。

    入力 含める内容
    インシデント概要 インシデント ID、対象時間帯(UTC)、期待した動作と実際の動作、判明している影響、現在の状況。確認済みの件数と推定値を区別します。
    リリースの特定情報 デプロイしたコミットの SHA、デプロイ時刻、関連する設定値(シークレットを除く)、判明していれば最後に正常動作を確認したリリース。
    関連付けに使える証拠 タイムスタンプ、イベント ID、リクエスト ID、トレース ID、参照元ファイルが分かるログとトレースの抜粋。機密情報を除く際も、識別子の対応関係を保ちます。
    安全な再現手順 テスト用のリクエストボディ、関連するフィクスチャ、稼働中のサービスを呼び出さずに疑わしい処理経路を実行する手順。
    リポジトリでの作業指針 関連するパス、データベースエンジン、テストコマンド、許可される変更、結果をレビューするエンジニア。

    資料にはアクセストークン、顧客のペイロード、個人データを含めないでください。

    監視ツールとの連携は任意です。使用する場合は、対象のプロジェクトと時間帯に限って読み取り専用アクセスを付与します。ファイルを使って調査する場合、この連携は不要です。テスト用の認証情報を本番設定と分けて管理するよう、リポジトリの作業手順に明記してください。

    開始用プロンプトの実行

    このページの開始用プロンプトを使い、プレースホルダーを対象のインシデント ID、リポジトリ、調査資料のパスに置き換えます。最初の回答では、証拠と該当するコードの対応関係、再現できる動作、不足している情報を示してもらいます。

    コードの不具合を裏付ける証拠がある場合にのみ、Codex に修正の実装まで進めるよう依頼します。それ以外の場合は調査内容をレビューし、不足している情報を補ってください。もっともらしい説明、最近のコード変更、別のリリースでのテスト成功は、いずれもそれだけでは原因を特定する根拠になりません。

    例:Webhook の再試行による注文の重複

    実行可能なインシデント調査サンプルを使って調査を練習できます。サンプルには、調査資料一式、不具合を再現できるハンドラー、修正済みのハンドラー、PostgreSQL を使ったテストが含まれています。

    このサービスは order.created Webhook を受信すると注文を書き込み、イベントを処理したことを記録します。インシデントの発生時には、再試行によって 2 件目の注文が作成されたように見えました。チームはすでにサービスを安定させ、レビュー用のパッチを必要としています。

    サンプルでは JavaScript、Node.js のテストランナー、PostgreSQL を使用します。使い捨てのフィクスチャには event_receipts テーブルがあり、イベントを識別する列は NULL を許容せず、(provider, account_id, event_id) には検査を遅延できない一意制約が設定されています。

    このスキーマでは、注文の作成前に受領記録を挿入してイベントの処理権を確保できます。また、同じイベントキーが異なる内容で再利用されたことを検出するため、正規化したペイロードのフィンガープリントを保持します。

    アプリケーションの仕様上、処理権を確保する時点で注文を参照する外部キーが必要な場合や、内容の不一致を検出するための証拠がない場合は、ここで作業を止め、スキーマや設計を別途検討してください。

    検証記録には、使用したランタイム、ロックファイルで固定した依存関係、PostgreSQL のバージョン、トランザクション分離レベル、受領記録のスキーマを正確に記載します。ダウンロードできるフィクスチャでは、Node.js 24.19.0、PostgreSQL 17.6、pg 8.23.1 にバージョンを固定しています。

    付属の Compose 構成を使う場合は、Docker デーモンが動作する環境を準備し、公開前に docker info が成功することを確認してください。この構成のデータベースが受け付けるのは公開されたテスト用の認証情報だけで、待ち受け先はローカルのループバックインターフェースに限定されています。テストを自分のアプリケーションに合わせる際は、そのアプリケーションのデータベースエンジンと関連設定を使用してください。

    この例では、PostgreSQL の分離レベルが READ COMMITTED であること、プロバイダーのイベント ID が再配信時にも変わらないこと、受領記録の保持期間がプロバイダーの再試行・再送期間全体をカバーすることを前提としています。

    修正済みのハンドラーは、受領記録と注文を同じデータベーストランザクション内で書き込みます。フィクスチャは使い捨てのデータベースと delivery_holds テーブルを用意し、既存のキューで配信を隔離する手順を再現します。このテーブルはテスト用で、アプリケーションへの追加を提案するものではありません。

    アプリケーションの修正では、キュー、データベースサービス、スキーマのマイグレーション、監視ツールとの連携は追加しません。前提が成り立たない場合は作業を止め、修正範囲を見直してください。

    サンプルの実行

    ZIP を展開し、展開先のディレクトリで Docker と Compose を使って次のコマンドを実行します。

    docker compose -p codex-incident-rehearsal up -d --wait postgres
    docker compose -p codex-incident-rehearsal run --rm runner node main.mjs baseline
    docker compose -p codex-incident-rehearsal run --rm runner node main.mjs regression
    docker compose -p codex-incident-rehearsal run --rm runner
    docker compose -p codex-incident-rehearsal down --volumes

    ベースラインのコマンドは終了コード 1 で終了する必要があります。再試行後に注文と受領記録が各 1 件であることを検証するアサーションに対し、実際には注文が 2 件、受領記録が 1 件あるためです。回帰テストのコマンドは修正済みのハンドラーに対して同じアサーションを実行し、終了コード 0 で終了する必要があります。

    最後のテストコマンドでは、独立した接続、データベースのロック競合、受領記録のない旧注文に対する再配信の隔離など、すべてのテストケースを実行します。

    環境で docker-compose が提供されている場合は、docker compose の代わりにその表記を使用してください。コマンドは順番に実行します。テストスイートがリセットするのは、テスト用として明示されたスキーマだけです。

    トランザクションを扱うハンドラーの確認

    以下はダウンロードファイルに含まれる main.mjs の抜粋です。実行する際は ZIP を展開し、テストとフィクスチャのセットアップを含む完全なファイルを使用してください。

    受領記録による処理権の確保と注文の書き込みをまとめたトランザクション
    async function fixed(client, input, hook = async (_boundary) => {}) {
      await assertNotHeld(client, input);
      await hook("beforeClaim");
      await client.query("BEGIN ISOLATION LEVEL READ COMMITTED");
      let committed = false;
      try {
        const claimed = await client.query(
          `INSERT INTO event_receipts(provider, account_id, event_id, payload_fingerprint)
           VALUES ($1,$2,$3,$4)
           ON CONFLICT (provider, account_id, event_id) DO NOTHING RETURNING event_id`,
          [...key(input), fingerprint(input)]
        );
        trace("receipt-claim-result", client, input, {
          inserted: claimed.rowCount === 1,
        });
        if (claimed.rowCount === 1) {
          await hook("afterClaim");
          await writeOrder(client, input);
          await hook("afterOrder");
        } else {
          // A new READ COMMITTED statement sees the winner after its commit.
          await checkReceipt(client, input);
        }
        await client.query("COMMIT");
        committed = true;
        trace("transaction-committed", client, input);
        await hook("afterCommit");
        trace("acknowledged", client, input, acknowledgment);
        return acknowledgment;
      } catch (error) {
        if (!committed) {
          await client.query("ROLLBACK");
          trace("transaction-rolled-back-no-ack", client, input);
        } else {
          trace("committed-response-lost-no-ack", client, input);
        }
        throw error;
      }
    }

    1. 要点を絞った調査資料の準備

    次のファイル構成を調査資料のひな型にします。

    incidents/INC-DEMO-014/incident.md
    incidents/INC-DEMO-014/logs.ndjson
    incidents/INC-DEMO-014/release.json
    incidents/INC-DEMO-014/order-created.sanitized.json

    このシナリオでは、release.json にサンプルのリリースの SHA として 4a1c9e7d2b6f8a0c3d5e7f9012a4b6c8d0e2f413、デプロイ時刻として 2026-09-22T11:58:00Z が記録されています。インシデントの対象時間帯は同日の 12:03:00Z–12:05:00Z です。調査資料によると、プロバイダーの 1 件のイベントに対して内部で 2 件の注文が作成されています。自分の調査資料には、影響が確認された件数を記載し、推定値はその旨を明記してください。

    機密情報を除いたログからは、次のように一連の処理を追跡できます。

    時刻(UTC) 対応する識別情報 確認できたこと
    12:03:04.110 イベント evt_demo_042、アカウント acct_demo_01、リクエスト req_demo_a、トレース trace_demo_a 初回配信が Webhook ハンドラーに到達します。
    12:03:04.145 同じイベント、初回のトレース 注文 ord_demo_101 がコミットされます。
    12:03:04.150 同じワーカー 受領記録の書き込み成功やレスポンスが記録される前に、ワーカーが終了します。この抜粋だけでは、受領記録が存在しないとは断定できません。
    12:03:14.203 同じプロバイダー、アカウント、イベント。リクエスト req_demo_b、トレース trace_demo_b 再試行のリクエストがハンドラーに到達します。ハンドラーは、一致する受領記録が見つからなかったとログに記録します。
    12:03:14.241 同じイベント、2 回目のトレース 注文 ord_demo_102 がコミットされ、受領記録が書き込まれます。

    再配信でも同じイベント ID を使い、リクエスト ID とトレース ID は配信ごとに別の値を使います。リクエスト単位の識別子だけでは、再試行と元のイベントの関係が分からなくなります。

    2. 証拠の追跡と仮説の検証

    インシデント報告では、再試行の集中が重複の原因として挙げられています。Codex にはこれを仮説として扱わせます。配信を 2 回観測しただけでは再試行が集中したとはいえません。また、再試行は Webhook ハンドラーが正常に扱えるべき事象です。

    不具合があるサンプルの実装を調べると、受領記録を検索し、注文をコミットしてから、別のトランザクションで受領記録を書き込んでいます。2 回のコミットの間に何が起こり得るかに着目すると、重複の発生をよりよく説明できます。この間にワーカーが停止すると、受領記録のない注文が残り、再試行で別の注文が作成される可能性があります。また、同時に配信された場合は、どちらも受領記録を書き込む前に、それぞれの事前チェックを通過する可能性があります。

    Codex に実際のハンドラーとトランザクション境界を追跡し、該当するコード行を示すよう依頼します。調査資料のリリースとテスト対象のコードが一致するか確認させてください。別のコミットをテストする場合は、その違いによって結論がどこまで制限されるかを説明させます。

    3. ハンドラー変更前の再現

    サンプルの Webhook と隔離された PostgreSQL データベースを使って回帰テストを追加します。元のハンドラーで、注文の書き込み直後、受領記録を書き込む前に、テスト時だけ障害を発生させるフックを置きます。

    実際のトランザクション境界を調べ、その時点で元の注文がコミット済みであることを確認してください。同じプロバイダー、アカウント、イベントで再試行し、注文と対応する受領記録がちょうど 1 件ずつあることを検証します。意図的に不具合を含めた実装では、注文が 2 件になってテストが失敗するはずです。

    修正後は、注文の書き込み文を実行した後、共通のトランザクションをコミットする前に、対応するフックで障害を発生させます。この試行が失敗した後は、注文も受領記録も 0 件でなければなりません。

    再試行し、注文と受領記録が各 1 件になることを検証します。業務上の期待結果を検証するアサーションは修正前後で同じものを使い、各リビジョンでのフックの位置を記録してください。

    新しいテストは、まず元の実装に対して実行します。このサンプルの不具合では、注文が 1 件ではなく 2 件になり、テストが失敗するのが期待どおりの結果です。コマンド、開始時のコミット、終了コード、アサーションの出力を保存します。想定した失敗が起きない場合は、ハンドラーを編集する前に、その理由を調べてください。

    例外を意図的に発生させることで、アプリケーションのトランザクション動作を検証できます。ただし、ホスト、ネットワーク、データベースで起こり得るすべての障害を再現するものではないことを記録してください。

    4. 証拠に基づく最小限の修正

    この不具合に対しては、1 本のデータベース接続の 1 つのトランザクション内で、受領記録を挿入してイベントの処理権を確保し、注文を書き込みます。既存の一意キーを使い、INSERT ... ON CONFLICT (provider, account_id, event_id) DO NOTHING RETURNING ... を実行します。

    注文を書き込めるのは、受領記録を挿入できた配信だけです。成功応答はコミット後にのみ返します。トランザクションが失敗すると、両方の書き込みがロールバックされます。一意制約違反を捕捉した後、中断されたトランザクションで処理を続けないでください。

    競合する配信は、先行トランザクションの終了を待つことがあります。先行トランザクションがコミットすると、後続の配信が処理権を確保しようとしても受領記録は挿入されません。READ COMMITTED で新たに SELECT を実行し、保存済みのイベント識別情報とペイロードを所定のルールに従って照合してから、注文を書き込まずに既定の重複時の応答を返します。

    先行トランザクションがロールバックした場合は、後続の配信が処理権を確保してイベントを処理できます。挿入をスキップしたのに受領記録が見つからない場合、識別情報が矛盾する場合、一時的なデータベースエラーが発生した場合は、成功扱いにせず、文書化された再試行・エラー処理の手順に従ってください。

    より厳しい分離レベルで直列化に失敗した場合は、リポジトリで定めた上限付きの再試行ポリシーに従い、トランザクション全体を再試行する必要があります。PostgreSQL の競合時の INSERT の動作とトランザクション分離を参照してください。

    メモリ内の集合を使っても、存在チェックと挿入を別々に行っても、複数ワーカーの同時実行や再起動に伴う重複は防げません。プロバイダーがイベントを一意に識別できると保証する範囲に合わせて、キーを定義してください。同じイベントのはずなのに識別情報やペイロードが矛盾する場合は、正当な重複として黙って処理せず、不整合を明示します。

    このデモでは、業務データの更新はすべて同じデータベース内で完結します。実際のハンドラーがメール送信、カード決済、別サービスの呼び出しも行う場合、このトランザクションだけでは外部への副作用の冪等性は保証できません。修正範囲を広げる前に、トランザクションが及ぶ範囲と、システムで既に使われている配信の仕組みや冪等性を確保する仕組みを特定してください。

    5. 修正後の動作の検証

    修正前の失敗と修正後の結果の両方を Codex に示してもらいます。どのトランザクション境界かを明示してテスト専用フックを置き、コミット済みの状態を別の接続から確認してください。サンプルはローカルの PostgreSQL で 11 件のテストに合格しています。ダウンロードに含まれる test-evidence.md には、その結果と、クラウドでの実行やデプロイに向けて残っているチェックが記録されています。公開済みの環境でテストスイートを実行し、コミット中に実際に接続を切ってテストしてから、その場合の復旧処理を採用してください。

    チェック項目・障害発生箇所 必要な動作
    初回配信とコミット後の重複配信 初回配信では注文と受領記録が各 1 件作成されます。同じイベントが再配信されると、取り決めどおりの応答が返り、件数は変わりません。正確なステータスとレスポンスボディを記録します。
    処理権の確保前 障害が発生しても、どちらの行も作成されず、成功応答も返りません。再試行で各 1 件が作成されます。
    処理権の確保後、注文の書き込み前 障害が発生すると受領記録はロールバックされ、注文も存在しません。再試行で処理権を確保し、通常どおり処理できます。
    注文の書き込み後、コミット前 障害が発生すると両方の行がロールバックされます。元の不具合で注文だけがコミットされ、受領記録が欠落していた箇所に相当します。修正後は再試行すると各 1 件が残ります。
    コミット後、応答前 レスポンスの消失をシミュレートします。両方の行はコミット済みのままです。再試行では別の注文を作成せず、重複時の応答を返します。
    コミット結果が不明 コミット前後の接続断を、通常の例外とは別に再現します。根拠のない成功応答を返してはいけません。最初のトランザクションがコミットされた場合もロールバックされた場合も、同じイベントキーで安全に再試行できる必要があります。この通信障害のケースを検証していない場合は、未実行と記録します。
    同時に届く重複配信:先行トランザクションがコミット 独立した 2 つの接続またはワーカーで、同じイベントキーを取り合う状況を作ります。先に処理権を確保した側をその時点で待機させ、待機を解除する前に後続側を開始します。実際に競合が発生したことを確認してから先行側の待機を解除します。双方が想定どおりの応答を返して完了し、注文と受領記録がちょうど 1 件ずつ存在します。
    同時に届く重複配信:先行トランザクションがロールバック 同じ手順で 2 つの処理が同時に進む状況を再現し、今回は処理権を確保した側をコミット前に失敗させます。ロールバック後、後続側が処理権を確保し、注文と受領記録を各 1 件作成します。両方のレスポンスと最終状態を確認します。
    イベントの識別情報とペイロードの矛盾 別々のイベントは独立して扱います。イベント ID が同じでも、アカウントやプロバイダーが異なれば独立して扱います。同じ範囲内で ID が一致し、正規化したペイロードが異なる場合は、取り決めどおりに競合を処理します。2 件目の注文を作成したり、異なる処理内容を黙って受理したりしてはいけません。
    受領記録のない旧注文と保留中の再試行 受領記録のないコミット済みの旧注文を用意します。再試行を再開する前にイベントキーを特定し、そのキーの配信を保留します。保留中の再試行では成功応答を返さず、行も作成しません。信頼できる証拠に基づいて照合を終えた場合に限り、既存の注文と受領記録が各 1 件の状態で重複時の応答を返せます。証拠が不足している場合や矛盾する場合は、そのキーの保留を維持します。
    サービス全体のチェック 関連する統合テスト、lint、型チェックを実行します。新たに発生した失敗、以前からの失敗、実行できなかったチェックを区別します。

    並行処理のタイミングは、バリアやラッチと上限付きのタイムアウトを使って制御してください。スリープを挟んだり、単に 2 つの Promise を並べたりするだけでは不十分です。テストの後処理でブロック中のワーカーを解放し、使い捨てのフィクスチャを使用します。例外を発生させるテストだけでは、プロセスの突然の終了、通信断、本番環境での同時実行時の動作は確認できません。

    サンプルでは、バージョンを固定したランナー内で node main.mjs baseline、node main.mjs regression、node main.mjs を使用します。構造化された出力には、トランザクション境界、コミット済みの行数、応答のステータスとボディ、実際にブロックの原因となったバックエンドの ID が記録されます。

    この手順を自分のリポジトリに適用する場合は、そこで実際に定義されているコマンドを使い、実行結果をそのまま報告してください。スキップしたチェックや利用できなかった依存関係がある場合は、結果の確度への影響とともに引き継ぎ資料に記載します。

    引き継ぎ内容のレビュー

    共通の検証記録

    受け入れ基準ごとに、インシデント調査で共通の形式を使って証拠を記録します。以下は記入用のテンプレートです。合格済みの結果ではないので、実際の出力を基に記入してください。ベースライン時点で存在しない新しいテストは「まだ存在しない」と記録し、既存のチェックが失敗した場合と区別します。

    Criterion / test: [specific behavior and test location]
    Environment: [published setup reference; runtime, database and dependency versions]
    Before: [revision + test-only diff ID; command; exit code; observed assertion; log path]
    After: [final revision or base + diff ID; command; exit code; observed assertion; log path]
    Status: [pass / fail / blocked / not run]
    Limits / reviewer action: [untested behavior, blocker, or decision required]

    失敗時と成功時の実行結果を、それぞれ実際にテストしたコードの状態に結び付けて記録します。元のリビジョンにテスト用の変更を加えた場合は、その変更も含めてください。ログ、レスポンスのフィクスチャ、最終差分を引き継ぎ資料とともに残します。最終リビジョンが変わった場合は、検証記録も更新する必要があります。未実行のチェックや、問題があって実行できなかったチェックは、合格に数えられません。

    コード差分と次のレビュー資料をリポジトリに残すよう Codex に依頼します。

    • incident-investigation.md:時系列、証拠の参照先、検証した仮説、証拠が裏付ける原因、残る不確実性
    • test-evidence.md:ベースラインのコミット、回帰テスト失敗時の出力、修正のコミットまたは差分、合格した対象箇所のチェック、より広い範囲のチェック、検証の限界
    • pr-draft.md:不具合の内容、動作の変更点、テストによる裏付け、残るリスクを簡潔にまとめた説明
    • deployment-handoff.md:確認済みのスキーマ・分離レベル・受領記録の保持期間に関する前提、ロールアウト責任者の氏名、デプロイ前のチェック、重複注文・応答・エラーの指標、以下に示すロールアウトとロールバックの判断条件

    パッチを受け入れる前に、元のコードで新しいテストが失敗し、修正後に合格すること、差分が実証された不具合を修正していることを確認します。Webhook の例では、イベントキーの有効範囲、トランザクション境界、並行処理のテスト範囲、トランザクション外への副作用を確認してください。既存の重複注文は別途レビューします。新たな重複を防いでも、過去の記録は修復されません。

    デプロイとロールバックの計画

    パッチが重複を防ぐのは、修正済みハンドラーを実行しているワーカーだけです。旧ワーカーは受領記録で処理権を確保する前に注文を書き込めるため、新旧バージョンが混在するカナリアデプロイの結果だけでは、不具合が解消したことは示せません。

    デプロイ責任者は、新旧バージョンの混在を想定してテスト済みの方式を選ぶか、既存の運用手順書に従う必要があります。後者では、取り込みを一時停止し、旧ワーカーで進行中の処理がすべて完了するのを待ち、すべてのコンシューマーを更新してから、保持していた配信を欠落なく再開します。

    後者を選ぶ前に、プロバイダーとキューの再試行および保持の動作を確認してください。一時停止しても配信が失われないと決めつけてはいけません。

    保持していた配信を再開する前に、対応する受領記録のないコミット済みの旧注文を特定してください。旧コンシューマーを一時停止し、進行中の処理が終わるのを待つことで、旧実装による新たな書き込みは防げますが、既存の注文は修復されません。既存のキューまたは隔離手順を使い、該当するすべてのイベントキーの配信を保留します。旧記録からイベントの識別情報を完全に特定できない場合は、調査完了とせず、該当する配信または該当時間帯の配信を停止したまま責任者のレビューに回してください。

    保留中のキーの照合を完了してよいのは、信頼できる変更不能なプロバイダー・アカウント・イベントの識別情報、正規化したペイロードの証拠、コミット済みの業務記録から、すでに行われた処理を特定できた場合だけです。

    承認済みの照合手順に従い、業務データの更新を繰り返さずに対応する受領記録を書き込み、そのキーの保留を解除します。

    証拠が不足している場合、ペイロードが矛盾する場合、業務記録が複数存在する場合は、該当するキーを隔離したまま責任者のレビューに回してください。元のイベントのフィンガープリントを再構成する際に、信頼できる元の証拠の代わりに再試行時のペイロードを使用することはできません。

    再開の条件を満たすのは、該当するすべてのキーについて、照合が完了しているか、すべてのコンシューマーで引き続き保留が有効になっている場合だけです。

    ステージング環境に旧注文とその再試行待ちの配信を用意します。保留中は別の注文が作成されず、成功応答も返らないことを確認してください。信頼できる証拠に基づく照合が終われば、既存の注文が 1 件のまま、再試行に重複時の応答を返せます。照合できないケースもリハーサルしてください。根拠のない受領記録を作らず、保留が継続される必要があります。この再開条件を、一時停止と処理完了の待機、コンシューマーのバージョン、配信の保持、ロールバックの各チェックとともにデプロイの引き継ぎ資料に記載します。

    ロールアウト前に、既存の一意制約が存在して有効であること、受領記録のキーとペイロードの扱いがすべてのコンシューマーで一貫していること、受領記録を削除しても再送可能期間内のイベントが再処理されないこと、外部への副作用がこのパッチの対象外であることを確認します。

    ステージング環境で、選択した手順とワーカー障害後の再試行をリハーサルします。責任者が定めた監視期間と、重複注文、Webhook エラー、応答の遅延、ロック待ち、再試行の滞留についてロールアウトを停止するしきい値を記録してください。本番用のしきい値を推測で設定してはいけません。

    旧ハンドラーに戻すと、注文の重複が再発する可能性があります。ロールバックの引き継ぎ資料には、影響を抑える手順(たとえば、承認済みの一時停止と処理完了の待機手順)、安全に戻せるリビジョン、処理再開を判断する責任者を明記してください。受領記録は保持します。再試行を通すために削除してはいけません。過去の重複注文を修復するには、別途レビューを受けた対応が必要です。

    デプロイの判断と本番での修復対応はエンジニアが担います。隔離環境でのテスト合格はレビューのための証拠であり、本番が正常であることまでは証明しません。ドラフトをレビューしたうえで pull request を作成し、通常の CI とレビューを経て、チームで定めた手順に従ってデプロイします。

    同じタスクでの追加調査

    不明点の解消や引き継ぎ資料の改善には、対象を絞って追加の指示を出します。

    2 件の配信が同時に届く場合と、ワーカーの障害後に再試行する場合について、パッチをレビューしてください。テストで確認できた動作と、まだテストしていない動作を示してください。変更はこのハンドラーとその回帰テストに限定し、残るリスクを引き継ぎ資料に追記してください。

    最初の実行でインシデントを再現できなかった場合は、次のように依頼します。

    実装を中止してください。証拠から除外できる仮説と、残る仮説のうち最も有力なものを挙げてください。その仮説を他の仮説と見分けるために必要な、機密情報を除いた最小限の追加資料、または隔離環境で行える最小限の実験を示してください。根本原因は未確定のままにしてください。

    Related use cases