第 6 章:制御権ハンドオーバープロトコル

本章は Handover_Policy(制御権ハンドオーバーポリシー)のプロトコルフローを定義する。3 種類のポリシーのセマンティクス、ハンドオーバーの原子性保証、タイムアウトロールバックおよび通知メカニズムを含む。本章はブループリント §2.4 の設計意図に対応する。

6.1 適用シナリオ

制御権ハンドオーバーは、複数の制御者が同一の Terminal_Resource を順次使用する必要があるシナリオで発生する。本仕様は 2 種類のコアハンドオーバーを定義する:

  1. Fay-to-Fay:ある Fay がその保有する Session 制御権を別の Fay に移転する
  2. Fay-to-Human:Fay が制御権を人間ユーザーに返却する、または人間ユーザーが制御権を Fay に委任する

本仕様は Fay-to-Fay を主要な記述対象とする。Fay-to-Human ハンドオーバーは同じフローを再利用し、「人間ユーザー」を特殊なインタフェースを通じて端末 OS と直接相互作用するエンティティとしてモデル化する。

6.2 ハンドオーバー発動

6.2.1 発動条件

ハンドオーバーは以下のシナリオのいずれかで発動される:

  1. 新 Fay によるリソース主動要求:Fay-B が AuthRequest を通じて Fay-A が占有しているリソースへのアクセスを要求
  2. 現 Fay の主動譲渡:Fay-A が SessionTransferRequest を通じて、指定された Fay-B に制御権を渡すことを主動要求
  3. 管理発動:人間ユーザーまたは管理インタフェースが制御権移転を要求

6.2.2 ハンドオーバー要求メッセージ

SessionTransferRequest (body of ProtocolMessage) {
  required source_session_id : Session_ID                  // 現在制御権を保有する Session
  required target_fay_id     : Fay_ID                      // 引き継ぎ対象
  required target_credential : CredentialContent           // 対象の認可クレデンシャル
  required handover_reason   : string
  optional handover_metadata : map<string, string>
}

端末はハンドオーバー要求受領後、ハンドオーバー評価フローを起動する。

6.3 ハンドオーバー評価

端末は以下のステップでハンドオーバーを許可するか評価する:

6.3.1 ステップ 1:ソース Session 検証

  • source_session_id に対応する Session が存在し、active 状態であることを検証
  • 発信者がハンドオーバーを要求する権限を有することを検証(当該 Session の iFay_Runtime または管理権限を有するエンティティ等)

失敗 → E_HANDOVER_INVALID_SOURCE を返却

6.3.2 ステップ 2:対象認可検証

  • target_credential が合法であることを検証(第 3/4 章規則に従い完全検証)
  • target_fay_id が当該リソースに対し元 Session の access_mode で認可されていることを検証

失敗 → E_HANDOVER_INVALID_TARGET を返却

6.3.3 ステップ 3:ポリシー評価

端末は §6.4 に従い Handover_Policy を適用しハンドオーバーを許可するか決定する。

失敗 → E_HANDOVER_REJECTED_BY_POLICY を返却

6.3.4 ステップ 4:原子性事前占有

端末はポリシー評価通過後、直ちに元 Session を handover_pending に切り替え、事前占有状態に入る:

  • 当該リソースはハンドオーバー完了前に他の制御権要求を受け入れない
  • 元 Session は依然ハートビートを維持できる(active と等価のハートビート要件を保持)が、新規リソース操作を発信できない

6.4 ハンドオーバーポリシー種別

Handover_Policy は Resource_ID 粒度で構成され、各リソースは MAY 異なるポリシーを採用する。本仕様は 3 種類のポリシー種別を定義し、すべての端末は MUST 少なくとも §6.4.1 優先順位ルールスクリプトを実装する。

6.4.1 優先順位ルールスクリプト(Priority Rule Script)

端末は事前定義されたルールスクリプトに従い、ソース Session と対象 Fay の優先順位スコアを計算し、スコアが高い方が制御権を獲得する。

構成構造

PriorityPolicyConfig {
  required policy_type : "priority_script"
  required script_id   : string                      // 端末事前構成のスクリプト識別子
  optional parameters  : map<string, string>
}

評価フロー

  1. 端末は script_id に対応するルールスクリプトをロード
  2. 入力:ソース Session 情報、対象 Fay 識別子、対象クレデンシャル grants、現在時刻、Resource_ID メタデータ
  3. 出力:ソース優先順位スコア S_source、対象優先順位スコア S_target
  4. 決定:S_target > S_source → ハンドオーバー許可;そうでなければ拒否

