# 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
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解決
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保存
既存Jobへ次iterationで投入] PREV -->|tool/package approval待ち| CONFLICT[409: 承認を先に要求] PREV -->|queued / retry| REUSE[既存pending Jobを再利用] PREV -->|ASK待ち・終端・Jobなし| FOLLOWUP[resumeMovement/askCount継承
Job作成] REUSE --> JOBDB FOLLOWUP --> JOBDB API_CONTINUE --> CONTINUE_GATE[所有権・Piece存在・前Job終端確認] CONTINUE_GATE --> HANDOFF[continuedFromJobIdとhandoff comment
新Jobは transcript replay で前会話を引き継ぐ] HANDOFF --> JOBDB OTHER --> JOBDB JOBDB --> POLL[Worker.processNext polling] POLL --> CLAIM[retry期限優先→queued
worker/model/profile適合
issue lockなし] CLAIM --> RUNNING[原子的にstatus=running
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
workspace commit・最終結果] RESULT -->|waiting_human: ASK| ASKWAIT[waiting_human
回答コメントで再開] RESULT -->|waiting_human: tool_request| TOOLWAIT[waiting_human
承認APIで再開] RESULT -->|waiting_human: package_request| PKGWAIT[waiting_human
承認・install後に再開] RESULT -->|waiting_subtasks| SUBWAIT[子Job完了待ち
全完了で親queued] RESULT -->|cancelled| CANCELLED[cancelled
子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あり
= Continue経由} P2 -->|yes| REPLAY[transcript.jsonl を loadFrom → allReplayableThreads
全スレッドを古い順に連結(system行が境界)
掃除はスレッド毎・重複call_idは付け替え
transition保持は最終スレッドかつ同一pieceのときだけ
seedContinuation で新会話の頭に再生] P2 -->|no| Q REPLAY --> Q[開始Movement選択
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
遷移先があるPieceではtransitionも追加] M1 --> STATIC[static preamble構築
AGENTS・Memory・Skills・tool説明] M1 --> DYNAMIC[dynamic context構築
時刻・files・Mission・handoff・console] M1 --> GUIDE[Movement guidance + task instruction] M1 --> SEED[プロンプト要素をConversationへ順序どおり配置] STATIC --> SEED DYNAMIC --> SEED GUIDE --> SEED SEED --> DCFG{delegate許可かつ
再帰深度上限未満} DCFG -->|yes| DINJECT[runDelegateをtoolCtxへ注入] DCFG -->|no| LOOP[iteration loop] DINJECT --> LOOP TOOLS --> REQUEST[LLM request入力
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
estimate・dedup・compact・summarize] PGUARD -->|overflow復旧不能| ABORT[context overflow result] PGUARD -->|変更あり| REWRITE[Conversation.rewrite
正規化後transcriptを再構築] PGUARD -->|変更なし| LLM[runLlmIteration] REWRITE --> LLM REQUEST --> LLM LLM --> CLIENT_GUARD[LLM client側の
最終token上限検査] CLIENT_GUARD --> STREAM[OpenAI-compatible streaming
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
隔離した子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で解決した
regular tools] --> TOOLS[tools配列を構築] TRANSITION[transition
遷移先がある場合のみ
enumはPiece全体の和集合] --> TOOLS COMPLETE[complete] --> TOOLS TOOLS --> TOOLS_STABLE[同一Piece内でtools配列を
バイト単位で安定化] STATIC[static preamble
共通ルール・tool概要・AGENTS・Memory Index
Skills Index・Working Directory] --> SYSTEM[先頭の単一system message] FIRST_GUIDANCE[開始Movement guidance] --> SYSTEM SYSTEM --> REPLAY{Continueかつ
再生可能transcriptあり} REPLAY -->|yes| PRIOR[過去の非system turnを順番に再生] REPLAY -->|no| NO_PRIOR[過去turnなし] PRIOR --> USER[末尾の単一user message] NO_PRIOR --> USER DYNAMIC[dynamic context
日時・Mission・files・console
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
user: dynamic + instruction"] NEW_MESSAGES --> NEW_CACHE[過去turnの再利用なし] KIND -->|同一Job内のMovement遷移| MOVE[Conversation.enterMovement] MOVE --> MOVE_MESSAGES["既存messages末尾へ
user: 次Movement guidance"] MOVE_MESSAGES --> MOVE_CACHE["toolsはPiece内で安定
既存prefixをそのまま再利用可能"] KIND -->|同一PieceをContinue| SAME[loadFrom → replayableTurns
keepTransitions=true] SAME --> SAME_MESSAGES["systemを新しく構築
過去の非system turnを再生
Error回答でないcompleteを除去
handoffを省略して末尾へdynamic + instruction"] SAME_MESSAGES --> SAME_CACHE["static・tools・再生turnが一致する範囲を再利用可能"] KIND -->|別PieceをContinue| CROSS[loadFrom → replayableTurns
keepTransitions=false] CROSS --> CROSS_MESSAGES["旧Pieceのtransitionを除去
新Pieceのsystem・toolsを使用
再生turnがあればhandoffを省略"] CROSS_MESSAGES --> CROSS_CACHE["Piece固有のguidance/toolsが変わるため
同一Piece相当の再利用は保証しない"] KIND -->|コメント・ASK回答による新Job| COMMENT[transcript replayなし] COMMENT --> COMMENT_MESSAGES["system: static + 開始guidance
user: dynamic + コメント文脈を含むinstruction"] COMMENT_MESSAGES --> COMMENT_CACHE["条件が一致する静的接頭辞のみ再利用可能
再利用量は保証せず、過去turnも再生しない"] ``` ### 3.3 transcript replayの整形 ```mermaid flowchart TD LOAD[transcript.jsonlをloadFrom] --> THREAD[system行を境界にスレッド分割
全スレッドを古い順に連結
掃除はスレッド毎・重複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で
同時出力されたtransitionも除外] COMPLETE_CHECK -->|completeなし| TRANSITION_CHECK{transitionか} KEEP_COMPLETE --> TRANSITION_CHECK DROP_COEMITTED --> VALIDATE TRANSITION_CHECK -->|回答済みtransition かつ keepTransitions=true
(最終スレッドかつ同一PieceのContinue)| KEEP_TRANSITION[call/resultペアを保持] TRANSITION_CHECK -->|keepTransitions=false
(別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)