From 553d67a910a6f098a3a04b8a1b267c76b8f6b44a Mon Sep 17 00:00:00 2001 From: Hare Date: Sat, 9 May 2026 03:19:30 +0900 Subject: [PATCH] =?UTF-8?q?docs(tickets):=20permission-extension-point?= =?UTF-8?q?=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 1 - tickets/permission-extension-point.md | 110 ------------------- tickets/permission-extension-point.review.md | 27 ----- 3 files changed, 138 deletions(-) delete mode 100644 tickets/permission-extension-point.md delete mode 100644 tickets/permission-extension-point.review.md diff --git a/TODO.md b/TODO.md index a8f49201..2ba2561a 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,6 @@ - 半自動開発運用 Workflow → [tickets/auto-maintain-workflow.md](tickets/auto-maintain-workflow.md) - Workflow を memory crate から独立させる → [tickets/workflow-crate-extraction.md](tickets/workflow-crate-extraction.md) - Prompt / Workflow 評価メトリクスと改善 Offer → [tickets/prompt-eval-metrics.md](tickets/prompt-eval-metrics.md) -- パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md) - Pod CLI: マニフェスト関連フラグの整理 → [tickets/pod-cli-manifest-flags.md](tickets/pod-cli-manifest-flags.md) - Pod: 空応答ターン (Submit 後 AI 応答ゼロで Pause/Cancel) を自動巻き戻し → [tickets/pod-empty-turn-rollback.md](tickets/pod-empty-turn-rollback.md) - Pod: 任意ターンからの Fork(複数ターン巻き戻しを汎用化) → [tickets/pod-session-fork.md](tickets/pod-session-fork.md) diff --git a/tickets/permission-extension-point.md b/tickets/permission-extension-point.md deleted file mode 100644 index 94924769..00000000 --- a/tickets/permission-extension-point.md +++ /dev/null @@ -1,110 +0,0 @@ -# パーミッション: パターンベースのツール実行制御 - -## 背景 - -現状の `Scope` はディレクトリ単位の書き込み制約で、静的な境界線。 -実際のエージェント運用では、ツール単位・引数パターン単位の動的な権限制御が必要になる。 - -OpenCode はパターンベースのルール(tool × pattern → allow/deny/ask)を持ち、 -`*.env` への書き込み拒否や `rm -rf` の実行拒否を宣言的に設定できる。 - -## 方針 - -Permission の評価点は `PreToolCall` Hook とする。マニフェストにルールを宣言し、 -insomnia 層が built-in の `PreToolCall` Hook として登録してツール呼び出し時に評価する。 - -`deny` はターン全体の Cancel/Abort ではない。対象 tool call を実行せず、 -permission denied を表す `is_error = true` の synthetic tool result を履歴に追加してターンを継続する。 -これにより provider が要求する `tool_use` / `tool_result` の対応を壊さず、LLM は拒否結果を見て別手段の検討やユーザーへの説明に進める。 - -`ask` は `deny` の代替ではなく、ユーザー承認待ちを明示する action として扱う。承認されれば元の tool call を実行し、拒否されれば `deny` と同じ synthetic tool result に落とす。 - -```toml -[permissions] -default_action = "allow" # allow | deny | ask - -[[permissions.rule]] -tool = "bash" -pattern = "rm *" -action = "deny" - -[[permissions.rule]] -tool = "file_write" -pattern = "*.env" -action = "deny" -``` - -allowlist 型にしたい場合: - -```toml -[permissions] -default_action = "deny" - -[[permissions.rule]] -tool = "read" -pattern = "*" -action = "allow" - -[[permissions.rule]] -tool = "grep" -pattern = "*" -action = "allow" -``` - -確認待ちを基本にしたい場合: - -```toml -[permissions] -default_action = "ask" - -[[permissions.rule]] -tool = "bash" -pattern = "rm *" -action = "deny" -``` - -評価順序: -1. `[permissions]` が無い場合、Permission 層は無効。従来通り実行する -2. `[permissions]` がある場合、`default_action` は必須 -3. `[[permissions.rule]]` は宣言順に評価し、最初に `tool` と `pattern` が一致した rule の `action` を採用する -4. 一致する rule が無ければ `permissions.default_action` を採用する - -## 設計ポイント - -- 設計原則3: 新しい trait は作らない。`PreToolCall` Hook として実装 -- 設計原則2: マニフェストに宣言した以上、insomnia 層が解決する -- Permission Hook は Pod が自動登録する built-in Hook とし、ユーザー追加 Hook より先に評価する -- `deny` は `PreToolAction::Abort` / 既存 `Skip` では表現しない。tool call 単位の拒否結果を履歴へ返すため、Worker 側に synthetic tool result を返せる action が必要 -- `ask` アクションは Pod Protocol の拡張が必要(Event に `PermissionRequest`、Method に `PermissionReply` を追加) -- `ask` を処理できない実行環境では暗黙に待機しない。設定時に validation error とするか、fail closed で `deny` 相当の synthetic tool result に落とす -- `Scope` との関係: Scope は書き込みの物理的境界、Permission はツール実行のポリシー。補完関係 -- ルール評価はパターンマッチのみ。コンテキスト依存の判断はしない(シンプルに保つ) - -## 段階的実装 - -1. **拡張ポイントの記録**(今): docs/pod.md の拡張ポイント表に追加 -2. **deny/allow の実装**(ツール実装時): `default_action` と rule 評価を manifest に追加し、built-in `PreToolCall` Hook でパターン評価 -3. **拒否 tool result の実装**: `deny` が turn Abort ではなく synthetic error tool result として履歴に入るよう Worker の pre-tool action を拡張 -4. **ask の実装**(Protocol 拡張時): Method/Event に Permission 関連メッセージを追加し、承認後に元 tool call を実行、拒否時は synthetic error tool result を返す - -## 受け皿になる外部仕様 - -### Agent Skills `allowed-tools` - -`tickets/agent-skills.md` で ingest した SKILL.md の frontmatter には agent-skills 仕様の experimental field である `allowed-tools` (例: `["Read", "Bash"]`) が含まれる場合がある。`crates/memory/src/skill.rs::parse_skill_md` 時点では `tracing::warn!` で受け流しているだけで、実効化していない。 - -本チケットの Permission 層が固まった時点で、Skill 由来 Workflow を実行中のみ当該 skill の `allowed-tools` リストに含まれるツールしか走れない形で反映する。スコープは「Workflow 実行中」相当 (Workflow の system message が context に乗っているターン) に限定する想定。skill 単位で local な permission 集合を持つので、グローバルな `[[permissions.rule]]` ルールとは独立に評価する。 - -実装上の足がかり: -- `WorkflowRecord` の出所は `WorkflowSource::Skill { dir }` で識別済み (`crates/memory/src/workflow.rs`)。`dir` は manifest `[skills] directories` に書かれた skill ルートそのもの -- 受け皿実装時に `SkillFrontmatter::allowed_tools` の保持先を `WorkflowRecord` に伸ばすか、別の SkillRecord registry を持つかは本チケット内で決める -- 現状の `tracing::warn!` は受け皿実装と同時に消す - -## 依存チケット - -- ~~[remove-hook-module.md](remove-hook-module.md)~~ — 完了。PreToolCall は Pod 層の `hook::Hook` として利用可能 - -## Review -- 状態: Approve with follow-up -- レビュー詳細: [./permission-extension-point.review.md](./permission-extension-point.review.md) -- 日付: 2026-05-05 diff --git a/tickets/permission-extension-point.review.md b/tickets/permission-extension-point.review.md deleted file mode 100644 index 21dcfca2..00000000 --- a/tickets/permission-extension-point.review.md +++ /dev/null @@ -1,27 +0,0 @@ -# Review: パーミッション: パターンベースのツール実行制御 - -## 前提・要件の確認 -- `[permissions]` が無い場合は無効、ある場合は `default_action` 必須: 満たされている。`PodManifestConfig.permissions` は `Option` で、resolve 時に `permissions.default_action` 不在を `MissingField` にしている (`crates/manifest/src/config.rs:39-42`, `crates/manifest/src/config.rs:430-440`)。 -- `[[permissions.rule]]` と first-match / fallback 評価: 満たされている。rule は `rename = "rule"` で TOML 形に対応し、`PermissionHook::action_for` が宣言順 `find` + `default_action` fallback を行う (`crates/manifest/src/lib.rs:246-276`, `crates/pod/src/permission.rs:25-35`)。 -- built-in `PreToolCall` Hook として、ユーザー Hook より先に評価: 満たされている。Pod 構築直後に `apply_permissions_from_manifest` が呼ばれ、permission hook は通常の user hook 登録前に `HookRegistryBuilder` へ追加される (`crates/pod/src/pod.rs:336-337`, `crates/pod/src/pod.rs:2295-2296`, `crates/pod/src/permission.rs:39-45`)。 -- `allow`: 満たされている。permission action `Allow` は `PreToolAction::Continue` に変換される (`crates/pod/src/permission.rs:49-56`)。 -- `deny` は turn Abort ではなく synthetic error tool result: 満たされている。`PreToolAction::SyntheticResult` が追加され、Worker は tool を実行せず result を commit する (`crates/llm-worker/src/interceptor.rs:48-64`, `crates/llm-worker/src/worker.rs:757-769`, `crates/llm-worker/src/worker.rs:1136-1144`)。 -- provider-visible `is_error` の保持: 満たされている。`ToolResult` / `Item::ToolResult` / session replay に `is_error` が通り、Anthropic request へ `tool_result.is_error` が投影される (`crates/llm-worker/src/tool.rs:281-310`, `crates/llm-worker/src/llm_client/types.rs:74-89`, `crates/session-store/src/logged_item.rs:34-40`, `crates/llm-worker/src/llm_client/scheme/anthropic/request.rs:327-347`)。 -- `ask`: 現時点のスコープとしては満たされている。型として受け付け、承認 protocol 未実装環境では待機せず fail-closed synthetic error result にしている (`crates/pod/src/permission.rs:53-56`, `docs/pod-factory.md:218-235`)。 - -## アーキテクチャ・スコープ -- `llm-worker` には汎用的な `SyntheticResult` action だけを追加し、manifest policy 本体は Pod 層の `permission.rs` に置かれているため、層の分離は保たれている。 -- 新しい trait は追加されておらず、既存の Hook / Interceptor 境界上で実装されている。 -- `Scope` の物理境界には手を入れておらず、permission は tool 実行ポリシーとして分離されている。 -- `ask` の protocol/UI 実装に踏み込まず fail-closed として明示している点は、今回の段階的実装として妥当。 - -## 指摘事項 -### Non-blocking / Follow-up -- mixed allow/deny 時の tool result 順序が tool call 順とずれる可能性がある — synthetic result は `synthetic_results` に集めて実行済み result の末尾へ `extend` されるため、先頭 call が deny で後続 call が allow の場合、履歴上の result 順が逆転する (`crates/llm-worker/src/worker.rs:745-769`, `crates/llm-worker/src/worker.rs:821-823`)。provider が call id で対応を取れる限り致命的ではないが、会話の時系列としては call 順を保つ方が安全。 -- manifest から Pod へ permission が実際に効く統合テストが薄い — manifest resolve と `PermissionHook` 単体、および Worker の synthetic result はカバーされているが、`[permissions]` を含む Pod 構築から tool deny までの結合テストがあると回帰検出しやすい。 - -## 判断 -Approve with follow-up — 要件は満たしており設計上の逸脱もないが、result 順序と Pod 統合テストは後続で固める価値がある。 - -## 確認コマンド -- `cargo test -p pod permission -- --nocapture`