ルールスクリプトの具体的言語(CEL、Lua サブセット、JSON Logic 等)は実装が選択するが、MUST:

  • 確定的(同じ入力で同じ出力を生成する)
  • ネットワークまたはファイルシステムにアクセスできない
  • 実行時間は 100 ms 内に制限される

6.4.2 AI モデルリアルタイム判定(AI Model Real-time Decision)

端末は事前統合された AI モデルを呼び出し、現在のコンテキストに対しリアルタイム判定を行う。

構成構造

AIPolicyConfig {
  required policy_type   : "ai_model"
  required model_endpoint : string                    // 端末がアクセス可能なモデル推論エンドポイント
  optional context_fields : array<string>
  required decision_timeout_ms : uint32 (default 500)
}

評価フロー

  1. 端末は決定要求を構築、source/target 情報と context_fields で指定されたコンテキストを含む
  2. AI モデル推論エンドポイントを呼び出し
  3. モデルは決定を返却:allow / reject / require_human
  4. require_human の場合 §6.4.3 人間ユーザー決定にデグレード

端末は MUST:

  • 強制タイムアウトを設定(デフォルト 500 ms)、タイムアウトは reject で処理
  • 高頻度変動回避のため最近の決定結果をキャッシュ(キャッシュ有効期間 ≤ 5 秒)
  • AI モデル利用不可時は優先順位ルールスクリプトにデグレード

6.4.3 人間ユーザー決定(Human User Decision)

端末はユーザーインタフェースを通じて人間ユーザーに決定を要求する。

構成構造

HumanPolicyConfig {
  required policy_type     : "human_decision"
  required ui_channel      : enum["system_dialog", "companion_app", "external_panel"]
  required decision_timeout_seconds : uint32 (default 30)
  required default_action  : enum["allow", "reject"]    // タイムアウト時のデフォルト動作
}

評価フロー

  1. 端末は ui_channel を通じて人間ユーザーにハンドオーバー要求の詳細を呈示
  2. ユーザー入力を待機:許可 / 拒否
  3. タイムアウト → default_action に従い処理

人間決定モードは MUST:

  • UI に明示的に表示:ソース Fay 識別子、対象 Fay 識別子、リソース識別子、アクセスモード、ハンドオーバー理由
  • UI に機微なクレデンシャル詳細(署名、鍵 ID 等)を表示しない
  • デフォルト動作は reject(保守的セキュリティ)に設定することを推奨

6.5 原子性保証

ハンドオーバーは MUST 原子性を満たす:任意の時点で 1 つの Resource_ID は最多 1 つのアクティブ制御者を持つ

6.5.1 原子シーケンス

端末は MUST 以下の順序でハンドオーバーを実行し、各ステップはクリティカルセクション内で完了する:

[T0] handover_pending 進入:ソース Session 状態切り替え、リソース事前占有状態へ
[T1] ソース Session 終了:source_session_id 状態 active → terminating
                          OS アクセス制御取り消しを発動
[T2] リソース回収完了:source 状態 terminating → terminated
                      この時点でリソースは「アクティブ Session なし」状態
[T3] 対象 Session 作成:§5.2 フローに従い対象 Fay のために新 Session を作成
                       状態は creating に進入
[T4] OS アクセス制御下発:対象 Fay にリソースアクセスを有効化
[T5] 対象 Session が active に切り替え:ハンドオーバー完了

外部観察者が [T2] と [T3] の間で観察するリソース状態は「アクティブ Session なし」であり、2 つの Session が同時にアクティブな状態を観察することはない

6.5.2 中間ステップ失敗時のロールバック

[T0]–[T2] 中の任意のステップが失敗した場合:

  • ソース Session は MUST active にロールバックする
  • リソース回収事前占有を取り消し
  • E_HANDOVER_FAILED_AT_RELEASE を返却

[T3]–[T4] が失敗(ソースは終了済みだが対象を作成不可)した場合:

  • 端末は MUST 直ちにリソースをクリーンアップ(リソースは「アクティブ Session なし」状態に進入)
  • 元 iFay_Runtime(ソース Session は終了済み)と対象 iFay_Runtime(引き継ぎ失敗)に通知
  • E_HANDOVER_FAILED_AT_ACQUIRE を返却
  • リソース状態:アイドル状態を維持し、後続要求が競合する。MUST NOT ソース Session を自動復元

6.5.3 並行ハンドオーバー要求の直列化

同一リソースに対する複数の並行ハンドオーバー要求は MUST 直列化される:

  • リソースが handover_pending 期間中、新規ハンドオーバー要求はキューに入れられるか拒否される
  • 実装は MAY 拒否(E_HANDOVER_IN_PROGRESS)またはキュー入り(最大キュー長 8)を選択できる
  • キュー入りした要求は FIFO で処理され、各要求は前のハンドオーバーが完了(成功または失敗)した後にのみ評価に進む

