# Review: system-prompt-template ## 前提・要件の確認 チケットの完了条件と要件をひとつずつ実装と照合した。 ### 評価モデル ✓ - `Pod` が `system_prompt_template: Option` を保持し、`Pod::from_manifest` でパース、`Pod::run`/`Pod::resume` 冒頭の `ensure_system_prompt_materialized` が `Option::take()` で1回だけ materialize する — `pod.rs:385-412`。 - 既存の `ensure_interceptor_installed` の直後に置かれており、hook と対称な ensure_\* パターンに揃っている。 - materialize 後は `worker.set_system_prompt(rendered)` で Worker の既存フィールドに流し込む。以降の compact 経由でも template フィールドは `None` のため再 render 不可 — 構造で担保されている。 ### テンプレート構文・変数 ✓ - minijinja 2.19 を採用、`UndefinedBehavior::Strict`、エスケープは `{{ '{{' }}`、single-named template 方式。 - 組み込み変数の初期セット(`date` / `time` / `datetime` / `cwd` / `scope.{readable,writable,summary}` / `tools` / `files`)が `SystemPromptContext::to_minijinja_value` で全部供給されている — `system_prompt.rs:82-135`。 - `files` は常に空 BTreeMap で、`{% if files.x is defined %}` 構文で安全にガードできることをテストで確認(`system_prompt.rs:234`)。 ### マニフェスト上の記法 ✓ - 既存 `WorkerManifest.system_prompt: Option` をテンプレート文字列として解釈。新フィールドは増えていない。 - `apply_worker_manifest` では敢えて `system_prompt` を触らず、doc コメントで理由を明示(`pod.rs:829-834`)。 ### 責務の分離 ✓ - テンプレート機構は `crates/pod/src/system_prompt.rs` に閉じ、`llm-worker` はテンプレートの存在を一切知らない。 - 唯一 llm-worker 側に入った変更は `ToolServerHandle::flush_pending` の `pub(crate) → pub` 化。これはテンプレート render 前にツール名を確定させるためで、Worker 層の機能性は変わらず、理由も doc で明示されている(`tool_server.rs:72-82`)。基盤原則への違反ではない。 ### エラー処理 ✓ - 構文エラー → `PodError::InvalidSystemPromptTemplate`、render エラー → `PodError::SystemPromptRender`。両方とも `#[source]` 付きで `SystemPromptError` をラップ。fail-fast 一貫。 ### 付随する変更 ✓ - `create_session` を `from_manifest` から外し、`ensure_session_head` 側で `create_session_with_id` を呼ぶように遅延化。`session-store` 側に `create_session_with_id` を新設 — ID を先に確定しつつ初回 log append を遅延するユースケース用、doc コメント付き。 - 結果として `SessionStartState.system_prompt` が materialize 後の値で埋まる。`session_start_state_captures_rendered_prompt` テストで log entry を直接検証。 ### 完了条件 ✓ - マニフェスト → テンプレート解釈 → 組み込み変数展開 → LLM system message 反映の一連の流れが `materialise_on_first_turn_populates_worker` で確認されている。 - compact を挟んでもシステムプロンプトが再評価されないことを `compact_preserves_system_prompt` が直接検証。 - `files` は空 Map の器として用意済み(AGENTS.md 供給は別チケット)。 ### テスト - `crates/pod/src/system_prompt.rs` 単体: 7本(parse 成功/失敗、date/cwd/tools 置換、未定義変数、`{{` エスケープ、scope.summary、files 空)。 - `crates/pod/tests/system_prompt_template_test.rs` 統合: 7本(parse エラー、first run 前未 materialize、first turn で materialize、SessionStart capture、render エラー伝播、2 turn 間で一意、compact 越え保存)。 - `cargo test --workspace`: 323 passed / 0 failed。 ## 指摘事項 いずれも blocking ではない。load-bearing な修正は無い。 ### 1. `Pod::new` が `manifest.worker.system_prompt` を黙って無視する(軽微) `Pod::new` は manifest を受け取るが template parse は行わず、`system_prompt_template: None` で初期化する。そのため `Pod::new` 経由で manifest 由来の system_prompt を効かせたい場合はテスト側で `SystemPromptTemplate::parse` + `set_system_prompt_template` を手動で呼ぶ必要がある。 現状 `Pod::new` は production からは使われておらず、用途はテストのみ(`controller_test.rs`, `system_prompt_template_test.rs`)。production 経路は `Pod::from_manifest` 一本で、そちらは正しく parse する。 - **判断**: 現状維持で OK。`Pod::new` は低レベルコンストラクタであり、manifest の解釈を完全に担う責務は持っていない(`apply_worker_manifest` 相当の処理も外から呼ぶ想定)。ただし doc コメントで「`system_prompt` template は Pod::new の対象外、必要なら `set_system_prompt_template` を呼べ」と一行書いておくと事故を防ぎやすい。 ### 2. `flush_pending` の public 化は「Pod 側でツール名を先取りする」ため(情報共有) Pod の `ensure_system_prompt_materialized` は Worker の lock() より前に走るので、その時点で pending factory はまだ materialize されていない可能性がある。そこで `worker.tool_server_handle().flush_pending()` を明示的に呼んでツール一覧を確定させている(`pod.rs:393`)。 flush_pending は冪等であり(実装上も空 Vec を drain するだけ)、Worker::lock() 側での二重呼び出しも問題ない。API 的には「force-materialise を higher layer が要求できる」という意味付けが明確なので、doc コメントがあれば十分。既に doc は追加済み。 - **判断**: 現状維持で OK。`llm-worker` を低レベル基盤に留める原則に照らしても、「明示的フラッシュ」という低レベル操作を公開するだけで、テンプレートの概念は漏れていない。 ### 3. `SystemPromptTemplate` が単一テンプレート専用に `Environment<'static>` を抱える(美観) `minijinja::Environment` はテンプレートの名前空間を持つが、ここでは固定名 `"system_prompt"` で1枚だけ登録している。代替としては `minijinja::Template::new` 相当のスタンドアロンパースもあるが、minijinja の public API は Environment 経由が主流で、コストは Arc 1 つ分。 - **判断**: 現状維持で OK。将来 filter / function を足したくなった時に Environment を使うほうが素直。 ### 4. `render` 時の `Environment::get_template` が `Result` を返すことへの扱い(微細) `SystemPromptTemplate::render` は `get_template` の失敗も `SystemPromptError::Render` にマップしているが、parse 時に必ず登録しているので実質到達しない経路。`expect` でも良いが、`.map_err` で統一されているのは無害。 - **判断**: 現状維持で OK。 ### 5. `SystemPromptContext` の `tool_names: Vec` を `&[String]` にできる余地(微細) 所有の必要性は無いので参照でも良い。ただし `ensure_system_prompt_materialized` が一時 Vec を作る以上、所有のほうが呼び出し側が楽。AGENTS.md 取り込み時に `files` を構築するコードと書き味を合わせる意味でも所有のままで良い。 - **判断**: 現状維持で OK。 ## 結論 **accept**。チケットの要件を過不足なく満たしており、テストも 1:1 で要件項目を検証している。指摘事項はいずれも doc コメントの追記レベルで、必須ではない。