diff --git a/TODO.md b/TODO.md index 5654e73f..06b6c133 100644 --- a/TODO.md +++ b/TODO.md @@ -5,7 +5,6 @@ - [ ] Compact の改善(要約品質 + 挙動詳細) → [tickets/compact-improvements.md](tickets/compact-improvements.md) - [ ] Protocol の設計 → [tickets/protocol-design.md](tickets/protocol-design.md) - [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md) -- [ ] AGENTS.md の取り込み → [tickets/agents-md-ingestion.md](tickets/agents-md-ingestion.md) - [ ] TUI 拡充 - [ ] 通知チャネル (Warn/Error 可視化) → [tickets/tui-notification-channel.md](tickets/tui-notification-channel.md) - [ ] 空 Pod 起動時の Greeting カード → [tickets/tui-greeting-card.md](tickets/tui-greeting-card.md) diff --git a/tickets/agents-md-ingestion.md b/tickets/agents-md-ingestion.md deleted file mode 100644 index 83f42a0a..00000000 --- a/tickets/agents-md-ingestion.md +++ /dev/null @@ -1,73 +0,0 @@ -# AGENTS.md の取り込み - -## レビュー状態 - -初回レビュー実施済み。[agents-md-ingestion.review.md](agents-md-ingestion.review.md) を参照。 -要件・完了条件すべて達成、無条件で受け入れ可。指摘1(切り詰め時の UTF-8 pop ループの非一貫)は任意修正。 - -## 背景 - -[agents.md](https://agents.md) は AI コーディングエージェント向けにプロジェクト固有の指示を置く標準ファイルとして普及しつつある(主要エージェント群が対応し、OpenAI の本体リポジトリだけでも 88 個の AGENTS.md が置かれている)。Insomnia の Pod も、作業ディレクトリに配置された `AGENTS.md` を自動でシステムプロンプトの文脈として取り込めるようにしたい。 - -agents.md 仕様では「編集対象ファイルから見て最近接の1つ」を読むモデルだが、Insomnia の Pod は cwd が1点に固定されるため、**cwd 直下の `AGENTS.md` のみ**を対象とする。サブプロジェクト向けにカスタム文脈を与えたい場合はそのディレクトリを cwd にして Pod を立てる運用を想定する。 - -## 依存 - -- `system-prompt-template.md`(実装済み / `crates/pod/src/system_prompt.rs`): 本チケットは読み取り結果を `SystemPromptContext.files` マップ経由でテンプレート変数として露出する。`files` キーは既に予約済み。 - -## spec との整合 - -`https://agents.md` の仕様は薄く、以下だけを規定している: - -- ファイル名は `AGENTS.md`、リポジトリルートに配置 -- ネスト時は**最近接優先、マージしない** -- 中身は任意の Markdown、必須セクション無し -- 優先順位は chat prompt > AGENTS.md - -サイズ上限・エンコーディング・不在時挙動・テンプレート機能は spec で規定されておらず、各エージェント実装の裁量。本チケットの取り決めは下記の通り spec と矛盾しない。 - -## 要件 - -### 探索 - -- Pod の cwd 直下に `AGENTS.md` があれば読む。親ディレクトリへの遡及は行わない。 -- 子ディレクトリのネスト AGENTS.md も扱わない(cwd 直下の1ファイルのみ)。 -- ファイルが存在しない場合は欠損ではなく「空」として扱い、エラーにしない。 - -spec のネスト解決(nearest-wins)は、Pod の cwd が1点に固定される都合で「cwd 直下の1ファイルのみ」に単純化している。サブプロジェクト向けにカスタム文脈を与えたい場合はそのディレクトリを cwd にして Pod を立てる運用。 - -### テンプレートへの露出 - -- `SystemPromptContext.files` マップに `agents_md` キーとして本文を挿入する。 -- 評価タイミングは system-prompt-template と同じく first turn 開始時の1回のみ。compact 後も再評価しない。 -- **不在・読み取り失敗時は `files` マップにキーを入れない**。テンプレ作者は `{% if files.agents_md is defined %}...{% endif %}` で存在をガードする前提(minijinja の `UndefinedBehavior::Strict` の下で安全に扱うため)。 - -### サイズ上限 - -- **64KB を上限**とする(約 20-25k token 相当、provider の 30k token/分レートリミットに対して余裕を持った値)。 -- 超過時は**先頭 64KB を採用し末尾に `\n\n[truncated: AGENTS.md exceeded 64KB limit]` を追記**。エラーにはしない。 -- `tracing::warn!` で切り詰めた旨をログ出力する。 - -### 読み取り失敗時 - -- 非 UTF-8、I/O エラーなど `std::fs::read_to_string` が失敗するケースは、**`tracing::warn!` を出して `files` マップにキーを入れずに続行**。Pod 起動は成功扱い。 -- ファイル自体が存在しない場合は正常系(warn は出さない)。 - -### warn の伝達 - -- 現状の Pod/Worker 層には「ユーザー可視のユーザー通知チャネル」は未整備(`tracing::warn!` のみ)。本チケットでは `tracing` に出すだけとし、TUI バナーや PodEvent への露出は別チケットで扱う。 - -## 完了条件 - -- Pod の cwd 直下に `AGENTS.md` を置き、`files.agents_md` を参照するテンプレートの Pod を起動すると、その内容が first turn のシステムプロンプトに反映される。 -- `AGENTS.md` を置かない場合でも Pod が正常に起動し、テンプレート側で `{% if files.agents_md is defined %}` ガードを書いていればシステムプロンプトが壊れない。 -- 64KB 超の `AGENTS.md` は先頭 64KB + 切り詰め注記でレンダリングされ、`tracing::warn!` がログに出る。 -- 非 UTF-8 の `AGENTS.md` があっても Pod 起動は成功し、`files.agents_md` は未定義扱いになり、`tracing::warn!` がログに出る。 -- compact を跨いで AGENTS.md の内容が再読込されないことを担保する。 - -## 範囲外 - -- 親ディレクトリ方向への遡及、ネスト AGENTS.md のマージ。 -- ユーザ単位の共通設定ファイル(将来 Insomnia 独自の user config として別チケット化)。 -- AGENTS.md 以外のフォーマット(CLAUDE.md 等)への自動対応。必要なら各自シンボリックリンクで解決する前提。 -- ユーザー可視の warn 通知チャネル(TUI バナー等)。本チケットは `tracing::warn!` に出すのみ。 diff --git a/tickets/agents-md-ingestion.review.md b/tickets/agents-md-ingestion.review.md deleted file mode 100644 index 481ab6e3..00000000 --- a/tickets/agents-md-ingestion.review.md +++ /dev/null @@ -1,77 +0,0 @@ -# レビュー: AGENTS.md の取り込み - -対象差分: `crates/pod/src/{lib,pod,agents_md}.rs`, `crates/pod/tests/system_prompt_template_test.rs`(いずれも未コミット) - -## 要件達成状況 - -| 要件 | 検証 | -|---|---| -| cwd 直下の `AGENTS.md` のみ読む(親遡及・ネスト無し) | ✅ `agents_md.rs:28` で `cwd.join("AGENTS.md")` 単発 | -| 不在時は `files.agents_md` キー自体を省略 | ✅ `pod.rs:407-410` が `Option::Some` のときのみ挿入 | -| サイズ上限 64KB、超過時は先頭 64KB + 注記 + warn | ✅ 定数 `AGENTS_MD_LIMIT` と `TRUNCATION_NOTICE`、`oversized_file_is_truncated_with_notice` で検証 | -| 非 UTF-8 / 読み取り失敗時は warn + キー省略 | ✅ `non_utf8_returns_none`, open/read エラー経路に warn | -| 不在時は warn を出さない(正常系) | ✅ `ErrorKind::NotFound` だけ warn をスキップ | -| first turn 時に1回のみ評価、compact 後も再評価しない | ✅ 既存の `ensure_system_prompt_materialized` 機構に乗る。`agents_md_not_reread_after_compact` が file を mutate + compact 経由で明示的に担保 | -| `{% if files.agents_md is defined %}` で取得可能(B 案) | ✅ `agents_md_absent_leaves_key_undefined`, `agents_md_is_injected_when_present` 両方でテンプレ側のガードを検証 | - -完了条件4ケース(正常・不在・64KB 超過・非UTF-8)および compact 跨ぎ、合計5ケースすべてが unit / integration テストで自動化されている。**要件は完全に達成**。 - -## アーキテクチャ統合 - -- 実装を `pod.rs` に書き足さず `crates/pod/src/agents_md.rs` として別ファイルに切り出している。機能モジュール分離のプロジェクト方針と整合。 -- `read_agents_md` は `pub(crate)` に閉じており、外部 API を汚さない。 -- `pod.rs` 側の配線は 5 行の最小侵襲(既存 `files: BTreeMap` の作り方を変えただけ)。system_prompt_template の評価タイミング保証を自前で再実装せず、既存の materialize 機構に素直に乗っている。 - -アーキテクチャ的な懸念なし。 - -## 指摘事項 - -### 1. 🟡 切り詰め時の UTF-8 pop ループが「意図的な非 UTF-8 の大型ファイル」を通してしまう - -`agents_md.rs:61-65`: - -```rust -if truncated { - while !buf.is_empty() && std::str::from_utf8(&buf).is_err() { - buf.pop(); - } -} -``` - -コメントは明確: 「切り詰め時だけ partial UTF-8 を trim する。切り詰めていない本物の非 UTF-8 は拒否する」。意図は正しい。 - -しかし副作用として: - -- 小さい非 UTF-8 ファイル → `String::from_utf8` で拒否(OK) -- **64KB を超える非 UTF-8 ファイル**で、先頭が偶然 UTF-8 valid な prefix を持つ場合 → pop ループで末尾の invalid バイトを削り落として採用されてしまう - -つまり「非 UTF-8 は拒否」という要件が**ファイルサイズによって挙動が変わる非一貫**になっている。 - -**実害**: -- 普通の AGENTS.md が意図せずこの経路に落ちることは稀(エディタは UTF-8 で保存する) -- 巨大バイナリを誤配置した場合のフォールバックとしては「読める prefix を採用」は妥当とも言える - -**判断**: 実害は小さいので**必須修正ではない**。ただし挙動を意図したものとして固めるなら、`str::from_utf8(&buf).unwrap_err().valid_up_to()` で truncation 時の末尾処理をより正確に(partial UTF-8 分だけ削る)記述するとコードが仕様と一致する。将来の誰かが読んで混乱しないためにも、コメントに「pop ループは truncation 末尾の partial UTF-8 だけを狙っているが、pathological には valid prefix を持つ非 UTF-8 ファイルも通る」と明記しておくと親切。 - -### 2. 🟢 non-UTF-8 / I/O エラー時に `files` キーが省略されることの integration 検証 - -`agents_md.rs` の unit test で `read_agents_md` が `None` を返すことは確認されているため、`pod.rs:407-410` の `if let Some(...)` 経路を通れば挙動は担保される。追加の integration test は不要。 - -ただし将来 `pod.rs` 側で `read_agents_md` の戻り値の扱いを変更した場合に unit test だけでは気付けない可能性がある。気になるなら `agents_md_non_utf8_leaves_key_undefined` という integration を1つ足す程度。**任意**。 - -### 3. 🟢 pod.rs で `std::collections::BTreeMap` がインラインのまま - -`pod.rs:406` `let mut files = std::collections::BTreeMap::new();` がフルパス。既存コードスタイルへの追随のはずなので不問。必要なら別途クリーンアップ。 - -## 良い点 - -- `File::open` → `take(LIMIT + 1)` → `read_to_end` の **extra-byte トリック**でメタデータが信頼できないファイルシステム(pipe, procfs 等)でも truncation を正しく検出できる -- `oversized = metadata_len.map(|n| n as usize > AGENTS_MD_LIMIT)` と `buf.len() > AGENTS_MD_LIMIT` の**二重チェック** -- エラー経路ごとに warn のフィールド(`path`, `error`, `limit`)が構造化されている -- テストの網羅性: absent / small / oversized / exact limit / UTF-8 char boundary / non-UTF-8 / integration 3ケース = **計9テスト** -- `exact_limit_is_not_truncated` というオフバイワン検証が入っている(地味に偉い) -- 各コメントが **what ではなく why** を書いている(extra byte を読む理由、pop ループの条件分岐の意図) - -## 結論 - -**無条件で受け入れ可**。指摘1はコメント追記または valid_up_to ベースに書き換えを**任意で**検討、指摘2・3は不問。