バックグラウンド作業のライフサイクル
ZeroClaw には、起動のきっかけとなったインバウンドリクエストの完了後も作業を継続するための方法がいくつかあります。Cron ジョブ、SOP 実行、委譲タスク、および実行時に生成されるサブエージェントは一部の実行機構を共有していますが、ライフサイクルや永続ストアは共有していません。ゴールモードは、エンドツーエンドではまだ接続されていない関連するターゲットコントラクトを定義します。
変更によってスケジュール実行される作業や自律的な作業が追加される場合、待機状態または承認状態が導入される場合、キャンセルや再起動の挙動が変更される場合、または子作業が所有タスクに関連付けられる場合は、このページを使用してください。最初に設計上検討すべき問いは「バックグラウンドでどのように実行されるか」ではなく、「そのライフサイクルを管理するのはどのサブシステムか」です。
所有権マップ
| 作業タイプ | 現在の所有者またはステータス表示 | 永続レコード |
|---|---|---|
| Cron ジョブ | Cronスケジューラーとストア | data/cron/jobs.db |
| SOP の実行 | SopEngine と SopRunStore | デフォルトではメモリ内で処理します。永続的なSQLiteの初期化に成功した場合はdata/sop/runs.dbを使用します |
| バックグラウンド委譲 | 永続タスクのコントロール プレーン | |A required task row in data/control_plane.db; output content in <workspace>/delegate_results/<task-id>.json |
Wait, that’s wrong. Let me restate my answer correctly: the translation should be Japanese.
必要なタスク行は data/control_plane.db にあります。出力内容は <workspace>/delegate_results/<task-id>.json に保存されます。|
|ランタイムによって起動されたサブエージェント|スポーンサイト。利用可能な場合はコントロールプレーンによる監視付き|起動済みデーモン下の data/control_plane.db 内のベストエフォートのタスク行|
永続的なメタデータは、永続的な実行と同一ではありません。結果ファイルやタスク行は、既知の情報を保持し、作業を実行していたプロセスローカルのfutureを保持することなく、リカバリが作業を「失敗」「タイムアウト」「終了」としてマークできるようにします。
Cron ジョブ
Cron は宣言的なメンバーシップと SQLite の実行ストアを組み合わせます。実行時に作成されたジョブと、調整された構成ジョブは、いずれも所有元の agent_alias を持ちます。実行時には、暗黙のデーモン ID で実行するのではなく、そのエージェントのセキュリティポリシーが解決されます。
スケジューラは、実行期限が来た有効な未クレームの行をポーリングします。行をクレームすることで、処理中の重複選択を防ぎます。完了時には出力を一定範囲に収めて記録し、その後、繰り返しジョブを再スケジュールするか、正常完了した自動削除のワンショットジョブを削除するか、それ以外のワンショットジョブを無効化します。クレームを解放する前にプロセスが終了した場合、次回のスケジューラ起動時に古いロックがクリアされます。
起動時の動作は明示的です。キャッチアップが有効な場合、期限切れのジョブは実行対象として考慮されます。無効な場合、期限切れのワンショットジョブは skipped の結果とともに無効化され、定期ジョブは実行結果を記録せずに次の将来の実行タイミングに進みます。スケジューラはポーリングの反復間でキャンセルトークンをチェックするため、シャットダウン時は現在の実行対象ジョブのバッチが完了するまで待機してからループを終了します。スケジューラのキャンセルは、既にディスパッチされた外部副作用がロールバックされることを保証するものではありません。
SOP の実行
SOP 定義は、設定された sops ディレクトリ配下に配置されます。SopEngine は、実行の進行、承認待ち、チェックポイント、終端遷移、インプロセスのステータスサーフェスを管理します。SopRunStore は、実行を受け入れて確保する際の同時実行制御における信頼できる唯一の情報源です。
実行の永続化はオプトイン方式です。デフォルトの sop.persist_runs = false では、エンジンはインメモリストアを使用します。永続化を有効にすると、デフォルトの SQLite バックエンドは run_state_dir で上書きされない限り、<data_dir>/sop の下に runs.db を書き込みます。ストアの初期化に成功すると、アクティブなスナップショット、ターミナルレコード、イベント、リビジョン、および並行実行クレームによって、再起動時の復元がサポートされます。ストアの初期化に失敗した場合、デーモンは警告をログに記録し、インメモリストアにフォールバックします。
Memory バックエンドの SOP 監査レコードは、別個の可観測性サーフェスです。これらは実行ストアに取って代わるものではなく、実行がアクティブ、一時停止中、承認済み、または終了済みかどうかを判断する権威として使用してはなりません。
承認状態とチェックポイント状態が永続的な制御状態になるのは、run store が永続的な場合のみです。タイムアウトポリシーはデフォルトでフェイルクローズのままです: タイムアウトした承認は、設定でキャンセルまたは従来の自動承認動作が明示的に選択されていない限り、エスカレートして待機を続けます。
委譲とサブエージェント
サブエージェントは、親の実効セキュリティ境界を継承します。ポリシーとメモリのオーバーライドによって親のエンベロープを狭めることはできますが、広げることはできません。また、子のアクションの計上には親のトラッカーが使用されるため、子を生成しても親のアクション予算を回避することはできません。
spawn_subagent パスは同期的です。親は子の実行が完了するまで待機し、このパスにはローカルのタイムアウトやバックグラウンドのキャンセルハンドルがありません。
delegate ツールは同期的に実行することも、バックグラウンドタスクを開始して UUID を返すこともできます。バックグラウンドタスクは、永続的なコントロールプレーンを開き、所有エージェントのエイリアスを解決し、タスク行を登録できた後にのみ開始されます。タスク行は、実行中、終了、エラー、リカバリの各状態に関する唯一のライフサイクルの権威です。完了した出力内容は、ツールに渡されたワークスペース配下でアトミックに書き込まれます。タスク行には内容を重複して保存せず、そのアーティファクト参照を保存します。
現行のリーダーは、レガシーのステータス付きアーティファクトおよびレガシーのインラインタスク出力との互換性を維持しています。逆方向の互換性は保証されません。現行の出力専用形式で書き込まれたアーティファクトは、埋め込みのライフサイクルフィールドを必要とする旧バイナリーやスクリプトでは読み取れません。ダウングレードする場合は、データディレクトリーとワークスペースのアーティファクトを一緒にロールバックするか、未完了のバックグラウンド作業を完了させてからダウングレードしてください。
ライブのキャンセル用レジストリは、タスクIDをプロセスローカルのトークンに対応付けます。トークンがまだ利用可能な場合、キャンセルは実行中のタスクに通知し、タスクストアでは compare-and-set による終端状態への遷移を用います。そのため、いずれか一つの終端結果が確定した後は、完了・失敗・キャンセル・リカバリが互いに上書きすることはありません。
バックグラウンドの delegate は、終了出力アーティファクトを公開する前に、正確なバイト列をシリアライズし、その SHA-256 ダイジェストを計算して、タスク ID、現在の所有者 PID とブート識別子、目的の終了ステータス、アーティファクトのパス/参照、ダイジェスト、終了エラーを含む終了確定インテントを永続化します。このインテントは未適用の遷移であり、TaskRecord.status が永続化されたライフサイクルの唯一の権威であり続けます。
委譲先はその後、正確なバイト列をアトミックに書き込み、同期します。単一の SQLite トランザクションで、まだ非終了状態かつ所有者が一致するタスク行を intent と比較し、目的の終了ステータスとペイロードを記録して、変更のない intent を削除します。intent の永続化前のストア障害では、成果物を公開せずにリトライします。公開後の障害では、プロセス内で昇格をリトライします。競合するキャンセルやその他の終了状態の勝者は保持され、古くなった intent を削除します。
A separate supervisor awaits the exact background worker’s Tokio タスクハンドルを待機しています。ワーカーがパニックしたり中止されたりしても親プロセスが生存している場合、スーパーバイザーは自身のタスクと記録済みのオーナーのみを回復します。永続化された決済インテントは、プロセス回復で使われるものと同じアーティファクト検査で検証されます。出力が欠落しているか破損している場合は Failed に解決され、インテント永続化前に終了した場合はオーナーに紐づいた Failed の結果に解決されます。ストアのエラーは上限付きバックオフで再試行されます。稼働中のワーカーは決して回収されず、競合する終端の勝者や移譲されたオーナーが上書きされることはありません。これはハートビートのタイムアウトや新しいリースではなく、ワーカーの直接的な終了証拠を用います。
起動時および定期的なリカバリでは、通常の Lost の照合より前に決済インテントを処理します。リカバリは、記録されたオーナーが存在しないこと、または PID が再利用されていることを証明した後にのみ動作します。成果物のバイト列は記録されたダイジェストと照合して検証します。成果物が欠落しているか破損している場合は、Completed とせず、永続化/リカバリエラーを伴う Failed として保守的に解決します。ランダムなプロセスノンスは OS のプロセス API からは観測できないため、PID と OS の起動時刻の組み合わせでは、同一秒内での PID 再利用に対して依然として保守的な判断となります。この場合、リカバリは同一のオーナーとして扱います。
現在の delegate 行と subagent 行には、agent、status、owner PID とプロセス識別子、depth、タイムスタンプが記録されます。delegate 行には、作成した agent のエイリアス、その上位にある呼び出し元エイリアスの順序付きチェーン、そして tool-loop セッションキーを principal として記録します。result、wait、list、cancel の各操作は、行を作成したエイリアスか、チェーンのどこかに現れるエイリアスであれば許可されます。セッションキーは事後調査用の情報であり、オーナーチェックでは一切参照されません。subagent 行では、route、chain、principal は引き続き未設定のままです。どちらの行でも親タスクは未設定です。
バックグラウンド delegate 行は、実行中にハートビートを保持します。進捗書き込みのたびにハートビートが更新され、少なくとも 1 分ごとに更新されるため、リーパーのハートビート経過時間タイムアウトがこれらの行に適用されます。subagent 行は引き続きハートビートを未設定のままにします。
起動時のリカバリは、記録されたオーナープロセスが確実に存在しない場合、または PID が再利用されたことが確認できた場合にのみ、実行中の行を lost に変更します。オーナーシップが不確かな場合は、安全側に倒して失敗させます。Windows のリカバリでは、PID が存在しないことと、アクセス拒否やその他の曖昧な照会結果とを区別し、不在を証明できない場合は行を保持します。
timed_out は、ハートビートを出力するプロデューサーにのみ適用されます。バックグラウンド delegate はハートビートを出力しますが、subagent は出力しません。バックグラウンド delegate のハートビートは進捗フラッシャーから送られます。進捗フラッシャーは独自のタスク上で、少なくとも 1 分ごとに行を書き直します。したがって、ハートビートが示すのは、所有プロセスが生存しており、フラッシュを行っているということだけであり、delegate が処理を進めていることを示すわけではありません。1 回のツール呼び出し内で止まっている delegate でも、ハートビートは新しいままであり、その場合はエージェントのタイムアウトによって制限されます。行のハートビートがリーパーの上限を超えて古くなるのは、フラッシャーが停止した場合、またはウィンドウ全体にわたってすべての進捗書き込みが失敗した場合に限られます。オーナープロセスが終了した行は、ハートビートの経過時間ではなく、起動時のリカバリによって回収されます。
タスク行により、中断された子プロセスは可視化されますが、その実行が再作成されることはありません。
レガシーの委任結果ファイルは、status を含み、対応するタスク行が存在しない場合でも読み取り可能なままです。originator_route が記録される前に作成された終了済みの委任行も、インライン出力を含めて正確なタスク ID で読み取り可能なままです。ただし、レガシー例外を通じてキャンセルやその他の変更を行うことはできません。また、非終了状態の NULL-origin 行は引き続き非表示です。originator chain が導入される前に書き込まれた行は空の chain を保持し、originator_route を通じて作成者のみに限定されます。新しい行では、読み取りと変更の両方について、作成時のエイリアスと、その chain に記録されたすべての祖先エイリアスを許可します。
ゴールモードのターゲット契約
ADR-008 は、ゴールのライフサイクル、所有権、ルート、プリンシパル、親子関係、およびリカバリの適格性について、タスク制御プレーンを将来の権威として位置付けています。リポジトリにはゴールのストレージと制御プレーン API が存在しますが、本番環境におけるゴールの受け入れと実行は、まだエンドツーエンドで接続されていません。
バックグラウンドパスは、所有元のゴールとの関係を維持し、終端状態とモデル使用状況をそのゴールに報告して初めて、ゴールモードに参加できます。それまでは、そのパスはゴールモードでの実行ではなく、通常のバックグラウンド作業です。
変更チェックリスト
バックグラウンド処理の変更については、レビュアーの承認前に次の質問に回答してください:
- どのサブシステムがライフサイクルを所有し、どのストアが信頼できる情報源となりますか?
- 作業はプロセスローカルか、永続的に管理されているか、それとも実際に再起動後も再開可能か?
- どのトークンまたはコントロールプレーンのアクションがそれをキャンセルするか、また何がインフライトのまま残る可能性があるか?
- このパスは、実際にどの親タスク、エージェント、ルート、プリンシパル、再帰深度、使用状況フィールドに値を設定しますか?
- 待機中、承認、チェックポイント、消失、タイムアウト、および終了の各状態は区別できますか?
- 起動時のリカバリーが副作用を重複させたり、クレームを暗黙のうちに宙ぶらりんにしたりする可能性はありますか?
- 再起動後に完了が観測された場合でも、結果の配信はべき等性を保ちますか?
ソースポインタ
- Cronスケジューラと永続化:
crates/zeroclaw-runtime/src/cron/scheduler.rs、crates/zeroclaw-runtime/src/cron/store.rs - SOPエンジンと実行ストア:
crates/zeroclaw-runtime/src/sop/engine.rs、crates/zeroclaw-runtime/src/sop/store/ - 委任とサブエージェントの動作: Delegation & SubAgents、
crates/zeroclaw-runtime/src/tools/delegate.rs、crates/zeroclaw-runtime/src/tools/spawn_subagent.rs、crates/zeroclaw-runtime/src/subagent/mod.rs - 耐久性のあるタスクのコントロールプレーンとリカバリ:
crates/zeroclaw-runtime/src/control_plane/ - Goal-mode に関する決定: ADR-008
- SOP運用ガイド: SOPの実行方法