yoi/tickets/submit-file-ref-resolver.md

82 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# サブミット入力: FileRef リゾルバ
## 背景
`tickets/submit-tui-completion.md``@<path>` が typed atom として入力され、submit 時に `Segment::FileRef { path }` で Pod へ届く経路が完成した。一方 Pod 側 (`Pod::flatten_segments` in `crates/pod/src/pod.rs`) は今 `FileRef` を見ても resolver を持たず、`Segment::flatten_to_text` の placeholder (`[unresolved file ref: ...]`) を user message に inline するだけで、Warn alert を吐いて終わっている。
ClaudeCode の `@<path>` と同等の挙動 — submit 時にファイル本文を読み、LLM context にそのまま見せる — を入れる。`compact/worker.rs` の `mark_read_required` 経路で完成済の auto-read`PodFsView::render_auto_read`と兄弟関係になる、submit 時版のリゾルバ。
## 要件
### Item 配置
履歴に永続化する形は以下の **2 つの Item** にする:
```
[..., user_message, system_message(file1), system_message(file2), ...]
```
user message 自体は今と同じく `Segment::flatten_to_text` 由来のテキスト(`@<path>` トークンが残った placeholder 込み)。直後に `[File: <path>]\n<本文>` 形式の system message を、`FileRef` の出現順に追加する。次ターン以降も LLM が見える状態で残すcompact が走った時点で既存の auto-read 機構が引き継ぐ)。
inline 結合user 1 メッセージに本文を流し込む)は採らない。
### 本文の取り扱い
- `PodFsView` (`crates/pod/src/fs_view.rs`) 経由で読む。スコープ判定は `ScopedFs` 任せ。
- 上限は通常の Tool Output と同じ `manifest::defaults::TOOL_OUTPUT_MAX_BYTES` (16 KB)。超過分は捨て、末尾に `[...truncated, <total> bytes total — use read_file for the rest]` を付ける。LLM が必要なら自分で `read_file` を呼ぶ前提。
- 非 UTF-8バイナリはリゾルバが拒否する。後述の失敗扱いに倒す。
### 失敗時の扱い
スコープ外 / NotFound / バイナリ拒否は **Alert + placeholder 残置**:
- ユーザー向け Alert を `AlertLevel::Warn` で発火(理由を含めた一文)
- 該当 segment の system message は出さないuser message 中の `[unresolved file ref: <path>]` プレースホルダーがそのまま LLM に届く)
これは「ユーザーの誤入力を早期に可視化する」狙い。silent fallback にしない。
### Worker 側 API 拡張
submit 時に user message と system messages を一つの turn の前置として履歴に積む経路を、既存の `Interceptor` action-return パターンに合わせて足す。`TurnEndAction::ContinueWithMessages(Vec<Item>)` (`crates/llm-worker/src/worker.rs:903`) と同形:
- `Interceptor::on_prompt_submit` の戻り値を拡張し、`Continue` / `Cancel(String)` に加えて `ContinueWith(Vec<Item>)` を返せるようにする
- Worker の `Locked::run``ContinueWith` を受けたら user_item の push 直後に extras を `history.extend` する
- Hook (`crates/pod/src/hook.rs`) 側の戻り値(`PromptAction`はこの拡張に乗せない。Hook は read-only な公開拡張面という設計hook.rs:8-15 のコメントを維持するため、Hook と Interceptor で戻り値型を分離する
### Pod 側の resolver 配線
- `PodFsView::resolve_file_ref(&self, path: &str, max_bytes: usize) -> Result<Item, ResolveError>` を新設。`ScopedFs` で読み、UTF-8 検証 + 16 KB 切詰めを行い `Item::system_message` を返す。エラーは `OutOfScope` / `NotFound` / `Binary` / `Io(io::Error)` を区別する
- `PodSharedState` に submit 中だけ使う stash (`Mutex<Vec<Item>>`) を一個追加。`pending_notifies` / `compact_state` と同じ流儀
- `Pod::run` で submit 直前に `Vec<Segment>` を走査して FileRef を resolver に通し、成功分は stash、失敗分は Alert に流す
- `PodInterceptor::on_prompt_submit` で stash を取り出して空でなければ `ContinueWith(items)` を返す
## 範囲外
- Knowledge / Workflow resolverそれぞれ `tickets/memory-phase2-consolidation.md``tickets/workflow.md` 側)
- 画像など binary attachment の typed メッセージ化(将来 `ContentPart::Image` 等を入れる別チケット)
- `@<path>:<line>-<line>` のような行範囲指定構文
- compact 後の auto-read との重複排除compact が user message 由来の FileRef を読み直す可能性は許容)
## 完了条件
- `@<path>` を含む submit が、user message + 解決済み system message の 2 Item として履歴に残る
- 16 KB を超えるファイルは truncate され、その旨が LLM に見える形で示される
- スコープ外 / NotFound / バイナリは Alert として通知され、LLM 側は placeholder を見るのみ
- Hook の戻り値型は据え置き、Interceptor のみ `ContinueWith` を受け付ける
- 既存ビルド・テストを壊さない
## 依存
- `tickets/submit-tui-completion.md`FileRef segment の wire 接続)
## 参照
- `crates/pod/src/pod.rs``flatten_segments`, `Pod::run`
- `crates/pod/src/fs_view.rs``PodFsView` — auto-read の隣に置く)
- `crates/pod/src/ipc/interceptor.rs``PodInterceptor::on_prompt_submit`
- `crates/pod/src/shared_state.rs`stash 追加先)
- `crates/llm-worker/src/interceptor.rs``PromptAction` 拡張)
- `crates/llm-worker/src/worker.rs:903``TurnEndAction::ContinueWithMessages` 既存パターン)
- `crates/pod/src/hook.rs:8-15`Hook と Interceptor の責務分離 doc
- `crates/manifest/src/defaults.rs``TOOL_OUTPUT_MAX_BYTES`