docs: refresh stale project docs

This commit is contained in:
Keisuke Hirata 2026-05-30 15:39:33 +09:00
parent 3d42c64238
commit 2109ed6403
No known key found for this signature in database
9 changed files with 343 additions and 663 deletions

View File

@ -24,11 +24,12 @@ insomnia は環境再現・コンテナ管理・VCS 統合などを自身の責
## Pod
独立したエージェントの実行単位。llm-worker の Worker をラップし、マニフェストによる宣言的構成とディレクトリスコープを加える。
独立したエージェントの実行単位。llm-worker の Worker をラップし、マニフェストによる宣言的構成、ディレクトリスコープ、Pod 名に紐づく永続状態を加える。
- 1 Pod = 1 プロセス = 1 セッション
- マニフェストTOMLから完結構築できる
- scope で書き込み可能なパスを制限(読み取りは自由)
- 1 live Pod process は 1 Pod name/current state を所有する
- 会話の永続化は `session-store``session_id` / `segment_id` ログ、Pod 名の current state は `pod-store` metadata が担う
- マニフェストまたは profile から完結構築できる
- scope で読み取り・書き込み可能なパスを制限する
- 独立した socket サーバーを持ち、Client (TUI / GUI) が接続して操作する
### 実行ループ
@ -50,19 +51,28 @@ Client → Method::Run { input }
### コンテキスト管理
- **Prune**: 古い tool result の content を除去summary は残す)。`pre_llm_request` で毎回判定
- **Compact**: 履歴全体を要約して圧縮。`input_tokens` が閾値を超えたとき、`PreRequestAction::Yield` で Worker を一旦中断し、Pod 側で要約 → 新セッションとして再開
- **Prune**: 古い tool result の content を除去summary は残す)。LLM request context だけを加工し、永続 history 本体は変更しない
- **Compact**: 履歴 prefix を要約し、同じ `session_id` 配下の新 `segment_id` へ rotate する。`SegmentStart.compacted_from` が元 segment を参照し、Pod metadata の active pointer は新 segment を指す
- サーキットブレーカー: compact が3回連続失敗したら無効化
## Protocol
Pod の制御・監視に使う JSONL ベースのメッセージプロトコル。トランスポートに依存しない。
Pod の制御・監視に使う JSONL ベースのメッセージプロトコル。トランスポートに依存しない。正確な wire enum は `crates/protocol/src/lib.rs::{Method, Event}` を正とし、この節はカテゴリの目次として扱う。
- **Method** (Client → Pod): `Run` / `Notify` / `Resume` / `Cancel` / `Shutdown` / `GetHistory`
- **Event** (Pod → Client, broadcast): `TurnStart` / `TurnEnd` / `TextDelta` / `ToolCallStart` / `ToolCallArgsDelta` / `ToolCallDone` / `ToolResult` / `Usage` / `RunEnd` / `Error` / `History` / `Notification` / `Shutdown`
- リクエストとレスポンスの紐付けはしない。Pod の状態遷移(イベント)を見れば何が起きているか分かる
- イベントは全リスナーに broadcast
- 操作の競合は先勝ちrun 中に別の run → AlreadyRunning エラー)
- **Client → Pod (`Method`)**
- turn 制御: `Run`, `Resume`, `Cancel`, `Pause`, `Shutdown`
- context/session 制御: `Compact`, `ListRewindTargets`, `RewindTo`
- typed injection / child lifecycle: `Notify`, `PodEvent`
- client 補助: `ListCompletions`
- Pod visibility / attach: `ListVisiblePods`, `InspectPod`, `AttachOrRestorePod`
- **Pod → Client (`Event`)**
- accepted input / history seed: `Snapshot`, `UserMessage`, `SystemItem`, `SegmentRotated`
- generation stream: `TurnStart`, `TurnEnd`, `LlmCallStart`, `LlmCallEnd`, retry/continuation events, `Text*`, `Thinking*`, `ToolCall*`, `ToolResult`, `Usage`, `RunEnd`
- control replies: completions, rewind, visible Pod / inspect / attach results
- operational status: `Status`, `Alert`, `MemoryWorker`, `Compact*`, `Error`, `Shutdown`
- リクエストとレスポンスの紐付けを一般化した RPC にはしない。多くの状態は broadcast event と Pod status で観測する
- 一部の reply例: completionsは要求 socket にだけ返る。broadcast event と request-local reply の違いは enum variant のコメントを正とする
- 操作の競合は先勝ちrun 中に別の run → `AlreadyRunning` エラー)
## マニフェストとファクトリ
@ -116,23 +126,28 @@ Pod が操作できるファイルパスの制御。
- `allow` ルールで読み取り・書き込みを許可、`deny` ルールで制限
- effective permission = allow - deny
- `recursive = false` で直下のみに制限可能summary に `[non-recursive]` マーカー)
- scope 排他: Pod 間の write 衝突は scope lock file (`$XDG_RUNTIME_DIR/insomnia/scope.lock`) で検出。scope 分譲spawn 時に譲渡、終了時に返却)の記録にも使う
- scope 排他: Pod 間の write 衝突は runtime registry / scope lock で検出する。child Pod へ委譲した write scope は親の effective scope から delegated-out deny として差し引かれ、child 停止・prune 時に reclaim される
## セッション永続化
- append-only JSONL ログ。1 エントリ = 1 行
- SHA-256 チェーンでエントリの整合性を保証
- ログ再生で Worker の状態を完全復元(スナップショット不要)
- Compact 時に新セッションを開始し、旧セッションへのリンクを保持
- `session-store` は append-only JSONL segment log。1 `LogEntry` = 1 行
- `SessionId` は論理会話の fork-tree root、`SegmentId` はその中の現在の書き込み先 segment
- fresh conversation だけが新 `SessionId` を作る。compact / fork は同じ `SessionId` 配下に新 `SegmentId` を作り、`SegmentStart.{compacted_from,forked_from}: SegmentOrigin` で出自を持つ
- segment log の先頭は `LogEntry::SegmentStart`。以降に `Invoke`, `UserInput`, `AssistantItem`, `ToolResult`, `SystemItem`, `TurnEnd`, `RunCompleted` / `RunErrored`, `ConfigChanged`, `LlmUsage`, `Extension` などを append する
- replay は segment log から Worker state を再構成する。lineage は entry hash ではなく `SegmentOrigin.at_turn_index` と segment id で参照する
- Pod 名の durable current stateactive pointer、resolved manifest snapshot、spawned child delegation/reclaim`pod-store` metadata が担う。socket path や runtime mirror は liveness authority ではない
## 組み込みツール
| ツール | 概要 |
| ------ | ------------------------------------------ |
| Read | ファイル内容の読み取り |
| Write | ファイルの新規作成・上書き |
| Edit | 既存ファイルの部分編集(事前 Read が必要) |
| Glob | ファイル名パターンマッチ |
| Grep | ファイル内容の正規表現検索 |
正確な callable set と description は ToolRegistry / manifest permission / scope / profile に依存する。高レベルには以下のカテゴリを持つ。
すべて scope の permission チェックを経由。`ScopedFs` が書き込み制限を、`Tracker` がセッション内のコンテンツハッシュ追跡を行う。
| カテゴリ | 例 | 概要 |
|---|---|---|
| File / shell | `Read`, `Write`, `Edit`, `Glob`, `Grep`, `Bash` | workspace ファイル操作と shell 実行。file tools は `ScopedFs` と read-before-edit tracker を通る。`Bash` は permission policy と出力退避で制御する |
| Task | `TaskCreate`, `TaskUpdate`, `TaskList`, `TaskGet` | セッション内の短期 task 状態管理 |
| Memory / Knowledge | `MemoryQuery`, `MemoryRead`, `MemoryWrite`, `MemoryEdit`, `MemoryDelete`, `KnowledgeQuery` | manifest の memory 設定が有効な時に登録される durable memory / knowledge 操作 |
| Pod orchestration | `SpawnPod`, `SendToPod`, `ReadPodOutput`, `StopPod`, `ListPods` | child Pod の起動・通信・停止・一覧 |
| Visible Pod state | `ListVisiblePods`, `InspectPod`, `AttachOrRestorePod` | durable Pod state と visibility に基づく Pod inspection / attach / restore |
| Web | `WebSearch`, `WebFetch` | manifest/env で明示設定された provider 経由の bounded web access |
すべての tool call は manifest tool permission と scope/policy のチェックを通る。ファイル write scope、Pod delegation、memory layout、web provider 設定はそれぞれ別の authority を持ち、UI 表示だけで権限を広げない。

View File

@ -20,8 +20,8 @@ CompactInterceptor (pre_llm_request) ← safety net
→ Worker が WorkerResult::Yielded で正常終了
Pod::handle_worker_result
persist_turn旧セッションに記録
→ compact() → resume()
turn 結果を現在 segment log に記録
→ compact() で同じ session_id 内の新 segment へ rotate → resume()
[ターンの合間 — 次の Pod::run 冒頭]
Pod::try_pre_run_compact ← proactive
@ -97,21 +97,29 @@ PreRequestAction::Cancel → WorkerError::Aborted → エラー
- **サーキットブレーカー**: 3回連続 compact 失敗で無効化 (`CompactState::disabled`)
- **Thrash 検出**: compact 直後に再び閾値超過 → `PodError::CompactThrash`
- **Yield 前の永続化**: `persist_turn` を compact の前に実行。失敗しても旧セッションにデータが残る
- **Yield 前後の永続化**: Worker が `Yielded` で抜けた turn は現在 segment log に記録してから compact する。compact が失敗しても元 segment の状態は残る
### セッション管理
compact は fork と同じ構造。旧セッションを保全し、新 SessionId で圧縮後のセッションを開始
compact は「新 SessionId を作る」操作ではなく、同じ `session_id` 配下で active `segment_id` を切り替える操作
```
旧セッション (abc-123):
[...entries...] → Outcome::Yielded (interrupted=true) ← そのまま残る
session_id = abc-123
新セッション (def-456):
[SessionStart { compacted_from: (abc-123, entryN.hash), history: [要約 + 直近] }] → ...
segment old:
SegmentStart { origin: fresh/fork/... }
[...entries...]
RunCompleted { result: Yielded }
segment new:
SegmentStart { compacted_from: SegmentOrigin { segment_id: old, at_turn_index: N } }
[system prompt]
[system: 構造化要約]
[system: retained tail / auto-read / references]
...以後の turn を append
```
`SessionStart``forked_from` / `compacted_from` フィールドで出自を追跡可能。
`SegmentStart.compacted_from` / `forked_from` が lineage を追跡する。Pod 名からの resume は `pod-store` metadata の active pointer が現在の `(session_id, segment_id)` を指し、conversation/history の replay は `session-store` の segment log から行う
---
@ -220,7 +228,7 @@ write_summary(text) — 構造化要約を出力/上書き
- `final_reserve_tokens` を割った後は `write_summary` 以外の探索 tool call に synthetic error を返し、最終 summary の余白を守る
- `worker_context_max_tokens` 超過は最後の hard stop
5. ターン終了時に write_summary 未呼び出し or read_required 空(かつファイル操作履歴がある場合)→ 追加プロンプトで促す
6. `summary_max_tokens``result_context_max_tokens` で compact 結果を検証してから新 session を作
6. `summary_max_tokens``result_context_max_tokens` で compact 結果を検証してから新 segment を作り、Pod metadata の active pointer を更新す
### 構造化要約の要件

View File

@ -1,372 +1,210 @@
# AI maintainer 運用設計
## 位置づけ
この文書は、insomnia を単なる Coding Agent ではなく、開発プロジェクトを継続的に運用する AI maintainer として使うための上位設計である。
`/auto-maintain` はこの設計の最初の実行形であり、TODO / tickets から小さな実装作業を選んで実装・レビューを orchestration する Workflow に留まる。本設計はその一段上で、設計相談、ticket 整理、実装委譲、レビュー、運用課題の記録、改善提案までを一つの maintainer loop として扱う。
ただし、これは unattended 自動開発ではない。最終判断、危険な権限拡大、push、未合意の要件変更は人間に戻す。
また、このフレームワークの対象はプロジェクトの新規立ち上げ・設計ではなく、ある程度タスクの関心対象が分化し、機能追加・課題対応をイテレーションするフェーズにあるプロジェクトである。
# AI Maintainer Workflow 設計
## 目的
AI maintainer の目的は、コードを書くことそのものではなく、開発状態を前に進めることである
AI maintainer は、insomnia リポジトリの開発を継続的に進めるための orchestration role である。単発の `/auto-maintain` より広く、設計相談、work item 整理、実装委譲、レビュー、運用課題の記録、改善提案を一つの maintainer loop として扱う。
具体的には以下を担う
`/auto-maintain` はこの設計の限定実行形であり、`tickets.sh` / `work-items/` から小さな実装作業を選んで実装・レビューを orchestration する Workflow に留まる。
- TODO / tickets / docs / git history から現在の開発状態を把握する
- 要件が十分に固まった作業を実装可能な単位へ落とす
- 設計判断が必要な作業を実装前に人間へ戻す
- 実装 Pod / reviewer Pod / 親 Pod 自身を使い分ける
- 実装結果を ticket の完了条件に照らして review する
- merge / ticket lifecycle / TODO 整理の順序と branch placement を守る
- 作業中に見つかった運用上の障壁を `docs/report/` に残す
- 繰り返し発生する作業を Workflow / Knowledge / ticket 候補として提案する
## 正本と権限境界
## 役割モデル
現在の作業管理の正本は `tickets.sh``work-items/` である。古い `TODO.md` / legacy `tickets/` directory を前提にした運用は superseded。
### Human maintainer
- work item の作成・コメント・レビュー・状態変更・完了は原則 `./tickets.sh` 経由で行う
- `work-items/{open,pending,closed}/<id>/item.md` は要件・受け入れ条件の正本
- `thread.md` は計画・判断・レビュー・実装報告の時系列 thread
- `resolution.md` は close 時の完了記録
- 時系列と状態遷移の最終根拠は git history
- `docs/report/` は観測・所感・改善候補の記録であり、最新仕様の authority ではない
- `.insomnia/memory` は個人/生成 state であり、project record の正本ではない
人間は設計上の最終責任者である。
AI maintainer は project record を勝手に膨らませない。明確な実装単位は work item 化し、小粒な所見は `KNOWN_ISSUES.md`、ドッグフーディング上の障壁やツール問題は `docs/report/` に記録する。
## Role model
### Maintainer Pod
人間と対話する親 Pod。設計判断、作業選定、scope 委譲、レビュー統括、merge / close の判断を担う。
主な責務:
- プロダクト方針、設計方針、優先順位の決定
- AI が提示した選択肢の採否
- scope / permission / history persistence など安全モデルに関わる判断
- push や外部公開に関わる判断
- AI maintainer の運用方針そのものの変更承認
- `work-items/`, `KNOWN_ISSUES.md`, `docs/`, git history, worktree 状態を読んで現在地を把握する
- 作業可能な work item と設計相談が必要な work item を分ける
- child worktree / child Pod に実装を委譲する
- 実装結果を work item の背景・要件・受け入れ条件に照らして review する
- review / close / merge の branch placement を守る
- 人間に判断が必要な境界で止まる
### AI maintainer / orchestrator
親 Pod が担う役割。コードを書くこともできるが、主な責務は制御面である。
主な責務:
- 状態把握: `TODO.md`, `tickets/`, `docs/`, git history, worktree 状態を読む
- 作業選定: 実装可能な ticket と設計相談が必要な ticket を分ける
// ↑ について、設計相談も含めてOrchestrator-Coder間で行わせて良いのでは
- 計画: 作業範囲、検証方法、child worktree / Pod scope を決める
- 委譲: 実装 Pod / reviewer Pod を spawn し、必要最小 scope を渡す
- 監督: Pod 出力、worktree diff、test 結果を確認する
- レビュー: ticket の背景・要件・完了条件に対して実装を確認する
- lifecycle: review artifact、ticket 完了、TODO 更新、merge の branch placement を守る
- 報告: 完了内容、検証結果、未解決事項、次の人間判断をまとめる
Maintainer Pod は「便利だから」project record を書き換えない。新規 work item 作成、既存 work item の大幅変更、close、merge、commit などは、ユーザー指示または会話上の合意に基づいて行う。
### Implementation Pod
狭い write scope を持つ作業者である。実装 Pod は ticket と worktree を渡され、その範囲で実装・検証・報告を行う。
禁止事項:
- `/auto-maintain` など上位 Workflow を実行しない
- ticket / TODO / review artifact / docs/report を勝手に編集しない
- git commit / merge / push をしない
- scope / permission / history persistence を勝手に再設計しない
### Reviewer Pod
原則 read-only の検証者である。実装 Pod とは分けるのが望ましいが、小さい作業や scope 衝突がある場合は親 Pod が review してよい。
主な確認項目:
- ticket の要件を満たしているか
- 既存設計を歪めていないか
- 不要な抽象化や過剰実装がないか
- test / build の結果が妥当か
- ticket lifecycle 上、どの branch に review / completion commit を置くべきか
## 状態と成果物
AI maintainer は、状態を会話内だけに閉じ込めない。継続的な運用に必要な情報は、用途ごとに既存のファイルへ残す。
### `TODO.md`
未完了 ticket の一覧。1 ticket = 1 行。作業選定の入口であり、完了したら feature branch 側で該当行を削除する。
AI maintainer は TODO と ticket の不整合を見つけた場合、勝手に ticket を作成・削除せず、人間に報告する。
### `tickets/`
実装可能な要件単位。作業の完了条件は ticket が基準になる。
AI maintainer は ticket を読むだけでなく、git history 上の作成・更新・削除も参照して前提を把握する。
ただし、`tickets/` は長い設計相談、実装 Pod の報告、review 指摘、修正依頼、lease、artifact を thread として扱うには弱い。長期的には WorkItem / Thread 抽象を導入し、ticket は WorkItem の linked artifact または backend view として扱う。
### WorkItem / Thread
AI maintainer の上位運用単位。`tickets/` より広く、design / feature / refactor / ops / investigation を含む project coordination record として扱う。
WorkItem は Git に依存しない domain model として定義し、初期 backend は repo-managed な `work-items/` または `issues/` directory にできるようにする。正本として残すのは description、acceptance criteria、discussion thread、decision、review、status history、linked branch / worktree / commit、durable artifact metadata である。
`.insomnia` に WorkItem 正本は置かない。`.insomnia/maintainer/` は Pod run、lease cache、polling cursor、temporary inbox、local-only trial log など runtime coordination state の置き場とする。
将来 network 越し workspace / remote maintainer hub を導入する場合も、AI maintainer は file path ではなく `WorkItemStore` / `LeaseStore` 相当の interface を通す。remote backend は後回しにするが、初期 file backend の時点で Git 前提の API に固定しない。
### `tickets/*.review.md`
review の判断根拠。review artifact は対象 feature branch 側に commit する。`develop` の first-parent に review / completion commit を直接置かない。
### `docs/plan/`
長期設計、概念設計、運用設計を置く。実装 ticket より上位の合意形成に使う。
本ファイルは `/auto-maintain` 単体ではなく、AI maintainer 全体の設計を置く場所である。
### `docs/report/`
実際の運用で発生した障壁や改善案を残す。明確な力不足、tool 問題、workflow 問題、ユーザーからの指示があった場合に作る。
report は愚痴ではなく、後から改善 ticket / Workflow 改訂 / Knowledge 化へつなげる観測記録である。
### `.insomnia/workflow/`
実行可能な手順。Workflow は人間が書く、または AI が提案して人間が承認する。AI が勝手に自律生成しない。
### memory / Knowledge
作業中の判断や設計を再利用可能な知識として扱う。ただし、turn を跨ぐ根拠を history に残さず context だけへ差し込むことは禁止する。
## 権限モード
AI maintainer の運用は、作業ごとに権限モードを明示する。
### Mode 0: Consultation
設計相談・方針整理のみ。ファイル編集しない。
使う場面:
- 要件が曖昧
- 複数の設計方針が自然に導ける
- ticket 化する前に合意したい
### Mode 1: Documentation / ticket maintenance
`docs/`, `tickets/`, `TODO.md` など制御面だけを編集する。実装コードは触らない。
使う場面:
- 設計文書作成
- ticket の詳細化
- report 作成
- TODO と ticket の整合確認
注意点:
- 新 ticket 作成や既存 ticket の大幅変更は、人間の合意後に行う
- 完了削除は対象 feature branch 側で行う
### Mode 2: Delegated implementation
child worktree を作り、実装 Pod に狭い write scope を渡して実装させる。親 Pod は orchestration と review を行う。
使う場面:
- 要件と完了条件が明確
- 影響範囲が限定されている
- test / build で確認可能
### Mode 3: Maintainer execution with explicit git authority
人間が明示的に許可した場合に、親 Pod が commit / merge / ticket 完了削除まで行う。
使う場面:
- 「レビューして問題なければマージして」など、明示的な実行指示がある
- 実装 branch / review / ticket lifecycle の配置が明確
狭い write scope を持つ作業者。対象 worktree と work item を渡され、その範囲で実装・検証・報告を行う。
制約:
- push は行わない
- unrelated dirty changes を commit に混ぜない
- review / completion commit は feature branch 側に置く
- `develop` first-parent には merge commit だけが載る形を目標にす
- 指定 scope 外を編集しない
- `.insomnia` や main workspace の control-plane record を勝手に編集しない
- work item / review / close は maintainer の責務として扱う
- 実装報告には変更点、検証、未解決点を含める
## 基本 loop
### Reviewer Pod
AI maintainer の基本 loop は以下である。
原則 read-only。実装 diff と対象 work item を読み、妥当性を確認する。
1. 状態把握
- `git status --short --branch`
- `TODO.md`
- 対象 ticket
- 関連 docs / report
- 既存 review artifact
見るべき点:
2. 分類
- 実装可能
- 設計相談が必要
- ticket 整理が必要
- 運用上の問題として report すべき
- work item の要件・受け入れ条件を満たしているか
- 設計を歪めていないか
- 不必要な後方互換性や局所最適な変更を作っていないか
- テストが適切か
- ドキュメント / work item / known issue の更新が必要か
3. 方針提示または実行
- 設計判断が必要なら、人間に質問する
- 実装可能なら、worktree / Pod / scope / test 方針を決める
## Workflow modes
4. 実装
- 原則 child worktree
- 実装 Pod に任せるか、親 Pod が直接行うかを選ぶ
- `.insomnia` は child worktree から除外する
### Mode 0: Design consultation
5. 検証
- focused test
- 必要なら broader test
- `git diff --check`
- `cargo fmt --check` は既存 unrelated 差分を区別して扱う
設計や方針を相談するだけの mode。コードや work item は編集しない。
6. Review
- ticket の背景・要件・完了条件に照らす
- 過剰実装や設計歪みを見る
- 必要なら修正依頼
使う場面:
7. Lifecycle
- implementation commit
- review commit
- ticket / review / TODO completion commit
- develop への merge
- first-parent 確認
- 要件がまだ曖昧
- work item 化する前に合意したい
- 複数案の設計判断が必要
8. 報告
- 実装概要
- 変更ファイル
- test 結果
- review 判断
- 未解決事項
- 残った dirty changes
### Mode 1: Project record maintenance
## エスカレーション基準
`work-items/`, `KNOWN_ISSUES.md`, `docs/`, `docs/report/` など control-plane record だけを編集する。実装コードは触らない。
以下では AI maintainer は実装を止め、人間に戻す。
例:
- ticket の要件から複数の設計方針が自然に導ける
- 長期構造、crate boundary、protocol、permission、scope、history persistence に触れる
- prompt context 加工原則に関わる
- 新 ticket の作成、既存 ticket の大幅変更、ticket 完了削除について合意がない
- git commit / merge / push などの書き込み権限が明示されていない
- test 不能、再現不能、または作業範囲外の不具合に遭遇した
- child worktree に `.insomnia` が出てしまった
- SpawnedPod の完了状態が不明で、worktree 状態からも安全に判断できない
- work item の作成・詳細化
- review / implementation report / resolution の追記
- stale docs の修正
- known issue の追加・削除
## Orchestration の制約
新規 work item 作成や大幅変更は、人間の合意後に行う。
### SpawnedPod の完了は自動検知しない
### Mode 2: Implementation orchestration
現状、親 Pod は SpawnedPod の完了を push 通知として自動検知しない。完了確認は以下で行う
Maintainer Pod が child worktree と implementation Pod を作り、実装と review を回す。親は scoped delegation と project record authority を保持する。
- `ReadPodOutput` による polling
- Pod が出した完了報告
- `stopped` 状態
- worktree の `git status` / `git diff`
- build / test 結果
基本手順:
そのため、AI maintainer は「Pod が何も言わないが実は完了している」ケースと「Pod が止まっているが失敗した」ケースを区別するため、Pod 出力だけでなく worktree 状態を確認する。
1. 対象 work item を main workspace で作成・詳細化し commit する
2. orchestrator が child worktree を作る
3. implementation Pod に対象 worktree と task を渡す
4. implementation Pod が実装・検証・報告する
5. reviewer Pod が diff と work item を読む
6. 必要なら修正を戻す
7. maintainer が merge-ready dossier を作る
### Scope は所有権として扱う
### Mode 3: Maintainer-managed completion
実装 Pod に write scope を渡している間、親 Pod は同じ path を編集しない。review artifact や ticket lifecycle を書く前に、必要なら `StopPod` して scope を回収する。
人間が明示的に許可した場合に、親 Pod が commit / merge / close まで行う。
### main workspace は制御面
条件:
main workspace は orchestration / docs / ticket lifecycle / merge のための場所である。実装差分は原則 child worktree に隔離する。
- 対象 work item が明確
- scope と worktree が分離されている
- review 結果が approve または残件が明示されている
- validation が通っている
- close / merge してよいというユーザー指示または会話上の合意がある
## Git / ticket lifecycle
push はしない。破壊的 git 操作は明示指示なしに行わない。
feature branch の ticket lifecycle は以下の形を正とする。
## Work item lifecycle
```text
* merge: <topic> # develop
|\
| * docs(tickets): complete <topic>
| * review: <topic>
| * feat/fix/refactor: <topic> # feature branch
|/
* previous develop
### 作成
```
./tickets.sh create --title "..." [--slug slug] [--kind task] [--priority P2] [--label a,b]
```
重要な検査:
作成後、必要なら `item.md` を詳細化し、背景・要件・受け入れ条件を明確にする。worktree を使う場合は、対象 work item を作成・詳細化して commit してから branch/worktree を切る。
- review / completion commit は feature branch 側にある
- `develop` first-parent に review / completion commit が直接載っていない
- unrelated dirty changes が staged / committed されていない
- ticket / review / TODO cleanup は merge 前に feature branch 側で完了している
### コメント / 計画 / 判断
## `/auto-maintain` との関係
```
./tickets.sh comment <id-or-slug> --role plan --file path
./tickets.sh comment <id-or-slug> --role decision --file path
./tickets.sh comment <id-or-slug> --role implementation_report --file path
```
`/auto-maintain` は、この設計の Mode 2 を安全側に制限した Workflow である。
thread は後から読まれる project record なので、実装ログをだらだら貼るより、判断理由・検証結果・残件を簡潔に残す。
現在の `/auto-maintain` は以下に留める。
### レビュー
- TODO / tickets を読む
- 将来的には WorkItemStore を入口にして、ticket は linked artifact または backend view として扱う
- 低リスク ticket を選ぶ
- worktree / implementation Pod / reviewer を orchestration する
- 完了候補を報告する
- 原則として commit / merge / ticket 完了削除は人間に戻す
```
./tickets.sh review <id-or-slug> --approve --file path
./tickets.sh review <id-or-slug> --request-changes --file path
```
一方、この文書が扱う AI maintainer は、ユーザーが明示的に許可した場合に Mode 3 として commit / merge / ticket lifecycle まで実行できる。ただし push はしない
レビューは diff の確認だけではない。work item の前提・要件・受け入れ条件が実装で満たされているか、コードベースを歪めていないかを確認する。
## 今後必要な機能
### 完了
この設計を安定運用するには、以下が必要になる。
```
./tickets.sh close <id-or-slug> --resolution "..."
```
### WorkItemStore / LeaseStore
close は `work-items/closed/` へ移動し、`resolution.md` と thread entry を残す。close commit には resolution と関連 project record 更新を含める。
`tickets/` file を直接読むだけでなく、AI maintainer が WorkItem / Thread / Event / Artifact を扱うための store interface。初期実装は repo-managed file backend でよいが、network 越し workspace / remote maintainer hub へ差し替えられるよう、Git 操作や path layout を上位 Workflow に漏らさない。
## Branch / worktree placement
Lease は thread の正本とは分け、`.insomnia/maintainer/` または local DB に置く runtime coordination state として扱う。remote hub を導入する場合は LeaseStore だけを先に集権化できる設計にする。
main workspace は orchestration / docs / work item lifecycle / merge の control plane。実装差分は原則 child worktree に隔離する。
```
main/develop workspace:
work item 作成・詳細化 commit
child worktree 作成
child worktree feature branch:
implementation commit(s)
validation
main/develop workspace:
reviewer Pod に read-only 依頼
merge / close / cleanup commit
```
child Pod に write scope を渡している間、親 Pod は同じ path を編集しない。必要なら `StopPod` で scope を回収してから project record を更新する。
## Human approval boundaries
AI maintainer は以下で人間に戻す。
- 要件から複数の設計方針が自然に導ける
- public API / protocol / manifest / durable data format を変える
- 大規模 rename / directory 移動 / repository-wide format を伴う
- work item の作成・大幅変更・close について合意がない
- reviewer が request-changes しており、修正方針が自明でない
- validation が失敗し、原因が対象変更か既存問題か不明
- destructive git operation や push が必要
## Reports / Knowledge / Memory
- `docs/report/`: ドッグフーディングで感じた障壁、改善案、ツール問題の記録。明確な作業単位になったら work item 化する
- `.insomnia/knowledge`: curated project knowledge。正本ではなく補助 context
- `.insomnia/memory`: generated/personal state。project record の代替にしない
- `KNOWN_ISSUES.md`: ticket 化するほどではないが、次に近所を触る時に拾いたい小粒所見
## Future extension points
### WorkItemStore API
現在は `tickets.sh` + repo-managed `work-items/` を正本とする。将来、remote maintainer hub や issue tracker へ差し替えるなら、WorkItem / Thread / Event / Artifact を扱う store interface を追加し、Git/path layout を上位 workflow に漏らさない形にする。
### Maintainer doctor
merge 前 / ticket 完了前に以下を検査するコマンド。
merge 前 / close 前に以下を検査する command
- current branch
- feature branch と develop の merge base
- review / completion commit の branch placement
- unrelated staged changes
- ticket / TODO の整合
- child worktree に `.insomnia` が含まれていないこと
- 対象 work item が存在する
- acceptance criteria と implementation report が対応している
- review status が明示されている
- validation log がある
- work item lifecycle と branch placement が矛盾していない
### Pod completion tracking
### Multi-maintainer coordination
SpawnedPod の状態を polling ではなく、親 Pod が扱いやすいイベントとして観測できる仕組み。
必要な情報:
- running / completed / failed / stopped
- 最終 assistant output
- 最終 tool result
- worktree path
- delegated scope
### Operation inbox / trial log
maintainer が見つけた改善候補、試走結果、未整理の気づきを一時的に集める場所。恒久的な ticket にする前の buffer として使う。
### Profile / role manifest
Orchestrator / Coder / Reviewer / Researcher で model・prompt・scope 既定値を分けるための manifest profile。人間が用途ごとに起動時設定を切り替えられるようにする。
### Workflow quality evaluation
Workflow 本文が実際に subagent に渡された時、どこで裁量補完が発生し、どこが曖昧だったかを構造化して report する仕組み。
## 非目標
当面やらないこと:
- 常駐 scheduler による unattended 開発
- AI による push
- AI による無承認の ticket 新規作成 / 大幅変更
- AI による Workflow 自律生成
- scope owner handoff の暗黙化
- history に残らない情報を context へ差し込む運用
## 現時点の方針
AI maintainer は「コードを書く agent」ではなく、「プロジェクト状態を読み、必要な作業単位へ分解し、適切な worker に委譲し、結果を検証し、履歴に残す agent」として扱う。
`/auto-maintain` はその最小実行単位であり、今後はこの文書を上位設計として、Workflow 本文・doctor・profile・Pod completion tracking を段階的に足していく。
複数 maintainer Pod が同じ repository を扱う場合は、work item / branch / scope の lease が必要。現状は人間と git history が最終調停者。

View File

@ -1,235 +1,28 @@
# 永続化データ構造の設計・実装プラン
# LLM 履歴永続化 plansuperseded
## Context
> Status: superseded / historical note.
>
> この文書は、`session-store` / segment log / Pod metadata 分離が入る前の検討メモ。現在の実装仕様として読まないこと。現行仕様は `docs/architecture.md` の「セッション永続化」と `crates/session-store` / `crates/pod-store` / `crates/pod` を正とする。
INSOMNIA の `llm-worker` クレートは現在すべてのセッション状態をインメモリで保持しており、
プロセス終了時に会話履歴やターン状態が失われる。
Coding Agent として Pause/Resume・Fork をまたいだセッション継続を実現するため、
永続化レイヤーを追加する。
## 現在の要点
**設計方針**: Codex CLI / Claude Code と同様の **JSONL append-only ログ** 方式を採用。
セッションログを replay することで Worker 状態を完全に復元する。
Pause/正常終了で永続化データの構造に差異を設けない。
理由: Worker の状態は Pause 時も正常終了時も同じ形(`history: Vec<Item>` + `turn_count` + `request_config`)であり、
APIレスポンスのデータ構造上、両者に本質的な違いがない。
`resume()` は「ユーザー入力を追加せず `run_turn_loop()` に再入する」だけなので、
復元に必要なのは history の中身であり、前回の終了理由ではない。
`RunOutcome``Finished`/`Paused` 区分は監査・デバッグ用メタデータであり、replay ロジックの分岐には使わない。
- 会話・worker replay の authority は `session-store` の append-only JSONL segment log。
- fresh conversation は新しい `SessionId` を作る。
- compact / fork / rewind は同じ `SessionId` 配下の `SegmentId` を切り替える。
- segment 出自は `LogEntry::SegmentStart` 内の `SegmentOrigin``compacted_from` / `forked_from`)で表す。
- Pod 名からの current state は `pod-store` metadata が持つ。
- active `(SessionId, SegmentId)` pointer
- `resolved_manifest_snapshot`
- spawned child delegation / reclaim state
- runtime socket path / registry mirror は live/derived state であり、conversation history や Pod-name current state の durable authority ではない。
**命名規約**:
- **SessionLog / LogEntry** — 状態復元用の構造化された記録(永続化の本体)
- **EventTrace / TraceEntry** — デバッグ用の生ストリームイベント全録(オプション)
## Superseded な旧前提
## クレート構成
この旧 plan は以下の前提を含んでいたため、現在の仕様とは一致しない。
永続化は `llm-worker` とは別クレートに分離する。
`llm-worker` は永続化を一切知らず、Session ラッパーが外から Worker を包む。
- session を単一ファイル・単一系列として扱う前提。
- compact/fork で新 SessionId を作る前提。
- entry hash を replay lineage の主参照にする前提。
- Pod 名 current state と conversation log authority を分離しない前提。
```
crates/
llm-worker/ ← 既存LLM クライアント + Worker、変更なし以外は最小限
llm-worker-macros/ ← 既存(変更なし)
llm-worker-persistence/ ← 新規クレート
Cargo.toml
src/
lib.rs -- モジュールルート・re-exports・SessionId 型エイリアス
session_log.rs -- LogEntry enumJSONL 1行 = 1エントリ
event_trace.rs -- TraceEntryデバッグ用生イベント記録
store.rs -- Store traitバックエンド抽象
fs_store.rs -- JSONL ファイルシステム実装
session.rs -- Session<C, St> ラッパー + replay/restore
insomnia/ ← 将来のトップレベルアプリ
docs/persistence.md -- 設計ドキュメント(このプランの清書版)
```
依存グラフ:
```
llm-worker-persistence → llm-worker → llm-worker-macros
insomnia (将来) → llm-worker-persistence, llm-worker
```
### llm-worker-persistence/Cargo.toml 依存
```toml
[dependencies]
llm-worker = { path = "../llm-worker" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["fs", "io-util"] }
uuid = { version = "1", features = ["v7", "serde"] }
thiserror = "2"
```
## 既存コードへの変更llm-worker 側)
### 1. `RequestConfig` に Serialize/Deserialize 追加
- **ファイル**: `crates/llm-worker/src/llm_client/types.rs:504`
- `#[derive(Debug, Clone, Default)]``#[derive(Debug, Clone, Default, Serialize, Deserialize)]`
### 2. Worker に復元用セッター追加
- **ファイル**: `crates/llm-worker/src/worker.rs`
- `impl<C: LlmClient> Worker<C, Mutable>` ブロックに追加:
- `pub fn set_turn_count(&mut self, count: usize)`
- `pub fn set_last_run_interrupted(&mut self, interrupted: bool)`
### 3. ワークスペース Cargo.toml にメンバー追加
- **ファイル**: `Cargo.toml`(ワークスペースルート)
- `members``"crates/llm-worker-persistence"` を追加
## 新規コード: データ型
### LogEntrysession_log.rs
状態復元に必要な構造化記録。JSONL 1行 = 1エントリ。
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum LogEntry {
// セッション開始ログ先頭、fork 時は history にシード状態を含む)
SessionStart {
ts: u64,
system_prompt: Option<String>,
config: RequestConfig,
history: Vec<Item>,
},
// ユーザー入力worker.rs:229 に対応)
UserInput { ts: u64, item: Item },
// アシスタント応答worker.rs:1040-1041 に対応)
AssistantItem { ts: u64, item: Item },
// ツール実行結果worker.rs:897-900, 1072-1076 に対応)
ToolResult { ts: u64, item: Item },
// typed system injection
SystemItem { ts: u64, item: SystemItem },
// ターン境界
TurnEnd { ts: u64, turn_count: usize },
// KV キャッシュロック/アンロック
CacheLocked { ts: u64, locked_prefix_len: usize },
CacheUnlocked { ts: u64 },
// run/resume の終了結果
RunOutcome { ts: u64, outcome: Outcome, interrupted: bool },
// RequestConfig 変更
ConfigChanged { ts: u64, config: RequestConfig },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Outcome { Finished, Paused, Error { message: String } }
```
**Replay ロジック**: 全エントリ種別を走査し、`AssistantItem` / `ToolResult` / `SystemItem` / `UserInput` → history に append、
`TurnEnd` → turn_count 更新、`CacheLocked` → locked_prefix_len 設定。
### TraceEntryevent_trace.rs
デバッグ用の生ストリームイベント全録。デフォルト OFF。
セッションログとは別ファイル `{session_id}.trace.jsonl` に記録。
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraceEntry {
pub ts: u64,
pub turn: usize,
pub event: Event,
}
```
replay 対象外。状態復元には使わない。
### SessionId
`uuid` クレートの UUID v7 をそのまま使用。型エイリアスのみ。
```rust
pub type SessionId = uuid::Uuid;
pub fn new_session_id() -> SessionId {
uuid::Uuid::now_v7()
}
```
UUID v7 はタイムスタンプ埋め込みで辞書順 = 時系列順。独自フォーマット不要。
### Store traitstore.rs
```rust
pub trait Store: Send + Sync {
fn append(&self, id: SessionId, entry: &LogEntry) -> impl Future<Output = Result<(), StoreError>> + Send;
fn read_all(&self, id: SessionId) -> impl Future<Output = Result<Vec<LogEntry>, StoreError>> + Send;
fn list_sessions(&self) -> impl Future<Output = Result<Vec<SessionId>, StoreError>> + Send;
fn create_session(&self, id: SessionId, entries: &[LogEntry]) -> impl Future<Output = Result<(), StoreError>> + Send;
fn exists(&self, id: SessionId) -> impl Future<Output = Result<bool, StoreError>> + Send;
// EventTrace 用(デバッグモード時のみ使用)
fn append_trace(&self, id: SessionId, entry: &TraceEntry) -> impl Future<Output = Result<(), StoreError>> + Send;
}
```
RPITIT (Rust 1.75+) 使用。`async_trait` 不要。
### FsStorefs_store.rs
ファイル配置:
- セッションログ: `{root}/{session_id}.jsonl`
- イベントトレース: `{root}/{session_id}.trace.jsonl`
append モードで書き込み。SQLite インデックスなし。
### Session ラッパーsession.rs
Worker を直接変更せず、**外部ラッパー** として実装:
```rust
pub struct Session<C: LlmClient, St: Store> {
pub worker: Worker<C, Mutable>, // pub で直接アクセス可能
store: St,
session_id: SessionId,
config: SessionConfig,
}
```
- `Session::new()` → SessionStart を append
- `Session::run()` → Worker::run() の前後で history.len() を比較、差分をログ記録
- `Session::resume()` → 同上
- `Session::fork()` → 現在の history をシードにした新 SessionStart を書き込み
- `Session::fork_at(store, source_id, entry_idx)` → 任意地点から分岐
**復元**: `restore_session(client, store, session_id)` → read_all → replay_entries → Worker 再構築
### EventTrace 記録(デバッグモード)
`SessionConfig::record_event_trace: bool`(デフォルト `false`)が `true` の場合、
Session が Worker に `OnStreamChunk` Hook を登録。
Hook 内で `TraceEntry``{session_id}.trace.jsonl` に append。
セッションログとは完全に分離。
## 実装順序
1. `RequestConfig` に Serialize/Deserialize 追加llm-worker 側)
2. Worker に `set_turn_count` / `set_last_run_interrupted` 追加llm-worker 側)
3. `crates/llm-worker-persistence/` クレート作成Cargo.toml + ワークスペース登録)
4. `session_log.rs` 作成LogEntry + Outcome + replay_entries
5. `event_trace.rs` 作成TraceEntry
6. `store.rs` 作成Store trait + StoreError
7. `fs_store.rs` 作成JSONL ファイルシステム実装)
8. `session.rs` 作成Session ラッパー + restore_session
9. `lib.rs` 作成re-exports・SessionId 型エイリアス・new_session_id
10. テスト作成replay round-trip, FsStore 読み書き, Session::run ログ記録)
11. `docs/persistence.md` 設計ドキュメント作成
## 検証方法
1. **ユニットテスト**: `replay_entries` に手動構築した LogEntry 列を渡し、復元状態を検証
2. **統合テスト**: MockLlmClient + FsStore で Session::run → restore_session → history 一致を確認
3. **Fork テスト**: fork → 新セッションの history が fork 時点と一致することを確認
4. **cargo test**: 既存テストが壊れていないことを確認
5. **cargo clippy / cargo check**: 警告なし
履歴としては有用だが、実装時の根拠には使わない。新しい変更を検討する場合は、まず current code と work item を読むこと。

