Files
maestro/docs/task-execution-architecture.md
T
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

24 KiB
Raw Blame History

MAESTRO タスク実行アーキテクチャ

この文書は、MAESTROでタスク依頼を受け付けてから、Worker、Piece、Movement、LLM、ツール、承認、再試行、通知、UI反映を経て完了するまでの現行処理を示す。機能追加やリファクタリング時の影響確認に使用する。

文書の扱い

  • 実装を正とし、処理経路を変更したPRではこの文書も更新する。
  • 通常経路だけでなく、ASK、承認待ち、サブタスク待ち、再試行、キャンセル、失敗を維持する。
  • 図中の名称は主要なファイル、関数、状態名に対応させる。
  • 特定機能の将来設計は含めず、現行コードの動作だけを記載する。

調査対象コミット

  • 基準ブランチ: origin/main
  • PR #935実装コミット: a86fea5523d9f8245cd42c53e3c1c167ccb26388
  • 最終照合HEAD: 2ba26737742464f9ef7ccfd4459012e00175fd88
  • 対象変更: PR #935「systemプロンプト静的化 + 動的コンテキスト末尾注入」
  • 調査日: 2026-07-24

主要な実装境界

境界 主なファイル・関数
タスク作成 src/bridge/local-tasks-crud-api.ts
コメント・interjection・ASK再開 src/bridge/local-tasks-comments-api.ts
継続・キャンセル src/bridge/local-tasks-control-api.ts
Tool承認 src/bridge/local-tasks-tool-requests-api.ts
Package承認 src/bridge/local-tasks-package-requests-api.ts
Job永続化・claim・再開 src/db/repositories/jobs.ts
Worker実行・終了処理 src/worker.ts
Piece・Movement制御 src/engine/piece-runner.ts
Agent iteration src/engine/agent-loop.ts
LLM stream src/engine/agent-loop/llm-iteration.ts、src/engine/llm-stream.ts
Tool dispatch src/engine/agent-loop/tool-dispatcher.ts、tool-execution.ts
チャット表示 ui/src/components/chat/ChatPane.tsx、ChatMessageFeed.tsx
承認UI ui/src/components/chat/ToolRequestApproval.tsx、PackageRequestApproval.tsx
通知・監査 src/notifications/、src/progress/、ui/src/components/activity/*

1. タスク依頼から完了までの全体フロー

flowchart TD
    U[ユーザー / 外部クライアント]
    UI[React UI<br/>CreateTaskDialog / ChatComposer]
    API_CREATE[POST /api/local/tasks]
    API_COMMENT[POST /api/local/tasks/:id/comments]
    API_CONTINUE[POST /api/local/tasks/:id/continue]
    OTHER[Scheduler / A2A / Reflection / Subtask]

    U --> UI
    UI --> API_CREATE
    UI --> API_COMMENT
    UI --> API_CONTINUE

    API_CREATE --> VALIDATE[入力・可視性・Space・LLM選択・添付検証]
    VALIDATE --> CLASSIFY[Piece選択 / Grounded固定 / LLM Router]
    CLASSIFY --> TASKDB[(local_tasks / comments)]
    CLASSIFY --> WORKSPACE[Space解決<br/>persistent/ephemeral workspace・runtime dir]
    WORKSPACE --> JOBDB[(jobs: queued)]

    API_COMMENT --> WRITE_GATE[Task write gate]
    WRITE_GATE --> PREV{最新Job状態}
    PREV -->|running / dispatching / waiting_subtasks| INTERJECT[interjection comment保存<br/>既存Jobへ次iterationで投入]
    PREV -->|tool/package approval待ち| CONFLICT[409: 承認を先に要求]
    PREV -->|queued / retry| REUSE[既存pending Jobを再利用]
    PREV -->|ASK待ち・終端・Jobなし| FOLLOWUP[resumeMovement/askCount継承<br/>Job作成]
    REUSE --> JOBDB
    FOLLOWUP --> JOBDB

    API_CONTINUE --> CONTINUE_GATE[所有権・Piece存在・前Job終端確認]
    CONTINUE_GATE --> HANDOFF[continuedFromJobIdとhandoff comment<br/>新Jobは transcript replay で前会話を引き継ぐ]
    HANDOFF --> JOBDB

    OTHER --> JOBDB

    JOBDB --> POLL[Worker.processNext polling]
    POLL --> CLAIM[retry期限優先→queued<br/>worker/model/profile適合<br/>issue lockなし]
    CLAIM --> RUNNING[原子的にstatus=running<br/>worker_id設定]
    RUNNING --> EXEC[Worker.executeJob]

    EXEC --> PREP[workspace / reporter / Space / Piece / model / MCP / browser / context準備]
    PREP --> RUNPIECE[runPiece]
    RUNPIECE --> MOVEMENT[Movement loop]
    MOVEMENT --> RESULT{PieceRunResult}

    RESULT -->|completed| SUCCESS[succeeded<br/>workspace commit・最終結果]
    RESULT -->|waiting_human: ASK| ASKWAIT[waiting_human<br/>回答コメントで再開]
    RESULT -->|waiting_human: tool_request| TOOLWAIT[waiting_human<br/>承認APIで再開]
    RESULT -->|waiting_human: package_request| PKGWAIT[waiting_human<br/>承認・install後に再開]
    RESULT -->|waiting_subtasks| SUBWAIT[子Job完了待ち<br/>全完了で親queued]
    RESULT -->|cancelled| CANCELLED[cancelled<br/>子Job取消]
    RESULT -->|error / aborted| RETRY{再試行可能か}
    RETRY -->|yes| RETRYDB[retry + next_retry_at]
    RETRY -->|fatal / 上限| FAILED[failed]

    ASKWAIT --> API_COMMENT
    TOOLWAIT --> APPROVE_TOOL[tool request decide API]
    APPROVE_TOOL -->|approve/deny| JOBDB
    PKGWAIT --> APPROVE_PKG[package request decide API]
    APPROVE_PKG -->|approve/deny| JOBDB
    SUBWAIT --> JOBDB
    RETRYDB --> POLL

    SUCCESS --> EVENTS[Audit / events.jsonl / activity / metrics]
    ASKWAIT --> EVENTS
    TOOLWAIT --> EVENTS
    PKGWAIT --> EVENTS
    SUBWAIT --> EVENTS
    CANCELLED --> EVENTS
    FAILED --> EVENTS
    EVENTS --> NOTIFY[Push / Webhook / Notification projection]
    EVENTS --> UIREFRESH[REST polling + SSE stream]
    UIREFRESH --> UI

2. WorkerとPiece実行

flowchart TD
    A[Worker.processNext]
    A --> B[claimNextRetryJob または claimNextJob]
    B --> C[runJobTracked]
    C --> D[executeJob]
    D --> E[issue lock取得 / running監査 / Push]
    E --> F{Reflection Jobか}
    F -->|yes| FR[Reflection専用経路]
    F -->|no| G[prepareJobWorkspace]
    G --> H[Space・folderContext・runtimeDir解決]
    H --> I[Piece読込・定義検証]
    I --> J{worker/model適合}
    J -->|不一致| JQ[queuedへ戻す]
    J -->|適合| K{required MCP tokenあり}
    K -->|不足| KH[waiting_human: mcp_auth_required]
    K -->|OK| L[LLM client生成]
    L --> M[Tool Policy・SSH・grant・package解決]
    M --> N[Mission / conversation / provenance / browser / skill context]
    N --> O[runPiece]
    O --> P[EventLogger・Conversation・ContextManager初期化]
    P --> P2{continuedFromJobIdあり<br/>= Continue経由}
    P2 -->|yes| REPLAY[transcript.jsonl を loadFrom → allReplayableThreads<br/>全スレッドを古い順に連結(system行が境界)<br/>掃除はスレッド毎・重複call_idは付け替え<br/>transition保持は最終スレッドかつ同一pieceのときだけ<br/>seedContinuation で新会話の頭に再生]
    P2 -->|no| Q
    REPLAY --> Q[開始Movement選択<br/>resume > override > piece start]
    Q --> R[loop/revisit/cancel guard]
    R --> S[prepareMovementContext]
    S --> T[executeMovement]
    T --> U[MovementResult mapping]
    U --> V{next}
    V -->|別Movement| Q
    V -->|COMPLETE / ASK / WAIT / ABORT| W[PieceRunResult]
    W --> X[handlePieceResult]
    X --> Y[DB状態・コメント・監査・通知・親子Job処理]
    Y --> Z[heartbeat/guard停止・metrics・unlock]

prepareMovementContextでは、Workspace Tool Policyを唯一のtool/edit/SSH権限源とし、タスク内承認済みtoolをunionする。task instructionへ、Movement feedback、follow-up notice、subtask resume notice、checklist、lessonsを加える。

transcript(runs/{taskId}/transcript.jsonl)はタスクのruntime directoryに置く正規化された会話記録で、通常はturnを追記し、Continuation seedやprompt guardによる整形時には全体を再構築する。transcript replayが走るのはContinue経由(continuedFromJobIdあり)のみ。コメントやASK回答による新Jobではreplayせず、コメント文脈をtask instructionへ含める。この場合に再利用可能なのは条件が一致する静的接頭辞までで、実際の再利用量は保証しない。replayの整形規則はConversation.replayableTurnsを参照。

3. Movement・LLM・ツール内部

flowchart TD
    M0[executeMovement] --> M1[prepareMovement]
    M1 --> TOOLS[getToolDefs + complete<br/>遷移先があるPieceではtransitionも追加]
    M1 --> STATIC[static preamble構築<br/>AGENTS・Memory・Skills・tool説明]
    M1 --> DYNAMIC[dynamic context構築<br/>時刻・files・Mission・handoff・console]
    M1 --> GUIDE[Movement guidance + task instruction]
    M1 --> SEED[プロンプト要素をConversationへ順序どおり配置]
    STATIC --> SEED
    DYNAMIC --> SEED
    GUIDE --> SEED
    SEED --> DCFG{delegate許可かつ<br/>再帰深度上限未満}
    DCFG -->|yes| DINJECT[runDelegateをtoolCtxへ注入]
    DCFG -->|no| LOOP[iteration loop]
    DINJECT --> LOOP
    TOOLS --> REQUEST[LLM request入力<br/>toolsはConversationの外で保持]

    LOOP --> CANCEL{cancel/deadline signal}
    CANCEL -->|yes| MCANCEL[cancel result]
    CANCEL -->|no| INJECT[未処理interjectionをuser messageへ追加]
    INJECT --> WATCH[watchdog / loop guard]
    WATCH --> PGUARD[prompt guard<br/>estimate・dedup・compact・summarize]
    PGUARD -->|overflow復旧不能| ABORT[context overflow result]
    PGUARD -->|変更あり| REWRITE[Conversation.rewrite<br/>正規化後transcriptを再構築]
    PGUARD -->|変更なし| LLM[runLlmIteration]
    REWRITE --> LLM
    REQUEST --> LLM
    LLM --> CLIENT_GUARD[LLM client側の<br/>最終token上限検査]
    CLIENT_GUARD --> STREAM[OpenAI-compatible streaming<br/>text/tool deltas/usage/retry/thinking/backend]
    STREAM --> ERR{LLM error}
    ERR -->|recoverable| LOOP
    ERR -->|terminal| LERR[llm error result]
    ERR -->|no error| CALLS{tool callsあり}

    CALLS -->|なし| TEXT[text-only warning/retry]
    TEXT --> LOOP
    CALLS -->|あり| SPLIT[regular と transition/completeを分類]
    SPLIT --> TLOOP[重複tool loop判定]
    TLOOP -->|上限| TABORT[tool loop abort]
    TLOOP --> DISPATCH[dispatchRegularToolCalls]
    DISPATCH --> PAR{read-only parallel可能か}
    PAR -->|yes| PEXEC[Promise.all]
    PAR -->|no| SEXEC[sequential]
    PEXEC --> TOOL[executeRegularToolCallCached]
    SEXEC --> TOOL
    TOOL --> AUTH[tool存在・引数・権限・sandbox・cache]
    AUTH --> TOOL_KIND{delegateか}
    TOOL_KIND -->|yes| DELEGATE[runDelegateSubAgent<br/>隔離した子ConversationでexecuteMovement]
    TOOL_KIND -->|no| IMPL[各tool execute]
    DELEGATE --> TResult[tool_resultをconversation・UI・eventへ記録]
    IMPL --> TResult

    TResult --> WAIT{待機フラグ}
    WAIT -->|browser| WB[waiting_human]
    WAIT -->|tool request| WT[waiting_human tool_request]
    WAIT -->|package request| WP[waiting_human package_request]
    WAIT -->|subtask| WS[waiting_subtasks]
    WAIT -->|なし| TERM{complete / transition winner}
    TERM -->|complete success + pending child| WS
    TERM -->|complete| COMPLETE[completed / needs_user_input / abort mapping]
    TERM -->|transition valid| TRANS[next Movement]
    TERM -->|invalid| RET[tool_result errorを追加してiteration再試行]
    TERM -->|none| LOOP

toolsはConversationへ格納されない。prepareMovementがmessagesと並ぶ別の戻り値として返し、runLlmIterationへ渡す。delegateは通常tool dispatchの一種だが、実行前にrunDelegateをtoolCtxへ注入し、呼び出された場合は隔離した子Conversationで再帰的にexecuteMovementを実行する。

3.1 LLMリクエストのキャッシュ境界

PR #935以降は、単にプロンプトの内容を分割するだけでなく、LLMへ渡すtoolsとmessagesの順序を次の形に固定する。図中の「キャッシュ再利用可能」は、同一モデル・同一チャットテンプレート・同一Pieceなど、先行要素がバイト一致し、バックエンド側に対象キャッシュが残っている場合を表す。再利用量そのものを保証するものではない。

flowchart TD
    REGULAR[Workspace Tool Policyで解決した<br/>regular tools] --> TOOLS[tools配列を構築]
    TRANSITION[transition<br/>遷移先がある場合のみ<br/>enumはPiece全体の和集合] --> TOOLS
    COMPLETE[complete] --> TOOLS
    TOOLS --> TOOLS_STABLE[同一Piece内でtools配列を<br/>バイト単位で安定化]

    STATIC[static preamble<br/>共通ルール・tool概要・AGENTS・Memory Index<br/>Skills Index・Working Directory] --> SYSTEM[先頭の単一system message]
    FIRST_GUIDANCE[開始Movement guidance] --> SYSTEM
    SYSTEM --> REPLAY{Continueかつ<br/>再生可能transcriptあり}
    REPLAY -->|yes| PRIOR[過去の非system turnを順番に再生]
    REPLAY -->|no| NO_PRIOR[過去turnなし]
    PRIOR --> USER[末尾の単一user message]
    NO_PRIOR --> USER
    DYNAMIC[dynamic context<br/>日時・Mission・files・console<br/>handoffは再生可能turnがないContinue時のみ] --> USER
    INSTRUCTION[task instruction] --> USER
    USER --> MESSAGES[messages配列完成]

    TOOLS_STABLE --> REQUEST[LLM request]
    MESSAGES --> REQUEST
    TOOLS_STABLE -. キャッシュの先行条件 .-> SYSTEM
    SYSTEM -. 同一なら静的接頭辞を再利用 .-> PRIOR
    PRIOR -. 過去requestと一致する範囲を再利用 .-> USER
    USER -. ここから新規評価 .-> GENERATE[生成開始]

実際のメッセージ列は次の形になる。dynamic contextとtask instructionは、厳密なrole交互制約を持つモデルでも扱えるよう、別々のuser messageではなく一つに結合する。

tools = [regular tools..., transition(遷移先がある場合), complete]

messages = [
  system(static preamble + 開始Movement guidance),
  ...replayable prior turns,                    // Continue時のみ
  user(dynamic context + task instruction),
]

Movement 2以降のguidance、interjection、watchdog警告などは、既存会話の末尾へuser messageとして追加する。先頭へ挿入しないため、そこまでのキャッシュ済み接頭辞を壊さない。

3.2 初回・遷移・再開経路ごとのプロンプト構築

flowchart TD
    START[Job開始] --> KIND{開始・再開の種類}

    KIND -->|新規Job| NEW[Conversation.seed]
    NEW --> NEW_MESSAGES["system: static + 開始guidance<br/>user: dynamic + instruction"]
    NEW_MESSAGES --> NEW_CACHE[過去turnの再利用なし]

    KIND -->|同一Job内のMovement遷移| MOVE[Conversation.enterMovement]
    MOVE --> MOVE_MESSAGES["既存messages末尾へ<br/>user: 次Movement guidance"]
    MOVE_MESSAGES --> MOVE_CACHE["toolsはPiece内で安定<br/>既存prefixをそのまま再利用可能"]

    KIND -->|同一PieceをContinue| SAME[loadFrom → replayableTurns<br/>keepTransitions=true]
    SAME --> SAME_MESSAGES["systemを新しく構築<br/>過去の非system turnを再生<br/>Error回答でないcompleteを除去<br/>handoffを省略して末尾へdynamic + instruction"]
    SAME_MESSAGES --> SAME_CACHE["static・tools・再生turnが一致する範囲を再利用可能"]

    KIND -->|別PieceをContinue| CROSS[loadFrom → replayableTurns<br/>keepTransitions=false]
    CROSS --> CROSS_MESSAGES["旧Pieceのtransitionを除去<br/>新Pieceのsystem・toolsを使用<br/>再生turnがあればhandoffを省略"]
    CROSS_MESSAGES --> CROSS_CACHE["Piece固有のguidance/toolsが変わるため<br/>同一Piece相当の再利用は保証しない"]

    KIND -->|コメント・ASK回答による新Job| COMMENT[transcript replayなし]
    COMMENT --> COMMENT_MESSAGES["system: static + 開始guidance<br/>user: dynamic + コメント文脈を含むinstruction"]
    COMMENT_MESSAGES --> COMMENT_CACHE["条件が一致する静的接頭辞のみ再利用可能<br/>再利用量は保証せず、過去turnも再生しない"]

3.3 transcript replayの整形

flowchart TD
    LOAD[transcript.jsonlをloadFrom] --> THREAD[system行を境界にスレッド分割<br/>全スレッドを古い順に連結<br/>掃除はスレッド毎・重複call_idは付け替え]
    THREAD --> DROP_SYSTEM[スレッド先頭のsystemを除外]
    DROP_SYSTEM --> COMPLETE_CHECK{assistantにcompleteがあるか}
    COMPLETE_CHECK -->|Errorで始まる結果あり| KEEP_COMPLETE[拒否された中間completeを保持]
    COMPLETE_CHECK -->|結果なし、またはError以外| DROP_COMPLETE[completeを除外]
    DROP_COMPLETE --> DROP_COEMITTED[同じassistant messageで<br/>同時出力されたtransitionも除外]
    COMPLETE_CHECK -->|completeなし| TRANSITION_CHECK{transitionか}
    KEEP_COMPLETE --> TRANSITION_CHECK
    DROP_COEMITTED --> VALIDATE
    TRANSITION_CHECK -->|回答済みtransition かつ keepTransitions=true<br/>(最終スレッドかつ同一PieceのContinue)| KEEP_TRANSITION[call/resultペアを保持]
    TRANSITION_CHECK -->|keepTransitions=false<br/>(別PieceのContinue、および古いスレッドでは一括除去)| DROP_TRANSITION[除外]
    TRANSITION_CHECK -->|通常tool call| KEEP_REGULAR[call/resultペアを保持]
    KEEP_TRANSITION --> VALIDATE[未回答call・孤立result・空assistantを除外]
    DROP_TRANSITION --> VALIDATE
    KEEP_REGULAR --> VALIDATE
    VALIDATE --> SEED_CONTINUATION[seedContinuation]

判定基準は厳密には「成功済みか」ではなく、対応するtool resultがErrorで始まるかである。Error回答されたcompleteは、その後も会話が続いた中間履歴として保持する。結果がない、またはError以外のcompleteは除外し、同じassistant messageで同時出力されたtransitionも一緒に除外する。

transcriptはタスクのruntime directoryに置く会話記録で、コメント追いメッセージのJobは新しいスレッド(先頭はsystem)を追記するため複数スレッドが蓄積し得る。replayは蓄積された全スレッドを古い順に連結して使う(Conversation.allReplayableThreads。issue #1343。以前は最後の再生可能スレッドだけを採っていたが、質問への回答で前の話が落ちる形で表に出たため既定から外した。経緯は docs/design/2026-07-26-transcript-last-thread-replay.html の冒頭の注記)。掃除(replayableTurns)はスレッド毎にかける——まとめてかけると、別スレッドが同じtool_callのidを使い回したときに対応付けが狂い、終了済みのcompleteが生き残る。連結後にスレッドをまたいで重なったidは決定的に付け替える(gen_call_{n}はジョブ毎に1番から振り直されるため実際に重なる)。keepTransitionsは最終スレッドにだけ当てる——その値の元になるsamePieceContinuationは直前のジョブ1件から決まる判断で、古いスレッドがどのpieceで走ったかは記録に無いため。最終スレッドが空でない限り、この形でも連結前より再生量は増える(古いスレッドは以前は捨てていた)。例外は最終スレッドが空のとき——クラッシュや書き込み途中で切れてsystem行しか無いジョブがそれで、以前のlastReplayableThreadは空スレッドを飛ばして1つ前を採っていたため、そこにkeepTransitionsが当たっていた。いまは1つ前が「古いスレッド」扱いになり遷移が落ちるので、この形では再生量が減る。遷移を落とす向きは安全側なので意図してそのままにしている。通常のturn追加では追記するが、seedContinuationと、prompt guardのdedup・compact・summarizeによってmessagesが変わった場合は、Conversation.rewrite()でファイル全体を再構築する。

3.4 キャッシュを維持するための変更時ルール

対象 維持すべき条件 変更時の影響
tools配列 Workspace Tool Policy、task内grant、動的tool module条件が不変なら、同一Piece内で名前、順序、schema、descriptionを安定させる 1バイトでも変わるとllama.cppで全prompt cache missになり得る
transition 遷移先があるPieceだけ追加し、enumはPiece全体の和集合にする Movementごとの差分がtools配列に入ると全prompt cache missになり得る
static preamble 日時、Mission、files、handoff、consoleを入れない Jobごとの差分が先頭に入り、継続時の共有prefixを失う
system message index 0の一つだけにする 厳格なchat templateで拒否される可能性がある
dynamic context task instructionと一つの末尾user messageに結合する 連続したuser roleに対応しないモデルで失敗し得る
Movement guidance 2番目以降は会話末尾へuserとして追記する 先頭を書き換えると既存prefixを無効化する
同一Piece transition replay時に回答済みcall/resultを保持する 中間履歴を削ると、その位置以降のcacheが分岐する
Error以外のcomplete replayから除外し、同時出力transitionも除外する 再生すると継続Jobが即時完了を模倣する可能性がある

キャッシュ再利用量はllm-streamのprompt_cache reused=... prompt=... pct=...ログと、LLMイベントのcachedPromptTokensで確認する。構造を変更した場合は、単体テストだけでなく実バックエンドでMovement遷移直後とContinue直後の再利用率を測定する。

4. 待機・再開・通知・UI

sequenceDiagram
    participant Agent as Agent Loop
    participant Worker
    participant DB
    participant API
    participant UI as Chat UI
    participant Notify as Push/Webhook/Inbox

    Agent->>Worker: PieceRunResult(waiting_human)
    Worker->>DB: status=waiting_human, waitReason, resumeMovement
    Worker->>Notify: waiting_human event
    UI->>API: task/tool/package request polling
    API->>DB: pending request取得
    API-->>UI: 承認カード / ASK表示

    alt ASK
        UI->>API: POST comment
        API->>DB: resumeMovementとaskCountを継承したJob作成
    else Tool Request
        UI->>API: approve / deny
        API->>DB: grantまたはdeny + parked Jobをqueuedへ戻す
    else Package Request
        UI->>API: approve / deny
        API->>DB: CAS決定、必要なら隔離install、parked Jobをqueuedへ戻す
    end

    DB-->>Worker: 次回pollでclaim
    Worker->>Agent: 同一Movementから再実行
    Agent->>Worker: completed / failed / cancelled
    Worker->>DB: 終端status・comment・audit・event
    Worker->>Notify: succeeded / failed
    DB-->>UI: task/comments/activity更新

チャットUIでは ChatPane → ChatMessageFeed → ChatMessage が永続コメントを表示し、ChatComposerPanelがTool RequestとPackage Requestの承認カードを表示する。useJobStreamは生成中text、tool call、LLM状態、delegate streamを反映する。ActivityはActivityTimeline、短期通知はToast、要対応通知はNotification Bell/Centerが担う。

4.1 Auto Prompterの透明性境界

Auto Prompterが有効なJobでは、ツール実行前に固定済みPolicy Snapshotを評価し、追加指示、拒否、引数に結び付いた一回限りの承認を行う。発火結果は許可リスト形式のPolicy Eventとして記録し、チャットへ通知する。無効なJobではこの経路を短絡し、従来のツール実行経路を変えない。

Policy Eventは追跡可能性を支援するbest-effort監査であり、hidden chain-of-thought、生のツール引数、認証情報、prompt全文を保存しない。記録失敗だけを理由にエージェント処理を停止する保証型台帳でもない。

この機構はEU AI Actその他の法令への準拠を自己宣言しない。Article 50だけでchain-of-thought保存や一律5年保持が要求されるとは解釈せず、適用範囲、事業者の役割、保持期間は法務確認の対象とする。保持期間の設定は将来のCompliance Phaseで扱う。詳細は EU AI Act透明性対応 設計付録 を参照する。

一次資料: Regulation (EU) 2024/1689、European Commission: AI Act