6.6 タイムアウト処理

各ハンドオーバーは MUST 強制タイムアウトを設定する。タイムアウト閾値はポリシー種別で決定される:

ポリシー種別デフォルトタイムアウト範囲
優先順位ルールスクリプト1 秒0.5–2 秒
AI モデルリアルタイム判定1 秒0.5–3 秒
人間ユーザー決定30 秒5–120 秒

6.6.1 タイムアウトロールバック原則

ハンドオーバーがタイムアウト内に完了しない場合([T5] に到達していない)、端末は MUST:

  1. 現在進行中のハンドオーバーステップを中止
  2. [T0]–[T2] にある場合(ソースが完全に終了していない):元 Session の active 状態へロールバック
  3. [T3]–[T4] にある場合(ソースは終了済み):§6.5.2 に従い処理(ソースを復元せず、リソースをアイドル状態にする)
  4. 関連方に HandoverFailedNotification を送信

6.6.2 タイムアウト通知

HandoverFailedNotification (body of ProtocolMessage) {
  required handover_id   : uuid
  required source_session_id : Session_ID
  required target_fay_id : Fay_ID
  required reason        : enum["timeout", "policy_rejected", "release_failed", "acquire_failed"]
  optional details       : map<string, string>
}

6.7 リトライポリシー

ハンドオーバー失敗後のリトライポリシーは発信者が決定する。本仕様はリトライメカニズムを強制しないが、リトライの境界を定義する:

  • 同一ハンドオーバー要求のリトライ間隔は SHOULD ≥ 1 秒
  • 同一 (source_session, target_fay) ペアのリトライ回数は SHOULD ≤ 3 回
  • リトライは MUST 新しい handover_id を使用する

端末は MAY 過度に頻繁なリトライ要求を拒否し E_HANDOVER_RETRY_LIMIT を返却できる。

6.8 Fay-to-Human ハンドオーバー

制御権を人間ユーザーに返却するハンドオーバーは上述のフローに従うが、対象は人間ユーザーである:

  • target_fay_id フィールドは特殊値 "human:" + terminal_id を使用する(現在の端末の人間ユーザーを意味する)
  • 対象認可検証はスキップされる(人間ユーザーは自身の端末のリソースに対しデフォルトで完全な権限を有する)
  • ポリシー評価は通常 §6.4.3 人間ユーザー決定を使用する(引き継ぎ意思の確認)
  • 対象 Session 作成時、Session はいかなる Fay にもバインドされず、OS ユーザープロセスが直接保有する

人間ユーザーが主動的に引き継ぐ逆フローも対称:元 Fay Session を人間が保有する OS プロセスに置き換える。

6.9 ハンドオーバーの可観測性

端末は MUST 以下にハンドオーバーイベントの可観測性を提供する:

受信者受信するイベント
ソース iFay_RuntimeSessionStateChanged(active → handover_pending → terminating → terminated)
対象 iFay_RuntimeAuthResult(新 Session 作成の成功応答)または HandoverFailedNotification
端末監査ログ完全なハンドオーバー記録(handover_id、source、target、policy 決定、最終結果を含む)

6.10 ハンドオーバーメッセージシーケンス

成功ハンドオーバーの完全メッセージシーケンス:

[Source Runtime]      [Target Runtime]      [Terminal]
       │                     │                  │
       │── SessionTransferRequest ─────────────→│
       │                     │                  │── ポリシー評価
       │                     │                  │── source: active → handover_pending
       │←─ SessionStateChanged(handover_pending)│
       │                     │                  │── source 終了
       │←─ SessionStateChanged(terminating)─────│
       │←─ SessionStateChanged(terminated)──────│
       │                     │                  │── target 作成
       │                     │←─ AuthResult ────│
       │                     │                  │── target: creating → active
       │                     │←─ SessionStateChanged(active)

失敗ロールバックのメッセージシーケンス:

[Source Runtime]      [Target Runtime]      [Terminal]
       │                     │                  │
       │── SessionTransferRequest ─────────────→│
       │                     │                  │── ポリシー評価
       │                     │                  │── source: active → handover_pending
       │←─ SessionStateChanged(handover_pending)│
       │                     │                  │── タイムアウトまたは失敗
       │                     │                  │── source: handover_pending → active
       │←─ SessionStateChanged(active)──────────│
       │←─ HandoverFailedNotification ──────────│
       │                     │←─ HandoverFailedNotification