instruction-file-refs完了

This commit is contained in:
Keisuke Hirata 2026-04-16 13:08:08 +09:00
parent 381d31a1dc
commit 710220c920
3 changed files with 0 additions and 187 deletions

View File

@ -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)
- [ ] instruction のファイル参照化と system prompt の hardcode wrap → [tickets/instruction-file-refs.md](tickets/instruction-file-refs.md)
- [ ] ネイティブ GUI クライアント MVP → [tickets/native-gui-mvp.md](tickets/native-gui-mvp.md)
- [ ] TUI 拡充
- [ ] Pod の明示的 shutdown → [tickets/tui-pod-shutdown.md](tickets/tui-pod-shutdown.md)

View File

@ -1,6 +1,4 @@
## Tool usage
You have access to these tools: {{ tools | join(", ") }}.
Prefer the most specific tool for the job. When reading files you already know the path of, use the file-read tool directly instead of searching.
When searching, use grep/glob primitives rather than shell pipelines.

View File

@ -1,184 +0,0 @@
# instruction のファイル参照化と system prompt 末尾の固定セクション
## 背景
pod-factory で `worker.system_prompt` を minijinja 文字列として扱う形にしたが、その後の運用方針の整理で以下のズレが見えてきた:
- **manifest を手で触らない前提**なので、`system_prompt` に minijinja 文字列をインラインで書く設計そのものが過剰。人間が書くのはプロンプトの**中身ファイル**だけで十分
- **AGENTS.md を `files.agents_md` として user template に露出**しているが、ユーザー領域のテンプレートに AGENTS.md 埋め込みを任せると、guard 忘れで静かに消えたり、preset を差し替えるたびに AGENTS.md の扱いが破綻する
- **scope はセキュリティ境界**なので、user の template が覚えていてくれる前提にすべきでない
- **prompt loader が by-name fallthrough**project → user → builtin を順に探索)になっており、「同名上書き」と「偶然同名」が区別できない曖昧さがある
- 現在バイナリに同梱されている `coder` / `reviewer` / `planner` / `common/tool-usage` は AI 任せで書いた placeholder で、設計者の意図が乗っていない
- 既存の `Scope::summary()` は許可されたパスを列挙するだけで、**`recursive = false` の情報が失われて**おり、非再帰ルールの意味が LLM に伝わらない
## ゴール
1. `worker.system_prompt` を**ファイル参照フィールド** (`worker.instruction`) に置き換える
2. `$insomnia` / `$user` / `$workspace`**import-map 形式の prefix** でプロンプト資産を addressing
3. scope と AGENTS.md を **コード側で system prompt 末尾に付加する固定セクション**として注入し、user template が触れない領域にする
4. 組み込みプロンプトを `$insomnia/default` の 1 本に整理
5. `Scope::summary()` のフォーマットを改善し、`recursive = false` を明示する
## 方針
### instruction フィールド
- manifest schema: `worker.system_prompt: Option<String>`**`worker.instruction: Option<String>`** に置き換えるminijinja 文字列を持つフィールドを消し、ファイル参照だけを受ける)
- 値は import-map 記法のファイル参照: `$insomnia/default` / `$user/my-style` / `$workspace/custom`
- `.md` 拡張子は省略する
- **デフォルト値**は `$insomnia/default`。manifest で `instruction` を書かなければこれが使われる(`defaults.rs` に `DEFAULT_INSTRUCTION: &str = "$insomnia/default"` を追加)
- サブディレクトリ許容: `$insomnia/common/header``resources/prompts/common/header.md` を指す
### Prefix 解決
| prefix | 解決先 |
|---|---|
| `$insomnia` | バイナリ同梱の `resources/prompts/``include_dir!` |
| `$user` | `$XDG_CONFIG_HOME/insomnia/prompts/`factory が設定した user prompts dir |
| `$workspace` | `<project>/.insomnia/prompts/`factory が設定した project prompts dir |
- 指定した prefix の dir に該当ファイルが無ければ **hard error**fallthrough しない)
- 現在の「by-name で層を fallthrough して探す」ロジックは撤廃
### Unqualified include の相対解決
`{% include "name" %}` のように prefix 無しで書かれた場合は、**include を書いたファイル自身の prefix + ディレクトリからの相対**で解決する:
- `$insomnia/default.md` 内の `{% include "sub" %}``$insomnia/sub`
- `$insomnia/common/header.md` 内の `{% include "nested/foo" %}``$insomnia/common/nested/foo`
- `$user/custom.md` 内の `{% include "$insomnia/default" %}` → 明示的 prefix が優先
これにより「同じディレクトリ内でかたまって動くプロンプト集」を自然に書ける。
### system prompt 末尾の固定セクション
`SystemPromptTemplate` のレンダリング後に、Rust 側で以下の構造を**必ず**付加するuser template からは触れない):
```
<instruction file のレンダ結果>
---
## Working boundaries
{scope.summary()}
{% if agents_md %}
---
## Project instructions (AGENTS.md)
{agents_md 本文}
{% endif %}
```
- 付加部分は Rust の `const` またはハードコードされたフォーマット文字列として実装し、`resources/` には置かないuser が触れる場所に置くと、触らないでほしいものが触れる場所に置かれる矛盾になる)
- scope セクションは **必ず** 出力される
- AGENTS.md セクションは不在時に省略(区切り `---` ごと省略)
### `SystemPromptContext.files` の削除
- `files` フィールドごと撤廃(元々 AGENTS.md 専用の予約席)
- user template から AGENTS.md を参照する手段は無くなる(末尾セクションが面倒を見る)
### `Scope::summary()` フォーマットの改善
`crates/manifest/src/scope.rs``Scope::summary()` を以下の方針で書き直す:
- **非再帰ルールを `[non-recursive]` でマークする**。例:
```
Readable:
- <local-path> [non-recursive]
Writable:
- <repo>
```
- マーカーの位置はパスの末尾(パースしやすさより人間可読性を優先)
- recursive = true の場合は何も足さない(デフォルトなので無印)
- deny ルールは現行どおり summary には出さない(`from_config` の時点で effective permission に焼き込まれるため)
- `Readable` / `Writable` のセクション分けは現行どおり
この変更により、最終セクションが出力する scope 情報が LLM にとって正確になる。
### 組み込みプロンプトの整理
- **削除**: `resources/prompts/coder.md` / `reviewer.md` / `planner.md` / `common/tool-usage.md`
- **新規**: `resources/prompts/default.md` 1 本のみ(内容は本チケットの実装時に author が記述)
- 将来、役割ごとの preset を復活させたくなったら別チケットで追加するpreset 概念そのものが pod-factory の範囲外だった経緯もあり、安易に戻さない)
## 要件
### Schema
- `manifest::WorkerManifest``manifest::WorkerManifestConfig` から `system_prompt` を削除、`instruction: Option<String>` を追加(部分形は Option、resolve 時に `defaults::DEFAULT_INSTRUCTION` で埋める)
- `manifest::defaults``DEFAULT_INSTRUCTION: &str = "$insomnia/default"` を追加
### Loader
- `PromptLoader` を prefix addressing 版に書き換える
- API: `PromptLoader::resolve(ref: &str, current: Option<&PromptRef>) -> Result<String, Error>`
- `ref``$prefix/path` 形式なら該当 dir を引く
- 素の `name` なら `current` の prefix + dir を前置して再帰 resolve
- `current` が無い状態で素の `name` が来たら **error**
- minijinja の `Environment::set_loader` 内で `current` を追跡する
### 末尾セクションの組み立て
- `SystemPromptTemplate::render` 相当の経路で、**user template 出力 + 固定末尾セクション**を連結する
- scope / AGENTS.md を formatter に渡す
- `Pod::ensure_system_prompt_materialized` の既存フローを素直に置き換える
### `Scope::summary()`
- `recursive = false` のルールに `[non-recursive]` マーカーを付ける
- 既存の `summary` 形式は維持(`Readable:` / `Writable:` のセクション + インデントのフォーマット)
- `crates/manifest/src/scope.rs` の既存テスト `summary_lists_readable_and_writable` / `summary_excludes_deny_rules` を更新または補完
### テスト
- 既存: `include_resolves_builtin_prompt` / `agents_md_is_injected_when_present` / `files_reserved_namespace_is_empty` / `files_map_*` 系を書き換えまたは削除
- 新規:
- `instruction_default_resolves_to_insomnia_default`
- `instruction_prefix_addressing_{insomnia,user,workspace}`
- `include_unqualified_resolves_relative_to_current_prefix`
- `include_explicit_prefix_overrides_relative`
- `prefix_with_missing_file_is_hard_error`
- `trailing_section_always_contains_scope_summary`
- `trailing_section_contains_agents_md_when_present`
- `trailing_section_omits_agents_md_when_absent`
- `scope_summary_marks_non_recursive_rules`
### ドキュメント
- `docs/pod-factory.md` を更新:
- `files.agents_md` の記述を削除
- loader の by-name fallthrough 説明を prefix addressing に置き換え
- `instruction` フィールドと `$insomnia/default` デフォルトの記述を追加
- system prompt 末尾の固定セクション構造を図示
- 必要なら `docs/system-prompt-template.md` も追随
## 他チケットとの関係
- **pod-factory (完了済み)**: 本チケットは pod-factory で実装した `PromptLoader` の**一部書き換え**になる。特に「3 層 by-name fallthrough」は撤廃される。pod-factory 自体をロールバックしない
- **tickets/native-gui-mvp.md**: 直接の交差なし。GUI が instruction を差し替えたいときは同じ prefix 形式で渡せば良い
- **tickets/protocol-design.md**: protocol 非依存
- **tickets/agents-md-ingestion.md (完了済み)**: AGENTS.md 読み取り経路は維持。表面の user template 経路だけが消える
## 完了条件
- manifest `[worker]` から `system_prompt` が消え、`instruction` でファイル参照を渡す形になっている
- `instruction` を省略すれば `$insomnia/default` が使われる
- `$insomnia` / `$user` / `$workspace` の 3 種の prefix でプロンプト資産を参照できる
- prefix 無しの `{% include %}` が include 元ファイルの prefix に相対解決される
- 未知の prefix や不在ファイルは hard error になる
- Pod の system prompt は常に「instruction の render 結果 + scope 要約 + (AGENTS.md があれば)」の構造で組み立てられる
- `SystemPromptContext` から `files` が削除され、user template から AGENTS.md を参照する手段は残っていない
- `Scope::summary()``recursive = false` ルールに `[non-recursive]` マーカーを付ける
- `resources/prompts/``default.md` 1 本のみ
- 上記挙動がすべて単体テストで担保されている
- `docs/pod-factory.md` が新方針に追随している
## 範囲外
- **preset 概念の復活**: 削除した `coder` / `reviewer` / `planner` を preset として体系化する議論は別チケット
- **instruction をファイル参照以外にする拡張**: インライン minijinja、TOML 配列、等は入れない
- **CLI 変更**: `pod` バイナリの flag は `--overlay` で instruction を上書きできる(`--overlay 'worker.instruction = "$user/foo"'`)形でそのまま使える。新規 flag は追加しない
- **テンプレートエンジンの差し替え**: minijinja を維持
- **`default.md` 本文の著者判断**: ticket の範囲は枠組み。本文は実装者が書く
- **scope summary に deny ルールを出す改善**: 現行どおり deny は effective permission に焼き込むだけで、summary には出さない