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

388 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. タスク依頼から完了までの全体フロー
```mermaid
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実行
```mermaid
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・ツール内部
```mermaid
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など、先行要素がバイト一致し、バックエンド側に対象キャッシュが残っている場合を表す。再利用量そのものを保証するものではない。
```mermaid
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ではなく一つに結合する。
```text
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 初回・遷移・再開経路ごとのプロンプト構築
```mermaid
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の整形
```mermaid
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
```mermaid
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透明性対応 設計付録](./auto-prompter-eu-ai-act-transparency-design.md) を参照する。
一次資料: [Regulation (EU) 2024/1689](https://eur-lex.europa.eu/eli/reg/2024/1689/oj/eng)、[European Commission: AI Act](https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai)