From 123fc3b0ad6a5a3a425b3d17a11319e44ab6bbbf Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 26 Apr 2026 17:00:38 +0900 Subject: [PATCH] =?UTF-8?q?memory=E5=AE=9F=E8=A3=85=E3=83=81=E3=82=B1?= =?UTF-8?q?=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 12 +- docs/plan/memory.md | 2 +- docs/research/llm-worker-vs-rust-llm-libs.md | 244 +++++++++++++++++++ tickets/memory-file-format.md | 74 ++++++ tickets/memory-gc.md | 61 +++++ tickets/memory-phase1-extract.md | 56 +++++ tickets/memory-phase2-consolidation.md | 80 ++++++ tickets/memory-resident-injection.md | 32 +++ tickets/memory-search-tools.md | 51 ++++ tickets/memory-usage-metrics.md | 60 +++++ tickets/submit-segment-protocol.md | 81 ++++++ tickets/submit-tui-completion.md | 61 +++++ tickets/test-design.md | 11 - 13 files changed, 812 insertions(+), 13 deletions(-) create mode 100644 docs/research/llm-worker-vs-rust-llm-libs.md create mode 100644 tickets/memory-file-format.md create mode 100644 tickets/memory-gc.md create mode 100644 tickets/memory-phase1-extract.md create mode 100644 tickets/memory-phase2-consolidation.md create mode 100644 tickets/memory-resident-injection.md create mode 100644 tickets/memory-search-tools.md create mode 100644 tickets/memory-usage-metrics.md create mode 100644 tickets/submit-segment-protocol.md create mode 100644 tickets/submit-tui-completion.md delete mode 100644 tickets/test-design.md diff --git a/TODO.md b/TODO.md index bbaad04a..9a3ff87f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,3 @@ -- [ ] テスト設計 → [tickets/test-design.md](tickets/test-design.md) - [ ] Agent Skills サポート → [tickets/agent-skills.md](tickets/agent-skills.md) - [ ] ツール設計 - [ ] Bash ツール (Permission 層と統合) → [tickets/bash-tool.md](tickets/bash-tool.md) @@ -9,3 +8,14 @@ - [ ] TUI 拡充 - [ ] フルスクリーン化によるオーバーホール → [tickets/tui-fullscreen-overhaul.md](tickets/tui-fullscreen-overhaul.md) - [ ] 新しい Pod を spawn する UI の設計 → [tickets/tui-pod-spawn-ui.md](tickets/tui-pod-spawn-ui.md) +- [ ] サブミット入力 + - [ ] protocol Segment 化 → [tickets/submit-segment-protocol.md](tickets/submit-segment-protocol.md) + - [ ] TUI 補完 + 型付き atom 化 → [tickets/submit-tui-completion.md](tickets/submit-tui-completion.md) +- [ ] メモリ機構 + - [ ] ファイル形式 + Linter 土台 → [tickets/memory-file-format.md](tickets/memory-file-format.md) + - [ ] memory / Knowledge 検索ツール → [tickets/memory-search-tools.md](tickets/memory-search-tools.md) + - [ ] `model_invokation: ON` の常駐注入 → [tickets/memory-resident-injection.md](tickets/memory-resident-injection.md) + - [ ] Phase 1 活動抽出 → [tickets/memory-phase1-extract.md](tickets/memory-phase1-extract.md) + - [ ] Phase 2 consolidation → [tickets/memory-phase2-consolidation.md](tickets/memory-phase2-consolidation.md) + - [ ] 使用頻度メトリクス + Knowledge 化候補レポート → [tickets/memory-usage-metrics.md](tickets/memory-usage-metrics.md) + - [ ] GC(定期再評価) → [tickets/memory-gc.md](tickets/memory-gc.md) diff --git a/docs/plan/memory.md b/docs/plan/memory.md index 8488830f..34629361 100644 --- a/docs/plan/memory.md +++ b/docs/plan/memory.md @@ -27,7 +27,7 @@ Workflow(`/` で呼び出される制約付き作業フロー)は別 p - 同主題の content 進化 = 上書き update + git log で履歴追跡 - 別主題が古い主題を置き換える場合のみ、別 slug で新規作成し古い方に `status: replaced` + `replaced_by: <新 slug>` を記録 - Phase 1 の中間ストアとして `memory/_staging/.json` を使う(短命、P2 完了で cleanup。ここは衝突回避と順序のため UUIDv7 可) -- Raw session log は既存 `llm-worker-persistence` で保持する。memory 対象外、参照経路のみ +- Raw session log は既存 `session-store` で保持する。memory 対象外、参照経路のみ ### Knowledge の呼び出し制御 diff --git a/docs/research/llm-worker-vs-rust-llm-libs.md b/docs/research/llm-worker-vs-rust-llm-libs.md new file mode 100644 index 00000000..d8828c91 --- /dev/null +++ b/docs/research/llm-worker-vs-rust-llm-libs.md @@ -0,0 +1,244 @@ +# `llm-worker` vs Rust LLM ライブラリ群 比較レポート + +調査日: 2026-04-26 +対象: `crates/llm-worker` (本プロジェクト) / `rig` / `genai` / `swiftide` + +調査方法: 各リポジトリを external checkout で取得し、ソース(README, Cargo.toml, src/, examples/)を直接読解。 + +--- + +## 0. TL;DR + +``` + 低レイヤ ←──────────────────────────────────────→ 高レイヤ + HTTP wrapper Worker/Loop Agent Framework Pipeline/RAG + ───────────── ──────────── ──────────────── ────────────── +genai ●●●●● (out-of-scope) +llm-worker ●●●●● (一部) +rig ●●● ●●●●● ●●● +swiftide ●● ●●●● ●●●●● (主軸) +``` + +- **`genai`** … マルチプロバイダの「統一 chat client」。エージェントループ・状態管理は明示的にスコープ外。 +- **`llm-worker`** … Worker = 「LLM 対話の中央実行器」。低レベル(HTTP)と高レベル(Agent / RAG / Pipeline)の中間に位置するレイヤを精緻に作っている。 +- **`rig`** … Agent / Tool / Pipeline / VectorStore を一通り揃えた汎用フレームワーク。Provider 数とエコシステムが最大の武器。 +- **`swiftide`** … 一次目的はストリーミング Indexing / Query パイプライン。Agent はあとから足された二の矢で、`async-openai` / `async-anthropic` をラップ。 + +要点: **`llm-worker` の独自性は「型状態でのキャッシュ保護」「Interceptor による上位層への決定委譲」「Prune を context projection として非破壊で扱う」の 3 点**。これらは他 3 者の誰も持っていない。一方、Provider カバレッジ・VectorStore・Pipeline DAG では rig に大差で負ける。 + +--- + +## 1. スコープ宣言(各プロジェクトが自分を何と呼んでいるか) + +| プロジェクト | 自称 | 提供する | 提供しない(明示・暗黙) | +|---|---|---|---| +| **llm-worker** | "LLM との対話を管理する低レベル基盤クレート" | Worker による turn 実行、Tool 実行、stream イベント、Interceptor、cache 保護 | RAG / Embedding / VectorStore / Pipeline DAG / 永続化 | +| **rig** | "Ergonomic & modular library for building LLM applications" | Agent / Tool / Pipeline / Vector / Embedding / OTel | (ほぼ全部入り。明示的な non-goal は薄い) | +| **genai** | "Standardizing chat completion APIs across major AI services" | Chat (sync/stream)、Tool 定義、Embedding | **Agent loop / 会話状態管理を明示的に out-of-scope** | +| **swiftide** | "Fast, streaming indexing, query, and agentic LLM applications" / "data pipeline" | Indexing pipeline / Query state machine / Agent / Hook | (LLM HTTP は外部 SDK = `async-openai` 等に委譲) | + +`llm-worker` のスコープ宣言は他より控えめで、「何かの上位層から呼ばれる前提」が読み取れる(`Interceptor` の存在がそれを裏付ける)。 + +--- + +## 2. レイヤ階層と被り + +`Worker` が担っているレイヤを基準にすると、各プロジェクトの「同じ層」と「上下層」は次のように整理できる。 + +### 同じ層(== 競合) + +| 機能 | llm-worker | rig | genai | swiftide | +|---|---|---|---|---| +| Provider 抽象 (HTTP) | ◎ 自前 (4) | ◎ 自前 (20+) | ◎ 自前 (18+) | ○ 外部 SDK ラップ | +| Streaming イベント正規化 | ◎ | ◎ | ◎ | ○ | +| Tool 定義 + 実行ループ | ◎ | ◎ | △ (定義のみ、loop は呼び出し側) | ◎ | +| Multi-turn loop | ◎ | ◎ | ✕ | ◎ | +| Hook / Interceptor | ◎ Interceptor + closure | ◎ PromptHook | △ Resolver のみ | ◎ 6+ Hook | +| 履歴管理 | ◎ Item / ContentPart | ◎ Message | △ ChatRequest を呼び出し側で append | ◎ MessageHistory trait | + +### 上下層(== 補完関係) + +| 機能 | llm-worker | rig | genai | swiftide | +|---|---|---|---|---| +| Embedding / VectorStore | ✕ | ◎ 10+ | △ (Embed のみ) | ◎ 9+ | +| RAG | ✕ | ◎ | ✕ | ◎ | +| Pipeline DAG | ✕ | ◎ `parallel!` / `try_op` | ✕ | ◎ Indexing pipeline | +| 永続化 | ✕ | △ | ✕ | ◎ MessageHistory swap | +| OTel / Observability | △ tracing のみ | ◎ GenAI Semantic Conv | △ | ◎ Langfuse 等 | +| 型状態でのキャッシュ保護 | **◎ 唯一** | ✕ | ✕ | ✕ | + +--- + +## 3. コアの抽象を並べる + +### `llm-worker::Worker` +- `C: LlmClient`(プロバイダ)、`S: WorkerState`(`Mutable` | `Locked` の sealed trait) +- ライフサイクル: `Mutable` で履歴/プロンプト編集 → `lock()` → `Locked` で `run()` / `resume()` → `unlock()` で戻る +- `state.rs:1-61`: `Locked` 中は履歴 append-only、`set_system_prompt` 等は型レベルで呼べない +- `prune.rs:66-118`: `prunable_indices()` + `project()` で context のみ縮約。永続履歴は不変 = "context projection" + +### `rig::Agent` + `PromptRequest` +- 状態機械型: `max_turns`、`extended_details()` で usage / message tracking +- `PromptHook` trait: `on_completion_call`, `on_completion_response`, `on_tool_call` (skip 可), `on_tool_result`, `on_text_delta`, … +- `ToolSet` + `ToolEmbedding` で RAG 可能なツール +- Pipeline DAG: `Op` trait + `parallel!` macro + +### `genai::Client` +- `exec_chat(model, ChatRequest, options) -> ChatResponse` +- `ChatStreamEvent { Start, Chunk, ReasoningChunk, ToolCallChunk, End }` +- Adapter pattern: `to_web_request_data` / `to_chat_response` / `to_chat_stream` +- モデル名の prefix で provider 推論 (`gpt-` → OpenAI, `claude-` → Anthropic) +- 状態管理は呼び出し側(`ChatRequest` を毎ターン clone & append) + +### `swiftide-agents::Agent` +- Builder: `llm`, `context`, `tools`, `toolboxes`, `hooks` +- `AgentContext` trait + `DefaultContext` (AtomicUsize で completions ポインタ管理) +- State enum: `Pending` / `Running` / `Stopped(StopReason)` +- Tool execution: `LocalExecutor` と MCP (Model Context Protocol) +- Provider 層は `async-openai` 0.33+ / `async-anthropic` 0.6 を流用 + +--- + +## 4. 三大独自点(`llm-worker` だけが持っているもの) + +### 4.1 型状態によるキャッシュ保護 — `state::WorkerState` + +``` +Worker Worker + ├─ history_mut() ✓ ├─ history_mut() ✗ (compile error) + ├─ set_system_prompt() ✓ ├─ set_system_prompt() ✗ + ├─ register_tool() ✓ ├─ run() / resume() ✓ + └─ lock() ─────────────────→ │ + └─ unlock() ──────────→ Mutable +``` + +- **何を防ぐか**: KV cache の prefix が変わるような編集を `Locked` 中に行うこと(=次の `run()` で cache miss を引く事故) +- **誰がやっているか**: rig / genai / swiftide のいずれもキャッシュ整合性は「呼び出し側のお気持ち」レベル。genai は `CacheControl::Ephemeral / Stable` enum を持つが、それはマーク用であって状態機械ではない +- **意義**: production の Anthropic / OpenAI prompt cache 利用で、誤った `set_system_prompt` が静かに課金を倍増させる事故を**コンパイル時に潰せる** + +### 4.2 Interceptor による上位層への決定委譲 — `interceptor.rs` + +`Interceptor` trait が持つフック点: +- `on_prompt_submit` → `PromptAction` +- `pre_llm_request` → `PreRequestAction` +- `pre_tool_call` → `PreToolAction` +- `post_tool_call` → `PostToolAction` +- `on_turn_end` → `TurnEndAction` + +**rig の `PromptHook` との違い**: rig のフックは「観察 + skip/abort のフラグ」。`llm-worker` は **戻り値が ActionEnum** で、上位層が次の挙動を選択できる(例: `PreToolAction::Override(custom_result)`)。これは `Worker` を「自律ループ」と「上位 orchestrator から駆動される実行器」の両方として使える設計上の選択。 + +### 4.3 Context Projection による prune — `prune.rs` + +- 削るのは `Item::ToolResult { content }` の中身だけ。`Item` 自体は履歴に残す(summary は維持) +- `project()` は **イミュータブル変換** で永続履歴を変えない +- savings 推定は意図的に上位層に委譲(`prune.rs` 末尾コメント) +- swiftide も MessageHistory に `Summary` メッセージで似たことをやるが、**永続履歴を直接置換**する方式。`llm-worker` の方が rollback / 再生 / debugging に強い + +--- + +## 5. 各競合の「これは llm-worker に勝っている」点 + +### rig +- **Provider 数**: 20+(OpenAI / Anthropic / Gemini / Bedrock / Vertex / Azure / Groq / DeepSeek / Cohere / Mistral / OpenRouter / Together / xAI / Perplexity / HuggingFace …) +- **VectorStore**: MongoDB / LanceDB / Neo4j / Qdrant / SQLite / SurrealDB / Milvus / ScyllaDB / S3Vectors / HelixDB +- **Pipeline DAG**: `parallel!` macro / `try_op` / agent_ops(Agent を Op として組み立て可能) +- **OTel**: GenAI Semantic Convention に完全準拠 +- **WASM**: `rig-core` のみ WASM 互換 +- **更新頻度**: 直近 20 commit が 1〜2 週間以内 + +### genai +- **Provider 数**: 18+ +- **薄さ**: 6,539 LOC / 35 deps で multi-provider client が完成している(`llm-worker` の `llm_client/` は 1.5k LOC で 4 provider) +- **Native protocol サポート**: Anthropic reasoning, Gemini thinking, OpenAI strict mode, OpenAI Responses API の `previous_response_id`/`store` を素通し +- **Tool call streaming**: `ToolCallChunk` が分離イベント +- **Resolver** で auth / model / endpoint をフレキシブルに差し替え + +### swiftide +- **Indexing pipeline**: streaming loader → transformer → chunker → storage の fluent パイプライン +- **Query state machine**: Pending → Retrieved → Answered の型状態(くしくも型状態だが対象が違う) +- **Hook 粒度**: `before_all` / `before_completion` / `after_completion` / `before_tool` / `after_tool` / `on_new_message` / `on_stop` 等 6+ +- **MCP サポート**: ToolExecutor として LocalExecutor と MCP の両方 +- **MessageHistory trait** で永続化バックエンド(Redis 等)に差し替え可能 +- **既存 SDK の活用**: `async-openai` / `async-anthropic` をベースに薄く乗せる戦略 + +--- + +## 6. 各競合の「これは llm-worker の方が良い」点 + +### vs rig +- **キャッシュ整合性**: rig は `extended_details()` で usage を観測できるが、prefix 変更を**防止**するメカニズムはない +- **依存の薄さ**: rig-core 25.5K LOC + 大量 sub-crate vs llm-worker 5.8K LOC +- **Worker の単一責務**: rig の `Agent` は LLM 呼び出し + Tool + (optional) RAG までを抱え込む。`llm-worker` は orchestrator を分離 + +### vs genai +- **Tool 実行ループ**: genai は呼び出し側で書く必要がある(README/example で明示) +- **会話履歴の構造化**: genai は `ChatRequest` を毎ターン clone する素朴な API +- **Interceptor**: genai は `Resolver` で初期化時のカスタマイズのみ。実行中フックなし + +### vs swiftide +- **Provider 直接実装**: swiftide は `async-openai` 0.33 / `async-anthropic` 0.6 に依存しており、その上流に出来る/出来ないが縛られる。`llm-worker` は HTTP まで自前で握る +- **Agent loop の中核設計**: swiftide-agents は `DefaultContext` の AtomicUsize ベースで素朴。型状態保護なし +- **依存の少なさ**: swiftide-agents 単体でも `async-openai` を呼ぶ前提 + +--- + +## 7. 「ライブラリ化したとき競合するか」の解像度 + +| 相手 | 競合度 | 理由 | +|---|---|---| +| **rig** | 🟥 高 | 同じ "agent + tool + multi-provider" を狙う層。rig は既にエコシステムを持っており、正面衝突は不利 | +| **genai** | 🟨 中 | 層が違う(client vs worker)。むしろ `llm-worker` の `llm_client/` を捨てて genai に乗る選択肢がある | +| **swiftide** | 🟩 低 | 一次目的が違う(pipeline)。agent の中核実行を `llm-worker` に置き換える "embed" 戦略すら成立し得る | + +--- + +## 8. ポジショニング案(仮にライブラリ化するなら) + +`llm-worker` がぶつからない・かつ需要がある場所を考えると、次のような立ち位置が現実的: + +> **"Production-grade LLM worker primitive — 型状態でキャッシュ整合性を守り、Interceptor で上位層 (rig / swiftide / 自社 orchestrator) から駆動できる低レベル実行器"** + +具体的な打ち出し方: +1. **キャッシュ整合性をコンパイル時に保証する唯一のクレート** という明確な売り文句。Anthropic の prompt cache 課金事故で困った人を狙う +2. **Tool 実行ループ + Interceptor** をプリミティブとして提供し、上位フレームワークから plug できることを主張 +3. **HTTP まで自前** はやめて、`llm_client/` を外す or feature 化し、`genai` バックエンドを optional 提供する選択肢も検討に値する(実装コストを 1.5k LOC 削れる) +4. **Pipeline / RAG / VectorStore は提供しない** ことを明示。rig / swiftide の代替を狙わないことで、共存ストーリーが書ける + +--- + +## 9. ライブラリ化の判断材料(再) + +- **需要がある層か**: yes。rig / swiftide / genai は揃って "もう一段下のキャッシュ整合性プリミティブ" を持っていない +- **既存と被るか**: 上記 3 案でかわせる +- **維持コスト**: API 安定化 + provider 追従が恒常的に乗る。`llm_client/` を外すか genai に寄せるかでだいぶ軽くなる +- **タイミング**: insomnia 本体がリリースされ、`Worker` の API が stress test を受ける前に公開すると、後から破壊的変更を強いられる。**先に insomnia をリリースして、production 使用例として参照させてからライブラリ化する**方が安全 + +--- + +## 付録: 主要ファイルへの参照 + +### llm-worker +- `crates/llm-worker/src/lib.rs:1-59` — モジュール構成 +- `crates/llm-worker/src/worker.rs:101-191` — Worker +- `crates/llm-worker/src/state.rs:1-61` — Mutable / Locked 型状態 +- `crates/llm-worker/src/interceptor.rs:1-147` — Interceptor trait +- `crates/llm-worker/src/prune.rs:66-118` — context projection +- `crates/llm-worker/src/llm_client/{auth,client,event,transport,types}.rs` +- `crates/llm-worker/src/tool_server.rs:27-52` — Deferred 登録 + +### rig +- `github.com/0xPlaygrounds/rig/rig/rig-core/src/{agent,completion,tool,pipeline,vector_store,streaming}/mod.rs` + +### genai +- `github.com/jeremychone/rust-genai/src/lib.rs:1-25` +- `.../src/client/client_impl.rs:62-67` +- `.../src/chat/chat_request.rs:10-34` +- `.../src/chat/chat_stream.rs:11-86` +- `.../src/adapter/adapter_types.rs:11-59` + +### swiftide +- `github.com/bosun-ai/swiftide/swiftide-agents/src/agent.rs:45-100` +- `.../swiftide-agents/src/hooks.rs` +- `.../swiftide-core/src/chat_completion/traits.rs` +- `.../swiftide-indexing/src/pipeline.rs` +- `.../swiftide-query/src/` diff --git a/tickets/memory-file-format.md b/tickets/memory-file-format.md new file mode 100644 index 00000000..d5e02e9a --- /dev/null +++ b/tickets/memory-file-format.md @@ -0,0 +1,74 @@ +# メモリ機構: ファイル形式 + Linter 土台 + +## 背景 + +`docs/plan/memory.md` で決めたメモリ機構の永続化レイヤの土台。`memory/*` と `knowledge/*` の record を保存・編集する際の静的スキーマと、汎用 CRUD への post-write Hook として挟む Linter を成立させる。Phase 1/2、検索ツール、常駐注入、GC はすべてこの層に乗る。 + +Workflow(`docs/plan/workflow.md`)も同じ frontmatter / Linter 経路で扱うため、`memory/workflow/.md` の frontmatter 検証と書き込み制限も本チケットに含める。実行経路(`/` dispatch)は別。 + +## 要件 + +### ディレクトリと record 種別 + +- `memory/summary.md` — Always-on サマリ(1 ファイル固定) +- `memory/decisions/.md` — Decisions +- `memory/requests/.md` — Requests +- `memory/workflow/.md` — Workflow(frontmatter 検証のみ対象) +- `memory/_staging/.json` — Phase 1 中間(本チケットはパス予約と Linter 対象外化のみ) +- `knowledge/.md` — Knowledge(`memory/` の兄弟) + +slug は kebab-case(小文字英数とハイフン)。ファイル名がそのまま識別子で、frontmatter に `id` / `name` は持たない。 + +### frontmatter スキーマ + +種別ごとの必須フィールドは `docs/plan/memory.md` §ファイル形式 / §書き込み経路と Linter の表に従う。具体的な必須項目: + +- 共通: `created_at`, `updated_at` +- Decisions: `sources`, `status: open | resolved | replaced`、置き換え時 `replaced_by: ` +- Requests: `sources` +- Knowledge: `kind`, `description`, `model_invokation`, `user_invocable`, `last_sources` +- Summary: `updated_at`(optional: `last_rewritten_from_range`) +- Workflow: `description`, `auto_invoke`, `user_invocable`, `requires` + +### Linter ルール + +静的 error(post-write Hook が turn を戻し、sub-Worker に自己修正させる。N 回失敗で abort): + +- frontmatter 必須 field 欠落・型違反 +- `memory/workflow/` への書き込み禁止(sub-Worker のみ。人間編集は対象外) +- 同 slug での新規作成禁止(既存があれば update に切り替えるサイン) +- `#` / `replaced_by: ` / `requires: [..]` が実在 record を指す +- Decisions `status` の enum 違反 +- `model_invokation: true` な Knowledge の description 1024 chars 上限 +- 種別ごとの char 硬上限(具体値は設定ファイルで tune) + +膨張抑制 Warn(error ではなく改善ヒント): + +- 低重要度 × char の天秤 +- `sources` 配列長の累積 +- 類似 slug 乱立 + +### 適用経路 + +- sub-Worker の汎用 CRUD(read/write/edit)への post-write Hook として挟む +- 人間編集(エディタ / git commit)に対しても同一ルールで検証できる CLI または pre-commit hook 経路を用意(詳細は実装で判断、結果として同じルールが一箇所で定義されていること) + +## 範囲外 + +- 検索ツール、常駐注入、Phase 1/2、GC の実装 +- 意味破壊(rewrite で主張が落ちる等)の検出 — 監査 LLM 層は将来検討 +- staging JSON の schema — Phase 1 チケット +- Workflow の `/` 実行経路 + +## 完了条件 + +- 上記パスに手で record を置いて Linter を走らせると、スキーマ違反 / 参照切れ / 同 slug 競合が error として返る +- sub-Worker が CRUD で書き込んだ際、違反時は turn が戻り自己修正が走る +- Warn は error を止めず、出力されることが確認できる +- `memory/workflow/` への sub-Worker からの書き込みは error で止まり、人間編集は通る +- 既存ビルド・テストを壊さない + +## 参照 + +- `docs/plan/memory.md` §ファイル形式 / §書き込み経路と Linter / §Knowledge の採択基準 +- `docs/plan/workflow.md` §格納先とファイル形式 / §生成・更新ポリシー diff --git a/tickets/memory-gc.md b/tickets/memory-gc.md new file mode 100644 index 00000000..c25320a4 --- /dev/null +++ b/tickets/memory-gc.md @@ -0,0 +1,61 @@ +# メモリ機構: GC(定期再評価) + +## 背景 + +`docs/plan/memory.md` §GC の実装。Phase 2 は情報統合寄りだが、それでも残る重要度低下・類似 slug 乱立・`replaced` 滞留・sources 累積・現状不整合を整理する定期再評価経路。人間 offer はかけず、結果は git diff で検証する建て付け。 + +保護閾値は使用頻度メトリクスの明示 invoke frequency に依存する。 + +## 要件 + +### Trigger + +- 定期実行(累積 input token ベース推奨、具体値は設定で tune、実装判断) + +### 実行主体と渡すツール + +- GC Agent が spawn +- Phase 2 と同じ汎用 CRUD + 検索ツール + post-write Linter Hook +- 入力: GC 対象 record 群 + Linter Warn + 使用頻度メトリクス + `replaced` chain + sources 過多情報 + +### 操作粒度 + +- ファイル単位: 丸ごと drop / 複数ファイル merge / 1 ファイルの split +- ファイル内部分: 節・箇条の削除 or 圧縮、`sources` 古いエントリの trim + +### 評価カテゴリ + +`outdated`, `superseded`, `unused`, `noisy` を GC 理由の分類として使う。record に一律の「stale」フラグは付けない。drop / merge / split / trim / rewrite のどれを選ぶかをこのカテゴリで説明可能にする。 + +### 判断ルール + +- 保護閾値: **明示 invoke** の `frequency >= 1.0 invokes/Mtoken` の record は drop / 大幅圧縮の対象外 + - 初期値 1.0、workspace 設定でカスタマイズ可 + - `model_invokation` 注入による常駐は計数対象外(別指標として参照のみ) +- 単一 record が複数カテゴリに該当してもよい +- 直接削除してよいが、誤判定しやすいものは merge / trim を優先(prompt 側で誘導) + +### prompt + +- `docs/plan/memory-prompts.md` §GC prompt に従う + +## 範囲外 + +- 監査 LLM 層(将来検討) +- Vector index / FTS5 導入(将来検討、GC 判断には影響しない) +- Workflow の GC 対象化(初期は触らない) + +## 完了条件 + +- Trigger で GC Agent が走り、不要 record が整理される +- Linter Warn で検出した類似 slug 乱立などが GC でまとめて収斂する +- 保護閾値超過 record が drop / 大幅圧縮から外れる +- 置き換えは `status: replaced` + `replaced_by` で残り、直接削除と区別可能 +- git diff で drop / merge / split / trim / rewrite の理由が読める + +## 参照 + +- `docs/plan/memory.md` §GC / §使用頻度メトリクス / §判断ルール +- `docs/plan/memory-prompts.md` §GC prompt +- `tickets/memory-phase2-consolidation.md`(ツール構成の共通化) +- `tickets/memory-usage-metrics.md`(保護閾値の依存) diff --git a/tickets/memory-phase1-extract.md b/tickets/memory-phase1-extract.md new file mode 100644 index 00000000..294d500e --- /dev/null +++ b/tickets/memory-phase1-extract.md @@ -0,0 +1,56 @@ +# メモリ機構: Phase 1 活動抽出 + +## 背景 + +`docs/plan/memory.md` §Phase 1 の実装。activity tokens の累積閾値で発火し、前回 Phase 1 以降の session log 範囲から「起きたこと」を 4 種の活動ログ候補として抽出、`memory/_staging/.json` に書き出す。Knowledge 化や summary rewrite は Phase 2 に委ねる。 + +Pod を立てずに既存 compact と同じ Worker spawn 機構を再利用する。raw session log は `session-store` で保持されており、ここから range を切り出して入力に使う。 + +## 要件 + +### Trigger + +- activity tokens 累積閾値(設定ファイルで tune) +- tool call カウントは不採用(ツールカスタマイズ非依存・大小重みづけのため) + +### 実行主体と入出力 + +- 既存 compact の Worker spawn 機構を再利用、Pod は立てない +- 入力: 前回 Phase 1 以降の session log 範囲(処理済み境界 pointer は session 側に保持、寿命を session と揃える) +- 出力 JSON schema: `decisions`, `discussions`, `attempts`, `requests` の候補配列。抽出対象なしは空配列 +- 出力に自由文の補足説明を入れさせない(schema 準拠のみ) + +### 書き込み + +- 書き込み先: `memory/_staging/.json`(1 件 1 ファイル、UUIDv7 可) +- pod 側ラッパーが `source: { session_id, range: [start_entry, end_entry] }` を**機械付与**して LLM 出力と wrap +- LLM に source を推論させない + +### モデル + +- 設定 key `memory.extract_model`(軽量だが文脈理解できる中堅クラス想定) + +### prompt + +- prompt 要件は `docs/plan/memory-prompts.md` §Phase 1: 活動抽出 prompt に従う + +## 範囲外 + +- Phase 2 による staging の消費・クリーンアップ(別チケット) +- staging の cleanup 戦略の詳細(Phase 2 で完了時に消す、実行中追加分は残す、という契約だけ本チケットで守る) +- compact Worker spawn 機構自体の拡張(既存をそのまま使う。共通化が必要になったら別途) + +## 完了条件 + +- Pod 稼働中に閾値超過で Phase 1 が発火し、`memory/_staging/.json` にファイルができる +- ファイルは schema に準拠、`source` が機械付与されている +- 抽出対象なしのときは空配列として書き出される(または発火そのものを skip、どちらでもよい) +- session 側の処理済み pointer が更新され、次回 Phase 1 は続きから走る +- 既存 compact の動作に回帰がない + +## 参照 + +- `docs/plan/memory.md` §Phase 1: 活動抽出 / §ファイル形式(staging) +- `docs/plan/memory-prompts.md` §共通原則 / §Phase 1: 活動抽出 prompt +- 既存 `session-store` クレート(session log range 取得) +- 既存 compact の Worker spawn 経路 diff --git a/tickets/memory-phase2-consolidation.md b/tickets/memory-phase2-consolidation.md new file mode 100644 index 00000000..07e3fa98 --- /dev/null +++ b/tickets/memory-phase2-consolidation.md @@ -0,0 +1,80 @@ +# メモリ機構: Phase 2 consolidation + +## 背景 + +`docs/plan/memory.md` §Phase 2 の実装。staging の活動ログ + 既存 `memory/*` + Knowledge 化候補レポートを入力に、consolidation Worker が汎用 CRUD + 検索ツール + Linter Hook で agentic に統合する。Phase 1 を終えた pod が spawn し、並走防止は staging 配下の進行状況ファイルで担保する。 + +Knowledge 新規作成は「候補レポート掲載の source から派生する場合」に限る。使用頻度メトリクス(候補レポートの集計元)が未完のうちは、レポートは空入力として動作し、Phase 2 は decisions / requests / summary / 既存 Knowledge update のみ行う。 + +## 要件 + +### Trigger + +- staging の累積ファイル数 or bytes 閾値(設定で tune) +- compact 発火時に必ず flush(compact で失われる raw を漏らさない) + +### 実行主体と入力 + +- Phase 1 を終えた pod が consolidation Worker を spawn +- 起動時スナップショットで consumed ID list を確定 +- 入力: + - consumed ID 分の staging エントリ(活動ログ + `source`) + - 既存 `memory/*`(summary / decisions / requests)全文 + - Knowledge 化候補レポート(メトリクスチケットの成果物。未完のうちは空) + - 既存 `knowledge/*` は prompt に埋めず、Knowledge 検索ツール経由で agent が引く + +### 渡すツール + +- 汎用 CRUD(file read / write / edit) +- memory 検索ツール +- Knowledge 検索ツール +- post-write Linter Hook(違反時 turn 戻し、N 回失敗 abort) + +### 処理内容 + +- 新規 decisions / requests を 1 件 1 ファイルで追加、`sources` は staging の `source` をコピー(LLM 推論ではない) +- 活動ログから派生する Knowledge を新規作成 or 既存 patch。**新規作成は候補レポート掲載の source 由来に限る** +- summary を必要に応じて rewrite(1-5k tokens 目安) +- 削除は `status: replaced` + `replaced_by: ` で置き換え記録、直接削除しない +- 書き込み先: `memory/*`, `knowledge/*`。`memory/workflow/` は Linter で弾かれる + +### 並走防止 + +- staging 配下に 1 ファイル(Pod 識別子 + consumed ID list) +- 存在し、示された Pod が動作している間、そのプロセスが排他占有 +- 実行中に Phase 1 が追加した staging は触らず、次回 Phase 2(Coalesce)に委ねる +- 完了時は consumed ID list の staging のみ cleanup、追加分は残す +- Phase 2 完了時に staging 新着があれば次を発火(Coalesce) +- 占有の実現方法(pid 存在確認 / flock / 他)は実装判断 + +### モデル + +- 設定 key `memory.consolidation_model`(reasoning 系) + +### prompt + +- `docs/plan/memory-prompts.md` §共通原則 / §Phase 2: 統合 prompt / §Phase 2: Knowledge 書き込み prompt に従う + +## 範囲外 + +- 使用頻度メトリクスと Knowledge 化候補レポートの集計(別チケット。未完の間は空レポートで動作) +- GC(別チケット) +- Workflow 関連の offer(別チケット、Notification 経路が先) +- 意味破壊検出の監査 LLM 層(将来検討) + +## 完了条件 + +- Phase 1 が staging に残した活動ログを Phase 2 が `memory/*` / `knowledge/*` に統合する +- Linter 違反時は turn が戻り、sub-Worker が自己修正する +- 並走防止ファイルが想定通り機能し、複数 Phase 2 の重複起動が防げる +- Coalesce で実行中追加分が次回に引き継がれる +- compact 発火時に Phase 2 が flush される +- 空レポートでも新規 Knowledge を作らずに動く(decisions / requests / summary / 既存 Knowledge update のみ) + +## 参照 + +- `docs/plan/memory.md` §Phase 2 / §Phase 2 agent への原則 / §Compact との関係 +- `docs/plan/memory-prompts.md` §Phase 2 関連 +- `tickets/memory-file-format.md`(Linter) +- `tickets/memory-search-tools.md`(検索ツール) +- `tickets/memory-phase1-extract.md`(staging 生産) diff --git a/tickets/memory-resident-injection.md b/tickets/memory-resident-injection.md new file mode 100644 index 00000000..38dcd970 --- /dev/null +++ b/tickets/memory-resident-injection.md @@ -0,0 +1,32 @@ +# メモリ機構: `model_invokation: ON` の常駐注入 + +## 背景 + +`docs/plan/memory.md` §retrieval 経路 の「常駐注入」項目。`model_invokation: ON` な Knowledge record の description を通常 Pod の system prompt に載せ、モデルが必要と判断した時点で検索ツール経由で本文を引く形を成立させる。Phase 2 Pod には注入しない。 + +専用の auto-invoke 経路は用意しない。モデルが description を見て自発的に検索ツールを呼ぶ経路に一本化する。 + +## 要件 + +- Pod 起動時に `knowledge/*` を走査し、`model_invokation: ON` の record の description を system prompt に連結 +- Phase 2 Pod では注入しない(consolidation は検索ツール経由で自律探索) +- 予算はシステムプロンプト全体予算に含める。`memory/summary.md` の 5k 枠とは別管理にしない +- 超過時の件数キャップ / 優先順位ルールは初期不要(description 1024 chars 上限で通常は収まる前提)。ON record 数が増えて問題になったら別チケットで対応 + +## 範囲外 + +- auto-invoke 用の別経路(採用しない) +- ON/OFF 切替の自動判定(初期は手動。将来検討) +- Workflow 側の `auto_invoke` 同等機能 — 仕様対称だが本チケットは Knowledge のみ + +## 完了条件 + +- `model_invokation: true` の knowledge を置いた状態で通常 Pod を起動すると、system prompt に description が含まれる +- `model_invokation: false` のものは含まれない +- Phase 2 Pod では注入されない +- 既存の system prompt 構成(AGENTS.md / scope summary / skills 等)と共存する + +## 参照 + +- `docs/plan/memory.md` §retrieval 経路 / §Knowledge の呼び出し制御 +- `tickets/memory-file-format.md`(依存: `model_invokation` frontmatter) diff --git a/tickets/memory-search-tools.md b/tickets/memory-search-tools.md new file mode 100644 index 00000000..3eaf169a --- /dev/null +++ b/tickets/memory-search-tools.md @@ -0,0 +1,51 @@ +# メモリ機構: memory / Knowledge 検索ツール + +## 背景 + +`docs/plan/memory.md` §retrieval 経路 で定義した 2 本の検索ツールを Pod から呼べる LLM ツールとして実装する。memory 検索と Knowledge 検索は対象ディレクトリが違うだけで同型の仕様。grep ベースで始め、FTS / vector は将来検討。 + +このツールは Phase 2 Pod の agentic 探索経路としても、通常 Pod の `#` 展開経路としても、使用頻度メトリクスの観測点としても使う(メトリクスの hook 挿入は本チケットの範囲外、経路だけ揃える)。 + +## 要件 + +### ツール仕様(両者共通) + +- Input: + - `query: string`(自由文字列、必須) + - `slug: string`(完全一致 1 件返し、`#` 解決に使う) + - `kind: string`(Knowledge のみ、filter) +- Output: `{ slug, kind, description, model_invokation, excerpt }` の配列 + - `excerpt` はマッチ箇所の前後数行 +- ソート: grep 出現順(初期) +- ヒット件数上限と excerpt 行数は設定ファイルで tune +- 対象ファイルは都度スキャン。派生 index は持たない + +### 対象 + +- memory 検索: `memory/{summary,decisions,requests}/*.md`(workflow / \_staging は除外) +- Knowledge 検索: `knowledge/*.md` + +### 登録 + +- 通常 Pod と Phase 2 Pod の両方に渡せる tool 定義 +- Phase 2 Pod には Knowledge 検索を必ず渡す(全 Knowledge 本文を prompt に埋めない前提) + +## 範囲外 + +- 使用頻度メトリクス本体(hook 点の予約のみ。カウント・レポートは別チケット) +- slug サジェスト補完 UI(TUI 側、別途) +- FTS / vector index +- 常駐注入(別チケット) + +## 完了条件 + +- 通常 Pod / Phase 2 Pod 双方から両ツールが呼べる +- `slug` 指定で完全一致の 1 件が返り、`#` の本文展開経路として成立する +- `query` 指定で frontmatter 含む全文から excerpt 付きでヒットが返る +- Knowledge 検索の `kind` filter が効く +- 対象外ディレクトリ(`memory/workflow/`, `memory/_staging/`)はヒットしない + +## 参照 + +- `docs/plan/memory.md` §retrieval 経路 / §Knowledge の採択基準 +- `tickets/memory-file-format.md`(依存: frontmatter スキーマ) diff --git a/tickets/memory-usage-metrics.md b/tickets/memory-usage-metrics.md new file mode 100644 index 00000000..4b5452dc --- /dev/null +++ b/tickets/memory-usage-metrics.md @@ -0,0 +1,60 @@ +# メモリ機構: 使用頻度メトリクス + Knowledge 化候補レポート + +## 背景 + +`docs/plan/memory.md` §使用頻度メトリクス の実装。memory 検索ツール / Knowledge 検索ツール内に invoke 計測フックを入れ、時間単位ではなく累積 input token で正規化した頻度を算出する。Phase 2 の Knowledge 新規作成 gate と GC の保護閾値の両方で使われる。 + +## 要件 + +### 観測経路 + +- memory 検索ツール / Knowledge 検索ツール内に hook を挿入 +- `#`(slug 完全一致経路)、`/`(workflow 側、将来接続)、明示検索呼び出しをすべて同じ経路に集約 + +### カウント対象 + +- **明示 invoke**: 検索ツール経由の読み取り / `#` / `/` を `n回 / Mtoken` でスコア化 +- **`model_invokation` 注入**: 明示 invoke の分子には含めない。**コスト側**(注入した record に対する消費 input tokens)として別途記録。使われ率 ratio や ON/OFF 判断材料として後段で参照 +- ファイル token 数 + +### 記録先 + +- staging と独立 +- workspace 側に記録(session データ喪失で統計が消えない) +- invoke event を UUID + Stats 形式 +- 具体 schema / フォーマットは実装で決定 + +### 集計 + +- 累積方式: 最大 10 回前の invoke から現在までの時系列窓でフィルタして集計 +- **Knowledge 化候補レポート**: + - 対象は `memory/*` 配下の record(Phase 1 成果物の decisions / requests + 既存 knowledge) + - 明示 invoke 頻度が閾値超過のものを列挙 + - 同一 session 内の連続参照は 1 count に丸める + - 複数 session での再参照を要件とする(spike 除外) + - 閾値は設定ファイルで tune + +### 消費者 + +- Phase 2 Worker の入力として候補レポートを渡す +- GC Agent の保護閾値判定(明示 invoke frequency >= 1.0 invokes/Mtoken) + +## 範囲外 + +- GC の実装本体(別チケット。本チケットは保護閾値判定に必要なメトリクスの提供まで) +- `model_invokation` ON/OFF の自動判定ロジック(将来検討) +- Shallow request の自動除外(将来検討) + +## 完了条件 + +- 検索ツール呼び出しで invoke event が workspace 側に積まれる +- `model_invokation` 注入のコスト側集計が別口で積まれる +- 候補レポート API が Phase 2 Worker の起動時に呼べる +- 閾値未満の record は候補レポートに載らない +- 同一 session 内連続参照は 1 count に丸まる + +## 参照 + +- `docs/plan/memory.md` §使用頻度メトリクス / §判断ルール / §retrieval 経路 +- `tickets/memory-search-tools.md`(hook 挿入点) +- `tickets/memory-phase2-consolidation.md`(消費者) diff --git a/tickets/submit-segment-protocol.md b/tickets/submit-segment-protocol.md new file mode 100644 index 00000000..664cc392 --- /dev/null +++ b/tickets/submit-segment-protocol.md @@ -0,0 +1,81 @@ +# サブミット入力: protocol Segment 化 + +## 背景 + +現状の `Method::Run { input: String }` はユーザー入力を素の文字列で運んでいる。TUI の `Atom::Paste`(bracketed paste で受けた塊)は表示用には `[Clipboard #N | X chars, Y lines]` ラベルを持っているが、submit 時に `InputBuffer::submit_text` で flatten され、Pod や session log にはただの長い user message として伝わる。 + +memory / workflow 導入に伴い、submit には paste 以外にも以下を載せたくなる: + +- `@` のファイル参照 + auto_read +- `#` の Knowledge 参照(`docs/plan/memory.md`) +- `/` の Workflow 起動(`docs/plan/workflow.md`) + +これを protocol 上で **String + marker パース**で扱うと、Pod が正規表現と escape 規則を抱え、code block 内の `#foo` を literal にするか等の ambiguity と escape 漏洩が常に付きまとう。さらにリッチクライアント(GUI / web / ネイティブ)はファイル添付や paste を file chip / clipboard card として描くため、protocol が String だと「chip → string marker → Pod で再 parse → 表示用に再構築」という 3 段 round-trip を全 client で再実装する形になる。 + +protocol を typed segment の列に上げると、Pod は parser を持たず resolve に集中でき、client は自分の表現単位のまま intent を送れる。最低限 `Segment::Text(String)` を fallback として置くので、CLI piping 等の dumb client は 1 segment を作るだけで従来通り動く(protocol の dictation が「Text を作れること」に閉じる)。 + +## 要件 + +### protocol + +`Method::Run` の payload を `String` から `Vec` に置き換える。Segment は最低限以下の variant を持つ: + +- `Text` — 自由文字列。dumb client の fallback +- `Paste` — TUI 等の bracketed paste 由来の塊。`id` と本文を持つ +- `FileRef` — `@` の auto_read 対象 +- `KnowledgeRef` — `#` の Knowledge 参照 +- `WorkflowInvoke` — `/` の Workflow 起動 + +`Event::UserMessage` の payload も同じ `Vec` に揃え、複数 client で見たり log replay する際に typed のまま再構築できる状態にする。 + +未知 variant に対する forward compatibility(`#[serde(other)]` 相当の吸収)を入れる。 + +### Pod 側 resolve + +Pod は `Vec` を LLM context へ flatten する単一経路を持つ: + +- `Text` → そのまま +- `Paste` → 本文を inline 展開(現状 TUI が flatten してきたのと同じ結果)。**session log / Event::UserMessage 上ではラベル化情報を保持**し、表示時に `[Clipboard #N | X chars]` を再構築できること +- `FileRef` → scope 内なら本文 read + inline、scope 外は明示エラー +- `KnowledgeRef` / `WorkflowInvoke` → resolver が登録されていれば展開、未登録はフォールバック(後述) + +resolver の trait 化と memory / workflow 用 resolver 実装は別チケット。本チケットは **Text と Paste を typed のまま end-to-end で運び切る経路**で完結させる。`FileRef` 以降は variant 定義と「resolver 未登録時のフォールバック」だけ仕込んで実装は後続に委ねる。 + +### TUI 側 + +`InputBuffer::Atom::Paste` を submit 時に flatten せず、`Segment::Paste` として送出する。`Event::UserMessage` を typed segment で受けて `Block::UserMessage` を typed のまま描画(paste は引き続き magenta ラベル)。 + +text しか作れない client が引き続き存在しても良いことを protocol 仕様に明記する(`vec![Segment::Text(_)]` のみで動く)。 + +### unknown variant / 未登録 resolver の扱い(要決定) + +新 variant を持つ client が古い Pod に投げる、または resolver 未登録の variant を Pod が受けるケースの挙動を本チケットで決める。候補: + +- (a) 黙って drop — 静かに情報が落ちて user/LLM 双方が気付けない +- (b) `[unknown input: kind=foo]` 相当の placeholder を LLM context に差し込み、LLM が気づいて指摘できる +- (c) hard error で submit 拒否 + +(b) を仮の第一候補として実装方針を詰める。serde 側は unknown variant を吸収する形を初期から入れる。 + +## 範囲外 + +- `@` / `#` / `/` token の TUI parsing と補完 UI(`tickets/submit-tui-completion.md`) +- KnowledgeRef / WorkflowInvoke の resolver 実装(memory / workflow チケット) +- FileRef の引数拡張(行範囲、glob 等) + +## 完了条件 + +- `Method::Run` / `Event::UserMessage` が `Vec` で wire を通る +- TUI から paste した内容が `Segment::Paste` で送出され、Pod の LLM context では本文展開、Event 経由の再描画では `[Clipboard #N | ...]` が復元される +- `vec![Segment::Text(_)]` だけ送る client は従来通り動く +- 未登録 variant の扱いが決定済みルール通りに動く(決定は本チケット内で合意) +- forward compat: 未知 variant を含む payload を deserialize しても panic / parse error にならない +- 既存ビルド・テストを壊さない + +## 参照 + +- `docs/plan/memory.md` §retrieval 経路 / §Knowledge の呼び出し制御 +- `docs/plan/workflow.md` §呼び出しと依存 +- `crates/protocol/src/lib.rs`(`Method::Run`, `Event::UserMessage`) +- `crates/tui/src/input.rs`(`Atom::Paste`, `submit_text`) +- `crates/tui/src/app.rs`(`submit_input`, `Block::UserMessage` 描画) diff --git a/tickets/submit-tui-completion.md b/tickets/submit-tui-completion.md new file mode 100644 index 00000000..9e9da91d --- /dev/null +++ b/tickets/submit-tui-completion.md @@ -0,0 +1,61 @@ +# サブミット入力: TUI 補完 + 型付き atom 化 + +## 背景 + +`tickets/submit-segment-protocol.md` で protocol が `Vec` を運べるようになった前提で、TUI 側に「`@` / `#` / `/` を打鍵中に候補を出し、確定したら typed atom (= 送出時の `Segment`) に昇格させる」UX を載せる。 + +`@` / `#` / `/` は TUI の入力アフォーダンスであって protocol contract ではない(GUI などは同じ intent をボタンや picker で表す)。本チケットは TUI 限定の UX に閉じる。 + +## 要件 + +### token 検出と昇格 + +入力中に prefix を検出して候補を浮かせる: + +- `@<部分パス>` — workspace 内のファイル / ディレクトリ候補 +- `#<部分slug>` — Knowledge slug +- `/<部分slug>` — Workflow slug + client-side コマンド(`/clear` など) + +確定(Tab / Enter 等、入力 UX の詳細は実装で)で対象範囲を `Atom::FileRef` / `Atom::KnowledgeRef` / `Atom::WorkflowInvoke` の indivisible atom に置き換える。挙動は既存の `Atom::Paste` と同等(cursor は中に入れない、Backspace で塊ごと削除)。submit 時に対応する `Segment` 変種に変換して送る。 + +### 候補列挙のための protocol query + +補完用に Pod へ問い合わせる軽量経路を追加: + +- ファイル候補(scope 内、prefix マッチ) +- Knowledge / Workflow slug 候補(kind 指定 + prefix マッチ) + +`Event` ストリームに載せる性質ではないため、request/response 形式を新設する(具体形式は実装で判断、既存 `Method` の枠に増やすか別経路を作るかも実装側で決める)。 + +### 表示 + +確定後の atom は paste と同じ「indivisible chip」スタイルで描画する。`@` / `#` / `/` ごとに色を変える程度の差異化を入れる。`Block::UserMessage` 側でも同一スタイルで再描画する(`Event::UserMessage` が typed segment で来る前提)。 + +### client-side `/` の dispatch + +`/clear` のような client 完結コマンドは Pod に送らず TUI 内で処理する。TUI 内に簡易な dispatch 表を持ち、未知の `/` は `Segment::WorkflowInvoke` として送る。初期 dispatch 表は `/clear` 程度で良く、拡張は別途。 + +## 範囲外 + +- Pod 側の resolver 実装(memory / workflow チケット) +- 候補スコアリング、fuzzy search、preview 等の高度な補完体験 +- リッチクライアント(GUI / web)の同等 UX + +## 完了条件 + +- `@` / `#` / `/` を打鍵すると候補が出て、確定で chip 化される +- chip 化された atom が対応する `Segment` として Pod に送出され、`Event::UserMessage` で戻ってきた typed segment が同じ見た目で再描画される +- 候補列挙の query / response が wire を通る +- `/clear` が client-side で処理され、Pod には届かない +- 既存ビルド・テストを壊さない + +## 依存 + +- `tickets/submit-segment-protocol.md` + +## 参照 + +- `crates/tui/src/input.rs`(`Atom` 体系の拡張) +- `crates/tui/src/app.rs`(`submit_input`、`Block::UserMessage` 描画) +- `docs/plan/memory.md` §retrieval 経路(slug 補完対象) +- `docs/plan/workflow.md` §呼び出しと依存 diff --git a/tickets/test-design.md b/tickets/test-design.md deleted file mode 100644 index 15195c6e..00000000 --- a/tickets/test-design.md +++ /dev/null @@ -1,11 +0,0 @@ -# テスト設計 - -## 背景 - -各クレートのテスト方針が未策定。クレート間の依存関係と非同期処理が絡むため、 -テストの層(単体/結合/E2E)と mock 境界を明確にする必要がある。 - -## 検討事項 - -低層部分のテストを信頼し、上層までモックデータを引っ張ってきてテストする必要は無いのか? -実際の認証を使ったE2Eはどの様に結果を出すか?/