View File

@ -1,76 +1,33 @@
# AI maintainer 用 WorkItem / Thread 抽象メモ
## 位置づけ
> Status: mostly implemented / historical design memo.
>
> この文書は `tickets.sh` + `work-items/` MVP の前に書かれた設計メモ。現在の運用上の正本は `AGENTS.md` の Work item / Ticket 運用と、実際の `tickets.sh` / `work-items/{open,pending,closed}`。古い `TODO.md` / `tickets/` 前提は superseded。
AI maintainer が単なる coding agent ではなく、作業の発見・分解・実装委譲・review・完了判断まで扱うには、現在の `TODO.md` / `tickets/*.md` だけでは足りない。必要なのは、個々の file path ではなく「作業単位」と「その会話・判断・成果物」を扱う抽象である。
## 現在実現済みの部分
このメモは実装 ticket ではなく、将来こういうものが必要になるという設計メモとして置く。具体的な最初の実装は `tickets.sh` / `work-items/` の MVP で試す。
- WorkItem 相当: `work-items/{open,pending,closed}/<id>/item.md`
- Thread 相当: `thread.md`
- Event 相当: `tickets.sh comment/review/status/close` が append する thread entry
- Artifact 相当: `artifacts/` directory
- Resolution 相当: close 時の `resolution.md`
- ID: timestamp + slug 形式
- Doctor: `./tickets.sh doctor`
## 必要になりそうな概念
`work-items/` は repo-managed な project coordination record であり、`.insomnia/memory` はその代替ではない。
- WorkItem
- 作業単位。title / status / kind / priority / labels / acceptance criteria / links を持つ。
- Thread
- WorkItem に紐づく append-only な会話・判断・review・実装報告の流れ。
- Event
- comment / plan / decision / implementation_report / review / status_change など。
- Artifact
- review log、test log、設計メモ、branch / commit / worktree への link など。
- Lease / Run
- どの Pod / agent がどの worktree / scope で作業中かを表す runtime coordination 情報。
## 現在も残る設計余地
## 配置の分担
- Rust crate / Store interface としての WorkItemStore
- LeaseStore / Run tracking / maintainer inbox
- remote maintainer hub / GitHub Issues などへの backend 差し替え
- TUI 統合
repo-managed な project-visible 領域には、作業の正本として人間が読める coordination data を置く。
これらは必要になった時点で改めて work item 化する。
```text
repo/
work-items/ # WorkItem / Thread / Artifact
tickets/ # 当面の既存 ticket。将来 WorkItem view に寄せる候補
docs/ # plan / report
```
## 現在の運用参照
`.insomnia` は local runtime state として扱い、project coordination の正本にはしない。
```text
.insomnia/
memory/
workflow/
maintainer/
leases/
runs/
inbox/
```
## ID と backend の考え方
WorkItem ID は中央 `SEQUENCE` にしない。複数 branch / worktree / Pod が同時に作業を作ると conflict しやすいため、timestamp + slug などの衝突しにくい ID にする。
```text
YYYYMMDD-HHMMSS-<slug>
YYYYMMDD-HHMMSS-<short-rand>-<slug>
```
backend は最初は repo 内 file backend でよいが、抽象としては Git directory 固定にしない。将来、remote maintainer hub や GitHub Issues などへ移る可能性を潰さない。
## 移行イメージ
1. 既存の `TODO.md` / `tickets/` 運用は維持する。
2. `tickets.sh` MVP で `work-items/` の file backend と thread 操作を試す。
3. 既存 `TODO.md` / `tickets/` を手動で `work-items/` に寄せ、`doctor` が通る状態にする。
4. その運用が安定したら、`TODO.md` を generated view にするか、廃止するかを判断する。
5. 必要になった段階で Rust crate / Store interface / LeaseStore / remote backend を検討する。
## 当面やらないこと
- remote maintainer hub の実装。
- SQLite / index / search daemon の導入。
- Pod lifecycle / completion tracking の完全実装。
- LeaseStore の本格実装。
- TUI 統合。
## 参照
- `tickets/tickets-sh-workitem-thread-mvp.md`
- `docs/plan/ai-maintainer.md`
- `tickets/auto-maintain-workflow.md`
- `AGENTS.md` — Work item / Ticket の運用ルール
- `tickets.sh` — 実際の操作コマンド
- `work-items/` — 現在の project-visible coordination data
- `docs/plan/ai-maintainer.md` — AI maintainer workflow の現行設計

View File

@ -46,7 +46,7 @@ Available tools: {{ tools | join(", ") }}
### タイミング
1. `Pod::from_manifest` 時点: テンプレート文字列を `SystemPromptTemplate::parse`**構文検査のみ** 行い、`Pod.system_prompt_template: Option<SystemPromptTemplate>` に保持する。この時点で Worker 側の system_prompt は `None`、session log もまだ書かれない (`head_hash: None`)
1. `Pod::from_manifest` 時点: テンプレート文字列を `SystemPromptTemplate::parse`**構文検査のみ** 行い、`Pod.system_prompt_template: Option<SystemPromptTemplate>` に保持する。この時点で Worker 側の system_prompt は `None`、segment log の head もまだ作られない
2. `Pod::run` / `Pod::resume` 初回呼び出し冒頭 (`ensure_system_prompt_materialized`):
1. `worker.tool_server_handle().flush_pending()` で pending な tool factory を materialize して tool 名を確定させる
@ -54,12 +54,12 @@ Available tools: {{ tools | join(", ") }}
3. `template.render(ctx)` で文字列化して `worker.set_system_prompt(rendered)` を呼ぶ
4. `Pod.system_prompt_template = None` (`Option::take()` で構造的に1回性を保証)
3. その直後の `ensure_session_head` が `head_hash` を見て初回なら `create_session_with_id` を呼び、materialize 後の system_prompt を **SessionStart ログエントリに焼き付ける**
3. その直後の `ensure_segment_head` が現在 segment の entry count を見て初回なら `SegmentStart` を append し、materialize 後の system_prompt を segment log に焼き付ける
### 1回性の保証
- `Pod.system_prompt_template``Option<SystemPromptTemplate>` で、materialize 時に `take()` する。2 ターン目以降はフィールドが `None` なので `ensure_system_prompt_materialized` は早期 return し、再 render は発生しない。
- compact は Worker の system_prompt フィールドを触らない (pod.rs の `compact``w.get_system_prompt()` を読み取って新セッションに引き継ぐだけ)。そのため compact 前後で同じ文字列が流れ続ける。
- compact は Worker の system_prompt フィールドを触らない (pod.rs の `compact``w.get_system_prompt()` を読み取って新 segment に引き継ぐだけ)。そのため compact 前後で同じ文字列が流れ続ける。
- 統合テスト `compact_preserves_system_prompt` が実装で直接検証している。
### 責務分離
@ -79,7 +79,7 @@ Available tools: {{ tools | join(", ") }}
## 関連ファイル
- `crates/pod/src/system_prompt.rs``SystemPromptTemplate` / `SystemPromptContext` / `SystemPromptError`
- `crates/pod/src/pod.rs``Pod.system_prompt_template` フィールド、`ensure_system_prompt_materialized`、`ensure_session_head` の初回 append ロジック
- `crates/pod/src/pod.rs``Pod.system_prompt_template` フィールド、`ensure_system_prompt_materialized`、`ensure_segment_head` の初回 append ロジック
- `crates/manifest/src/scope.rs``Scope::summary` / `readable_paths` / `writable_paths`
- `crates/session-store/src/session.rs` — `create_session_with_id` (事前生成 ID で SessionStart を書くための entry point)
- `crates/session-store/src/segment_log.rs` — `LogEntry::SegmentStart` / segment replay entries
- `crates/llm-worker/src/tool_server.rs``ToolServerHandle::flush_pending` (pub)

View File

@ -1,6 +1,6 @@
# TUI キーバインド
`crates/tui` の対話画面で効くキー一覧。`main.rs:handle_key` を単一情報源とし、このドキュメントは人間向けの目次。
`crates/tui` の対話画面で効くキー一覧。`crates/tui/src/main.rs:handle_key` を単一情報源とし、このドキュメントは人間向けの目次。
## 入力編集
@ -11,9 +11,10 @@
| `Backspace` | カーソル直前を削除(ペーストプレースホルダは 1 回で全削除) |
| `Delete` | カーソル直後を削除(同上) |
| `Left` / `Right` | カーソル移動 |
| `Up` / `Down` | 論理行単位の上下移動(桁位置を保持) |
| `Up` / `Down` | 通常は論理行単位の上下移動。入力欄の先頭/末尾では送信済み composer history を browse |
| `Home` / `End` | 現在行の行頭 / 行末へ |
| `Alt-Enter` | 改行を挿入Input を複数行化) |
| `Esc` | completion popup があれば閉じる |
### ペーストプレースホルダ
@ -23,13 +24,34 @@
カーソルはプレースホルダ内部には入れず、`Backspace` / `Delete` 1 回で
プレースホルダ全体が消える。`#N` の通し番号は TUI プロセス起動中で連番。
### Completion
`@` file ref / `#` knowledge ref / `/` workflow invocation などは completion popup を開く。
| キー | 動作 |
|---|---|
| `Up` / `Down` | completion 候補を移動 |
| `Tab` | 選択候補をテキストとして適用 |
| `Enter` | committable な候補は chip 化して空白を追加。directory などはテキスト適用して drill-in |
| `Esc` | popup を閉じる |
| 空白文字 | exact match なら chip 化してから空白を挿入 |
## 送信Enter
| 状態 | 入力あり | 入力空 |
|---|---|---|
| Idle | `Method::Run` で新ターン開始 | no-op |
| Paused | `Method::Run`(前ターンは割り込み終了として扱い、新ターンとして開始) | `Method::Resume`(前ターンの続きを再開) |
| Running | 入力は TUI 側でバッファのみ、送信はしない | 同左 |
| Running | 入力は TUI 側 queue に積まれ、現在 turn の終了後に自動送信 | no-op |
### Running 中の queue
Running 中に入力ありで Enter すると、現在の provider stream には割り込まず、typed input segments を TUI-local queue に積む。`RunResult::Finished` / `LimitReached` 後に先頭の queued input が次の `Method::Run` として自動送信される。`Paused` / `RolledBack` では自動送信しない。
| キー | 動作 |
|---|---|
| `Alt-Q` | queue 先頭を composer に戻すcomposer が空の時のみ) |
| `Alt-C` | queued input を全消去 |
### Paused からの挙動
@ -42,16 +64,32 @@ Paused 中に Enter すると、入力の有無で 2 通り:
3. 入力を新しい user メッセージとして append
4. ターン開始
## Command mode
composer が空の通常入力で `:` を押すと command mode に入る。command mode 中は composer が command line として扱われる。
| キー | 動作 |
|---|---|
| `Enter` | command を実行 |
| `Esc` | command mode を終了 |
| `Backspace` | 文字を削除。空なら command mode を終了 |
| `Ctrl-U` | command input をクリア |
| `Tab` | command completion を適用 |
| `Up` / `Down` | command completion 候補があれば移動、なければカーソル上下 |
主な command は `:help`, `:noop`, `:compact`, `:rewind` / `:rollback`。`:compact` は idle 時だけ即時 compaction を要求し、`:rewind` / `:rollback` は rewind target picker を開く。
## 履歴ビューのナビゲーション
履歴ビューは全画面 TUI の上段にあり、TUI 内部で全ブロックを state として
保持してスクロール可能。スクロールバック(端末側の履歴)ではなく TUI の
自前バッファを動かす点に注意。
| キー | 動作 |
| キー / 操作 | 動作 |
|---|---|
| `Shift-Up` / `Shift-Down` | 1 論理行スクロール |
| `PageUp` / `PageDown` | 1 ページスクロール(`area_height - 1` 行) |
| mouse wheel | 数行単位でスクロール |
| `PageUp` / `PageDown` | 1 ページスクロールtask pane が開いている時は task pane をスクロール) |
| `Ctrl-[` / `Ctrl-]` | 前 / 次のターン先頭へジャンプ |
| `Ctrl-Home` | 履歴の先頭へ |
| `Ctrl-End` | 履歴の末尾へ(末尾追従モードを再開) |
@ -69,7 +107,7 @@ Paused 中に Enter すると、入力の有無で 2 通り:
合わせる。多数のツール呼び出しが挟まった長いターンでも前後ターンの先頭に
1 発で戻れる。末尾ターンから `Ctrl-]` を押すと末尾追従に復帰する。
## 表示モード
## 表示モード / 補助 pane
履歴各ブロックの密度を 3 段階で切り替える。現在のモードはステータスバー
右端に `[mode]` として表示される。
@ -77,12 +115,11 @@ Paused 中に Enter すると、入力の有無で 2 通り:
| キー | 動作 |
|---|---|
| `Ctrl-O` | `detail``normal``overview``detail` の順に循環 |
| `Ctrl-T` | task pane を開閉 |
- **detail**: 全ブロック完全表示。ツールブロックは結果本体も全量
- **normal**: 完了ブロックは概ね 56 行に圧縮、実行中のツールブロックは
detail と同じ扱い
- **overview**: 各ブロック 1 行に畳む(ツールブロックは
`ToolResult.summary` 1 行、長文 AssistantText は先頭 1 行 + 省略記)
- **normal**: 完了ブロックは概ね 56 行に圧縮、実行中のツールブロックは detail と同じ扱い
- **overview**: 各ブロック 1 行に畳む(ツールブロックは `ToolResult.summary` 1 行、長文 AssistantText は先頭 1 行 + 省略記)
モード切替は全体に一括適用。個別ブロックの開閉は持たない。
@ -90,9 +127,10 @@ Paused 中に Enter すると、入力の有無で 2 通り:
| キー | Running 中 | Idle / Paused |
|---|---|---|
| `Ctrl-X` | `Method::Cancel`(進行中ターンを破棄 → Idle | `Method::Shutdown`Pod を終了) |
| `Ctrl-X` | queued input を消去して `Method::Cancel`(進行中ターンを破棄 → Idle | `Method::Shutdown`Pod を終了) |
| `Ctrl-C` | `Method::Pause`(進行中ターンを中断 → Paused | 1 回目 warn、3 秒以内の 2 回目で TUI 終了Pod は残る) |
| `Ctrl-D` | TUI 終了Pod は残る、Pause しない) | TUI 終了Pod は残る) |
| `Ctrl-R` | rewind picker 要求は拒否される | rewind target picker を開く |
### Cancel と Pause の違い
@ -101,9 +139,21 @@ Paused 中に Enter すると、入力の有無で 2 通り:
Running 中に割り込みたい場合、ほとんどのケースで `Ctrl-C`Pauseが自然。Ctrl-XCancelは明示的に破棄したい時LLM が暴走した時など用。Pod を終了したい場合は、先に Running ではない状態Idle / Pausedにしてから `Ctrl-X` で Shutdown する。
### Rewind picker
`Ctrl-R` または `:rewind` / `:rollback` で Pod に `Method::ListRewindTargets` を送り、main area に rewind target picker を開く。picker 中の操作は以下。
| キー | 動作 |
|---|---|
| `Up` / `Down` | target を移動 |
| `Enter` | 選択 target へ `Method::RewindTo`。復元された user input は composer に戻る |
| `Esc` | picker を閉じる |
Running 中の rewind request は拒否される。Paused 中は picker を開けるが、実際の apply は idle で composer が空の時だけ行う。
### Ctrl-C と Ctrl-D の終了 UX
- Ctrl-X Running 中: `Method::Cancel`。終了したい場合は、明示的にターンを止めて Idle に戻してからもう一度 `Ctrl-X`
- Ctrl-X Running 中: queued input を消去して `Method::Cancel`。終了したい場合は、明示的にターンを止めて Idle に戻してからもう一度 `Ctrl-X`
- Ctrl-X Idle / Paused: `Method::Shutdown` を送って Pod を終了
- Ctrl-C Running 中: 1 回目で即 Pause破壊的ではない
- Ctrl-C Idle / Paused: 1 回目で warn メッセージ、3 秒以内の 2 回目で TUI 終了Pod は残る)
@ -115,10 +165,7 @@ TUI のダイアログから Pod を起動する経路では、起動した Pod
## 履歴メモ
- かつて存在した `Ctrl-R`Resume 専用)は、空 Enter での Resume に統合されたため廃止
- かつて存在した `Esc`TUI 終了)は、`Ctrl-C` の 2 連打 UX に統合されたため廃止
- `Ctrl-R` はかつて Resume 専用だったが、空 Enter での Resume に統合された。現在の `Ctrl-R` は rewind picker の導線
- かつて存在した `Esc`TUI 終了)は、`Ctrl-C` の 2 連打 UX に統合されたため廃止。現在の `Esc` は popup / picker / command mode のキャンセル用途
- かつて `Ctrl-D` は Pod に `Method::Shutdown` を送っていたが、TUI だけを抜けるデタッチ操作に変更された
- 旧 inline viewport 時代は履歴スクロールを端末側スクロールバックに
任せていたため TUI 内のスクロールキーは存在しなかった。全画面 alt screen
への移行(`tickets/tui-fullscreen-overhaul.md`)で `Shift-Up/Down` ほか
のナビゲーションキーが追加された
- 旧 inline viewport 時代は履歴スクロールを端末側スクロールバックに任せていたため TUI 内のスクロールキーは存在しなかった。全画面 alt screen への移行で `Shift-Up/Down` ほかのナビゲーションキーが追加された

