Files
yoi/docs/report/2026-10-28-t787-authorization-boundary-inventory.md
Hare a35d9a3d45 fix: retain Host-bound Job input authority through every model supply
Bind immutable Job kind/id/input digest to retained input provenance. Recheck current Host authority after hooks and at each compactor request; restored or rewritten input cannot become a local grant or switch Job identities. Add actual Controller, lifecycle, correction and signed Host/model regressions for Journal and consolidation.
2026-10-28 12:53:48 +09:00

88 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# T-787 操作認可境界の棚卸し
## 観測範囲
2026-10-28、HEAD `618880d38ded5e501d7c926006d1869282f06892` に対する共有作業ツリーの未commit差分を読んだ記録。T-787の本文とAGENTSの文書管理・検証規則を基準にした。実装とテストは並行して変更中であり、この記録は受入条件の充足、テスト成功、全入口の保護完了を示さない。将来の仕様正本や維持対象のAPI一覧ではなく、一律gate撤去で到達可能になる境界の調査証拠として残す。
共通の判断基準は [正本・権限・実行の原則](../principle/authority-and-history.md#featureの構成と実操作) に置く。下表の操作名は完全一致の例であり、`ticket` が `ticket.create` を含むという意味ではない。`Workspace` 資源はcollection操作用で、別kindの個別資源を含まない。操作名を列挙した行も、列挙以外を許可するものではない。
## Feature・経路別の境界
資源名は `crates/server-api/src/authorization.rs::PermissionResource` のvariantを指す。共通境界欄は読取り時点で確認した呼出し位置であり、provider欄の既存検査とは別の制約である。通常の登録はローカル処理であり、表の外部処理が起動に必須かどうかとは分離する。
以下、`WB/` は `crates/worker/src/feature/builtin/`、`WS/` は `crates/workspace-server/src/server/`、`W/` は `crates/worker/src/`。
| Feature・経路 | 操作例と実資源 | 共通境界・別入口 | 合成するprovider側の検査 | 起動必須性・拒否時の扱い |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| task | 保護された外部操作なし | `WB/task/mod.rs` のTaskStore・Tool登録・履歴replay | セッション内の入力検証・保存契約。Subject policyを要求しない | ローカル初期化。権限未設定でも起動・操作・restore可能であるべき |
| image・通常のファイル操作・Bash | `filesystem.read`、`filesystem.write`、`bash.execute` / `Workdir` のID・path・再帰範囲 | `W/authorized_workdir.rs` のsession wrapper。Toolごとの認可だけに頼らない | attachment選択、provider scope、解決済みpath・symlink、capability、明示command grant | Tool登録だけなら外部読取りなし。ViewImageの読取り・Workdir由来の起動入力は別途検査対象。画像bytesの再供給までの証明は未確認 |
| web | `web.search` / `Network` のprovider endpoint URL、`web.fetch` / 各取得・redirect先URL | `WB/web.rs::WorkspaceWebAuthorizer` → `crates/tools/src/web.rs`。検索のsecret取得・network前、fetchのDNS・network前にcallback | 公開HTTP(S)・private/local拒否、credential禁止、redirect・byte・timeout上限、secret provider | 任意Toolの登録でsecret読取り・通信をしない。要求時の拒否を通信や空の検索結果に置き換えない |
| sub_worker | `subworker.spawn`、`subworker.send_input`、`subworker.stop`、`subworker.observe` / Hostで束縛した親runtime/worker+子nameの `SubWorker` | `W/spawn/tool.rs`、`WB/manage_worker.rs`、`WB/worker_observation.rs` → bound-check | 親の委譲scope、明示command、子所有、name予約、Reviewer capability、現行委譲上限 | 登録はローカル。spawn・入力・観測・停止時は実操作。standaloneのローカル親所有child互換を他のWorker制御へ広げない |
| flow | ローカルなFlow状態遷移にFeature起動権限なし。verifierのWorkdir読取りは `filesystem.read` / `Workdir` | `WB/flow_transition.rs` のcoordinator・固定capture・read-only internal verifier | Flow graph・typed verdict、Session commit、read-only wrapper。verifierにはWorkspaceやBashを渡さない | 登録・復元はローカル。verifier実行やファイル読取りまで「ローカルだから無認可」と一般化しない。LLM/provider接続の独立検証はこの読取り調査では未確認 |
| worker・worker observation | `worker.list`、`worker.create`、`worker.observe.list` / `Workspace`。`worker.send_input`、`worker.notify`、`worker.cancel`、`worker.stop`、`worker.restore`、`worker.remove`、`worker.observe` / 正確な `Worker` | `WB/manage_worker.rs` のcontrol/lifecycle service、`WB/worker_observation.rs` | control/resource grant、Runtime/Worker所有・状態、観測範囲、削除・restore条件、Subject delegation専用経路 | 登録でWorker作成・復元を行わない。既存grantやWorker生存を操作認可の代わりにしない |
| workspace_worker_discovery | `worker.discover` / `Workspace` | `WB/workspace_worker_discovery.rs` のlist前 | bounded query、cursor、Workspace一覧のprovider契約。発見はcontrol/observeの付与ではない | 任意Tool。拒否時は下流一覧取得前に失敗 |
| manage_workdir・workdir_catalog | `repository.list`、`workdir.list`、`workdir.attachment.list` / `Workspace`。`repository.read`、`workdir.create` / `Repository`。`workdir.read`、`workdir.attach`、`workdir.delete` / `Workdir`。`workdir.attachment.read`、`workdir.detach` / `WorkdirAttachment` | `WB/manage_workdir.rs` と `manage_workdir/wip.rs`。detachはHostがaliasからconnection IDを束縛 | provider scope、attachment接続CAS・release fence、稼働占有・削除条件、repository materializer・既存資源grant | 登録はローカル。設定済attachmentの準備と任意の作成/解除Toolを区別。解除と削除を分離し、古いalias観測で別接続を解除しない |
| workspace_config | `workspace_config.attach`、`workspace_config.observe`、`workspace_config.read`、`workspace_config.commit` / `WorkspaceConfig` のpath・再帰範囲 | `WB/workspace_config/backend.rs::authorize_post` → 共通HTTP送信。commitはroot entrypoint変更と各path、renameの両側を検査 | config tree所有・path検証、observed content/CAS、commitのatomicity | WIP登録はローカル。attach/read/observe/commit要求が保護操作であり、FeatureをONにしただけでは許可しない |
| drive | `drive.capabilities`、`drive.root`、`drive.search` / `Workspace`。`drive.metadata`、`drive.list`、`drive.read_text`、`drive.read_chunk`、create/update/delete/relocate/uploadの各完全名 / `DriveEntry`。`drive.request_status` / `DriveRequest` | `WB/drive/backend.rs` のtyped backend。Tool・WIPは同backend、chunk/imageは各読取り、relocateは元entry・移動先parentを検査 | Drive grant、entry/version/hash、chunk条件、mutation CAS、request receipt・upload条件 | 登録でroot取得をしない。任意操作の拒否は下流取得/変更前。DriveはWorkdirでもfilesystem権限でもない |
| ticket | `ticket.create`、`ticket.query`、`ticket.list`、`ticket.tag.list` 等 / `Workspace`。`ticket.read`、`ticket.edit_item`、`ticket.case.update`、`ticket.assignment.answer`、`ticket.implementation_result.write` 等 / 個別 `Ticket` | `WB/ticket.rs::WorkspaceHttpTicketBackend`、`ticket/assignment_tracking.rs`、`assignment_public.rs`、`notification_diagnostics.rs`。Tool・WIP・typed serviceを照合 | authenticated actor、対象canonical化、assignment/event CAS、operation key・exact replay、対象資源条件。relationは両Ticketを検査 | 登録・service公開だけでは起動認可なし。createは `ticket.create` のみで旧 `ticket` の追加要求なし。起動prompt投影の読取りも保護操作 |
| objective | `objective.query`、`objective.create` / `Workspace`。`objective.read`、`objective.edit`、`objective.set_state`、`objective.link_ticket`、`objective.unlink_ticket` / `Objective`。リンク情報取得の `ticket.read` / `Ticket` | `WB/objective.rs::WorkspaceHttpObjectiveBackend`、native Tool/WIP adapter | Objective/Ticket対象検証、Backendの更新条件。関連Ticket本文の読取りをObjectiveの権限に含めない | 登録はローカル、明示操作時に認可。関連情報の追加読取りまで証拠を照合する必要あり |
| merge_request | `merge_request.open` / `Ticket`。`merge_request.read`、`merge_request.readiness`、`merge_request.review`、`merge_request.complete` / `MergeRequest` | `WB/merge_request.rs::MergeRequestTool` とnative WIP dispatch | repository selector、source・Ticket digest・snapshot、review capability、actor、integration evidence | 登録はローカル。操作認可は承認・統合の実証を代替しない |
| orchestration | 下位の `ticket.read` / `Ticket`、`worker.create` / `Workspace` 等を合成。独立したFeature起動権限なし | `WB/orchestration.rs` → TicketServiceとWorkerLifecycleService | 現行Orchestrator assignment、Ticket targetから導くattachment access、Profile/Flow解決、資源finalization | service依存の登録とspawnの実操作を分ける。Toolだけでなくserviceを直呼びする経路の作用0も照合対象 |
| memory(Workspace・Subject) | `memory.query`、`memory.document.read`、`memory.document.update` 等 / legacy `Workspace`。Subject側は `memory.query`、`memory.read`、`memory.changes.list`、`memory.resident.read`、`memory.resident.supply`、`memory.staging.write` 等 / Host束縛の `Subject` | `WB/memory.rs` のtyped dispatch、`WorkspaceSubjektivResidentSummarySource::fetch`、`load_initial_checked`・refresh/restore/pre-requestのsupply検査 | Subject本体所有・現行policy、Memory change/proposal条件、Job grant、履歴へのcommit・既存source provenance | Subject resident contextは必須処理、Workspace summaryは任意処理。必須拒否は起動失敗、任意拒否は未開始診断。読取り許可は再供給の許可ではない |
| memory extraction・surface lifecycle | `memory.extract.read`、`memory.extract.supply`、`memory.extract.commit`、`memory.staging.write`、`memory.consolidation.request`、`memory.surface.supply` 等 / 接続に応じた `Subject` またはlegacy `Workspace` | `WB/memory_lifecycle.rs::MemoryExtractionAuthority`、`memory_surface_lifecycle.rs`、Host側Job要求 | 凍結input範囲、抽出cursor、stage/commit条件、Subject/Workspaceの分離、attempt・writer fence | hook/task登録はローカル。任意背景処理でも開始・各読取り・モデル供給・commit・次の要求で再検査。取消受付と停止完了は別 |
| Subject Journal・Subject Job入力 | `journal.startup.read`、`journal.startup.supply`、`journal.compaction.read`、`journal.compaction.supply`、`journal.recording.request`、`journal.job.input.read`、`journal.job.input.supply` 等 / Host束縛の `Subject` | `WB/journal.rs`、`WB/backend_job_input.rs`、`W/subjektiv.rs` → `WS/subjektiv_journal.rs`、`subjektiv_job.rs`、`subjektiv_memory_journal.rs` | domain grant、現在の本体、Job immutable batch・origin・attempt・digest・期限・writer、receipt・journal位置 | startup grantがない構成は読取りを開始しない。構成されたstartup contextは必須、recording/compaction補助は各拒否・unavailableを診断。Job入力は各モデル要求とrestore後にも再検査 |
| MCP(暫定維持) | 既存の完全名 `mcp` / `Feature` | `W/feature.rs::authorize_requested_manifest`、controller discovery、既存activation guard | 既存stdio設定・プロセスspawn/終了・Tool登録・cleanup/retry・restore契約 | discovery前の既存拒否を保持。他Featureの新原則を用いて既存gateを削除しない。新しいserver/Tool/resource別認可や常時接続は対象外 |
## 旧grant・Job・文書の整合
差分の `ScopedPermission::covers` は操作名を完全一致で比較し、resource containmentはkindを跨がない。`Feature` variantと旧トップレベル名の保存形式は残るが、MCP以外の旧Feature grantは新しい操作の権限にならない。旧Workspace grantも個別Ticket/Subject/Worker等へ展開しない。schemaの自動書換えや全Subjectへの基礎権限付与を移行の説明にしない。
`WS/subject_authorization.rs::job_permissions` はJournal/consolidationの限定操作と同じSubject資源に絞り、現行policyとの積集合を取る。`active_grant`、凍結originの現在の本体所有も検査する。一般Jobは空のSubject権限であり、旧Jobのorigin欠落は拒否される。これはJob grantだけで許可する契約でも、Subject policy全体をJobへコピーする契約でもない。権限未設定・domain不一致・失効を非Subject Worker互換へfallbackさせない。
調査時点の維持文書 `docs/principle/`・`docs/development/` には、一律のFeature起動認可を要求する旧説明は見つからなかった。principleへ明示的な境界原則を追加した。`docs/report/` は過去の証拠であり、旧状態の記述を現行仕様に書き換えない。所有コードのコメントについては、`W/feature.rs` と `server-api/src/authorization.rs` の差分がMCP限定・操作境界へ更新されている。`worker-runtime/src/worker_backend.rs::saved_subjektiv_authorization_fixture_grants_only_exact_feature_activation` は依然 `subjektiv`・`workdir_catalog` の旧Feature名だけを許可するfixtureであり、新しいSubject resident操作・bound-checkの実接続証拠にはならない。
## 生成契約の所有とscope
実際の出力は `web/workspace/src/lib/generated/` と `openapi/server-api.json` である。付与された `apps/web/src/generated/` は既存出力先と一致しないため、新規folder作成・生成物コピー・手修正を行わなかった。所有者は `crates/server-api/examples/` と `src/repository_openapi_typescript.rs`、ts-rs系は `src/lib.rs` の対応関数である。
- OpenAPIの生成・照合は `cargo run -p server-api --example export_openapi` と `cargo run -p server-api --example export_openapi -- --check`。既定の書込み先は `openapi/server-api.json`。
- OpenAPI closureからの出力は `cargo run -q -p server-api --example generate_repository_openapi_types`、`generate_workdir_api_types`、`generate_runtime_api_types`、`generate_worker_launch_api_types`、`generate_ticket_api_types`、`generate_companion_api_types`。各exampleの `-- --check` は既存出力との照合。出力名はそれぞれのexampleが参照する定数を正本とし、一般的な `generated/` へredirectしない。
- Memoryの出力は `cargo run -q -p server-api --features typescript --example generate_memory_api_types > web/workspace/src/lib/generated/memory-api.ts`。同様のts-rs系は `generate_auth_api_types`、`generate_skill_api_types`、`generate_repository_access_types`、`generate_drive_api_types`、`generate_legacy_typescript`。stdout出力と直接書込み型のexampleを混同しない。
- `server-api/src/lib.rs` の `generated_worker_launch_api_contract_is_current` はOpenAPI closure出力を、`generated_memory_api_contract_is_current` はtypescript feature時のMemory出力を照合する。これらのCargo testはこの調査では実行していない。
- 追加された `authorization/bound-check` と既存 `authorization/check` は `openapi = false` の内部契約。`PermissionResource` のts-rs deriveがあることだけから、全てのenumが既存Web artifactへ出力されるとは判断しない。新variantの影響するschema closureと実際の生成差分は所有者で確認する必要がある。生成物が古いとも差分不要とも、この読取りだけでは断定しない。
## 未確認の接続・coverage
以下はコード読取りから必要と判断した確認範囲であり、テスト失敗を観測したという意味ではない。
- task-onlyの起動・direct install・restoreでFeature認可HTTPが0、Ticket Tool/WIP/serviceで未付与・範囲外・失効時に下流作用0、許可時に実作用完了を同じ接続で確認する。
- providerの拒否、delegation失効・policy縮小・Subject本体交代、attachmentの入替え競合を実Host接続で確認する。fake clientで認可callbackを数えた証拠だけでは代替できない。
- 初期resident入力の拒否時の読取り0・モデル供給0、許可時の取得・commit、optional Workspace summaryの未開始診断と通信障害分類を区別する。`load_initial_checked` が初期化で呼ばれる差分はあるが、その読取りだけでライフサイクル全体の成功を主張しない。
- 保存済みpromptのsourceが設定変更等で除去されたrestore、同じbodyのcache再供給、Journal compactionの凍結済み参照、Subject Jobの凍結入力にも現行権限が適用されるか確認する。source objectが残る場合のcallback成功だけでは除去後の経路を証明できない。
- Journal/consolidation Jobの限定grantと現行policy、origin欠落の旧Job、一般Job、current attempt・writer・期限拒否の組合せを確認する。Job入力のpre-request hook追加は、全Job操作のgrant整合の証明ではない。
- 継続処理について取消要求後の停止確認、途中失敗cleanup、停止後の追加配送・読取り・作用0を確認する。登録時の拒否テストで継続中の権限変更を代替しない。
- MCPのallow/deny、拒否時discovery spawn0、stdioの発見・各呼出し・restore・cleanup/retryを既存の粗い認可のまま回帰確認する。本Ticketの変更から新しい各呼出し認可を推論しない。
- 旧保存grantを読めること、新しい操作へ暗黙展開されないこと、明示standalone・非Subject互換・Workspace障害が区別されること、生成契約の整合は実接続/生成チェックが別途必要。
この調査でCargo build/testや生成処理は実行していない。文書のみの静的検査は実装の受入判定を代替しない。
## 同日・候補安定後の追補
上記は棚卸し時点の観測であり、以下はその後に実装担当が行った検証である。独立レビューの判断とは分ける。
- `worker-runtime/src/worker_backend.rs` の旧Feature名fixtureを、Subjectの実操作とbound targetを検査するfixtureに更新した。task-onlyのFIFO・receipt replayはHost呼出し0、pending head修復はresident/Journal取得と保護入力checkpointを検査する。既存headの再修復では保護入力を再取得しない。
- `worker.rs` の `protected_input_tests` はcached prompt、source除去後restore、fork・compaction・rewind、未知provenance拒否とhook後の再検査を扱う。Journalはstartup/compactionを区別し、空payloadは保護入力として偽装しない。追加の `backend_job_input.rs` とcontroller結合テストは、凍結Job入力を各モデル要求前に再検査し、拒否時にモデル要求・結果送信が発生しないことを確認した。
- `server/tests/subject_authorization_tests.rs` の署名Host接続テストで、unset task、MCPのdiscovery前拒否、Ticket作成の下流作用、alias正規化、provider scopeとの積集合、attachment再接続、本体交代を確認した。`subjektiv_jobs_tests.rs` は限定Job grantと現行policy、origin/attemptの失効を検査する。これは任意のFeatureコードを自動監視できるという主張ではない。
- 安定候補でroot `cargo check`、変更crateの通常suite(`server-api`、`tools`、`worker`、`worker-runtime`、`yoi-workspace-server`)、`server-api --features typescript` のsuite、変更crateのrustdoc生成、format・差分・状態契約名検査が成功した。restoreを含むsuiteにはcheckout内 `target/restore-test-environment/` 以下の専用config/data/runtimeを使用した。最初のServer再実行は隔離環境変数不足でpreflight失敗し、設定後の全suite再実行が成功した。preflightを削除して通してはいない。
- OpenAPIと影響する各OpenAPI closure出力は所有exampleの `--check` で一致した。Webの型検査と変更型を使う関連APIテストが成功した。描画変更はなく、browser/E2E・稼働環境更新は実施していない。通常suiteで元から明示選択のignoredテストを実行済みとは扱わない。
## 初回レビューで確認されたJob入力供給の不足
初回候補の通常suite成功は、凍結Job入力をcompactorへ再供給する経路と、Jobの通常hook後に権限が失効する経路を証明していなかった。正式レビューは実Controllerと捕捉モデルで漏れを再現し、request_changesとした。上記の成功記録をこの境界の成功へ拡張してはならない。
修正では、Job種別・Job ID・入力digestの保持義務を既存の保護入力provenanceに追加し、Host由来の実行bindingとの一致と現行認可を共通供給guardで検査する。保存値はgrantではない。attemptは現在のHost正本で毎回検査し、同じ凍結入力への正当な再試行と別Jobへの付替えを区別する。通常hookは早期診断に留まり、hook後とcompactorの各dispatchを代替しない。
恒久回帰は `worker_job_input_supply_tests.rs`(rewrite/restoreとcompactor継続)、`tests/controller/subject_job_input_supply.rs`(実Controllerと後続hook失効)、`server/tests/subject_job_model_supply_tests.rs`(署名Host・実policy/Job台帳・捕捉モデル)へ追加した。Journal/consolidation双方で許可時の実供給と拒否時のモデル要求0を観測する。origin切断の履歴fixtureは稼働Controllerの停止確認を主張せず、既存のconnected release検証とは分ける。修正前の恒久回帰失敗と修正後の成功、最終suiteの結果はMRで区別して記録する。
件数・生ログ・source識別はMRの検証記録に置く。これらの成功だけで受入条件の網羅、正式承認、統合、Ticketの完了を推論しない。