Compare commits
2 Commits
023de0f58d
...
cb24586362
| Author | SHA1 | Date | |
|---|---|---|---|
| cb24586362 | |||
| e32b208dee |
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
name: "ticket-reviewer"
|
||||
description: "Use this agent when a ticket implementation is submitted for review in this project (insomnia). The agent reviews the ticket's premises/requirements and the actual implementation, creates `tickets/<ticket>.review.md` with findings, and updates the original `tickets/<ticket>.md` with review status. Do NOT use this agent for general code review unrelated to a ticket. Examples:\\n<example>\\nContext: User has just finished implementing a feature described in tickets/foo.md and wants it reviewed.\\nuser: \"tickets/foo.md の実装が終わったのでレビューして\"\\nassistant: \"I'll use the Agent tool to launch the ticket-reviewer agent to review the implementation against tickets/foo.md's requirements and produce tickets/foo.review.md.\"\\n<commentary>\\nThe user is explicitly requesting a ticket-scoped review with the project's .review.md workflow, which is this agent's purpose.\\n</commentary>\\n</example>\\n<example>\\nContext: User finishes a chunk of work and mentions the ticket name.\\nuser: \"scopedfs-scripting のチケット、一通り実装出来た\"\\nassistant: \"Let me use the Agent tool to launch the ticket-reviewer agent to review the implementation and produce the review artifacts.\"\\n<commentary>\\nCompletion of a ticket implementation is the trigger for the ticket-reviewer agent per project's lifecycle (c. レビュー).\\n</commentary>\\n</example>\\n<example>\\nContext: User requests re-review after addressing feedback.\\nuser: \"指摘を反映したので再レビューお願い\"\\nassistant: \"I'll use the Agent tool to launch the ticket-reviewer agent to re-review and update the .review.md accordingly.\"\\n<commentary>\\nRe-review updates the existing .review.md and ticket status; this agent handles that workflow.\\n</commentary>\\n</example>"
|
||||
description: "Use this agent when a ticket implementation is submitted for review in this project (insomnia). The agent reviews the ticket's premises/requirements and the actual implementation, creates `tickets/<ticket>.review.md` with findings, and updates the original `tickets/<ticket>.md` with review status. Do NOT use this agent for general code review unrelated to a ticket. "
|
||||
model: opus
|
||||
color: purple
|
||||
---
|
||||
|
|
@ -117,4 +117,3 @@ Do not modify the ticket's 背景・要件 sections unless the user explicitly a
|
|||
4. Did I avoid making git writes?
|
||||
5. Did I update both `<name>.review.md` and `<name>.md`?
|
||||
6. Is my judgment line unambiguous?
|
||||
|
||||
|
|
|
|||
|
|
@ -3,3 +3,4 @@
|
|||
{"id":"019e1ac0-d0d0-7151-ae38-4cd148263cda","occurred_at":"2026-05-12T05:55:04.528959274Z","session_id":"019e1706-d800-7021-8042-e40debe644cd","event":"use","source":"WorkflowInvoke","records":[{"kind":"workflow","slug":"worktree-workflow","file_bytes":4188,"file_tokens_estimate":1047}]}
|
||||
{"id":"019e1b66-b238-7342-a75f-3b4258de8c92","occurred_at":"2026-05-12T08:56:15.672925627Z","session_id":"019e1b63-ae47-7123-89b6-3a49d73ae200","event":"use","source":"WorkflowInvoke","records":[{"kind":"workflow","slug":"auto-maintain","file_bytes":6783,"file_tokens_estimate":1696}]}
|
||||
{"id":"019e1b66-b239-71f0-90d7-968f4c0d2ee0","occurred_at":"2026-05-12T08:56:15.673018070Z","session_id":"019e1b63-ae47-7123-89b6-3a49d73ae200","event":"use","source":"WorkflowInvoke","records":[{"kind":"workflow","slug":"worktree-workflow","file_bytes":4188,"file_tokens_estimate":1047}]}
|
||||
{"id":"019e1d46-abf0-7e82-85d6-652eb8535494","occurred_at":"2026-05-12T17:40:31.344574876Z","session_id":"019e1d42-3b60-7c02-9815-f90c89466a46","event":"use","source":"WorkflowInvoke","records":[{"kind":"workflow","slug":"worktree-workflow","file_bytes":4188,"file_tokens_estimate":1047}]}
|
||||
|
|
|
|||
1
TODO.md
1
TODO.md
|
|
@ -9,6 +9,7 @@
|
|||
- Pod: 任意ターンからの Fork(複数ターン巻き戻しを汎用化) → [tickets/pod-session-fork.md](tickets/pod-session-fork.md)
|
||||
- Pod: 子→親の TurnEnded/Errored callback を親由来ターンのみに絞る → [tickets/pod-parent-turn-callback.md](tickets/pod-parent-turn-callback.md)
|
||||
- Pod: セッションログをバックエンドにした Pod 単位の永続化 → [tickets/pod-persistent-state.md](tickets/pod-persistent-state.md)
|
||||
- Pod: イベントハンドラからターン起動を分離(描画ループ式 dispatch) → [tickets/pod-event-loop-dispatch.md](tickets/pod-event-loop-dispatch.md)
|
||||
- 永続化層のセマンティック整理 → [tickets/persistence-semantics.md](tickets/persistence-semantics.md)
|
||||
- Exchange / Turn / Call セマンティクス整理 → [tickets/exchange-turn-call-semantics.md](tickets/exchange-turn-call-semantics.md)
|
||||
- llm-worker のエラー耐性
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
ただし、これは unattended 自動開発ではない。最終判断、危険な権限拡大、push、未合意の要件変更は人間に戻す。
|
||||
|
||||
また、このフレームワークの対象はプロジェクトの新規立ち上げ・設計ではなく、ある程度タスクの関心対象が分化し、機能追加・課題対応をイテレーションするフェーズにあるプロジェクトである。
|
||||
|
||||
## 目的
|
||||
|
||||
AI maintainer の目的は、コードを書くことそのものではなく、開発状態を前に進めることである。
|
||||
|
|
@ -45,6 +47,7 @@ AI maintainer の目的は、コードを書くことそのものではなく、
|
|||
|
||||
- 状態把握: `TODO.md`, `tickets/`, `docs/`, git history, worktree 状態を読む
|
||||
- 作業選定: 実装可能な ticket と設計相談が必要な ticket を分ける
|
||||
// ↑ について、設計相談も含めてOrchestrator-Coder間で行わせて良いのでは
|
||||
- 計画: 作業範囲、検証方法、child worktree / Pod scope を決める
|
||||
- 委譲: 実装 Pod / reviewer Pod を spawn し、必要最小 scope を渡す
|
||||
- 監督: Pod 出力、worktree diff、test 結果を確認する
|
||||
|
|
|
|||
9
docs/plan/multi-pod-interface.md
Normal file
9
docs/plan/multi-pod-interface.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# マルチエージェントを統括するインタフェース・ワークフロー・UI/UXの設計
|
||||
|
||||
基本的には [./ai-maintainer.md] を用いて使うものと考えている
|
||||
|
||||
ワークスペース上のすべてのPodをリスト化し、リストから直に各Podにアタッチし、進捗を確認可能にする。
|
||||
|
||||
とか言ってたら
|
||||
May 11, 2026にAnthropicからなんか出てた↓
|
||||
[https://claude.com/blog/agent-view-in-claude-code]
|
||||
|
|
@ -23,55 +23,129 @@
|
|||
- 将来 DB backend を追加しても歪みにくいデータ構造を設計する。
|
||||
- 既存の session-store JSONL 実装から段階的に移行できる命名・API 境界を決める。
|
||||
|
||||
## 検討事項
|
||||
## 結論: Session / Segment / Entry の 3 階層
|
||||
|
||||
- 会話継続単位と append-only log 単位を分けるべきか。
|
||||
- 例: user-visible conversation/thread と、internal log segment の分離。
|
||||
- compaction の扱い。
|
||||
- compaction 後の履歴を新しい低レベル log として扱うのは自然か。
|
||||
- その場合、ユーザー視点では同じ会話の継続としてどう表現するか。
|
||||
- fork の扱い。
|
||||
- fork は新しい会話単位を作るのか、同一会話内の branch とするのか。
|
||||
- fork 起点を entry hash / turn boundary / checkpoint など、どの抽象度で表すか。
|
||||
- Pod state の責務。
|
||||
- Pod 名から active な会話 / log を復元するために何を持つべきか。
|
||||
- Pod が過去に辿った session / log の順序付き履歴をどこに持つべきか。
|
||||
- runtime state と persistent state の境界。
|
||||
- `history.json` / `status.json` / `spawned_pods.json` を永続正本として扱わない方針の確認。
|
||||
- session log の `pod.scope` extension entry を撤廃するか(要検討)。
|
||||
- 現状: session log に `PodScopeSnapshot` を extension として append し、session 復元時に scope も復元できる。
|
||||
- Pod 単位永続化(`tickets/pod-persistent-state.md`)が入ると、scope の正本を Pod state に持つほうが責務が明確で、session log との重複も解消できる。
|
||||
- ただし scope は session 内で動的に変化し得るため、「最新 snapshot は Pod state 」「変化履歴は session log の event として残す」のような分割もあり得る。conversation timeline 上の scope 変化を session 単独で再現する必要があるかが論点。
|
||||
- 撤廃の選択肢: (a) session log から完全に削除し Pod state を唯一の正本にする / (b) snapshot 保持責務だけ Pod state に寄せ、scope 変更 event は session log に残す / (c) 現状維持で Pod state は session への参照のみ。
|
||||
- DB backend を想定した場合のテーブル / relation 相当の構造。
|
||||
- append-only entry log
|
||||
- lineage / origin
|
||||
- active pointer
|
||||
- Pod / child Pod registry
|
||||
- index / listing / GC の余地
|
||||
- 既存 API / CLI 名称の移行方針。
|
||||
- `--session` の扱い
|
||||
- debug 用 ID とユーザー向け ID の分離
|
||||
```
|
||||
Session ← ユーザー視点の会話の家系(fork tree 全体。Segment 群の grouping)
|
||||
└── Segment ← Compaction / Fork で切れる物理 append-only 単位(現在の SessionId 相当)
|
||||
└── Entry ← 1 永続化イベント
|
||||
```
|
||||
|
||||
## 一案: Thread / Segment / Checkpoint に分ける案
|
||||
セマンティクスの対応:
|
||||
|
||||
これは現時点の決定ではなく、検討材料の一案として置く。
|
||||
- **resume** = 同 Session 内の指定 leaf Segment に append
|
||||
- **compaction** = 同 Session, new Segment(lineage: `compacted_from`)
|
||||
- **fork** (live / 過去いずれも) = 同 Session, new Segment(lineage: `forked_from`)
|
||||
- **branching は Session 内で完結**する。Segment 間が DAG、Segment 内は完全 linear。
|
||||
- **Session は分岐ツリーを持つ静的な構造**。「今どの Segment に書いているか」(current active Segment) は Pod state 側が動的に保持する。
|
||||
|
||||
- `Pod`: agent 実行主体 / process identity。
|
||||
- `Thread`: ユーザー視点の会話・作業継続単位。compaction しても同じ Thread と見なす。
|
||||
- `Segment`: append-only log の物理的 / 復元上の単位。現在の `SessionId` に近い。compaction / fork で新しい Segment が生まれる。
|
||||
- `Entry`: Segment 内の 1 永続化イベント。
|
||||
- `Checkpoint`: fork / rollback / UI 選択などの起点を表す抽象境界。内部的には Segment + EntryHash を指してもよいが、表層 API では entry pointer を直接露出しすぎない。
|
||||
文脈合成 (merge / cherry-pick) は今後の要件として想定しないため、entry レベルの DAG(任意の親 pointer)は採用しない。Segment 内 linear + Segment 間 DAG の 2 階層に閉じることで、同 Segment 内は `(segment_id, seq)` の連番 PK で直線探索でき、Segment 間の lineage は粗粒度な DAG として軽量に管理できる。
|
||||
|
||||
この案では:
|
||||
fork で新しい Session を切らないのは、compaction で Segment を切るのと fork で Segment を切るのが対称な操作であり、ユーザー視点でも「同じ起源から派生した枝」として 1 つの単位で扱う方が自然なため。これにより Session 数が分岐で爆発せず、`WHERE session_id = ?` だけで fork tree 全体が取れる。
|
||||
|
||||
- compaction = same Thread, new Segment
|
||||
- fork = new Thread または branch, new Segment
|
||||
- resume = same Thread の active Segment に append
|
||||
- Pod state = active Thread / spawned children / 必要な runtime 復元メタデータを保持
|
||||
- lineage = Segment origin または Checkpoint reference として保持
|
||||
### Restore / Resume の API
|
||||
|
||||
この案を採用するかは本チケット内で改めて比較・判断する。
|
||||
Session が分岐を含むため、resume の指定は **(SessionId, leaf SegmentId) の組** で行う:
|
||||
|
||||
- TUI 経路: ユーザーは **Session を選択 → その Session 内の leaf を選択**。
|
||||
- 内部 API: restore は `(SessionId, SegmentId)` を取り、指定 Segment から replay する。leaf 以外を指定すれば read-only な過去状態の参照も可能。
|
||||
- 「最後に active だった leaf」は Pod state が保持するので、TUI が初期選択候補として使える。
|
||||
|
||||
### 既存コードとの対応
|
||||
|
||||
| 既存 | 新名称 |
|
||||
|---|---|
|
||||
| `SessionId` | `SegmentId` にリネーム |
|
||||
| (なし) | `SessionId` 新設(Segment 群をまとめる、ユーザー視点 ID) |
|
||||
|
||||
`session-store` crate の型・関数は Segment 中心の命名に揃える。crate 名自体を変えるかは別論点(中身は引き続き Segment 単位の append-only log を扱う)。
|
||||
|
||||
### llm-worker への影響
|
||||
|
||||
llm-worker は session 概念を持たない(`Worker` は `history` / `turn_count` / `RequestConfig` を持つだけで永続化への hook も無い)。Session / Segment 階層の導入は llm-worker 層に染み出さず、影響範囲は `session-store` / `pod` / `pod-registry` / `pod-cli` に閉じる。
|
||||
|
||||
## Entry hash の廃止
|
||||
|
||||
現状、各 entry は SHA-256 hash chain (`prev_hash` → `hash`) を持つが、実際に効いている用途は 2 つだけ:
|
||||
|
||||
1. `ensure_head_or_fork` (pod.rs:1348) — store の末尾と Pod の保持する `head_hash` を比較し、不一致なら auto-fork。**末尾識別子があれば良い**(hash chain そのものは要らない)。
|
||||
2. `fork_at(source_id, at_hash)` (session.rs:425) — 過去 entry pointer から fork。`pod-session-fork.md` の入口仕様は turn number で、entry hash は内部 pointer に過ぎない。turn boundary (TurnEnd entry の index) で代替可能。
|
||||
|
||||
改竄検知 (tamper-evident chain) は宣伝されているがコード上は walk して verify するルートが無いため、削除しても regression にならない。
|
||||
|
||||
廃止に伴う対応:
|
||||
|
||||
- `HashedEntry` 廃止、JSONL は 1 行 1 `LogEntry`。
|
||||
- `SessionOrigin.at_hash` → `at_turn_index` (TurnEnd 由来) に置換。
|
||||
- `ensure_head_or_fork` の検知ロジックは、Segment 末尾の terminal marker entry または末尾 seq 比較に置換(形式は実装時に決める)。
|
||||
|
||||
## Fork: 2 種類の書き込み方
|
||||
|
||||
Session 境界の話ではなく **元 Segment への marker 書き込みの有無**で 2 種類を分ける。Session はどちらの場合も同じで、新 Segment が同 Session 内に生える。
|
||||
|
||||
- **live auto-fork**(concurrent writer 検知)
|
||||
- 元 Segment の末尾に terminal marker (`Forked { to: SegmentId }` 等) を append → 以降の writer は marker を見て新 Segment へ自動移動。
|
||||
- CoW semantics: 元 Segment は immutable、生まれた Segment 同士は対等な兄弟。
|
||||
- **過去 fork**(UI で turn 選択)
|
||||
- 元 Segment は **無変更**、replay して新 Segment を生やすだけ。
|
||||
- 起点は `(source_segment_id, at_turn_index)`。
|
||||
- 元 Segment に書き込まないため、過去 fork を nested に重ねても解釈が単純。
|
||||
|
||||
両者を同じ marker で扱おうとすると、過去 fork から更に過去で fork した場合に元 Segment への marker 位置解釈が複雑化して破綻するため、過去 fork 側は元 Segment に触れない方針で固定する。
|
||||
|
||||
### fork の物理操作(物理コピーは不要)
|
||||
|
||||
「fork = 同 Session 内に Segment を増やす」と言っても、過去 Segment を丸ごとコピーする必要は無い:
|
||||
|
||||
- source segment の seq=N までを replay して得られた `Vec<Item>` を、新 Segment の seed entry (`SessionStart` 相当) に 1 回書き込む。
|
||||
- compaction の transitive な圧縮効果がここに乗る(source segment の `SessionStart.history` に過去 Segment の compaction 結果が既に埋まっている)ので、**source segment 1 本だけ replay すれば fork seed が作れる**。さらに過去の Segment は touch しない。
|
||||
- 新 Segment が持つ lineage 情報は `(parent_segment_id, fork_at_turn)` のメタデータのみ。
|
||||
|
||||
これは現状の `fork_at` (`session.rs:430-456`) の挙動と同じで、Session 階層が乗っても操作は変わらない。
|
||||
|
||||
## RDB backend を想定した概念モデル
|
||||
|
||||
将来 DB backend を追加しても歪みにくい形として、以下の関係を仮定する:
|
||||
|
||||
```
|
||||
sessions (
|
||||
id PK,
|
||||
... -- ユーザー視点 metadata
|
||||
)
|
||||
|
||||
segments (
|
||||
id PK,
|
||||
session_id FK,
|
||||
parent_segment_id FK NULL, -- compaction / fork の元
|
||||
fork_at_turn INT NULL,
|
||||
origin_kind ENUM (new | compact | fork),
|
||||
lineage_path ltree, -- 祖先・子孫の逆引き用 (materialized path)
|
||||
...
|
||||
)
|
||||
|
||||
entries (
|
||||
segment_id FK,
|
||||
seq INT,
|
||||
kind, payload, ts,
|
||||
PRIMARY KEY (segment_id, seq) -- 同 Segment 内 linear scan
|
||||
)
|
||||
```
|
||||
|
||||
- 同 Segment 内 entry は `(segment_id, seq)` PK で linear scan、surrogate identity なので hash 不要。
|
||||
- Segment 間 lineage は `parent_segment_id` chain。深さは compaction / fork 回数のみ(Session あたり数〜数十)。
|
||||
- Segment lineage の祖先・子孫逆引きは `lineage_path` の `<@` / `@>` で 1 index 引き。entry 単位の DAG ではないため materialized path のメンテコストも軽い。
|
||||
- 通常 append は `lineage_path` 更新を伴わない(Segment 生成時に確定)。
|
||||
- 「Session の fork tree 全体」は `WHERE session_id = ?` で取れる。Session 単位の listing / GC が自然。
|
||||
|
||||
FsStore 実装はこの構造のサブセット相当として位置付ける(1 Segment = 1 jsonl、`session_id` は Segment の metadata に持たせるか別ファイルに index する)。
|
||||
|
||||
## 残る検討事項
|
||||
|
||||
- pod.scope extension entry を Pod state 側に寄せるか、Segment log に残すか(`tickets/pod-persistent-state.md` 側と合わせて決定)。
|
||||
- 撤廃の選択肢: (a) Segment log から削除し Pod state を唯一の正本にする / (b) snapshot 保持責務だけ Pod state に寄せ、scope 変更 event は Segment log に残す / (c) 現状維持で Pod state は Segment への参照のみ。
|
||||
- live auto-fork の marker 形式 (terminal entry vs Segment 末尾 seq 比較)。
|
||||
- pod-cli / TUI の `--session` 引数を Session 単位にするか Segment 単位にするか。debug 用 ID とユーザー向け ID の分離。
|
||||
- `session-store` crate 名のリネーム要否。
|
||||
|
||||
## 完了条件
|
||||
|
||||
|
|
@ -87,6 +161,7 @@
|
|||
- DB backend の実装。
|
||||
- UI の履歴表示 / branch 表示の詳細 UX。
|
||||
- GC / retention policy の実装。
|
||||
- Session を跨ぐ merge / cherry-pick。
|
||||
|
||||
## 関連
|
||||
|
||||
|
|
|
|||
65
tickets/pod-event-loop-dispatch.md
Normal file
65
tickets/pod-event-loop-dispatch.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Pod: イベントハンドラからターン起動を分離する
|
||||
|
||||
## 背景
|
||||
|
||||
`crates/pod/src/controller.rs` の controller タスクは、 outer loop の Method ハンドラ内に **イベント処理とターン起動を同居** させている。
|
||||
|
||||
該当する箇所:
|
||||
|
||||
- `Method::Run` → outer arm 内で `run_with_cancel_support(pod.run(...)).await` と `finish_controller_run.await` を呼ぶ
|
||||
- `Method::Notify` (Idle) → outer arm 内で `set_controller_status(Running).await` のあと `run_with_cancel_support(pod.run_for_notification()).await` / `finish_controller_run.await`
|
||||
- `Method::PodEvent` (Idle) → 同上 (副作用処理 `apply_event_side_effects.await` も同じ arm 内)
|
||||
- `Method::Resume` → 同上
|
||||
|
||||
ハンドラ自身が「ターンを丸ごと await する長時間処理」 を抱えているため、 以下が起きる:
|
||||
|
||||
- 同じターン起動コード (`set_controller_status` → `run_with_cancel_support` → `finish_controller_run`) が 4 箇所に重複する
|
||||
- ターン起動の発火元 (Run / Notify / PodEvent / Resume) と起動本体が分離されていないため、 起動条件の変更が複数 arm に波及する
|
||||
- ターン起動に必要な前処理 (history への user message append / NotifyBuffer への push / status 遷移) も各 arm に分散している
|
||||
|
||||
ターン起動を outer loop の周回トップに集約し、 各イベントハンドラは「次のターンに渡す入力をその場で確定 (history append / NotifyBuffer push) + `needs_run` フラグセット」 までに留めることで、 起動コードの重複と入力経路の分散をなくす。
|
||||
|
||||
加えて、 `PodController::spawn` 自体が約 615 行に膨らんでおり、「組み立て (channel/runtime_dir/Worker hook 配線/ツール登録/PodHandle 構築)」と「実行ループ (controller タスク)」が単一関数に同居している。 outer loop を上記の形に書き直すタイミングで、 spawn の構造分解も同時に行う方が、 重複コードの統合先が見やすくなる。
|
||||
|
||||
なお、 別途観測されている「auto-kick されたターンの内部で controller がデッドロックする現象」 はこの整理では解決しない (inner select! のアーム内 await が pending する経路は構造上残る)。 根本原因の特定は別チケットの対象。
|
||||
|
||||
## 要件
|
||||
|
||||
- イベントハンドラは **「状態更新と `needs_run` フラグ立て」 まで** にとどめる。ハンドラ内で `run_with_cancel_support.await` / `finish_controller_run.await` を呼ばない
|
||||
- outer loop の各周回はまず `needs_run` を評価し、 立っていればターン起動 (`run_with_cancel_support` → `finish_controller_run`) を実行してフラグを降ろす。 立っていなければ `method_rx.recv().await` で次の Method を待つ
|
||||
- 既存の inner select! によるターン中の Method 並行受信は維持する。 ターン本体の借用構造 (`&mut Pod` を Worker が抱える) も変更しない
|
||||
- `needs_run` を立てる契機は最低限以下を含める (= 現状 auto-kick している経路):
|
||||
- `Method::Run`
|
||||
- `Method::Resume`
|
||||
- `Method::Notify` (Idle 時のみ)
|
||||
- `Method::PodEvent` (Idle 時のみ)
|
||||
- 「次のターンの起動意図と入力」 は `needs_run` を起動意図を持つ enum として表現する:
|
||||
- `Run { input }` / `InterruptAndRun { input }` / `RunForNotification` / `Resume`
|
||||
- `Method::Notify` / `Method::PodEvent` の Idle 経路は NotifyBuffer に push したうえで `RunForNotification` を立てる (現状の `pod.run_for_notification()` が NotifyBuffer から自動取得する挙動に乗るため、 enum に入力を載せる必要はない)
|
||||
- `Worker::run` / `Pod::run` の入力受け取り API には触らない (interceptor の `on_prompt_submit` cancel / 書き換え / extras ordering の invariant に踏み込まない)
|
||||
- Pause / Shutdown / Cancel はハンドラ内で完結する (フラグ化しない、 既存通り即時処理)
|
||||
- Running 中に来た `Method::Notify` / `Method::PodEvent` の挙動 (NotifyBuffer に積むだけ、 副作用は実行) は変えない
|
||||
- 上記改修に合わせて `PodController::spawn` を以下の責務単位に分解する:
|
||||
- 初期化 (channel 群 / `RuntimeDir` / pod-immutable snapshot / `SpawnedPodRegistry` / `alerter` 装着 / bash-output scope)
|
||||
- Worker への event bridge コールバック配線 (`on_turn_*` / `on_*_block` / `on_tool_result` / `on_usage` / `on_warning` / `on_history_append` 等)
|
||||
- ツール登録 (builtin / memory / spawn orchestration)
|
||||
- 初期ファイル書き出し + `PodSharedState` / `PodHandle` 構築 + `SocketServer` 起動
|
||||
- `controller_loop` — Idle/Paused 状態の event loop + 後処理 (現 `tokio::spawn(async move { loop { ... } })` の本体)
|
||||
- `drive_turn` — Running 状態の event loop。 現 `run_with_cancel_support` を改名し、 `controller_loop` と同格の関数として並べる (「cancel support」 という実装詳細名から役割ベースの名前に改める)
|
||||
- 分解後の `spawn` はこの順序で各責務を呼び出す薄いフローになる。 Idle/Paused 側 (`controller_loop`) と Running 側 (`drive_turn`) の event loop が同格の関数として並ぶ形に揃える
|
||||
|
||||
## 完了条件
|
||||
|
||||
- `Method::Run` / `Method::Resume` / `Method::Notify(Idle)` / `Method::PodEvent(Idle)` のいずれも、 ターン起動が outer loop の周回トップ 1 箇所に集約されており、 ハンドラ側にはターン起動コードが残っていない
|
||||
- 各イベントハンドラの async body 内に「ターン丸ごと」 や `finish_controller_run` 等の長時間 await が無い
|
||||
- 既存挙動が変わらない (どの Method で auto-kick されるか、 ターン中の Cancel / Pause / Shutdown が効くか、 NotifyBuffer に積まれた内容が次ターンで反映されるか、 ターン起動順序)
|
||||
- `PodController::spawn` が責務単位の関数列を順に呼ぶ薄いフローになっており、 単一関数の中に組み立てと実行ループが同居していない
|
||||
- Idle/Paused 状態の event loop (`controller_loop`) と Running 状態の event loop (`drive_turn`) が同格の関数として並んでおり、 名前が役割を表している (`run_with_cancel_support` の名前は残らない)
|
||||
- `crates/pod/tests/controller_test.rs` の既存テストが通る
|
||||
|
||||
## 範囲外
|
||||
|
||||
- 観測されたデッドロックの根本原因特定 (別チケット予定)
|
||||
- `Pod` 構造体の `&mut self` borrow を分解して outer loop を完全 dispatcher 化する大改修 (将来検討)
|
||||
- Cancel / Shutdown の専用チャネル化 (controller が固まっている時には別経路でも効かないため、 メリット薄と判断して見送り)
|
||||
- `Worker::run` / `Pod::run` の入力なし API 化 (controller の dispatch を bool に圧縮するメリットに対し、 worker interceptor invariant を壊すコストが見合わない)
|
||||
Loading…
Reference in New Issue
Block a user