5.7 KiB
レビュー: 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:
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は不問。