スケジューリングガイド¶
概要¶
管理された実行ガイドは、このブランチのランタイム動作を説明しています。登録は設定を保存するだけでエージェントを実行しません。API、手動、タイマーによる各実行には異なる実行 ID が割り当てられ、実際の終了結果が返されます。履歴は実行とクリーンアップの完了後に succeeded となり、出力、エラー、保護された監査の参照情報を含みます。管理 CLI のスケジューリング、ルーティングされたモデル、外部エージェントのトランスポートは、既定のサービスでは引き続き利用できません。選択されたコマンド境界は、対応するゲストアーティファクトが用意されていれば、ワンショットツール、パーサー、MCP stdio、PTY セッションに Firecracker を使用できます。運用者から見える差分と現在のカバー範囲についてはブランチガイドを参照してください。
Symbiontのスケジューリングシステムは、AIエージェント向けの本番レベルのcronベースタスク実行機能を提供します。以下の機能をサポートしています:
- cronスケジュール: 定期タスク用の標準的なcron構文
- ワンショットジョブ: 指定時刻に一度だけ実行
- ハートビートパターン: 監視エージェント向けの継続的な評価-アクション-スリープサイクル
- セッション分離: エフェメラル、共有、または完全分離のエージェントコンテキスト
- 配信ルーティング: 複数の出力チャネル(Stdout、LogFile、Webhook、Slack、Email、Custom)
- ポリシー適用: 実行前のセキュリティおよびコンプライアンスチェック
- 本番環境の堅牢化: ジッター、同時実行制限、デッドレターキュー、AgentPin検証
アーキテクチャ¶
スケジューリングシステムは3つのコアコンポーネントで構成されています:
┌─────────────────────┐
│ CronScheduler │ バックグラウンドティックループ(1秒間隔)
│ (Tick Loop) │ ジョブ選択と実行オーケストレーション
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ SqliteJobStore │ 永続的なジョブストレージ
│ (Job Storage) │ トランザクションサポート、状態管理
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│DefaultAgentScheduler│ エージェント実行ランタイム
│ (Execution Engine) │ AgentContextライフサイクル管理
└─────────────────────┘
CronScheduler¶
CronSchedulerは主要なエントリポイントです。以下を管理します:
- 1秒間隔で動作するバックグラウンドティックループ
- 次回実行時刻に基づくジョブ選択
- 同時実行制御とジッター挿入
- メトリクス収集とヘルスモニタリング
- 実行中ジョブの追跡を伴うグレースフルシャットダウン
SqliteJobStore¶
SqliteJobStoreは以下の機能を備えた永続的なジョブストレージを提供します:
- ジョブ状態更新のためのACIDトランザクション
- ジョブライフサイクル追跡(Active、Paused、Completed、Failed、DeadLetter)
- 監査証跡付きの実行履歴
- ステータス、エージェントIDなどによるフィルタリングクエリ機能
DefaultAgentScheduler¶
DefaultAgentSchedulerはスケジュールされたエージェントを実行します:
- 分離または共有の
AgentContextインスタンスを作成 - セッションライフサイクル(作成、実行、破棄)を管理
- 設定されたチャネルへの配信をルーティング
- 実行前にポリシーゲートを適用
DSL構文¶
スケジュールブロックの構造¶
スケジュールブロックはSymbiont DSLファイルで定義されます:
schedule {
name: "daily-report"
agent: "reporter-agent"
cron: "0 0 9 * * *"
session_mode: "ephemeral_with_summary"
delivery: ["stdout", "log_file"]
policy {
require_approval: false
max_runtime: "5m"
}
}
Cron構文¶
6つのフィールドを持つ拡張cron構文(秒が先頭、オプションの7番目のフィールドは年):
┌─────────────── 秒 (0-59)
│ ┌───────────── 分 (0-59)
│ │ ┌─────────── 時 (0-23)
│ │ │ ┌───────── 日 (1-31)
│ │ │ │ ┌─────── 月 (1-12)
│ │ │ │ │ ┌───── 曜日 (0-6, 日曜日 = 0)
│ │ │ │ │ │
* * * * * *
例:
# 毎日午前9時
cron: "0 0 9 * * *"
# 毎週月曜日の午後6時
cron: "0 0 18 * * 1"
# 15分ごと
cron: "0 */15 * * * *"
# 毎月1日の深夜0時
cron: "0 0 0 1 * *"
ワンショットジョブ(At構文)¶
指定時刻に一度だけ実行するジョブの場合:
schedule {
name: "deployment-check"
agent: "health-checker"
at: "2026-02-15T14:30:00Z" # ISO 8601タイムスタンプ
delivery: ["webhook"]
webhook_url: "https://ops.example.com/hooks/deployment"
}
ハートビートパターン¶
評価 → アクション → スリープの継続的な監視エージェント向け:
schedule {
name: "system-monitor"
agent: "heartbeat-agent"
cron: "0 */5 * * * *" # 5分ごとに起動
heartbeat: {
enabled: true
context_mode: "ephemeral_with_summary"
max_iterations: 100 # 安全制限
}
}
ハートビートエージェントは以下のサイクルに従います:
- 評価: システム状態を評価(例:メトリクス、ログの確認)
- アクション: 必要に応じて是正措置を実行(例:サービスの再起動、運用チームへのアラート)
- スリープ: 次のスケジュールされたティックまで待機
CLIコマンド¶
symbi cron はスケジュールされたジョブのライフサイクルを管理します。以下の各サブコマンドは、実際に受け付けるオプションとともに記載しています。お使いのビルドで確認するには symbi cron <subcommand> --help を実行してください。
ジョブ一覧¶
list はオプションを取りません。個別のジョブの詳細には symbi cron status <job-id> を、実行記録の絞り込みには symbi cron history を使用してください。
ジョブ追加¶
--name、--cron、--agent は必須です。エージェントは事前に登録されている必要があります。add は DSL ファイルを読み込みません。
symbi cron add --name "daily-report" --cron "0 0 9 * * * *" --agent reporter
# タイムゾーン(既定は UTC)とポリシーを指定し、1回実行したら無効化する
symbi cron add --name "one-off" --cron "0 */5 * * * * *" --agent reporter \
--tz "America/New_York" --policy strict --one-shot
| オプション | 必須 | 意味 |
|---|---|---|
--name <NAME> |
はい | ジョブ名 |
--cron <EXPR> |
はい | cron式、7フィールド(秒が先頭) |
--agent <AGENT> |
はい | 実行する登録済みエージェント名 |
--tz <TIMEZONE> |
いいえ | IANA タイムゾーン、既定は UTC |
--policy <POLICY> |
いいえ | 実行に適用するポリシー |
--one-shot |
いいえ | 1回だけ実行し、その後ジョブを無効化 |
削除、一時停止、再開¶
いずれもジョブの UUID を1つ取り、オプションはありません。
symbi cron remove <job-id>
symbi cron pause <job-id> # スケジューリングを停止、状態は保持
symbi cron resume <job-id> # 未解決の実行がある場合は再開できない
ステータス¶
status はジョブの UUID を取り、オプションはありません。ジョブの詳細と直近の実行を報告します。呼び出しが突き合わせ済みの場合は、元のエラーと運用者による解決内容を含む Reconciled ステータスも表示されます。
即時実行¶
ジョブを即時に強制実行します。REST サーフェス経由の手動実行には管理者トークンと Idempotency-Key の UUID が必要です。2回実行するのではなく再試行するために、同じ UUID を再利用してください。スケジューラーの再試行状態および cron の復旧を参照してください。
履歴¶
# すべてのジョブにわたる直近の実行(既定は20件)
symbi cron history
# 1つのジョブに絞り、件数上限を明示
symbi cron history --job <job-id> --limit 50
| オプション | 意味 |
|---|---|
--job <JOB_ID> |
1つのジョブに絞り込む |
--limit <N> |
表示する最大件数、既定は20 |
ハートビートパターン¶
HeartbeatContextMode¶
ハートビートのイテレーション間でコンテキストがどのように保持されるかを制御します:
pub enum HeartbeatContextMode {
/// Fresh context each iteration, append summary to run history
EphemeralWithSummary,
/// Shared context across all iterations (memory accumulates)
SharedPersistent,
/// Fresh context each iteration, no summary (stateless)
FullyEphemeral,
}
EphemeralWithSummary(デフォルト):
- イテレーションごとに新しいAgentContextを作成
- 前回のイテレーションのサマリーをコンテキストに追加
- 無制限のメモリ増加を防止
- 関連するアクション間の継続性を維持
SharedPersistent:
- すべてのイテレーションで単一のAgentContextを再利用
- 完全な会話履歴を保持
- メモリ使用量が高い
- 深いコンテキストを必要とするエージェントに最適(例:デバッグセッション)
FullyEphemeral:
- イテレーションごとに新しいAgentContext、引き継ぎなし
- 最小のメモリフットプリント
- 独立したチェックに最適(例:APIヘルスプローブ)
ハートビートエージェントの例¶
agent heartbeat_monitor {
model: "claude-sonnet-4.5"
system_prompt: """
You are a system monitoring agent. On each heartbeat:
1. Check system metrics (CPU, memory, disk)
2. Review recent error logs
3. If issues detected, take action:
- Restart services if safe
- Alert ops team via Slack
- Log incident details
4. Summarize findings
5. Return 'sleep' when done
"""
}
schedule {
name: "heartbeat-monitor"
agent: "heartbeat_monitor"
cron: "0 */10 * * * *" # 10分ごと
heartbeat: {
enabled: true
context_mode: "ephemeral_with_summary"
max_iterations: 50
}
delivery: ["log_file", "slack"]
slack_channel: "#ops-alerts"
}
セッション分離¶
セッションモード¶
pub enum HeartbeatContextMode {
/// Ephemeral context with summary carryover (default)
EphemeralWithSummary,
/// Shared persistent context across all runs
SharedPersistent,
/// Fully ephemeral, no state carryover
FullyEphemeral,
}
設定:
schedule {
name: "data-pipeline"
agent: "etl-agent"
cron: "0 0 2 * * *"
# 実行ごとに新しいコンテキスト、前回実行のサマリーを含む
session_mode: "ephemeral_with_summary"
}
セッションライフサイクル¶
スケジュールされた各実行について:
- 実行前: 同時実行制限の確認、ジッターの適用
- セッション作成:
session_modeに基づいてAgentContextを作成 - ポリシーゲート: ポリシー条件を評価
- 実行: 入力とコンテキストでエージェントを実行
- 配信: 設定されたチャネルに出力をルーティング
- セッションクリーンアップ: モードに基づいてコンテキストを破棄または保持
- 実行後: 実行記録の更新、メトリクスの収集
配信ルーティング¶
サポートされるチャネル¶
pub enum DeliveryChannel {
Stdout, // Print to console
LogFile, // Append to job-specific log file
Webhook, // HTTP POST to URL
Slack, // Slack webhook or API
Email, // SMTP email
Custom(String), // User-defined channel
}
設定例¶
単一チャネル:
複数チャネル:
schedule {
name: "security-scan"
agent: "scanner"
cron: "0 0 1 * * *"
delivery: ["log_file", "slack", "email"]
slack_channel: "#security"
email_recipients: ["ops@example.com", "security@example.com"]
}
Webhook配信:
schedule {
name: "metrics-report"
agent: "metrics-agent"
cron: "0 */30 * * * *"
delivery: ["webhook"]
webhook_url: "https://metrics.example.com/ingest"
webhook_headers: {
"Authorization": "Bearer ${METRICS_API_KEY}"
"Content-Type": "application/json"
}
}
DeliveryRouterトレイト¶
カスタム配信チャネルは以下を実装します:
#[async_trait]
pub trait DeliveryRouter: Send + Sync {
async fn route(
&self,
channel: &DeliveryChannel,
job: &CronJobDefinition,
run: &JobRunRecord,
output: &str,
) -> Result<(), SchedulerError>;
}
ポリシー適用¶
PolicyGate¶
PolicyGateは実行前にスケジュール固有のポリシーを評価します:
pub struct PolicyGate {
policy_engine: Arc<RealPolicyParser>,
}
impl PolicyGate {
pub fn evaluate(
&self,
job: &CronJobDefinition,
context: &AgentContext,
) -> Result<SchedulePolicyDecision, SchedulerError>;
}
ポリシー条件¶
schedule {
name: "production-deploy"
agent: "deploy-agent"
cron: "0 0 0 * * 0" # 日曜深夜
policy {
# 実行前に人間の承認を要求
require_approval: true
# 強制終了までの最大実行時間
max_runtime: "30m"
# 特定のケイパビリティを要求
require_capabilities: ["deployment", "production_write"]
# 時間枠の適用(UTC)
allowed_hours: {
start: "00:00"
end: "04:00"
}
# 環境制限
allowed_environments: ["staging", "production"]
# AgentPin検証を要求
require_agent_pin: true
}
}
SchedulePolicyDecision¶
pub enum SchedulePolicyDecision {
Allow,
Deny { reason: String },
RequiresApproval { approver: String, reason: String, policy_id: String },
}
本番環境の堅牢化¶
ジッター¶
複数のジョブが同じスケジュールを共有する場合のサンダリングハード問題を防止します:
例:
ジョブごとの同時実行制限¶
リソース枯渇を防ぐためにジョブごとの同時実行数を制限します:
schedule {
name: "data-sync"
agent: "sync-agent"
cron: "0 */5 * * * *"
max_concurrent: 2 # 最大2つの同時実行を許可
}
ジョブが最大同時実行数で既に実行中の場合、スケジューラーはそのティックをスキップします。
デッドレターキュー¶
max_retriesを超えたジョブは手動レビューのためにDeadLetterステータスに移行します:
schedule {
name: "flaky-job"
agent: "unreliable-agent"
cron: "0 0 * * * *"
max_retries: 3 # 3回失敗後、デッドレターに移動
}
復旧:
# ジョブを一覧表示し、無効化されたものを探す
symbi cron list
# 失敗分も含めて、そのジョブの実行記録を確認
symbi cron history --job <job-id>
# 原因を修正したうえで再度有効化
symbi cron resume <job-id>
cron list と cron history はステータスによる絞り込みを行いません。list のステータス列を読むか、history の実行記録を確認してください。cron reset というサブコマンドは存在しません。ジョブを再度有効化するのは resume であり、未解決の実行がある場合は運用者がそれを突き合わせるまで再開はブロックされます。
AgentPin検証¶
実行前にエージェントのIDを暗号的に検証します:
schedule {
name: "secure-task"
agent: "trusted-agent"
cron: "0 0 * * * *"
agent_pin_jwt: "${AGENT_PIN_JWT}" # agentpin-cliからのES256 JWT
policy {
require_agent_pin: true
}
}
スケジューラーは以下を検証します:
1. ES256(ECDSA P-256)を使用したJWT署名
2. エージェントIDがissクレームと一致
3. ドメインアンカーが期待されるオリジンと一致
4. 有効期限(exp)が有効
検証失敗時はSecurityEventType::AgentPinVerificationFailed監査イベントが発行されます。
HTTP APIエンドポイント¶
スケジュール管理¶
POST /api/v1/schedule 新しいスケジュールジョブを作成します。
curl -X POST http://localhost:8080/api/v1/schedule \
-H "Content-Type: application/json" \
-d '{
"name": "hourly-report",
"agent_id": "reporter",
"cron_expr": "0 0 * * * *",
"session_mode": "ephemeral_with_summary",
"delivery": ["stdout"]
}'
GET /api/v1/schedule すべてのジョブを一覧表示(ステータス、エージェントIDでフィルタリング可能)。
GET /api/v1/schedule/{job_id} ジョブの詳細を取得します。
PUT /api/v1/schedule/{job_id} ジョブを更新(cron式、配信先など)。
curl -X PUT http://localhost:8080/api/v1/schedule/job-123 \
-H "Content-Type: application/json" \
-d '{"cron_expr": "0 0 */2 * * *"}'
DELETE /api/v1/schedule/{job_id} ジョブを削除します。
POST /api/v1/schedule/{job_id}/pause ジョブを一時停止します。
POST /api/v1/schedule/{job_id}/resume 一時停止中のジョブを再開します。
POST /api/v1/schedule/{job_id}/run 即時実行をトリガーします。
curl -X POST http://localhost:8080/api/v1/schedule/job-123/run \
-H "Content-Type: application/json" \
-d '{"input": "Run with custom input"}'
GET /api/v1/schedule/{job_id}/history 実行履歴を取得します。
GET /api/v1/schedule/{job_id}/next_run 次回スケジュール実行時刻を取得します。
ヘルスモニタリング¶
GET /api/v1/health/scheduler スケジューラーのヘルスとメトリクス。
レスポンス:
{
"status": "healthy",
"active_jobs": 15,
"paused_jobs": 3,
"in_flight_jobs": 2,
"metrics": {
"runs_total": 1234,
"runs_succeeded": 1180,
"runs_failed": 54,
"avg_execution_time_ms": 850
}
}
SDKの例¶
JavaScript SDK¶
import { SymbiontClient } from '@symbiont/sdk-js';
const client = new SymbiontClient({
baseUrl: 'http://localhost:8080',
apiKey: process.env.SYMBI_API_KEY
});
// スケジュールジョブを作成
const job = await client.schedule.create({
name: 'daily-backup',
agentId: 'backup-agent',
cronExpr: '0 0 2 * * *',
sessionMode: 'ephemeral_with_summary',
delivery: ['webhook'],
webhookUrl: 'https://backup.example.com/notify'
});
console.log(`Created job: ${job.id}`);
// アクティブなジョブを一覧表示
const activeJobs = await client.schedule.list({ status: 'active' });
console.log(`Active jobs: ${activeJobs.length}`);
// ジョブのステータスを取得
const status = await client.schedule.getStatus(job.id);
console.log(`Next run: ${status.next_run}`);
// 即時実行をトリガー
await client.schedule.runNow(job.id, { input: 'Backup database' });
// ジョブを一時停止
await client.schedule.pause(job.id);
// 履歴を表示
const history = await client.schedule.getHistory(job.id, { limit: 10 });
history.forEach(run => {
console.log(`Run ${run.id}: ${run.status} (${run.execution_time_ms}ms)`);
});
// ジョブを再開
await client.schedule.resume(job.id);
// ジョブを削除
await client.schedule.delete(job.id);
Python SDK¶
from symbiont import SymbiontClient
client = SymbiontClient(
base_url='http://localhost:8080',
api_key=os.environ['SYMBI_API_KEY']
)
# スケジュールジョブを作成
job = client.schedule.create(
name='hourly-metrics',
agent_id='metrics-agent',
cron_expr='0 0 * * * *',
session_mode='ephemeral_with_summary',
delivery=['slack', 'log_file'],
slack_channel='#metrics'
)
print(f"Created job: {job.id}")
# 特定のエージェントのジョブを一覧表示
jobs = client.schedule.list(agent_id='metrics-agent')
print(f"Found {len(jobs)} jobs for metrics-agent")
# ジョブの詳細を取得
details = client.schedule.get(job.id)
print(f"Cron: {details.cron_expr}")
print(f"Next run: {details.next_run}")
# cron式を更新
client.schedule.update(job.id, cron_expr='0 */30 * * * *')
# 即時実行をトリガー
run = client.schedule.run_now(job.id, input='Generate metrics report')
print(f"Run ID: {run.id}")
# メンテナンス中に一時停止
client.schedule.pause(job.id)
print("Job paused for maintenance")
# 最近の失敗を表示
history = client.schedule.get_history(
job.id,
status='failed',
limit=5
)
for run in history:
print(f"Failed run {run.id}: {run.error_message}")
# メンテナンス後に再開
client.schedule.resume(job.id)
# スケジューラーのヘルスを確認
health = client.schedule.health()
print(f"Scheduler status: {health.status}")
print(f"Active jobs: {health.active_jobs}")
print(f"In-flight jobs: {health.in_flight_jobs}")
設定¶
CronSchedulerConfig¶
pub struct CronSchedulerConfig {
/// Tick interval (default: 1 second)
pub tick_interval: Duration,
/// Global concurrency limit (default: 100)
pub max_concurrent_cron_jobs: usize,
/// Persistent job store path (default: None)
pub job_store_path: Option<PathBuf>,
/// Catch up missed runs on startup (default: true)
pub enable_missed_run_catchup: bool,
}
TOML設定¶
[scheduler]
tick_interval_seconds = 1
max_jitter_seconds = 30
max_concurrent_jobs = 20
enable_metrics = true
default_max_retries = 3
shutdown_timeout_seconds = 60
配信チャネル¶
配信チャネル(stdout、log_file、webhook、slack、email など)は、グローバルな [scheduler.delivery] 設定セクションではなく、各スケジュールの delivery ブロック内でスケジュールごとに設定します(上記の例を参照)。
log_file配信にはSYMBIONT_LOG_DIRが必要です。 出力先パスはその許可ディレクトリ内に限定されます。..による相対脱出、絶対パス、シンボリックリンクで外部に出るパスは拒否され、SYMBIONT_LOG_DIRが未設定の場合、ログファイル配信はフェイルクローズ(無効化)されます。
可観測性¶
メトリクス(Prometheus互換)¶
# 合計実行数
symbiont_cron_runs_total{job_name="daily-report",status="succeeded"} 450
# 失敗した実行
symbiont_cron_runs_total{job_name="daily-report",status="failed"} 5
# 実行時間ヒストグラム
symbiont_cron_execution_duration_seconds{job_name="daily-report"} 1.234
# 実行中ジョブゲージ
symbiont_cron_in_flight_jobs 3
# デッドレター化されたジョブ
symbiont_cron_dead_letter_total{job_name="flaky-job"} 2
監査イベント¶
すべてのスケジューラーアクションはセキュリティイベントを発行します:
pub enum SecurityEventType {
CronJobCreated,
CronJobUpdated,
CronJobDeleted,
CronJobPaused,
CronJobResumed,
CronJobExecuted,
CronJobFailed,
CronJobDeadLettered,
AgentPinVerificationFailed,
}
インタラクティブシェルから監査ログをクエリ:
またはランタイム HTTP API 経由でプログラム的に — /api/v1/audit エンドポイントについては API リファレンス を参照してください。
ベストプラクティス¶
- 共有スケジュールにはジッターを使用: 複数のジョブが同時に開始するのを防止
- 同時実行制限を設定: リソース枯渇から保護
- デッドレターキューを監視: 失敗しているジョブを定期的にレビューして修正
- EphemeralWithSummaryを使用: 長時間実行されるハートビートでの無制限のメモリ増加を防止
- AgentPin検証を有効化: エージェントのIDを暗号的に検証
- 配信ルーティングを設定: ジョブタイプに応じた適切なチャネルを使用
- ポリシーゲートを設定: 時間枠、承認、ケイパビリティチェックを適用
- 監視にはハートビートパターンを使用: 継続的な評価-アクション-スリープサイクル
- ステージングでスケジュールをテスト: 本番環境前にcron式とジョブロジックを検証
- メトリクスをエクスポート: 運用の可視化のためにPrometheus/Grafanaと統合
トラブルシューティング¶
ジョブが実行されない¶
- ジョブのステータスを確認:
symbi cron status <job-id> - cron式を検証: crontab.guruを使用
- スケジューラーのヘルスを確認:
curl http://localhost:8080/api/v1/health/scheduler - ログを確認:
symbi logs --lines 200(ストリーミングするには--followを追加)
ジョブが繰り返し失敗する¶
- 履歴を表示:
symbi cron history --job <job-id> - 実行記録のエラーメッセージを確認
- エージェントの設定とケイパビリティを検証
- スケジューラー外でエージェントをテスト:
symbi run <agent-id> --input "test" - ポリシーゲートを確認: 時間枠とケイパビリティが一致しているか確認
デッドレター化されたジョブ¶
- ジョブを一覧表示し、無効化されたものを探す:
symbi cron list - 失敗パターンを確認:
symbi cron history --job <job-id> - 根本原因を修正(エージェントコード、権限、外部依存関係)
- ジョブを再度有効化:
symbi cron resume <job-id>
高メモリ使用量¶
- セッションモードを確認:
ephemeral_with_summaryまたはfully_ephemeralに切り替え - ハートビートのイテレーションを削減:
max_iterationsを低く設定 - コンテキストサイズを監視: エージェントの出力の冗長性を確認
- コンテキストのアーカイブを有効化: 保持ポリシーを設定
v0.9.0からの移行¶
v1.0.0リリースでは本番環境の堅牢化機能が追加されました。ジョブ定義を更新してください:
schedule {
name: "my-job"
agent: "my-agent"
cron: "0 0 * * * *"
+
+ # 同時実行制限を追加
+ max_concurrent: 2
+
+ # ID検証のためのAgentPinを追加
+ agent_pin_jwt: "${AGENT_PIN_JWT}"
+
+ policy {
+ require_agent_pin: true
+ }
}
設定を更新:
[scheduler]
tick_interval_seconds = 1
+ max_jitter_seconds = 30
+ default_max_retries = 3
+ shutdown_timeout_seconds = 60
破壊的なAPI変更はありません。すべてのv0.9.0ジョブは引き続き動作します。