View File

@ -7,7 +7,7 @@ kind: task
priority: P2
labels: [docs, maintenance]
created_at: 2026-05-30T06:28:52Z
updated_at: 2026-05-30T06:29:24Z
updated_at: 2026-05-30T06:39:11Z
assignee: null
legacy_ticket: null
---

View File

@ -19,4 +19,26 @@ Implementation plan:
5. Review the resulting diff against the audit findings before closing.
---
<!-- event: implementation_report author: hare at: 2026-05-30T06:39:11Z -->
## Implementation report
Implementation report:
- Refreshed `docs/architecture.md` for current protocol categories, session-store vs pod-store responsibilities, profile/manifest input, scope delegation, and current built-in tool categories.
- Updated `docs/compaction.md` to describe same-session segment rotation and `SegmentStart.compacted_from` instead of the old new-SessionId model.
- Rewrote `docs/tui-keybindings.md` around current command mode, queued input behavior, completion, task pane, and `Ctrl-R` rewind picker behavior.
- Marked `docs/plan/llm_presistence.md` as superseded and replaced old implementation details with current-session persistence pointers.
- Refreshed `docs/plan/ai-maintainer.md` and `docs/plan/maintainer-work-items.md` so current `tickets.sh` + `work-items/` authority replaces old `TODO.md` / legacy `tickets/` assumptions.
- Updated `docs/system-prompt-template.md` for current segment log naming (`ensure_segment_head`, `SegmentStart`) while fixing nearby stale session terminology.
Validation:
- `./tickets.sh doctor`
- `git diff --check`
- grep sweep for the high-priority stale phrases from the audit; remaining hits are historical reports/ref docs or explicit superseded notes.
---