--- title: 'Workspace Backendのembedded Runtimeをfs-store永続化に切り替える' state: 'closed' created_at: '2026-06-28T13:34:29Z' updated_at: '2026-06-28T19:16:51Z' assignee: null queued_by: 'workspace-panel' queued_at: '2026-06-28T17:12:16Z' --- ## 背景 Workspace Backend の embedded Runtime は現在、`EmbeddedWorkerRuntime::new_memory_with_execution_backend(...)` で memory-backed Runtime として起動している。そのため Backend を再起動すると Runtime catalog、Worker snapshot、transcript、ConfigBundle store が失われる。 `worker-runtime` 側には既に fs-store backed Runtime が存在する。 - `Runtime::with_fs_store(...)` - `Runtime::with_fs_store_and_execution_backend(...)` - `FsRuntimeStoreOptions { root, runtime_id, display_name, limits }` 一方で Workspace Backend 側の embedded wrapper には fs-store backed constructor が無く、`WorkspaceApi::new_with_execution_backend(...)` は memory-backed constructor を呼んでいる。 この Ticket では、Workspace Backend embedded Runtime を `worker-runtime` fs-store に接続し、Backend restart 後も Runtime record と transcript projection が残るようにする。 ## 依存関係 - `00001KW7726H9 Runtime Worker起動経路を正規のExecution/ConfigBundle経路に一本化する` の設計に従う。 - この Ticket は fs-store 接続と restart persistence を扱う。canonical create request の field 設計や旧 Pod store 削除はこの Ticket の主対象にしない。 ## 調査済みの現状 ### Workspace Backend 側 - `crates/workspace-server/src/hosts.rs` - `WorkspaceApi::new_with_execution_backend(...)` が `EmbeddedWorkerRuntime::new_memory_with_execution_backend(...)` を呼んでいる。 - embedded spawn path は `WorkerSpawnRequest` を Runtime `CreateWorkerRequest` に変換している。 - `crates/workspace-server/src/server.rs` - `ServerConfig` は `workspace_root`、`database_path`、host URLs などを持つ。 - Runtime fs-store root はまだ持っていない。 - `crates/workspace-server/src/main.rs` - `WorkspaceIdentity::load_or_init(...)` で workspace_id は取得済み。 - 現状の `ServerConfig::local_dev(...)` には Runtime store root が渡されていない。 - `crates/workspace-server/Cargo.toml` - `worker-runtime` dependency は `features = ["ws-server"]`。 - fs-store を使うには `fs-store` feature も有効化する必要がある。 ### worker-runtime 側 - `crates/worker-runtime/src/runtime.rs` - `Runtime::with_fs_store_and_execution_backend(options, backend)` が存在する。 - persisted state restore 時、Worker の live execution handle は復元せず `WorkerExecutionStatus::unconnected()` / `execution_handle: None` になる。 - `crates/worker-runtime/src/fs_store.rs` - store root 配下に runtime_id 別の directory を作る。 - runtime snapshot、worker snapshot、events、transcript entry を保存する。 - `crates/worker-runtime/src/config_bundle.rs` - `ConfigBundle` は `metadata.id` と `metadata.digest` を持つ。 - fs-store backed Runtime の persisted runtime state に ConfigBundle store も含まれる。 ## 目的 - Workspace Backend embedded Runtime を memory-backed から fs-store backed に切り替える。 - Backend restart 後に Runtime catalog / Worker snapshot / transcript / ConfigBundle store が復元される。 - live execution handle は再起動で復元できないものとして扱い、connected Worker のように見せない。 - Runtime store root は Browser-facing API に出さない。 - `.yoi/tickets` など project record と Runtime local state を混ぜない。 ## 実装方針 ### 1. workspace-server で worker-runtime fs-store feature を有効化する `crates/workspace-server/Cargo.toml` の `worker-runtime` dependency に `fs-store` feature を追加する。 ```toml worker-runtime = { workspace = true, features = ["ws-server", "fs-store"] } ``` これにより `Runtime::with_fs_store_and_execution_backend(...)` と `FsRuntimeStoreOptions` を Workspace Backend から使えるようにする。 ### 2. Runtime store root を ServerConfig に持たせる `ServerConfig` に embedded Runtime 用の store root を追加する。 候補名: ```rust pub embedded_runtime_store_root: PathBuf ``` 決定事項: - store root は Backend local state であり、Browser-facing API に出さない。 - store root は `.yoi/tickets` 配下に置かない。 - tests は temp dir を明示的に渡す。 - CLI/default path は `WorkspaceIdentity.workspace_id` を使って workspace ごとに衝突しない path にする。 推奨 default: ```text /workspace-server//worker-runtime ``` `workspace-server` crate が `manifest::paths::data_dir()` を使えない場合は、実装時に `manifest` dependency を追加するか、`main.rs` で default を解決して `ServerConfig` に注入する。workspace root 直下の git-tracked project record と混ぜないことを優先する。 ### 3. EmbeddedWorkerRuntime に fs-store constructor を追加する `EmbeddedWorkerRuntime` に fs-store backed constructor を追加する。 候補名: ```rust EmbeddedWorkerRuntime::new_fs_store_with_execution_backend(...) ``` この constructor は内部で次を呼ぶ。 ```rust Runtime::with_fs_store_and_execution_backend( FsRuntimeStoreOptions { root: embedded_runtime_store_root, runtime_id: embedded_runtime_id, display_name, limits, }, execution_backend, ) ``` runtime_id は embedded Runtime として安定した値にする。 - 既存の `EmbeddedWorkerRuntime::runtime_id()` / embedded Runtime id 方針がある場合はそれに従う。 - workspace ごとの identity は store root 側で分けるため、runtime_id に raw workspace path を入れない。 - runtime_id が変わると既存 fs-store を復元できないため、restart 間で安定させる。 ### 4. WorkspaceApi の constructor を fs-store backed に切り替える `WorkspaceApi::new_with_execution_backend(...)` は memory constructor ではなく、fs-store constructor を呼ぶ。 変更前の趣旨: ```rust EmbeddedWorkerRuntime::new_memory_with_execution_backend(...) ``` 変更後の趣旨: ```rust EmbeddedWorkerRuntime::new_fs_store_with_execution_backend(...) ``` この変更により、Workspace Backend 経由の embedded Runtime catalog / worker snapshot / transcript / ConfigBundle が fs-store に保存される。 ### 5. restart 後の execution status を明示する `worker-runtime` の restore は現状、Worker の live execution handle を復元せず `WorkerExecutionStatus::unconnected()` にする。これは正しい前提として扱う。 この Ticket では次を確認・必要に応じて補強する。 - Backend restart 後の Worker は catalog / transcript 上は復元される。 - live execution handle は復元済みのように見せない。 - input-capable として扱えない場合は `unconnected` / stale execution mapping / typed diagnostic として表示・API 応答される。 - restart 後に同じ WorkerId を使って fake response を生成しない。 ### 6. ConfigBundle store の復元を確認する fs-store backed Runtime は persisted runtime state に ConfigBundle store を持つ。Workspace Backend 側で embedded Runtime を fs-store に切り替えた後、Backend restart で ConfigBundle が失われないことを test で確認する。 この Ticket では ConfigBundle create API の再設計は行わない。`00001KW7726H9` の canonical create 設計と矛盾しないよう、現行 store の復元だけを確認する。 ## Test plan Focused tests を `yoi-workspace-server` 側に追加する。 ### embedded Runtime fs-store restore 1. temp dir を store root として `WorkspaceApi` / embedded Runtime を起動する。 2. Runtime に ConfigBundle を store する。 3. Worker を作成し、transcript に entry を追加する。 4. WorkspaceApi / Runtime を drop する。 5. 同じ store root で WorkspaceApi / Runtime を再作成する。 6. Runtime catalog に同じ Worker が復元されることを確認する。 7. transcript entry が復元されることを確認する。 8. ConfigBundle が復元されることを確認する。 9. execution status が live connected として偽装されていないことを確認する。 ### memory-backed bypass absence - Workspace Backend の production constructor が `new_memory_with_execution_backend(...)` を呼ばないことを test または grep-resistant な unit test で確認する。 - memory-backed Runtime は test helper / explicit in-memory constructor に限定する。 ### store root isolation - store root が `ServerConfig` から注入されることを確認する。 - Browser-facing API response に store root が出ないことを確認する。 - `.yoi/tickets` 配下を Runtime fs-store root にしないことを確認する。 ## 受け入れ条件 - `crates/workspace-server` が `worker-runtime` の `fs-store` feature を有効化している。 - `ServerConfig` が embedded Runtime store root を持ち、Workspace Backend 起動時に明示的に渡される。 - CLI/default path が user data 配下の workspace_id 別 Runtime store root を使う。 - `EmbeddedWorkerRuntime` に fs-store backed constructor が追加されている。 - Workspace Backend の通常 constructor が `EmbeddedWorkerRuntime::new_memory_with_execution_backend(...)` を呼ばない。 - embedded Runtime が `Runtime::with_fs_store_and_execution_backend(...)` を通って起動する。 - Backend restart 後に Runtime catalog / Worker snapshot / transcript / ConfigBundle store が復元される。 - restart 後の live execution handle は復元済みとして扱われず、unconnected / stale diagnostic として扱われる。 - Browser-facing API に Runtime store root / raw path / execution handle が漏れない。 - Focused tests が embedded fs-store restore、ConfigBundle restore、transcript restore、execution unconnected restore、memory-backed bypass absence を確認する。 - `cargo test -p yoi-workspace-server` が通る。 - `cargo test -p worker-runtime --features ws-server,fs-store` が通る。 - `cargo check -p yoi` が通る。 - `git diff --check` が通る。 - `nix build .#yoi --no-link` が通る。 ## 対象外 - canonical Runtime Worker create request の field 確定。 - per-worker cwd / tool scope の新 API。 - remote Runtime execution provisioning。 - live execution handle の restart 後自動再接続。 - 旧 Pod store / pod-registry の削除。