# Drive共有の接続不足と検証(T-725、2026-10-19) ## 判断の基準 基準 `adfa42297352c5a64fe8100969aad6fda4df8a81` と着手HEAD `cea0225a6e2d9e23c59a7d4a453f99bc2e1eba97` を比較した。差分81 filesはSession保存、Journal、Ticket公開操作、復元親の子操作等であり、以下のDrive保存/API/Worker/Webテストに差分はない。Cargo.lockの `wip-{client,http,protocol,text-view}` はすべてcrates.ioの0.2.0で、ローカル差替えはしていない。 検証したテスト実装は専用branch `work/T-725-drive-connections` の `ec6c5bed`。以下の結果はこの実装に対する証拠であり、対象developへの統合判断とは分ける。 T-721〜724の保存・公開操作を再実装せず、**別入口の返却値が次の操作へ渡る接続と、cleanupの所有境界**だけを追加した。製品修正は不要だった。Ticket一巡は実装・採用済みT-744の `ticket-regression-test-design.md`、通常suiteの判断はT-761の `regression-test-design.md` と分担する。関連Ticketの変更・再開はしていない。 ## 優先順位と追加した契約 配置は `crates/workspace-server/src/server/tests/worker_drive_tests.rs`。以下はすべて通常のServer lib targetであり、ignoreやテスト専用featureは追加しない。 | 優先 | 利用結果/具体故障・コード根拠 | 既存証拠の限界/採否 | | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | P1 | ユーザーuploadの返却node/mutationをWorkdirなしTools/WIPが読み更新し、owner/別Workerが同一資料を取得する。`server_drive::upload/read_text/public_node`、Worker `drive/backend.rs::read/mutate` とWIPの投影がずれると、個別成功でも本文や観測が別物になる。 | `workdirless_workers_share_markdown_through_current_read_and_write_grants` はWorker作成から別Worker読取り。`image_attachment_retains_original_bytes_after_drive_update_and_revocation` はuser uploadから画像添付。`native_create_and_write_use_client_managed_committed_requests_and_reject_stale_worker` はWIP作成から更新。この間の**user Markdown binary upload→Tools/WIP更新**を新規接続。 | | P2 | Workerを止め、保持規則に従い削除・交換してもDrive文書の参照と内容が残る。`server/worker_operations.rs::stop/delete_from_plan`、`WorkerRemovalService` と `store::commit_worker_catalog_removal` はWorker権限を終え、Drive資料を削除してはならない。 | `cleanup_service_deletes_stopped_singleton_worker` は実Runtime停止・削除・交換、storeの `drive_grants_migrate_and_preserve_audit_through_revoke_worker_delete_and_restart` はgrant取消監査を確認するが、Drive資料の再取得はない。**元参照→交換Workerの明示grant後読取り**を新規接続。停止だけでgrantが失効するという別契約は追加しない。 | | P2 | source Workdirを本当に削除した後も保存した資料を使える。`drive.rs` のSaveWorkdirはbytesをAPIへ渡し、`execute_workdir_removal`→Runtime→`working_directory/cleanup.rs` はmaterializationだけを削除する。誤ったroot削除やsource依存を検出する。 | `scoped_workdir_import_publishes_document_and_image_bytes_with_bounded_reads` は独立metadata、完全download bytes、画像添付を確認するがsourceが残る。**実provider削除の後に同node/mutation/bytes/receiptを読む**接続を追加。保存crateのbackup/GC行列は増やさない。 | ### 入力・fixture・観測点 - `user_uploaded_markdown_roundtrips_through_workdirless_tools_and_native_wip_at_one_identity`:既存Fixtureの実SQLite/FeatureStorage/LocalFileSystem、2 Workerの空Workdir links、明示read_write/read_only grantを使用。owner binary PUTはsize/SHA256と日本語Markdown bytesを渡す。Toolsとnative WIPの2経路で読取り・writeを行う。返却Workspace/node、uploadのlast_mutation_id、更新request ID=last_mutation_id、別Worker readのentry/本文、owner metadata、committed receiptのentry、mutation固定downloadの完全bytesを照合する。WIPは本番install/Inspect/Invoke、外部LLMは呼ぶとpanicする。Runtime起動とHTTP transportはfixtureだが、毎要求の署名・router・現grant・storeは本番。 - `worker_removal_preserves_drive_reference_for_explicitly_granted_replacement`:real embedded Runtime+既存deterministic controller backendでRepository/WorkdirなしWorkerが文書を作る。本番WorkspaceWorker stop→Runtime stopped→cleanup plan→delete_from_planを通し、Runtime/catalog除去、旧Workerの拒否を確認。別IDの交換Workerはgrant前拒否、read_only付与後に**元参照**で完全entry(latest_url、last_mutation_idを含む)と本文が一致し、元receiptもcommitted。保持対象Sessionは既存fixtureで準備する。lifecycle admissionは内部Backend test contextであり、browser/Worker-control認証の新証拠ではない。実controllerのshutdown/モデル挙動は代替されている。 - `source_workdir_removal_keeps_published_drive_bytes_and_receipt`:`ticket_worker_launch_tests::LaunchFixture` の既存認証付きloopback Runtimeを共有する。通常のServer Workdir createでlocal Git sourceからcleanなREADME.mdをmaterializeする。source sessionはLocalWorkdirSessionでread scopeと**実materializer lease**を渡す(Runtime session transportは代替)。DriveSaveWorkdirはalias `source`+論理path `README.md`、元bytes `clean\n`。session close/drop後、Server removal→認証付きRuntime HTTP→実materializer unlinkを通し、Removed/nonretryable・物理root不存在・registry不存在をassert。その後、新しいWorkdirなしDriveFeatureから元entry/bytes/receiptを照合する。`on_api`のcloneは同じstore/drive_host/Runtimeを共有し、別DBを作らない。 Workdir用に新しい起動基盤は作らず、既存Launch backendにcleanupの実materializerへの委譲を足し、materializer配置をfixtureのTempDir配下へ閉じた。listenerは既存Dropでabort、通信は既存有界timeoutを利用する。embedded Server adapterは仕様上Workdir create/status/cleanup非対応(`hosts.rs`)。最初のembedded試行はAttentionRequiredとなったため不採用とし、成功fakeへ変えず既存HTTP harnessへ切り替えた。 ## 追加しなかった保証と既存assertion パスは、断りがなければServer `src/server/tests/` 配下。 | 保証/採否 | 主な実在テストと観測、代替境界 | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | stale先勝ち:維持 | `simultaneous_worker_edits_publish_exactly_one_last_mutation_id_and_preserve_the_loser_conflict` は成功1件・敗者StructuredConflict・勝者本文/metadata、`stale_worker_observations_cannot_write_edit_relocate_or_delete_newer_last_mutation_id` はwrite/edit/move/delete拒否と本文/名前保持。nativeは前掲create/write testでvalidator conflictと勝者bytes。Tools/WIP→実署名router、Runtimeのみfixture。 | | 現grant取消:維持 | `revocation_invalidates_cached_metadata_reads_writes_and_receipt_queries` はread/write/list/search/status等のdeniedと別Worker本文保持。`native_tree_does_not_index_entries_but_direct_inspect_and_invoke_use_current_grants` はcached invoke/refreshed inspect拒否。`drive_api_tests::drive_verified_upload_revoked_during_reception_cannot_commit_or_replay` はbody frameで取消、403/denied、uncommitted、list空。最後の注入はverified-source Extensionを使用し、署名ingress自体の証拠とは分ける。 | | commit後応答喪失:維持 | `lost_mutation_response_is_resolved_by_receipt_without_resending_effect` と `lost_binary_upload_reply_reconciles_same_request_without_duplicate_publication` は本番router成功後の返答だけをtransportで落とす。committed/元node、POSTまたはPUT数1、元requestのGET照会、別Worker一覧1件とmutation一致をassert。単なる失敗応答fakeではない。 | | Web接続:維持/明示実行 | `drive_web_roundtrip_tests::repositoryless_drive_shares_worker_bytes_and_enforces_current_read_only_grants_over_http` はRepositoryなしbootstrap、実HTTP、Worker grant/revokeの403、一覧entryと実blob bytesを確認。`repositoryless_drive_real_web_adapter_preserves_bytes_cas_and_latest_reference_lifecycle` は実HTTPと本番Deno adapter(parser/operation/grant)を接続する既存ignored test。Worker起動はfixture、browser DOM/Passkey loginは対象外。画面draft/取消等は別のsynthetic API browser suiteを重複しない。 | | 保存/reopen/backup/GC:所有crateを維持 | `workspace-drive/tests/storage.rs::markdown_image_empty_and_folders_survive_restart_with_stable_identity` はDrive再open後のmetadata/本文/画像。`receipts_survive_response_loss_and_restart_without_double_mutation` はreceipt/replay一致・別actor拒否・list1件。`stopped_writer_backup_restores_ids_last_mutation_ids_receipts_and_sequence_with_blobs` は一致するDB+blobsの復元、元metadata/bytes/receiptと新ID採番。`src/tests.rs::saved_blob_before_metadata_failure_is_not_published_and_restart_collects_it` は未公開・uncommitted・再open回収、`commit_failure_rolls_back_metadata_and_receipt_together` はmetadata/receipt同時rollback。`collection_cannot_remove_in_flight_upload_or_selected_read` はin-flight保護。これらをServer全体再起動や実電源断保証に拡張せず、新たな保存障害行列は不採用。 | ## 故障検出試行と復元 以下は意図的なmutantであり、製品不具合の発見ではない。patchとログは `target/t725/` に保持し、製品sourceは復元した。 | 試行 | 注入と検出 | 実行結果(外側wall/libtest) | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | | `mutant-read` | `server_drive::read_text` の返却本文を `String::new()` にする。P1 filterの元upload本文比較が空文字≠日本語Markdownで失敗。 | exit101、64.156秒/0.41秒(build込み) | | `mutant-cleanup` | `execute_workdir_removal` に誤ったDrive blob rootのremove_dir_allを挿入。実source削除後のfresh DriveReadが公開error「Drive storage or transport unavailable」で失敗。 | exit101、88.103秒/0.59秒(build込み) | | 復元 | `server.rs` と `server_drive.rs` に差分がないことを検査後、Server全crateと3つの個別filterを実行。 | 下表の全成功。故障をテストから避ける製品修正はない。 | 開発中にはsource fixtureの未定義git helperによるcompile error、準備用Repository FK不足、embedded adapter非対応の試行失敗もあった。helperを増やさず既存clean READMEを使用し、最終fixtureは本番Server create+既存HTTP Runtimeを利用した。これらを製品regressionや故障検出成功には数えない。いずれも `cargo test --locked -p yoi-workspace-server --lib source_workdir_removal_keeps` の試行で、`source-removal-first` はcompile exit101/wall6.875秒、`source-removal` はFK失敗exit101/wall84.053秒/libtest1.64秒、`source-removal-fixed` は非対応判定exit101/wall56.519秒/libtest2.14秒、HTTP経路への修正後 `source-removal-http` は1 pass/wall83.709秒/libtest0.61秒。時間採取は `/usr/bin/time` がないためBash `TIMEFORMAT` を使用し、出力のなかった背景起動の試行はbuild成功に数えなかった。 ## 実行区分・費用とT-761への接続 すべて専用checkoutで実行した単発実測であり、CIや全通常suiteの速度ではない。`wall` はCargo起動/build込み、libtest時間は実行のみ。既存dead_code warningsは継続。 | command(root) | 結果 | wall/実行秒 | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------- | | `cargo test --locked -p yoi-workspace-server --lib worker_drive_tests --no-run` | 初回build成功 | 234.981/実行なし | | `cargo test --locked -p yoi-workspace-server --lib worker_drive_tests -- --test-threads=4` | P1+Worker寿命追加時20 pass | 71.131/9.69 | | `cargo test --locked -p yoi-workspace-server -- --test-threads=4` | 最終実装1210 lib+12 bin pass、既存3 ignored、doctest対象0 | 400.021/291.85(lib) | | `cargo test --locked -p yoi-workspace-server --lib user_uploaded_markdown_roundtrips` | 復元後1 pass(Tools/WIP両方) | 0.994/0.72 | | `cargo test --locked -p yoi-workspace-server --lib worker_removal_preserves_drive_reference` | 復元後1 pass | 4.458/4.20 | | `cargo test --locked -p yoi-workspace-server --lib source_workdir_removal_keeps` | 復元後1 pass | 0.872/0.62 | | `cargo test --locked -p workspace-drive` | 40 unit+13 storage pass、doctest対象0 | 4.934/0.21+0.09 | | `cargo check --locked` | root通常compile closure成功 | 75.368 | | `cargo test --locked -p yoi-workspace-server --lib drive_web_roundtrip_tests -- --ignored` | **明示**Web adapter1 pass | 1.051/0.75 | 最終module境界コメントの補正後にも同じ `worker_drive_tests -- --test-threads=4` を実行し21 pass(wall71.296秒、libtest9.75秒)を確認した。`cargo doc --locked -p yoi-workspace-server --no-deps` は成功(wall70.312秒)。`cargo fmt --all -- --check`(3.824秒)、`git diff --check HEAD`(0.004秒)、`env -u LD_LIBRARY_PATH deno run --allow-read --allow-run=git tools/check-state-contract-names.ts`(0.189秒、0 unapproved/0 stale exceptions)、2文書の `deno fmt --check` も成功した。文書の相対参照先は実在する。 T-761の通常runnerはCargo通常lib targetを列挙するため、追加3件は同targetのままで選択される。3件の逐次libtest実測合計5.54秒(単独Cargo wall合計6.324秒)。既存runnerのshard/並列負荷やcold build増分は未測定であり、全体時間へ単純加算しない。最も重いWorker寿命4.20秒は実永続Runtime停止・保持・再作成を検出する費用で、既存DB単体や共有2Workerの成功では代替できない。通常suiteへの追加はこの3件だけとし、Web adapterの既存ignoreは変更しない。 全Workspace `deno task test`、browser、製品全体E2E、実モデル判断、物理電源断、Server/Runtime全体再起動、CI実行は未実施。新規CI jobは追加しておらず、CIへの自動配線・実行済みとは扱わない。実モデルの調査・待機・完了判断を固定LLM応答で証明せず、追加したのは機構の接続保証である。 証拠は `target/t725/*.log`・`*.time` と2つのmutant patch。報告書へ生ログ/秘密は転載しない。稼働環境・対象develop・Ticket caseは変更していない。