diff --git a/TODO.md b/TODO.md index 1f28ebf5..dc0fa005 100644 --- a/TODO.md +++ b/TODO.md @@ -22,7 +22,6 @@ - 巻き戻されたターンの入力テキストを編集領域に復元 → [tickets/tui-empty-turn-restore.md](tickets/tui-empty-turn-restore.md) - セッションコンテキスト長 / ウィンドウ占有率の常時表示 → [tickets/tui-context-usage-indicator.md](tickets/tui-context-usage-indicator.md) - Manifest: Tool Output / File Upload 上限の分離とデフォルト緩和 → [tickets/manifest-output-upload-limits.md](tickets/manifest-output-upload-limits.md) -- FileRef / file tools の symlink 診断と外部参照導線 → [tickets/file-ref-symlink-diagnostics.md](tickets/file-ref-symlink-diagnostics.md) - Prune: 保護境界を turn 数から末尾 token budget に置き換え → [tickets/prune-token-budget.md](tickets/prune-token-budget.md) - メモリ機構 - 使用頻度メトリクス + Knowledge 化候補レポート → [tickets/memory-usage-metrics.md](tickets/memory-usage-metrics.md) diff --git a/tickets/file-ref-symlink-diagnostics.md b/tickets/file-ref-symlink-diagnostics.md deleted file mode 100644 index 81261db8..00000000 --- a/tickets/file-ref-symlink-diagnostics.md +++ /dev/null @@ -1,82 +0,0 @@ -# FileRef / file tools の symlink 診断と外部参照導線 - -## 背景 - -insomnia 開発では、外部の参考プロジェクトを `ghq` のローカル clone として参照する運用がある。今回、workspace 内へ外部 clone を指す symlink を置き、`@tmp/` として読ませようとしたが、参照側からは期待通りに読めなかった。 - -このような symlink は、壊れている場合、相対リンクの基準が利用者の期待と違う場合、または canonicalize 後の target が Pod の readable scope 外に出る場合がある。現状は `file not found` のように見えやすく、壊れた symlink なのか、scope 外なのか、Glob / FileRef が symlink directory を辿らない仕様なのか判断しにくい。 - -一方で、symlink を無条件に辿ると workspace scope の外へ読み書きできる escape になるため、単純に許可してはいけない。canonicalized target が effective scope 内かどうかを明確に検査し、失敗時は理由を出す必要がある。 - -## 要件 - -### symlink 診断 - -FileRef / completion / read 系の file handling で、対象 path または途中 component が symlink の場合に、失敗理由を区別して報告できるようにする。 - -最低限、以下を区別する。 - -- symlink target が存在しない(broken symlink) -- symlink target が Pod の readable scope 外に解決される -- target は scope 内だが directory traversal / glob 側が symlink directory を辿っていない -- target は scope 内だが file / directory 種別が期待と違う - -エラー文には、可能な範囲で元 path と解決後 target を含める。 - -### scope safety - -symlink は canonicalize 後の target で scope 判定する。 - -- read: canonicalized target が readable scope 内の場合のみ許可 -- write: canonicalized target が writable scope 内の場合のみ許可 -- scope 外の場合は拒否し、`read scope に target を追加する` / `workspace 内へコピーする` / `正しい symlink を作り直す` などの対処を示す - -既存の symlink escape 防止テストは維持する。 - -### 外部 ghq 参照の導線 - -外部参考プロジェクトを `ghq` から読む運用に対して、推奨手順を明文化する。 - -候補: - -- Pod 起動時 / spawn 時に ghq clone の実体 path を read scope に入れる -- workspace 内 symlink を使う場合も、target が readable scope 内に入っている必要があることを明記する -- broken relative symlink を検出した場合、絶対 symlink または正しい相対 symlink を促す - -### 対象 surface - -少なくとも以下のいずれか、または共通層を通じて同等の挙動になること。 - -- submit 時 `Segment::FileRef` の resolver -- TUI completion の file candidate 表示 -- generic file read / glob 系 tool -- auto-read / fs view が同じ file handling を使う場合はその経路 - -## 範囲外 - -- readable scope 外 symlink を安全確認なしに自動許可すること -- symlink target を自動で scope に追加すること -- ghq clone の自動探索や自動 clone -- write scope の owner handoff / Pod sleep など権限モデル自体の変更 -- Git submodule / worktree の設計変更 - -## 完了条件 - -- broken symlink、scope 外 symlink、scope 内 symlink directory の挙動が区別できる -- scope 外 symlink は canonicalized target で拒否され、明確なエラーになる -- scope 内 symlink file / directory は、仕様として許可するか拒否するかが明文化され、テストされている -- FileRef または file tool のテストで symlink 経路がカバーされる -- ghq など外部参照プロジェクトを読む場合の推奨手順が docs または ticket 内に記録されている - -## 参照 - -- `crates/tools/src/scoped_fs.rs` -- `crates/tools/tests/edge_cases.rs` -- `crates/pod/src/pod.rs` `resolve_file_refs` -- `crates/pod/src/fs_view.rs` -- `crates/manifest/src/scope.rs` - -## Review -- 状態: Approve with follow-up -- レビュー詳細: [./file-ref-symlink-diagnostics.review.md](./file-ref-symlink-diagnostics.review.md) -- 日付: 2026-05-07 diff --git a/tickets/file-ref-symlink-diagnostics.review.md b/tickets/file-ref-symlink-diagnostics.review.md deleted file mode 100644 index 0353b423..00000000 --- a/tickets/file-ref-symlink-diagnostics.review.md +++ /dev/null @@ -1,31 +0,0 @@ -# Review: FileRef / file tools の symlink 診断と外部参照導線 - -## 前提・要件の確認 - -- broken symlink / scope 外 symlink / symlink directory / file-vs-directory の診断: 概ね満たされている。`ToolsError` に `BrokenSymlink`、`SymlinkOutOfScope`、`SymlinkTargetIsDirectory`、`SymlinkDirectoryNotTraversed` が追加され、元 path と target を含むエラー文になっている(`crates/tools/src/error.rs:19-58`)。 -- scope safety: 満たされている。`ScopedFs::read_bytes` / `write` は引き続き `scope.is_readable` / `scope.is_writable` を通し、symlink target が scope 外の場合は許可せず診断へ落としている(`crates/tools/src/scoped_fs.rs:108-141`, `crates/tools/src/scoped_fs.rs:160-226`)。既存 edge-case test でも外部 target が書き換わらないことを確認している(`crates/tools/tests/edge_cases.rs:83-158`)。 -- scope 内 symlink file / directory の仕様: 満たされている。scope 内 symlink file は読み取り可能、既存 symlink file への write は symlink を置換せず canonical target を更新する仕様としてテストされている(`crates/tools/src/scoped_fs.rs:435-543`)。symlink directory root は Glob/Grep で辿らず明示エラーにする実装になっている(`crates/tools/src/glob.rs:97-147`, `crates/tools/src/grep.rs:254-304`)。 -- file tool のテスト: 満たされている。`ScopedFs` unit test と `edge_cases` で broken / out-of-scope / in-scope file / directory type がカバーされている(`crates/tools/src/scoped_fs.rs:389-543`, `crates/tools/tests/edge_cases.rs:83-158`)。 -- 外部参照導線: 満たされている。外部 clone を読む場合は実体 path を read scope に入れること、workspace symlink だけでは権限が増えないこと、Glob/Grep は symlink directory を辿らないことが文書化された(`docs/file-ref-symlinks.md:1-12`)。 - -## アーキテクチャ・スコープ - -- 変更は `tools` crate の scope-aware filesystem と Glob/Grep surface に閉じており、Pod / protocol / manifest の権限モデル自体には踏み込んでいない。チケットの範囲外である scope 自動追加や symlink の無条件許可も入っていない。 -- symlink 診断は `ScopedFs` の helper と `ToolsError` に集約されており、既存の `Scope` 判定を迂回していない。scope escape safety を保ったまま診断を改善する方向として妥当。 -- write で既存 symlink file の canonical target を更新する変更は、従来の atomic tempfile persist が symlink 自体を置換し得る挙動より安全で、今回の目的にも合っている。 - -## 指摘事項 - -### Non-blocking / Follow-up - -- `Grep` でも symlink directory root の明示エラーが実装されているが、専用テストは `Glob` 側のみ確認できる(`crates/tools/src/grep.rs:298-304`, `crates/tools/src/glob.rs:344-372`)。挙動は単純で `cargo test -p tools` も通っているため blocking ではないが、将来の退行防止として `grep_reports_scope_inside_symlink_directory_is_not_traversed` 相当のテストを追加してもよい。 -- nested symlink directory は walker の `follow_links(false)` により従来通りスキップされ、skip ごとの診断は出ない。実装 Pod の報告通り、per-entry diagnostics は結果形式の拡張が必要なので本チケットでは妥当な見送り。 - -## 判断 - -Approve with follow-up — 要件は満たされ、scope safety も維持されている。Grep の専用テスト追加と nested symlink directory 診断は follow-up として扱えばよい。 - -## 確認したコマンド - -- `cargo test -p tools` -- `cargo fmt -p tools --check`