diff --git a/.yoi/objectives/00001KVJPT2PP/item.md b/.yoi/objectives/00001KVJPT2PP/item.md index 76423e7c..0f90a21d 100644 --- a/.yoi/objectives/00001KVJPT2PP/item.md +++ b/.yoi/objectives/00001KVJPT2PP/item.md @@ -20,14 +20,14 @@ Yoi を、単一のローカル開発ディレクトリで動くエージェン - Workspace: チームまたはプロジェクトの管理単位。Ticket、Objective、Memory、Knowledge、Artifact、Policy、Actor、Repository、Runtime state を持つ。Git Repository root ではない。 - Control plane: Workspace の正本を持ち、Web UI / API / CLI から操作される管理システム。 -- Runtime: Worker 群を束ねる実行基盤。Worker lifecycle、sandbox、mount、cache、checkout/worktree/container filesystem などの Execution Workspace materialization、event/control plane を管理する。将来的には 1 つの Runtime が複数 Workspace / Repository の Worker を抱えられる。 -- Worker: Runtime が管理する 1 つの agent/session/process。Runtime が用意した Execution Workspace と authority の中で動く。 +- Runtime: Worker 群を束ねる実行基盤。Worker lifecycle、sandbox、mount、cache、checkout/worktree/container filesystem などの working directory materialization、event/control plane を管理する。将来的には 1 つの Runtime が複数 Workspace / Repository の Worker を抱えられる。 +- Worker: Runtime が管理する 1 つの agent/session/process。Runtime が用意した working directory と authority の中で動く。 - Repository: Workspace に接続される source/storage。コード、ドキュメント、local directory、object storage、artifact store、dataset などを含む。Git Repository も Repository の一種であり、基本的には filesystem path ではなく URI / URL で識別する。 - RepositoryId: Workspace 内でどの Repository を対象にするかを指す安定 identifier。Git hash、branch、path ではない。 - Repository provider: Repository の種類ごとの実装。Git、local filesystem、object store、artifact store、将来の non-Git VCS など。 - RepositorySelector: Repository provider に渡す未解決の地点指定。branch/tag/PR/revspec/bookmark/revset/path@revision/object version/latest など provider-specific な symbolic / mutable / query-like locator であり、それ自体は再現性の authority ではない。 - RepositoryPoint: RepositorySelector をある時点で解決した具体地点。Git commit/tree、Mercurial changeset、SVN revision、object store version/manifest digest、file snapshot など provider ごとの immutable / reproducible point を表し、Artifact/evidence に残す。 -- Execution Workspace: Runtime が Worker のために作る作業環境。1 つ以上の RepositoryPoint から materialize される作業用ディレクトリ、container filesystem、sandbox mount の集合であり、Git worktree、clone、sparse checkout などはこれを作る手段である。 +- working directory: Runtime が Worker のために作る作業環境。1 つ以上の RepositoryPoint から materialize される作業用ディレクトリ、container filesystem、sandbox mount の集合であり、Git worktree、clone、sparse checkout などはこれを作る手段である。 - Ticket: チームで扱う作業単位。目的、要件、判断、議論、完了条件、関係、証跡を持つ。 - Objective: 複数の Ticket を束ねる長期目標や設計方針。 - Artifact: Ticket や Worker 実行に紐づく成果物や証跡。diff、log、validation result、review result、report など。 @@ -46,7 +46,7 @@ Yoi を、単一のローカル開発ディレクトリで動くエージェン - 管理システムと Runtime を分ける。 - まず Web から Ticket、Objective、Memory、Knowledge、Artifact、Runtime / Worker state を見られるようにする。 - 最初はローカル Runtime を使い、後でリモート Runtime、クラウド Runtime、runtime pool、resource allocation、quota、billing、sandboxing に拡張する。 -- Git ホスティング機能を取り込むのではなく、Git Repository / worktree / clone は Repository provider と Execution Workspace materialization の手段として扱う。 +- Git ホスティング機能を取り込むのではなく、Git Repository / worktree / clone は Repository provider と working directory materialization の手段として扱う。 OSS として Control plane、Runtime、Web frontend、protocol を公開しつつ、managed service では hosted control plane、runtime fleet、リソース柔軟性、team auth、backup、audit、availability、multi-tenant operations で価値を出す。 @@ -68,7 +68,7 @@ Ticket と Objective は Repository 配下に置かず、Workspace 配下に平 RepositorySelector は Git branch/tag の抽象化ではない。Selector は provider-specific な未解決 locator であり、Git provider なら branch/tag/ref/revspec/PR/commit、Mercurial provider なら bookmark/revset/changeset、SVN provider なら path/revision、object store provider なら prefix/version/latest などを解釈する。実行時には Control plane または Repository provider が Selector を RepositoryPoint に解決し、Runtime はその RepositoryPoint を materialize する。 -Worker launch request は Ticket の target selector を concrete RepositoryPoint に解決し、その RepositoryPoint から Runtime が Execution Workspace を materialize する。Git worktree 相当の機能は、この Execution Workspace を作るための実装戦略として扱う。 +Worker launch request は Ticket の target selector を concrete RepositoryPoint に解決し、その RepositoryPoint から Runtime が working directory を materialize する。Git worktree 相当の機能は、この working directory を作るための実装戦略として扱う。 Backend は cwd や `--workspace` を暗黙の Repository として扱わない。`--workspace` は当面 workspace config root / local descriptor root を指すだけであり、Repository registry は明示設定された Workspace config から構築する。短期的には `.yoi/workspace-backend.local.toml` の `[[repositories]]` を local descriptor として使い、`uri = "."` のような local repository も明示 entry として登録する。`./` を暗黙 Repository として自動採用しない。 @@ -86,7 +86,7 @@ Ticket target は intent/selector であり、実行再現性のための immuta Ticket -> target selectors: Repository + ref selector + path + intent -> resolved RepositoryPoint - -> Execution Workspace + -> working directory -> WorkerRef / Artifact / Evidence -> Review / Decision -> Audit / Notification @@ -119,7 +119,7 @@ Ticket には次の概念が必要になる。 - Actor identity: human / agent / system / service account. - Assignment / owner / reviewer / watcher. - Typed thread events: comment, decision, plan, review, implementation report, state transition. -- Linked Objective / Artifact / WorkerRef / Repository / RepositoryPoint / Execution Workspace. +- Linked Objective / Artifact / WorkerRef / Repository / RepositoryPoint / working directory. - Permission / visibility. - Audit trail. - Notification / mention. @@ -170,17 +170,17 @@ Ticket / user intent -> RepositoryId + RepositorySelector + path scope + required authority -> resolved RepositoryPoint -> WorkerLaunchRequest / ConfigBundle / AuthorityBundle - -> Runtime ExecutionWorkspaceMaterializer - -> Execution Workspace allocation + -> Runtime WorkingDirectoryMaterializer + -> working directory allocation -> Worker process -> WorkerRef + Artifact/evidence ``` -Control plane は Workspace authority、Ticket target、Repository registry、Actor permission、設定 bundle の正本を持つ。Control plane または Repository provider は RepositorySelector を RepositoryPoint に解決し、どの地点を対象にしたかを evidence に残す。Runtime は RepositoryPoint、materialization policy、sandbox policy、mount/cache/secret policy、Worker config を受け取り、Runtime-local な Execution Workspace を確保して Worker を起動する。 +Control plane は Workspace authority、Ticket target、Repository registry、Actor permission、設定 bundle の正本を持つ。Control plane または Repository provider は RepositorySelector を RepositoryPoint に解決し、どの地点を対象にしたかを evidence に残す。Runtime は RepositoryPoint、materialization policy、sandbox policy、mount/cache/secret policy、Worker config を受け取り、Runtime-local な working directory を確保して Worker を起動する。 -この境界では、Worker は host filesystem path や Repository credential を自分で発見しない。Worker は Runtime が materialize した workspace root、mount、環境変数、tool authority、config bundle だけを見る。Runtime は Execution Workspace の lifecycle、cleanup、cache reuse、namespace、quota、sandbox boundary、event collection を管理する。Control plane / Browser-facing API は raw host path、secret、socket、internal runtime path を authority-bearing internals として扱い、必要な evidence だけを Artifact として公開する。 +この境界では、Worker は host filesystem path や Repository credential を自分で発見しない。Worker は Runtime が materialize した workspace root、mount、環境変数、tool authority、config bundle だけを見る。Runtime は working directory の lifecycle、cleanup、cache reuse、namespace、quota、sandbox boundary、event collection を管理する。Control plane / Browser-facing API は raw host path、secret、socket、internal runtime path を authority-bearing internals として扱い、必要な evidence だけを Artifact として公開する。 -v0 materializer は existing local root を明示的な Execution Workspace として返してよい。ただし型と呼び出し順序は、後で Git worktree、clone、sparse checkout、container filesystem、remote object snapshot、multi-repository mount に置き換えられる形にする。Runtime process 起動時の `--workspace` はこの v0 materializer の legacy bootstrap input であり、Runtime identity や long-term workspace binding ではない。 +v0 materializer は existing local root を明示的な working directory として返してよい。ただし型と呼び出し順序は、後で Git worktree、clone、sparse checkout、container filesystem、remote object snapshot、multi-repository mount に置き換えられる形にする。Runtime process 起動時の `--workspace` はこの v0 materializer の legacy bootstrap input であり、Runtime identity や long-term workspace binding ではない。 その後で、remote Runtime、self-hosted Runtime、hosted cloud runtime fleet、runtime pool、resource allocation、quota、billing、sandbox、network policy、secret distribution を追加する。 @@ -204,7 +204,7 @@ Web UI は Ticket、Objective、Memory、Knowledge、Runtime、Worker、Artifact ### 7. 多重起動コストと runtime placement を見直す -Cloud/remote execution を成立させるには、多数のエージェント実行を安く管理できる必要がある。logical Worker session と Runtime process/resource placement を分ける。Runtime は Git Repository root や Workspace path に固定されず、request ごとに必要な Execution Workspace を materialize できる実行基盤として扱う。 +Cloud/remote execution を成立させるには、多数のエージェント実行を安く管理できる必要がある。logical Worker session と Runtime process/resource placement を分ける。Runtime は Git Repository root や Workspace path に固定されず、request ごとに必要な working directory を materialize できる実行基盤として扱う。 初期 Workspace DB では、Worker を canonical table として永続化しない。Runtime / Worker 一覧は backend-local runtime inspection や将来の Runtime protocol から逐次取得する live view とし、Ticket に関わった Worker は Ticket thread events と WorkerRef snapshot / TicketWorkerLink として記録する。 @@ -214,7 +214,7 @@ Worker の一元管理、データ永続化、アーカイブは将来的には - Worker identity と Runtime process/resource placement の分離。 - 1 Runtime が複数 Workspace / Repository の Worker を抱える場合の namespace、quota、cleanup、audit boundary。 -- Runtime-side Execution Workspace materialization、sandbox、mount、checkout/worktree/container filesystem の責務。 +- Runtime-side working directory materialization、sandbox、mount、checkout/worktree/container filesystem の責務。 - Provider client、tool registry、resource cache の共有可能性。 - Prompt/resource/profile/config bundle resolution cache。 - Model call multiplexing and scheduling。 @@ -226,7 +226,7 @@ Worker の一元管理、データ永続化、アーカイブは将来的には ## Initial phases / candidate tickets 1. **Vocabulary / architecture record** - - Workspace / RepositoryId / RepositorySelector / RepositoryPoint / Execution Workspace / Runtime / Worker / Control Plane / Ticket / Memory / Knowledge の用語と境界を固める。 + - Workspace / RepositoryId / RepositorySelector / RepositoryPoint / working directory / Runtime / Worker / Control Plane / Ticket / Memory / Knowledge の用語と境界を固める。 2. **Team-space canonical data model** - Ticket / Objective / Target / Artifact / Actor / Permission / Audit / Memory / Knowledge の entity/event model を設計する。 3. **Ticket evidence model** @@ -238,8 +238,8 @@ Worker の一元管理、データ永続化、アーカイブは将来的には 6. **Web control plane MVP design** - read-only Ticket / Objective / Memory / Knowledge / Runtime / Worker state UI/API の範囲を決める。 7. **Local Runtime protocol design** - - Web/control plane から local Runtime に安全な操作を送り、Runtime が Worker lifecycle と Execution Workspace materialization を担う protocol と authority boundary を設計する。 -8. **Repository and Execution Workspace materialization model** + - Web/control plane から local Runtime に安全な操作を送り、Runtime が Worker lifecycle と working directory materialization を担う protocol と authority boundary を設計する。 +8. **Repository and working directory materialization model** - Repository URI、Repository provider capability、RepositorySelector resolution、RepositoryPoint evidence、Git worktree / clone / sparse checkout / future source backend を Runtime-side materialization strategy として抽象化する。 9. **Remote/hosted runtime foundation** - runtime registration, heartbeat, capability advertisement, job assignment, logs/events, secrets, sandbox/resource policy を設計する。 @@ -258,7 +258,7 @@ Worker の一元管理、データ永続化、アーカイブは将来的には ## Success criteria / exit conditions -- Workspace / RepositoryId / RepositorySelector / RepositoryPoint / Execution Workspace / Runtime / Worker / Control Plane / Ticket / Memory / Knowledge の境界が文書化されている。 +- Workspace / RepositoryId / RepositorySelector / RepositoryPoint / working directory / Runtime / Worker / Control Plane / Ticket / Memory / Knowledge の境界が文書化されている。 - Ticket が team coordination record として、target selector / Artifact / Actor / Permission / Audit と分離された model を持つ。 - `.yoi` local backend は compatibility/local backend として整理され、server-side canonical backend の設計を阻害しない。 - Web UI/API が Ticket / Objective / Runtime / Worker state を中心とした read-only view を提供できる設計または MVP を持つ。Memory / Knowledge は既存 record の表示または将来 placeholder に留め、本格再設計をこの段階の必須条件にしない。 @@ -266,7 +266,7 @@ Worker の一元管理、データ永続化、アーカイブは将来的には - Runtime は single Workspace / Git repository root 専用 process ではなく、sandbox/authority が成立すれば複数 Workspace / Repository の Worker を抱えられる execution substrate として設計されている。 - Git Repository root に依存しない Workspace model があり、Git Repository は Repository provider の一種として扱われている。 - Ticket と Objective は Workspace 配下に平たく存在し、Repository への所属ではなく RepositoryId / RepositorySelector / path scope / intent で対象を表現する。 -- Git worktree 相当は Execution Workspace materialization strategy として扱われ、Artifact/evidence が concrete RepositoryPoint を記録する。 +- Git worktree 相当は working directory materialization strategy として扱われ、Artifact/evidence が concrete RepositoryPoint を記録する。 - Memory / Knowledge は Ticket / Artifact の authority を置き換えない record として platform contract だけを持つ。本格的な意味論・抽出・承認・検索・staleness 処理は、Memory の保存先を Workspace backend / control plane record に移すタイミングで回収する。 - Hosted Runtime / resource allocation / SaaS offering に進むための後続 Ticket が切れる状態になっている。 - 既存 local dogfooding runtime を壊さず、local use と remote-capable architecture が両立している。 diff --git a/.yoi/objectives/00001KWW44EXK/item.md b/.yoi/objectives/00001KWW44EXK/item.md index 4b7ad6a6..3329c9d0 100644 --- a/.yoi/objectives/00001KWW44EXK/item.md +++ b/.yoi/objectives/00001KWW44EXK/item.md @@ -1,5 +1,5 @@ --- -title: "Runtime execution workspace materialization and sandboxed agent environments" +title: "Runtime working directory materialization and sandboxed agent environments" state: "active" created_at: "2026-07-06T16:28:12Z" updated_at: "2026-07-06T16:28:12Z" @@ -8,11 +8,11 @@ linked_tickets: ["00001KWPC13WQ", "00001KWMBAA6V"] ## Goal -Runtime が Worker ごとに安全で安価な作業環境を用意できるようにする。Yoi の Runtime は、単に既存ディレクトリで Worker process を起動する launcher ではなく、RepositoryPoint から Execution Workspace を materialize し、sandbox / mount / cache / cleanup / evidence を管理する実行基盤になる。 +Runtime が Worker ごとに安全で安価な作業環境を用意できるようにする。Yoi の Runtime は、単に既存ディレクトリで Worker process を起動する launcher ではなく、RepositoryPoint から working directory を materialize し、sandbox / mount / cache / cleanup / evidence を管理する実行基盤になる。 -この Objective の中心は、Worktree ライクな作業ディレクトリ管理、Repository cache、Execution Workspace allocation、sandboxed agent environment の境界を設計し、将来的に 1 つの Runtime が複数 Workspace / Repository の Worker を抱えられるようにすることである。 +この Objective の中心は、Worktree ライクな作業ディレクトリ管理、Repository cache、working directory allocation、sandboxed agent environment の境界を設計し、将来的に 1 つの Runtime が複数 Workspace / Repository の Worker を抱えられるようにすることである。 -初期実装では Git/local repository を主対象にしてよい。ただし設計は Git worktree 固定にしない。Git worktree、bare object cache、sparse checkout、copy-on-write snapshot、reflink copy、APFS clonefile、btrfs snapshot、overlay filesystem、container filesystem、remote object snapshot は、すべて Execution Workspace materialization strategy の候補として扱う。 +初期実装では Git/local repository を主対象にしてよい。ただし設計は Git worktree 固定にしない。Git worktree、bare object cache、sparse checkout、copy-on-write snapshot、reflink copy、APFS clonefile、btrfs snapshot、overlay filesystem、container filesystem、remote object snapshot は、すべて working directory materialization strategy の候補として扱う。 ## Motivation / background @@ -24,7 +24,7 @@ Runtime が Worker ごとに安全で安価な作業環境を用意できるよ - Worker ごとに full clone すると容量と時間のコストが大きすぎる。 - `.yoi` / Backend fs-store / Workspace descriptor と、Worker 実行用 checkout / scratch / build output の lifecycle が混ざりやすい。 -Yoi は Workspace / Repository / Runtime を分ける方針になっている。Backend は Repository registry、RepositorySelector、RepositoryPoint、Ticket/Artifact evidence の authority を持つ。一方 Runtime は RepositoryPoint を受け取り、Worker が使う Execution Workspace を用意する責務を持つべきである。 +Yoi は Workspace / Repository / Runtime を分ける方針になっている。Backend は Repository registry、RepositorySelector、RepositoryPoint、Ticket/Artifact evidence の authority を持つ。一方 Runtime は RepositoryPoint を受け取り、Worker が使う working directory を用意する責務を持つべきである。 したがって、Repository を持ってくる実体ディレクトリは Backend / Workspace store ではなく Runtime 管理領域に置く。`./.yoi` は local descriptor / compatibility / project record surface であり、Worker ごとの checkout、worktree、snapshot、sandbox root、build cache、dependency cache を置く場所ではない。 @@ -32,11 +32,11 @@ Yoi は Workspace / Repository / Runtime を分ける方針になっている。 ## Glossary -- Runtime root: Runtime が自身の store、cache、Worker metadata、Execution Workspace allocation を管理する root。長期的には `~/.yoi/runtimes//` 配下など、Workspace backend store とは別に置く。 +- Runtime root: Runtime が自身の store、cache、Worker metadata、working directory allocation を管理する root。長期的には `~/.yoi/runtimes//` 配下など、Workspace backend store とは別に置く。 - Repository cache: Runtime-local の共有 source/cache。Git なら bare mirror / object cache / packfile cache など。重く、長寿命で、複数 Worker allocation から共有される。 -- Execution Workspace: Worker ごとの作業環境。短寿命で、Worker が読み書きする root / mounts / scratch / overlay を含む。 -- Materialization strategy: RepositoryPoint から Execution Workspace を作る実装戦略。Git detached worktree、sparse checkout、CoW snapshot、reflink copy、overlay、container filesystem など。 -- ExecutionWorkspaceAllocation: Runtime が払い出した作業環境の record。allocation id、worker id、workspace id、repository point、materializer kind、root、mounts、cleanup policy、status を持つ。 +- working directory: Worker ごとの作業環境。短寿命で、Worker が読み書きする root / mounts / scratch / overlay を含む。 +- Materialization strategy: RepositoryPoint から working directory を作る実装戦略。Git detached worktree、sparse checkout、CoW snapshot、reflink copy、overlay、container filesystem など。 +- WorkingDirectoryAllocation: Runtime が払い出した作業環境の record。allocation id、worker id、workspace id、repository point、materializer kind、root、mounts、cleanup policy、status を持つ。 - Sandbox policy: Worker が見られる filesystem、network、process、secret、tool authority の境界を表す policy。 - Dirty state policy: local uncommitted changes を Worker environment に含めるかどうかの方針。clean point only、patch artifact apply、snapshot current worktree など。 @@ -64,7 +64,7 @@ Runtime root - config bundles - worker metadata / transcript references - repository-cache - - execution-workspaces + - working-directories - sandbox state - build/dependency cache - cleanup ledger @@ -94,7 +94,7 @@ Runtime root git/ / bare.git - execution-workspaces/ + working-directories/ / root/ mounts/ @@ -106,14 +106,14 @@ Runtime root ### 2. clone ではなく materialize と呼ぶ -Runtime は Repository を毎回 clone するのではない。Runtime は RepositoryPoint を Worker 用の Execution Workspace として materialize する。 +Runtime は Repository を毎回 clone するのではない。Runtime は RepositoryPoint を Worker 用の working directory として materialize する。 ```text RepositoryId + RepositorySelector -> resolved RepositoryPoint -> Runtime repository-cache - -> ExecutionWorkspaceMaterializer - -> ExecutionWorkspaceAllocation + -> WorkingDirectoryMaterializer + -> WorkingDirectoryAllocation -> Worker process ``` @@ -130,13 +130,13 @@ Git の場合でも materialization strategy は複数あり得る。 ### 3. Repository cache と Worker workspace を分離する -full clone を Worker ごとに作らない。重い source/object data は Runtime-local repository cache に集約し、Worker ごとの Execution Workspace は cheap allocation にする。 +full clone を Worker ごとに作らない。重い source/object data は Runtime-local repository cache に集約し、Worker ごとの working directory は cheap allocation にする。 Git v0 の方向: ```text repository-cache/git//bare.git -execution-workspaces//root// +working-directories//root// ``` 初回: @@ -174,7 +174,7 @@ Dirty state を含める場合、Artifact/evidence には source RepositoryPoint ### 5. Sandbox を materialization と同じ境界で扱う -Execution Workspace は単なる directory path ではなく、Worker が見てよい filesystem view である。Runtime は Execution Workspace allocation と同時に sandbox/mount/authority を構築する。 +working directory は単なる directory path ではなく、Worker が見てよい filesystem view である。Runtime は working directory allocation と同時に sandbox/mount/authority を構築する。 Worker に渡すもの: @@ -197,14 +197,14 @@ Worker が自分で発見してはいけないもの: Sandbox v0 は strong isolation でなくてもよい。ただし型と lifecycle は、後で container sandbox、namespace, mount filtering, network policy, secret boundary に拡張できる形にする。 -### 6. Execution Workspace registry を持つ +### 6. working directory registry を持つ Runtime は materialized workspace を filesystem だけでなく registry でも管理する。 必要な record: ```text -ExecutionWorkspaceAllocation +WorkingDirectoryAllocation id runtime_id worker_id @@ -245,26 +245,26 @@ Rift の filtered CoW creation のように、重い artifacts を除外しな ### Phase 1: Materialization boundary and legacy allocation record -- `CreateWorkerRequest` に Execution Workspace request / target placeholder を追加する。 -- `ExecutionWorkspaceMaterializer` trait を `worker-runtime` に追加する。 +- `CreateWorkerRequest` に working directory request / target placeholder を追加する。 +- `WorkingDirectoryMaterializer` trait を `worker-runtime` に追加する。 - `WorkerRuntimeExecutionBackend` が Worker spawn 前に materializer を呼ぶ順序にする。 - v0 materializer は existing local root を explicit allocation として返してよいが、`direct_legacy_mount` として明示し、通常設計の final form と混同しない。 - Allocation record / cleanup policy / diagnostics の型を先に作る。 - Worker が source repository root を直接 scope として要求する経路を deprecated/legacy に閉じ込める。 -### Phase 2: Runtime root and execution workspace storage +### Phase 2: Runtime root and working directory storage -- `--runtime-root` を導入し、Runtime state / repository-cache / execution-workspaces / worker metadata / worker runtime dirs を Runtime root 配下へ寄せる。 +- `--runtime-root` を導入し、Runtime state / repository-cache / working-directories / worker metadata / worker runtime dirs を Runtime root 配下へ寄せる。 - `--workspace` は legacy bootstrap input としてだけ扱い、Runtime identity / long-term workspace binding から外す。 - Runtime root default は user data 配下にする。 -- Execution Workspace allocation registry を Runtime root に保存する。 +- working directory allocation registry を Runtime root に保存する。 ### Phase 3: Git cached detached worktree materializer - Git repository cache を Runtime root に作る。 - RepositorySelector を RepositoryPoint に解決する呼び出し境界を作る。 - resolved commit/tree を evidence として残す。 -- Worker ごとに detached worktree を `execution-workspaces//root/` に作る。 +- Worker ごとに detached worktree を `working-directories//root/` に作る。 - Worker stop / cleanup 時に `git worktree remove` と registry cleanup を行う。 - dirty state は v0 では `clean_point_only` を default にし、dirty local workspace は明示 diagnostic で拒否する。 @@ -284,13 +284,13 @@ Rift の filtered CoW creation のように、重い artifacts を除外しな - container filesystem / mount namespace / network policy / secret handle / tool authority を Runtime allocation と統合する。 - Runtime が複数 Workspace / Repository の Worker を同時に抱える場合の namespace、quota、cleanup、audit boundary を固める。 -- remote/self-hosted/hosted Runtime fleet で repository cache と execution workspace storage をどう扱うかを設計する。 +- remote/self-hosted/hosted Runtime fleet で repository cache と working directory storage をどう扱うかを設計する。 ## Non-goals - v0 で完全な container sandbox を実装すること。 - Worker ごとに full clone すること。 -- `.yoi` や Backend workspace store に Worker execution workspace を置くこと。 +- `.yoi` や Backend workspace store に Worker working directory を置くこと。 - Git worktree を唯一の materialization strategy として固定すること。 - Dirty local changes を暗黙に Worker に渡すこと。 - Browser-facing API に raw host path、secret、Runtime internal store path を公開すること。 @@ -298,10 +298,10 @@ Rift の filtered CoW creation のように、重い artifacts を除外しな ## Success criteria / exit conditions -- Runtime root、Repository cache、Execution Workspace、Backend/Workspace store の境界が文書化されている。 -- Runtime が Worker spawn 前に Execution Workspace materializer を呼ぶ型と順序を持つ。 -- Worker は source repository root ではなく materialized Execution Workspace を scope として起動する。 -- Execution Workspace allocation が Runtime registry に記録され、cleanup policy を持つ。 +- Runtime root、Repository cache、working directory、Backend/Workspace store の境界が文書化されている。 +- Runtime が Worker spawn 前に working directory materializer を呼ぶ型と順序を持つ。 +- Worker は source repository root ではなく materialized working directory を scope として起動する。 +- working directory allocation が Runtime registry に記録され、cleanup policy を持つ。 - Git/local repository の v0 materializer が full clone 連発ではなく shared cache / detached worktree / cheap allocation の方向に進んでいる。 - dirty state policy が明示され、clean point、patch artifact、snapshot のどれを使ったか evidence に残せる。 - Runtime process 起動時の `--workspace` は legacy bootstrap input として隔離され、Runtime identity や single workspace binding とみなされない。 @@ -315,10 +315,10 @@ Rift の filtered CoW creation のように、重い artifacts を除外しな ## Decision context - Runtime は Worker 群を束ねる実行基盤であり、Worker 用の作業環境を用意する責務を持つ。 -- Repository は clone されるものではなく、RepositoryPoint から Worker 用 Execution Workspace として materialize される。 -- Runtime execution storage は Backend/Workspace store と分離する。`.yoi` は Worker execution workspace 置き場ではない。 +- Repository は clone されるものではなく、RepositoryPoint から Worker 用 working directory として materialize される。 +- Runtime execution storage は Backend/Workspace store と分離する。`.yoi` は Worker working directory 置き場ではない。 - full clone を Worker ごとに作らない。Runtime-local repository cache と Worker-local cheap workspace allocation を分ける。 - Git detached worktree は v0 materialization strategy として有力だが、Git worktree 固定の設計にはしない。 - CoW snapshot / reflink / btrfs snapshot / APFS clonefile / Rift-like workspace creation は、将来の materializer backend として検討する。 - Dirty local state は暗黙に渡さず、policy と evidence を持って扱う。 -- Sandbox は後付けの別機能ではなく、Execution Workspace allocation と同じ境界で扱う。 +- Sandbox は後付けの別機能ではなく、working directory allocation と同じ境界で扱う。 diff --git a/.yoi/tickets/00001KWPC13WQ/item.md b/.yoi/tickets/00001KWPC13WQ/item.md index b4d3b61f..51e9d40d 100644 --- a/.yoi/tickets/00001KWPC13WQ/item.md +++ b/.yoi/tickets/00001KWPC13WQ/item.md @@ -43,7 +43,7 @@ queued_at: '2026-07-04T19:34:04Z' ## 非目標 -- Runtime materialization / Execution Workspace 作成をこのチケットで実装すること。 +- Runtime materialization / working directory 作成をこのチケットで実装すること。 - multi-workspace Backend store を `~/.yoi/` に完全移行すること。 - Repository credential / clone / fetch / write operation を実装すること。 - Ticket target から RepositoryPoint を解決して Worker launch に渡すこと。 diff --git a/.yoi/tickets/00001KWPC13WQ/thread.md b/.yoi/tickets/00001KWPC13WQ/thread.md index 3dae28e7..5d085276 100644 --- a/.yoi/tickets/00001KWPC13WQ/thread.md +++ b/.yoi/tickets/00001KWPC13WQ/thread.md @@ -85,7 +85,7 @@ Implementation latitude: - focused tests の構成は backend helper/route-level と web model/component-levelの現実的な範囲で選んでよい。 Escalate if: -- credential/clone/fetch/write operation、Runtime materialization、Execution Workspace 作成、multi-workspace store 移行、Ticket target からの RepositoryPoint 解決が必要になる場合。 +- credential/clone/fetch/write operation、Runtime materialization、working directory 作成、multi-workspace store 移行、Ticket target からの RepositoryPoint 解決が必要になる場合。 - Browser-facing API に secret/auth ref/internal absolute paths を出さないと実装できない場合。 - `RepositorySelector` / `RepositoryPoint` の概念境界を設計変更する必要が出た場合。 diff --git a/.yoi/tickets/00001KWW9DYH4/artifacts/orchestration-plan.jsonl b/.yoi/tickets/00001KWW9DYH4/artifacts/orchestration-plan.jsonl index c2b8ff66..29f20bfe 100644 --- a/.yoi/tickets/00001KWW9DYH4/artifacts/orchestration-plan.jsonl +++ b/.yoi/tickets/00001KWW9DYH4/artifacts/orchestration-plan.jsonl @@ -1 +1 @@ -{"id":"orch-plan-20260706-182951-1","ticket_id":"00001KWW9DYH4","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KWW9DYH4 は独立 queued Ticket で blocker なし。Repository registry の次段として、local Git configured repository から Runtime root 配下に Worker ごとの detached Execution Workspace を materialize する v0 を実装する。human authorized routing 済みのため queued->inprogress acceptance 後に worktree + coder/reviewer loop へ進める。","branch":"work/00001KWW9DYH4-execution-workspace-materializer","worktree":"/home/hare/Projects/yoi/.worktree/00001KWW9DYH4-execution-workspace-materializer","role_plan":"単一 sibling Coder Pod に implementation worktree を委譲し、worker-runtime の Execution Workspace materialization boundary / local Git detached worktree allocation / cleanup evidence / tests を実装する。完了後、別 sibling Reviewer Pod で Ticket IntentPacket / Objective boundary / acceptance criteria に照らして read-only review する。"},"author":"orchestrator","at":"2026-07-06T18:29:51Z"} +{"id":"orch-plan-20260706-182951-1","ticket_id":"00001KWW9DYH4","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KWW9DYH4 は独立 queued Ticket で blocker なし。Repository registry の次段として、local Git configured repository から Runtime root 配下に Worker ごとの detached working directory を materialize する v0 を実装する。human authorized routing 済みのため queued->inprogress acceptance 後に worktree + coder/reviewer loop へ進める。","branch":"work/00001KWW9DYH4-execution-workspace-materializer","worktree":"/home/hare/Projects/yoi/.worktree/00001KWW9DYH4-execution-workspace-materializer","role_plan":"単一 sibling Coder Pod に implementation worktree を委譲し、worker-runtime の working directory materialization boundary / local Git detached worktree allocation / cleanup evidence / tests を実装する。完了後、別 sibling Reviewer Pod で Ticket IntentPacket / Objective boundary / acceptance criteria に照らして read-only review する。"},"author":"orchestrator","at":"2026-07-06T18:29:51Z"} diff --git a/.yoi/tickets/00001KWW9DYH4/item.md b/.yoi/tickets/00001KWW9DYH4/item.md index cb2ba290..77ce56db 100644 --- a/.yoi/tickets/00001KWW9DYH4/item.md +++ b/.yoi/tickets/00001KWW9DYH4/item.md @@ -1,5 +1,5 @@ --- -title: 'Add local Git worktree Execution Workspace materializer' +title: 'Add local Git worktree working directory materializer' state: 'closed' created_at: '2026-07-06T18:00:46Z' updated_at: '2026-07-06T19:24:25Z' @@ -10,29 +10,29 @@ queued_at: '2026-07-06T18:28:26Z' ## 背景 -現在の Runtime は `--workspace` で与えられた単一 root を Worker の workspace scope として使っている。そのため、同じ repository root を使う Worker を複数 spawn すると worker allocation conflict が起きる。また、Runtime が source repository root を直接 Worker に渡しており、Execution Workspace materialization / cleanup / evidence / sandbox の境界がまだない。 +現在の Runtime は `--workspace` で与えられた単一 root を Worker の workspace scope として使っている。そのため、同じ repository root を使う Worker を複数 spawn すると worker allocation conflict が起きる。また、Runtime が source repository root を直接 Worker に渡しており、working directory materialization / cleanup / evidence / sandbox の境界がまだない。 -Objective `00001KWW44EXK` では、Runtime が RepositoryPoint から Worker ごとの Execution Workspace を materialize し、Worker は source repository root ではなく materialized workspace を scope として起動する方針を定めた。この Ticket ではその v0 として、remote/cache-backed Git clone は扱わず、Repository `uri` が local Git repository を指し、そこから `git worktree add --detach` できる前提で最小の materializer を実装する。 +Objective `00001KWW44EXK` では、Runtime が RepositoryPoint から Worker ごとの working directory を materialize し、Worker は source repository root ではなく materialized workspace を scope として起動する方針を定めた。この Ticket ではその v0 として、remote/cache-backed Git clone は扱わず、Repository `uri` が local Git repository を指し、そこから `git worktree add --detach` できる前提で最小の materializer を実装する。 ローカル origin の Git clone / Runtime repository-cache / bare mirror cache はこの Ticket では非目標とする。v0 は local source repository 自体を source cache 相当として扱い、Runtime root 配下に Worker ごとの detached worktree を作る。 ## 要件 -- Runtime は Worker spawn 前に Execution Workspace materializer を呼ぶ境界を持つ。 +- Runtime は Worker spawn 前に working directory materializer を呼ぶ境界を持つ。 - v0 materializer は configured local Git repository から detached worktree を Runtime root 配下に作る。 - Worker は source repository root ではなく、materialized worktree path を workspace scope / cwd として起動する。 - `RepositoryId` / `RepositorySelector` / resolved `RepositoryPoint` の将来境界を壊さない型にする。 - この Ticket では local URI の Git repository のみ対応する。remote URI、bare mirror cache、clone/fetch、credential resolution は扱わない。 - worktree は branch 名ではなく resolved commit に対する detached worktree として作る。v0 では branch を作らず、Worker の変更は detached worktree 上の diff / patch artifact として回収する。branch 作成は後続の merge/export policy として扱う。 - dirty local changes は暗黙に含めない。v0 は `clean_point_only` とし、必要なら dirty state diagnostic を返す。 -- Execution Workspace は `.yoi` や Backend workspace store ではなく Runtime root 配下に作る。 +- working directory は `.yoi` や Backend workspace store ではなく Runtime root 配下に作る。 - Worker stop / cleanup 時に worktree cleanup できる方針または最低限の cleanup record を持つ。 - Browser-facing/API-facing diagnostics は raw host path を不必要に漏らさない。 ## 受け入れ条件 -- `CreateWorkerRequest` または Runtime-side spawn path に Execution Workspace materialization request / allocation 境界が追加されている。 -- local Git repository `uri = "."` 相当から resolved commit を取得し、Runtime root 配下の `execution-workspaces//root/` に `git worktree add --detach` できる。 +- `CreateWorkerRequest` または Runtime-side spawn path に working directory materialization request / allocation 境界が追加されている。 +- local Git repository `uri = "."` 相当から resolved commit を取得し、Runtime root 配下の `working-directories//root/` に `git worktree add --detach` できる。 - Worker 用の Git branch は v0 materialization では作成されず、変更回収は detached worktree の diff / patch artifact として扱われる。 - Worker spawn 時、Worker scope/cwd は source repository root ではなく materialized worktree を指す。 - 同じ local Git repository を対象に複数 Worker を spawn しても、source root scope の allocation conflict で失敗しない。 diff --git a/.yoi/tickets/00001KWW9DYH4/resolution.md b/.yoi/tickets/00001KWW9DYH4/resolution.md index 158108c8..b318e85e 100644 --- a/.yoi/tickets/00001KWW9DYH4/resolution.md +++ b/.yoi/tickets/00001KWW9DYH4/resolution.md @@ -1,19 +1,19 @@ 完了。 実装内容: -- Runtime-side Execution Workspace materialization boundary を追加した。 -- v0 local Git materializer として、configured repository から resolved commit / tree evidence を取り、Runtime root 配下 `execution-workspaces//root/` に detached Git worktree を作成するようにした。 +- Runtime-side working directory materialization boundary を追加した。 +- v0 local Git materializer として、configured repository から resolved commit / tree evidence を取り、Runtime root 配下 `working-directories//root/` に detached Git worktree を作成するようにした。 - Worker spawn 時の workspace root / cwd / scope は source repository root ではなく materialized worktree path を使うようにした。 - 同一 source repository に対する複数 Worker が distinct materialized paths を使えるようにした。 - dirty source state は `clean_point_only` policy で拒否し、remote URI / non-Git provider / unsupported policy は typed diagnostics で fail closed するようにした。 - allocation evidence / cleanup target / cleanup policy / status を記録し、Worker stop cleanup hook で worktree cleanup と record update を行うようにした。 -- Browser/API-facing spawn boundary は raw `ExecutionWorkspaceRequest` / `local_path` を受け取らないようにし、safe `repository_id` / optional `selector` から host/server 内部で materialization request を構築するようにした。 +- Browser/API-facing spawn boundary は raw `WorkingDirectoryRequest` / `local_path` を受け取らないようにし、safe `repository_id` / optional `selector` から host/server 内部で materialization request を構築するようにした。 - Focused worker-runtime / workspace-server tests を追加した。 主な commit / merge: -- implementation: `8b7a5da0 feat: materialize execution workspaces` -- review fix: `c90ebf24 fix: keep execution workspace paths internal` -- merge into orchestration: `c4cdf1c3 merge: execution workspace materializer` +- implementation: `8b7a5da0 feat: materialize working directories` +- review fix: `c90ebf24 fix: keep working directory paths internal` +- merge into orchestration: `c4cdf1c3 merge: working directory materializer` Review: - 初回 external review は request_changes。 diff --git a/.yoi/tickets/00001KWW9DYH4/thread.md b/.yoi/tickets/00001KWW9DYH4/thread.md index 90542c72..a3c33942 100644 --- a/.yoi/tickets/00001KWW9DYH4/thread.md +++ b/.yoi/tickets/00001KWW9DYH4/thread.md @@ -39,15 +39,15 @@ Ticket を `workspace-panel` が queued にしました。 Routing decision: implementation_ready Reason: -- Ticket は local Git worktree Execution Workspace materializer の v0 範囲、非目標、acceptance criteria が具体化されている。 -- Objective `00001KWW44EXK` の境界(Runtime root / Execution Workspace / RepositoryPoint / materialization strategy / cleanup registry / dirty state policy)と整合している。 +- Ticket は local Git worktree working directory materializer の v0 範囲、非目標、acceptance criteria が具体化されている。 +- Objective `00001KWW44EXK` の境界(Runtime root / working directory / RepositoryPoint / materialization strategy / cleanup registry / dirty state policy)と整合している。 - `remote URI`, clone/fetch/cache, credential resolution, CoW/sandbox 強化、dirty snapshot/apply は非目標として明確。 - typed relation blocker は 0 件、OrchestrationPlan record は 0 件。 - queued notification は human authorized routing であり、今回の routing acceptance 後にのみ implementation side effect へ進める。 Evidence checked: - Ticket body / thread / artifacts。 -- Objective `00001KWW44EXK` body(Runtime execution workspace materialization direction, Runtime root separation, detached worktree strategy, clean point policy, cleanup registry)。 +- Objective `00001KWW44EXK` body(Runtime working directory materialization direction, Runtime root separation, detached worktree strategy, clean point policy, cleanup registry)。 - `TicketRelationQuery(00001KWW9DYH4)`: 0 件。 - `TicketOrchestrationPlanQuery(00001KWW9DYH4)`: 0 件。 - Orchestrator worktree git status: clean on `orchestration`。 @@ -59,11 +59,11 @@ Evidence checked: IntentPacket: Intent: -- Runtime Worker spawn の直前に Execution Workspace materialization boundary を追加し、configured local Git repository から Worker ごとの detached worktree を Runtime root 配下に作る v0 materializer を実装する。 +- Runtime Worker spawn の直前に working directory materialization boundary を追加し、configured local Git repository から Worker ごとの detached worktree を Runtime root 配下に作る v0 materializer を実装する。 - Worker の workspace scope / cwd を source repository root ではなく materialized worktree path にすることで、同じ local Git repository を対象に複数 Worker を spawn しても source root scope allocation conflict で失敗しないようにする。 Binding decisions / invariants: -- Execution Workspace は `.yoi` / Backend workspace store ではなく Runtime root 配下に作る。 +- working directory は `.yoi` / Backend workspace store ではなく Runtime root 配下に作る。 - v0 は local Git repository URI のみ対応する。remote URI / non-Git provider / clone/fetch/cache / credentials は typed diagnostic で拒否または非対応扱い。 - worktree は branch 名ではなく resolved commit に対する detached worktree として作る。v0 materialization では Worker 用 branch を作らない。 - dirty local changes は暗黙に含めない。v0 は `clean_point_only` とし、dirty state は typed diagnostic / test evidence で明示する。 @@ -73,8 +73,8 @@ Binding decisions / invariants: - `RepositoryId` / `RepositorySelector` / `RepositoryPoint` の将来境界を壊さず、Git branch/tag/hash 固定の抽象にしない。 Requirements / acceptance criteria: -- `CreateWorkerRequest` または Runtime-side spawn path に Execution Workspace materialization request / allocation 境界が追加されている。 -- local Git repository `uri = "."` 相当から resolved commit を取得し、Runtime root 配下の `execution-workspaces//root/` に `git worktree add --detach` できる。 +- `CreateWorkerRequest` または Runtime-side spawn path に working directory materialization request / allocation 境界が追加されている。 +- local Git repository `uri = "."` 相当から resolved commit を取得し、Runtime root 配下の `working-directories//root/` に `git worktree add --detach` できる。 - Worker spawn 時の scope/cwd は source repository root ではなく materialized worktree を指す。 - 同じ local Git repository を対象に複数 Worker を spawn しても source root scope conflict で失敗しない。 - unsupported non-local URI / non-Git provider は typed diagnostic。 @@ -124,7 +124,7 @@ Queued acceptance recorded after Workspace Dashboard Queue notification authoriz Checked context: - Ticket body / thread / artifacts。 -- Objective `00001KWW44EXK` の Execution Workspace materialization strategy / dirty policy / Runtime root separation / cleanup registry context。 +- Objective `00001KWW44EXK` の working directory materialization strategy / dirty policy / Runtime root separation / cleanup registry context。 - `TicketRelationQuery(00001KWW9DYH4)`: blocking relation 0 件。 - `TicketOrchestrationPlanQuery(00001KWW9DYH4)`: prior record 0 件。今回 accepted_plan を記録済み。 - Orchestrator worktree git status: clean on `orchestration`。 @@ -161,22 +161,22 @@ Implementation routing update: Implementation progress report: -- Coder Pod completed implementation and committed `8b7a5da0 feat: materialize execution workspaces` on branch `work/00001KWW9DYH4-execution-workspace-materializer`. +- Coder Pod completed implementation and committed `8b7a5da0 feat: materialize working directories` on branch `work/00001KWW9DYH4-execution-workspace-materializer`. - Worktree status was clean after commit. - Changed files include: - - `crates/worker-runtime/src/execution_workspace.rs` (new) + - `crates/worker-runtime/src/working_directory.rs` (new) - `crates/worker-runtime/src/{catalog.rs,execution.rs,http_server.rs,lib.rs,main.rs,runtime.rs,worker_backend.rs}` - `crates/workspace-server/src/{companion.rs,hosts.rs,server.rs}` - Implementation summary: - - Runtime-side Execution Workspace request/status types and local Git worktree materializer。 - - detached Git worktree creation under Runtime root: `execution-workspaces//root/`。 + - Runtime-side working directory request/status types and local Git worktree materializer。 + - detached Git worktree creation under Runtime root: `working-directories//root/`。 - clean HEAD/selector commit and tree evidence resolution。 - dirty source rejection under `clean_point_only`。 - typed diagnostics for remote URI / non-Git provider / unsupported policy。 - materialization/cleanup evidence persistence and best-effort worktree cleanup on Worker stop。 - Worker spawn cwd/scope uses materialized worktree path, not source repository root。 - workspace-server embedded runtime startup and Worker spawn pass configured repositories into materialization request。 - - API-facing execution workspace status summaries remain sanitized/path-free。 + - API-facing working directory status summaries remain sanitized/path-free。 - Orchestrator reran validation in the implementation worktree: - `git diff --check`: pass - `cargo test -p worker-runtime --features ws-server,fs-store`: pass(34 lib tests + 5 main tests + doc tests) @@ -197,16 +197,16 @@ Implementation progress report: External review result: request_changes Blocker: -- 実装が Browser/API-facing spawn boundary を広げ、API caller から full `ExecutionWorkspaceRequest`(`ExecutionWorkspaceRepository.local_path: Option` を含む)を受け取れる形になっている。 - - `crates/workspace-server/src/hosts.rs:263-283` は `WorkerSpawnRequest` を browser-safe として raw workspace roots/storage paths を受け取らない契約にしているが、現在は `execution_workspace: Option` を含む。 +- 実装が Browser/API-facing spawn boundary を広げ、API caller から full `WorkingDirectoryRequest`(`WorkingDirectoryRepository.local_path: Option` を含む)を受け取れる形になっている。 + - `crates/workspace-server/src/hosts.rs:263-283` は `WorkerSpawnRequest` を browser-safe として raw workspace roots/storage paths を受け取らない契約にしているが、現在は `working_directory: Option` を含む。 - `crates/worker-runtime/src/catalog.rs:58-66` の request は raw `local_path` を運べる。 - `/api/runtimes/{runtime_id}/workers` はこの request shape を受け取り、embedded/remote host spawn path が `CreateWorkerRequest` に pass-through している。 - これは Ticket / routing invariant の「raw source/internal paths を含む public launch boundary 拡張を避ける。必要なら escalate」に反し、既存コード上の browser-safe contract とも衝突している。 -- safer `/api/workers` path は server-side repository config から `repository_id` により解決しているため、generic API も caller-supplied raw `local_path` authority を受け取らない形にするか、Execution Workspace materialization request の組み立てを host/runtime 内部境界に閉じる必要がある。 +- safer `/api/workers` path は server-side repository config から `repository_id` により解決しているため、generic API も caller-supplied raw `local_path` authority を受け取らない形にするか、working directory materialization request の組み立てを host/runtime 内部境界に閉じる必要がある。 Evidence supporting the rest of the implementation: - Runtime/backend materialization boundary は存在する。`Runtime::create_worker` が request を backend に渡し、`WorkerRuntimeExecutionBackend::spawn_worker` が spawn 前に materializer を呼ぶ。 -- local materializer は `execution-workspaces//root/` 配下に resolved commit の detached worktree を作成し、branch checkout はしない。 +- local materializer は `working-directories//root/` 配下に resolved commit の detached worktree を作成し、branch checkout はしない。 - Worker `workspace_root` / `cwd` は `Worker::from_manifest_with_context` 前に materialized binding に差し替えられている。 - dirty source は `clean_point_only` で拒否され、remote/non-Git は typed diagnostic code を返す。 - allocation evidence/status/cleanup target/policy は記録・永続化され、worker stop cleanup は worktree removal / cleanup-pending 更新を行う。 @@ -228,15 +228,15 @@ Non-blocking follow-ups: Review-fix implementation report: -- Coder Pod completed requested changes and committed follow-up `c90ebf24 fix: keep execution workspace paths internal` on branch `work/00001KWW9DYH4-execution-workspace-materializer`. +- Coder Pod completed requested changes and committed follow-up `c90ebf24 fix: keep working directory paths internal` on branch `work/00001KWW9DYH4-execution-workspace-materializer`. - Worktree status was clean after commit. - Fix summary: - - Browser/API-facing `WorkerSpawnRequest` no longer deserializes full `ExecutionWorkspaceRequest` or caller-supplied raw `local_path`. - - Added browser-safe execution workspace request shape with `repository_id` and optional `selector` only。 - - Added `deny_unknown_fields` so raw fields such as `execution_workspace.local_path` and unexpected fields are rejected at the API boundary。 - - Server-side host code resolves configured repository registry into internal `ExecutionWorkspaceRequest`; raw `local_path` construction stays behind trusted host/server boundary。 + - Browser/API-facing `WorkerSpawnRequest` no longer deserializes full `WorkingDirectoryRequest` or caller-supplied raw `local_path`. + - Added browser-safe working directory request shape with `repository_id` and optional `selector` only。 + - Added `deny_unknown_fields` so raw fields such as `working_directory.local_path` and unexpected fields are rejected at the API boundary。 + - Server-side host code resolves configured repository registry into internal `WorkingDirectoryRequest`; raw `local_path` construction stays behind trusted host/server boundary。 - Existing `/api/workers` repository-id safe behavior remains preserved。 - - Added focused API tests for rejecting raw `execution_workspace.local_path` and accepting safe repository-id/selector materialization without exposing raw workspace path。 + - Added focused API tests for rejecting raw `working_directory.local_path` and accepting safe repository-id/selector materialization without exposing raw workspace path。 - Orchestrator reran validation in the implementation worktree: - `git diff --check`: pass - `cargo test -p worker-runtime --features ws-server,fs-store`: pass(34 lib tests + 5 main tests + doc tests) @@ -263,15 +263,15 @@ Evidence reviewed: - Fix commit `c90ebf24` and combined implementation after `8b7a5da0`。 Findings: -- Prior blocker is resolved. Browser/API `WorkerSpawnRequest` now exposes only `execution_workspace: { repository_id, selector? }` via `WorkerSpawnExecutionWorkspaceRequest` with `deny_unknown_fields`; internal `resolved_execution_workspace` is `#[serde(skip, default)]`。 -- `/api/runtimes/{runtime_id}/workers` resolves safe repository id/selector server-side from configured repositories into an internal `ExecutionWorkspaceRequest`。 +- Prior blocker is resolved. Browser/API `WorkerSpawnRequest` now exposes only `working_directory: { repository_id, selector? }` via `WorkerSpawnWorkingDirectoryRequest` with `deny_unknown_fields`; internal `resolved_working_directory` is `#[serde(skip, default)]`。 +- `/api/runtimes/{runtime_id}/workers` resolves safe repository id/selector server-side from configured repositories into an internal `WorkingDirectoryRequest`。 - `/api/workers` remains safe: `BrowserCreateWorkerRequest` only accepts optional `repository_id`; server-side code builds the internal request。 - Fix tests explicitly reject caller-supplied `local_path` and accept safe `repository_id`/`selector`, with response path redaction asserted。 -- Response/status projections remain browser-safe: execution workspace summaries carry ids/commit/tree/cleanup metadata, not raw workspace/source paths。 +- Response/status projections remain browser-safe: working directory summaries carry ids/commit/tree/cleanup metadata, not raw workspace/source paths。 - Original materializer acceptance remains satisfied: - local Git only, remote/non-Git/dirty source rejected with typed diagnostics。 - selector resolves to commit/tree。 - - detached `git worktree add --detach` is created under Runtime root `execution-workspaces//root/`。 + - detached `git worktree add --detach` is created under Runtime root `working-directories//root/`。 - worker factory uses materialized workspace root/cwd instead of source root。 - allocation evidence and cleanup target are recorded。 - cleanup hook removes worktree and updates record。 @@ -296,7 +296,7 @@ No merge-blocking issues found. Merge/validation acceptance: - Follow-up external review approved the implementation after blocker fix。 -- Implementation branch `work/00001KWW9DYH4-execution-workspace-materializer` was merged into the orchestration branch with merge commit `c4cdf1c3 merge: execution workspace materializer`。 +- Implementation branch `work/00001KWW9DYH4-execution-workspace-materializer` was merged into the orchestration branch with merge commit `c4cdf1c3 merge: working directory materializer`。 - Final validation in Orchestrator worktree passed: - `git diff --check`: pass - `cargo test -p worker-runtime --features ws-server,fs-store`: pass(34 lib tests + 5 main tests + doc tests) @@ -308,8 +308,8 @@ Merge/validation acceptance: - `nix build .#yoi --no-link` は Cargo.lock / dependency / resource packaging / Nix 変更ではないため未実行。 Implementation commits: -- `8b7a5da0 feat: materialize execution workspaces` -- `c90ebf24 fix: keep execution workspace paths internal` +- `8b7a5da0 feat: materialize working directories` +- `c90ebf24 fix: keep working directory paths internal` No merge-blocking issues remain. @@ -331,19 +331,19 @@ Ticket を closed にしました。 完了。 実装内容: -- Runtime-side Execution Workspace materialization boundary を追加した。 -- v0 local Git materializer として、configured repository から resolved commit / tree evidence を取り、Runtime root 配下 `execution-workspaces//root/` に detached Git worktree を作成するようにした。 +- Runtime-side working directory materialization boundary を追加した。 +- v0 local Git materializer として、configured repository から resolved commit / tree evidence を取り、Runtime root 配下 `working-directories//root/` に detached Git worktree を作成するようにした。 - Worker spawn 時の workspace root / cwd / scope は source repository root ではなく materialized worktree path を使うようにした。 - 同一 source repository に対する複数 Worker が distinct materialized paths を使えるようにした。 - dirty source state は `clean_point_only` policy で拒否し、remote URI / non-Git provider / unsupported policy は typed diagnostics で fail closed するようにした。 - allocation evidence / cleanup target / cleanup policy / status を記録し、Worker stop cleanup hook で worktree cleanup と record update を行うようにした。 -- Browser/API-facing spawn boundary は raw `ExecutionWorkspaceRequest` / `local_path` を受け取らないようにし、safe `repository_id` / optional `selector` から host/server 内部で materialization request を構築するようにした。 +- Browser/API-facing spawn boundary は raw `WorkingDirectoryRequest` / `local_path` を受け取らないようにし、safe `repository_id` / optional `selector` から host/server 内部で materialization request を構築するようにした。 - Focused worker-runtime / workspace-server tests を追加した。 主な commit / merge: -- implementation: `8b7a5da0 feat: materialize execution workspaces` -- review fix: `c90ebf24 fix: keep execution workspace paths internal` -- merge into orchestration: `c4cdf1c3 merge: execution workspace materializer` +- implementation: `8b7a5da0 feat: materialize working directories` +- review fix: `c90ebf24 fix: keep working directory paths internal` +- merge into orchestration: `c4cdf1c3 merge: working directory materializer` Review: - 初回 external review は request_changes。 diff --git a/.yoi/tickets/00001KWWE8E04/thread.md b/.yoi/tickets/00001KWWE8E04/thread.md index 857cf929..9bb5f67f 100644 --- a/.yoi/tickets/00001KWWE8E04/thread.md +++ b/.yoi/tickets/00001KWWE8E04/thread.md @@ -51,7 +51,7 @@ Evidence checked: - `TicketOrchestrationPlanQuery(00001KWWE8E04)`: 0 件。 - Orchestrator worktree git status: clean on `orchestration`。 - queued Ticket 一覧: この Ticket 1件のみ。inprogress は 0 件。 -- visible Pods: previous execution-workspace child Pods が idle で残っているが、`StopPod` 不使用方針のため停止せず、新規 unique Pod を使う。capacity blocker ではない。 +- visible Pods: previous working-directory child Pods が idle で残っているが、`StopPod` 不使用方針のため停止せず、新規 unique Pod を使う。capacity blocker ではない。 - `TicketDoctor`: 0 errors / 既存 diagnostics のみ。 - Bounded code map: `crates/workspace-server/src/{identity.rs,server.rs,hosts.rs,config.rs,records.rs,repositories.rs}`, `web/workspace/src/routes/**`, `web/workspace/src/lib/workspace-sidebar/**`, workspace settings/sidebar/console helpers。 diff --git a/.yoi/tickets/00001KWY8EHEJ/artifacts/orchestration-plan.jsonl b/.yoi/tickets/00001KWY8EHEJ/artifacts/orchestration-plan.jsonl index a80066ea..fb43f99a 100644 --- a/.yoi/tickets/00001KWY8EHEJ/artifacts/orchestration-plan.jsonl +++ b/.yoi/tickets/00001KWY8EHEJ/artifacts/orchestration-plan.jsonl @@ -1 +1 @@ -{"id":"orch-plan-20260707-134133-1","ticket_id":"00001KWY8EHEJ","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KWY8EHEJ は独立 queued Ticket で blocker なし。先行 Runtime materializer と workspace-id scoped API を前提に、Browser-managed pre-allocated Execution Workspace を Worker spawn に接続する。human authorized routing 済みのため queued->inprogress acceptance 後に worktree + coder/reviewer loop へ進める。","branch":"work/00001KWY8EHEJ-browser-execution-workspaces","worktree":"/home/hare/Projects/yoi/.worktree/00001KWY8EHEJ-browser-execution-workspaces","role_plan":"単一 sibling Coder Pod に implementation worktree を委譲し、workspace-scoped Browser-managed Execution Workspace API / Worker launch UI integration / allocation-id spawn connection / relative_cwd validation / cleanup/status tests を実装する。完了後、別 sibling Reviewer Pod で Ticket IntentPacket / authority boundary / acceptance criteria に照らして read-only review する。"},"author":"orchestrator","at":"2026-07-07T13:41:33Z"} +{"id":"orch-plan-20260707-134133-1","ticket_id":"00001KWY8EHEJ","kind":"accepted_plan","accepted_plan":{"summary":"Ticket 00001KWY8EHEJ は独立 queued Ticket で blocker なし。先行 Runtime materializer と workspace-id scoped API を前提に、Browser-managed pre-allocated working directory を Worker spawn に接続する。human authorized routing 済みのため queued->inprogress acceptance 後に worktree + coder/reviewer loop へ進める。","branch":"work/00001KWY8EHEJ-browser-working-directories","worktree":"/home/hare/Projects/yoi/.worktree/00001KWY8EHEJ-browser-working-directories","role_plan":"単一 sibling Coder Pod に implementation worktree を委譲し、workspace-scoped Browser-managed working directory API / Worker launch UI integration / allocation-id spawn connection / relative_cwd validation / cleanup/status tests を実装する。完了後、別 sibling Reviewer Pod で Ticket IntentPacket / authority boundary / acceptance criteria に照らして read-only review する。"},"author":"orchestrator","at":"2026-07-07T13:41:33Z"} diff --git a/.yoi/tickets/00001KWY8EHEJ/item.md b/.yoi/tickets/00001KWY8EHEJ/item.md index 5c4f685c..ec8aad65 100644 --- a/.yoi/tickets/00001KWY8EHEJ/item.md +++ b/.yoi/tickets/00001KWY8EHEJ/item.md @@ -1,5 +1,5 @@ --- -title: 'Enable Browser-managed execution workspaces for Worker spawn' +title: 'Enable Browser-managed working directories for Worker spawn' state: 'closed' created_at: '2026-07-07T12:22:06Z' updated_at: '2026-07-07T14:58:35Z' @@ -10,31 +10,31 @@ queued_at: '2026-07-07T13:40:23Z' ## 背景 -Runtime 側には `CreateWorkerRequest.execution_workspace` と local Git worktree materializer があり、Worker 作成時に RepositoryPoint 相当の入力から detached worktree を materialize して Worker の workspace/cwd/scope にする境界が既にある。一方、Browser UI からは作業環境を事前に用意・確認し、それを Worker spawn 操作で選択する経路がまだ無い。 +Runtime 側には `CreateWorkerRequest.working_directory` と local Git worktree materializer があり、Worker 作成時に RepositoryPoint 相当の入力から detached worktree を materialize して Worker の workspace/cwd/scope にする境界が既にある。一方、Browser UI からは作業環境を事前に用意・確認し、それを Worker spawn 操作で選択する経路がまだ無い。 -Browser から raw host path や内部 materialization request を直接指定させるのではなく、Workspace Backend が repository / selector / policy を検証して Execution Workspace を作成し、Browser には allocation id と安全な summary だけを返す形にする。 +Browser から raw host path や内部 materialization request を直接指定させるのではなく、Workspace Backend が repository / selector / policy を検証して working directory を作成し、Browser には allocation id と安全な summary だけを返す形にする。 ## 要件 -- Browser-facing API で Execution Workspace を作成・一覧・詳細確認できる。 +- Browser-facing API で working directory を作成・一覧・詳細確認できる。 - v0 の materializer は既存方針どおり local Git repository からの detached worktree とし、remote clone/cache や dirty source inclusion は扱わない。 - Browser-facing payload に backend-private absolute path、raw materialization request、内部 runtime path を露出しない。 -- Worker spawn form/API から、作成済み Execution Workspace を選択して Worker 作成に利用できる。 +- Worker spawn form/API から、作成済み working directory を選択して Worker 作成に利用できる。 - Worker が利用する cwd 指定は host absolute path ではなく、materialized workspace root からの安全な `relative_cwd` として扱う。 - `relative_cwd` は absolute path、`..` escape、symlink escape、存在しない/不正な working directory を拒否する。 -- Worker detail / list には execution workspace の safe summary を表示する。 +- Worker detail / list には working directory の safe summary を表示する。 - cleanup policy と手動 cleanup 操作の v0 方針を明示する。 ## 受け入れ条件 -- `POST /api/w//execution-workspaces` 相当の API で configured repository と selector から Execution Workspace を作成できる。 -- `GET /api/w//execution-workspaces` 相当の API で作成済み Execution Workspace の safe summary を一覧できる。 -- Worker launch UI で Execution Workspace を選択でき、spawn request が raw path ではなく safe id/selector 情報を送る。 -- Worker spawn 時に選択した Execution Workspace が Runtime の `CreateWorkerRequest.execution_workspace` または同等の backend-resolved request に接続される。 +- `POST /api/w//working-directories` 相当の API で configured repository と selector から working directory を作成できる。 +- `GET /api/w//working-directories` 相当の API で作成済み working directory の safe summary を一覧できる。 +- Worker launch UI で working directory を選択でき、spawn request が raw path ではなく safe id/selector 情報を送る。 +- Worker spawn 時に選択した working directory が Runtime の `CreateWorkerRequest.working_directory` または同等の backend-resolved request に接続される。 - 作成された Worker の workspace/cwd/scope が materialized worktree 配下になり、Browser/API には内部 path が漏れない。 - 不正な repository、dirty unsupported source、remote unsupported source、invalid `relative_cwd` は typed diagnostic として拒否される。 - frontend check/test、workspace-server tests、`yoi ticket doctor`、`nix build .#yoi` が通る。 ## 設計メモ -初期実装は pre-allocate 方式を優先する。つまり Browser で Execution Workspace を明示的に作成し、Worker spawn では `allocation_id` と任意の `relative_cwd` を選択する。単発 spawn request だけで暗黙 materialize する経路は、UI/cleanup/diagnostics が固まるまで後回しにする。 +初期実装は pre-allocate 方式を優先する。つまり Browser で working directory を明示的に作成し、Worker spawn では `allocation_id` と任意の `relative_cwd` を選択する。単発 spawn request だけで暗黙 materialize する経路は、UI/cleanup/diagnostics が固まるまで後回しにする。 diff --git a/.yoi/tickets/00001KWY8EHEJ/resolution.md b/.yoi/tickets/00001KWY8EHEJ/resolution.md index d837241d..f58129e9 100644 --- a/.yoi/tickets/00001KWY8EHEJ/resolution.md +++ b/.yoi/tickets/00001KWY8EHEJ/resolution.md @@ -1,24 +1,24 @@ 完了。 実装内容: -- Browser-managed Execution Workspace API を scoped Workspace API に追加した。 - - `POST /api/w//execution-workspaces` - - `GET /api/w//execution-workspaces` +- Browser-managed working directory API を scoped Workspace API に追加した。 + - `POST /api/w//working-directories` + - `GET /api/w//working-directories` - detail / cleanup endpoint -- Browser create payload は configured `repository_id` / selector / policy を使い、raw host path / raw internal `ExecutionWorkspaceRequest` / internal runtime path を受け取らないようにした。 +- Browser create payload は configured `repository_id` / selector / policy を使い、raw host path / raw internal `WorkingDirectoryRequest` / internal runtime path を受け取らないようにした。 - Browser response は allocation id、repository id、selector/commit evidence、status、cleanup policy など safe summary のみを返すようにした。 -- Worker launch UI で Execution Workspace を create/select できるようにした。 +- Worker launch UI で working directory を create/select できるようにした。 - Worker spawn は selected allocation id + optional `relative_cwd` を使い、Runtime materialized workspace に接続するようにした。 - `relative_cwd` は materialized root 相対として検証し、absolute path、`..` escape、symlink escape、nonexistent / non-directory を拒否するようにした。 -- invalid `relative_cwd` は Browser-facing API で typed `execution_workspace_relative_cwd_*` diagnostic として返るようにした。 -- Worker list/detail に execution workspace safe summary を含めた。 +- invalid `relative_cwd` は Browser-facing API で typed `working_directory_relative_cwd_*` diagnostic として返るようにした。 +- Worker list/detail に working directory safe summary を含めた。 - cleanup は allocation root に bounded され、typed/sanitized diagnostic を返すようにした。 - Focused backend/runtime/web tests を追加した。 主な commit / merge: -- implementation: `684b19e8 feat: add browser execution workspaces` -- review fix: `9adb0fae fix: preserve execution workspace cwd diagnostics` -- merge into orchestration: `b52986e5 merge: browser execution workspaces` +- implementation: `684b19e8 feat: add browser working directories` +- review fix: `9adb0fae fix: preserve working directory cwd diagnostics` +- merge into orchestration: `b52986e5 merge: browser working directories` Review: - 初回 external review は request_changes。 diff --git a/.yoi/tickets/00001KWY8EHEJ/thread.md b/.yoi/tickets/00001KWY8EHEJ/thread.md index 7df385d7..1fa4bf03 100644 --- a/.yoi/tickets/00001KWY8EHEJ/thread.md +++ b/.yoi/tickets/00001KWY8EHEJ/thread.md @@ -39,7 +39,7 @@ Ticket を `workspace-panel` が queued にしました。 Routing decision: implementation_ready Reason: -- Ticket は Browser-managed Execution Workspace の pre-allocate 方式、API/UI/spawn 接続、`relative_cwd` validation、path leak invariant、cleanup policy の v0 方針を具体化している。 +- Ticket は Browser-managed working directory の pre-allocate 方式、API/UI/spawn 接続、`relative_cwd` validation、path leak invariant、cleanup policy の v0 方針を具体化している。 - 先行 Runtime-side materializer / Repository registry / workspace-id scoped API が orchestration branch に統合済みで、Browser-facing API を `/api/w//...` に追加する実装基盤がある。 - raw host path / internal materialization request を Browser payload に露出しない binding invariant が明確で、Worker spawn は allocation id + safe `relative_cwd` で接続する要求になっている。 - typed relation blocker は 0 件、OrchestrationPlan record は 0 件。 @@ -53,17 +53,17 @@ Evidence checked: - queued Ticket 一覧: この Ticket 1件のみ。inprogress は 0 件。 - visible Pods: previous child Pods が idle で残っているが、`StopPod` 不使用方針のため停止せず、新規 unique Pod を使う。capacity blocker ではない。 - `TicketDoctor`: 0 errors / 既存 diagnostics のみ。 -- Bounded code map: `crates/worker-runtime/src/{catalog.rs,execution_workspace.rs,runtime.rs,worker_backend.rs,http_server.rs}`, `crates/workspace-server/src/{server.rs,hosts.rs,repositories.rs,config.rs}`, `web/workspace/src/lib/workspace-sidebar/**`, scoped routes/API under `web/workspace/src/routes/w/[workspaceId]/**`。 +- Bounded code map: `crates/worker-runtime/src/{catalog.rs,working_directory.rs,runtime.rs,worker_backend.rs,http_server.rs}`, `crates/workspace-server/src/{server.rs,hosts.rs,repositories.rs,config.rs}`, `web/workspace/src/lib/workspace-sidebar/**`, scoped routes/API under `web/workspace/src/routes/w/[workspaceId]/**`。 IntentPacket: Intent: -- Browser-facing API から configured repository + selector + policy により Execution Workspace を事前作成し、Browser には allocation id と safe summary だけを返す。 -- Worker launch UI/API で作成済み Execution Workspace を選択し、Worker spawn 時に allocation id と safe `relative_cwd` を Runtime materializer / Worker spawn boundary に接続する。 -- Worker detail/list に execution workspace の safe summary を表示し、cleanup policy と手動 cleanup 操作の v0 方針を実装/明示する。 +- Browser-facing API から configured repository + selector + policy により working directory を事前作成し、Browser には allocation id と safe summary だけを返す。 +- Worker launch UI/API で作成済み working directory を選択し、Worker spawn 時に allocation id と safe `relative_cwd` を Runtime materializer / Worker spawn boundary に接続する。 +- Worker detail/list に working directory の safe summary を表示し、cleanup policy と手動 cleanup 操作の v0 方針を実装/明示する。 Binding decisions / invariants: -- Browser から raw host path、backend-private absolute path、raw `ExecutionWorkspaceRequest`、internal runtime path を送らせない/返さない。 +- Browser から raw host path、backend-private absolute path、raw `WorkingDirectoryRequest`、internal runtime path を送らせない/返さない。 - pre-allocate 方式を優先する。単発 spawn request だけで暗黙 materialize する経路は後回し。 - v0 materializer は existing local Git detached worktree 方針を使う。remote clone/cache、dirty source inclusion は扱わない。 - Worker spawn は作成済み allocation id と任意の `relative_cwd` を選ぶ。 @@ -73,19 +73,19 @@ Binding decisions / invariants: - Workspace-scoped Browser API は `/api/w//...` を使い、Runtime `/v1/...` API を Browser contract として広げない。 Requirements / acceptance criteria: -- `POST /api/w//execution-workspaces` 相当で configured repository + selector から Execution Workspace を作成できる。 -- `GET /api/w//execution-workspaces` 相当で safe summary list を取得できる。 +- `POST /api/w//working-directories` 相当で configured repository + selector から working directory を作成できる。 +- `GET /api/w//working-directories` 相当で safe summary list を取得できる。 - 必要なら detail / cleanup endpoint を scoped API に追加し、safe summary / typed diagnostic を返す。 -- Worker launch UI で Execution Workspace を選択でき、raw path ではなく safe id/selector 情報を送る。 +- Worker launch UI で working directory を選択でき、raw path ではなく safe id/selector 情報を送る。 - Worker spawn 時に選択 allocation が Runtime の materialized workspace に接続され、Worker cwd/scope は materialized worktree 配下になる。 - invalid repository / dirty unsupported / remote unsupported / invalid `relative_cwd` は typed diagnostic。 -- Worker detail/list に execution workspace safe summary を表示する。 +- Worker detail/list に working directory safe summary を表示する。 - frontend check/test、workspace-server tests、`yoi ticket doctor`、`nix build .#yoi` を通す。 Implementation latitude: - Allocation store の所在/形式、API endpoint 名、UI placement、cleanup status 名、test helper は既存 workspace-server / worker-runtime / web style に合わせてよい。 - v0 では Runtime materializer の既存 allocation evidence を再利用し、Browser-managed allocation registry は必要最小限でよい。 -- Worker launch UI は既存 New Worker form に Execution Workspace select / create link / refresh を追加する形でよい。 +- Worker launch UI は既存 New Worker form に working directory select / create link / refresh を追加する形でよい。 - `relative_cwd` は empty/`.` を workspace root と扱ってよい。 Escalate if: @@ -103,7 +103,7 @@ Validation: - `nix build .#yoi --no-link`(Ticket acceptance に含まれるため実行する。実行不能なら理由を implementation report に明記する) Current code map: -- Runtime Execution Workspace: `crates/worker-runtime/src/catalog.rs`, `execution_workspace.rs`, `runtime.rs`, `worker_backend.rs`, `http_server.rs`。 +- Runtime working directory: `crates/worker-runtime/src/catalog.rs`, `working_directory.rs`, `runtime.rs`, `worker_backend.rs`, `http_server.rs`。 - Workspace Backend host/API: `crates/workspace-server/src/server.rs`, `hosts.rs`, `repositories.rs`, `config.rs`。 - Worker launch UI and API helpers: `web/workspace/src/lib/workspace-sidebar/WorkersNavSection.svelte`, worker launch model/tests, workspace API helper, scoped route layout。 - Worker detail/console: scoped worker console route and runtime worker detail/list projections。 @@ -134,7 +134,7 @@ Checked context: Acceptance basis: - concrete missing decision / information は残っていない。 -- Ticket の scope は Browser-managed pre-allocated Execution Workspace と Worker spawn 接続に閉じており、remote clone/cache, dirty source inclusion, raw path Browser contract, implicit one-shot materialization は非目標/後回しとして明確。 +- Ticket の scope は Browser-managed pre-allocated working directory と Worker spawn 接続に閉じており、remote clone/cache, dirty source inclusion, raw path Browser contract, implicit one-shot materialization は非目標/後回しとして明確。 - side effect はこの `queued -> inprogress` acceptance 後に、worktree-workflow に沿って implementation worktree を作成してから開始する。 --- @@ -146,8 +146,8 @@ Acceptance basis: Implementation routing update: - Worktree-workflow に従い implementation worktree を作成した。 - - Worktree: `/home/hare/Projects/yoi/.worktree/00001KWY8EHEJ-browser-execution-workspaces` - - Branch: `work/00001KWY8EHEJ-browser-execution-workspaces` + - Worktree: `/home/hare/Projects/yoi/.worktree/00001KWY8EHEJ-browser-working-directories` + - Branch: `work/00001KWY8EHEJ-browser-working-directories` - Worktree には tracked `.yoi` project records が見えている。 - `.yoi/memory` / `.yoi/runtime` / `.yoi/log` / `.yoi/logs` / `.yoi/local` / `.yoi/*.lock` / `.yoi/*secret*` は作成直後に存在しないことを確認した。 - Sibling Coder Pod `yoi-coder-00001KWY8EHEJ-browser-execws` を起動した。 @@ -162,20 +162,20 @@ Implementation routing update: Implementation progress report: -- Coder Pod completed implementation and committed `684b19e8 feat: add browser execution workspaces` on branch `work/00001KWY8EHEJ-browser-execution-workspaces`. +- Coder Pod completed implementation and committed `684b19e8 feat: add browser working directories` on branch `work/00001KWY8EHEJ-browser-working-directories`. - Worktree status was clean after commit. - Changed files include: - - `crates/worker-runtime/src/{catalog.rs,execution_workspace.rs,http_server.rs,runtime.rs,worker_backend.rs}` + - `crates/worker-runtime/src/{catalog.rs,working_directory.rs,http_server.rs,runtime.rs,worker_backend.rs}` - `crates/workspace-server/src/{companion.rs,hosts.rs,server.rs}` - `web/workspace/src/lib/workspace-sidebar/{WorkersNavSection.svelte,types.ts,worker-launch.ts,worker-launch.test.ts}` - Implementation summary: - - Runtime-side preallocated Execution Workspace create/list/detail/cleanup and allocation binding at Worker spawn。 + - Runtime-side preallocated working directory create/list/detail/cleanup and allocation binding at Worker spawn。 - `relative_cwd` validation rejects absolute paths, `..`, missing paths, files, and symlink escapes。 - - scoped Browser API: `POST/GET /api/w//execution-workspaces`, detail, and cleanup。 - - Browser API uses configured `repository_id` + selector + policy, not raw paths/internal `ExecutionWorkspaceRequest`。 + - scoped Browser API: `POST/GET /api/w//working-directories`, detail, and cleanup。 + - Browser API uses configured `repository_id` + selector + policy, not raw paths/internal `WorkingDirectoryRequest`。 - Browser responses expose safe summaries only。 - Worker spawn uses selected allocation id plus optional safe `relative_cwd`。 - - Worker list/detail projections include execution workspace safe summary。 + - Worker list/detail projections include working directory safe summary。 - Workspace UI Worker launch flow can select existing allocations or create a new allocation before launch。 - Orchestrator reran validation in the implementation worktree: - `git diff --check`: pass @@ -198,23 +198,23 @@ External review result: request_changes Blocker: -1. Invalid `relative_cwd` is rejected internally, but the Browser-facing Worker spawn API does not return the required typed Execution Workspace diagnostic。 +1. Invalid `relative_cwd` is rejected internally, but the Browser-facing Worker spawn API does not return the required typed working directory diagnostic。 - Ticket acceptance requires invalid `relative_cwd` to be rejected as a typed diagnostic。 - Materializer has specific typed codes: - - `execution_workspace_relative_cwd_invalid` - - `execution_workspace_relative_cwd_unavailable` - - `execution_workspace_relative_cwd_escape_rejected` - - `crates/worker-runtime/src/worker_backend.rs` converts `ExecutionWorkspaceDiagnostic` to a plain string in `WorkerExecutionResult::rejected(...)`。 + - `working_directory_relative_cwd_invalid` + - `working_directory_relative_cwd_unavailable` + - `working_directory_relative_cwd_escape_rejected` + - `crates/worker-runtime/src/worker_backend.rs` converts `WorkingDirectoryDiagnostic` to a plain string in `WorkerExecutionResult::rejected(...)`。 - Workspace host/API then maps this to generic diagnostics/error codes, including generic embedded execution rejection and generic `workspace_worker_create_failed`。 - - Result: Browser caller submitting invalid `relative_cwd` gets a generic worker-create/runtime rejection rather than a typed `execution_workspace_relative_cwd_*` diagnostic。 + - Result: Browser caller submitting invalid `relative_cwd` gets a generic worker-create/runtime rejection rather than a typed `working_directory_relative_cwd_*` diagnostic。 - Browser/scoped API-level tests cover materializer bind validation but not the API rejection shape。 Required fix: -- Preserve/propagate `ExecutionWorkspaceDiagnostic.code` through Worker spawn, or prevalidate allocation + `relative_cwd` in the Workspace API and return that typed diagnostic directly。 -- Add a scoped Browser API test for at least one invalid `relative_cwd` that asserts the typed `execution_workspace_relative_cwd_*` diagnostic is returned。 +- Preserve/propagate `WorkingDirectoryDiagnostic.code` through Worker spawn, or prevalidate allocation + `relative_cwd` in the Workspace API and return that typed diagnostic directly。 +- Add a scoped Browser API test for at least one invalid `relative_cwd` that asserts the typed `working_directory_relative_cwd_*` diagnostic is returned。 Non-blocking follow-ups: -- The implementation otherwise appears aligned with the pre-allocation design: scoped `/api/w//execution-workspaces` routes exist, Browser create payload uses configured `repository_id`/selector/policy rather than raw `ExecutionWorkspaceRequest`, summaries avoid raw paths, UI explicitly creates/selects allocations, and cleanup is bounded to the allocation root。 +- The implementation otherwise appears aligned with the pre-allocation design: scoped `/api/w//working-directories` routes exist, Browser create payload uses configured `repository_id`/selector/policy rather than raw `WorkingDirectoryRequest`, summaries avoid raw paths, UI explicitly creates/selects allocations, and cleanup is bounded to the allocation root。 Validation reviewed: - Reviewer read Ticket item/thread, inspected `git show --stat --check 684b19e8` / `git show --name-only 684b19e8`, and reviewed Runtime, Workspace API/host, and frontend launch files。 @@ -228,13 +228,13 @@ Validation reviewed: Review-fix implementation report: -- Coder Pod completed requested changes and committed follow-up `9adb0fae fix: preserve execution workspace cwd diagnostics` on branch `work/00001KWY8EHEJ-browser-execution-workspaces`. +- Coder Pod completed requested changes and committed follow-up `9adb0fae fix: preserve working directory cwd diagnostics` on branch `work/00001KWY8EHEJ-browser-working-directories`. - Worktree status was clean after commit. - Fix summary: - - Browser Worker spawn now prevalidates selected execution workspace allocation + `relative_cwd` through the materializer before spawn。 - - `ExecutionWorkspaceDiagnostic` is mapped directly into Browser API error diagnostics, preserving typed codes such as `execution_workspace_relative_cwd_invalid`, `execution_workspace_relative_cwd_unavailable`, and `execution_workspace_relative_cwd_escape_rejected`。 - - Added a scoped Browser API regression test asserting invalid `relative_cwd: "../escape"` returns typed `execution_workspace_relative_cwd_invalid` diagnostic。 - - Existing scoped execution workspace routes, safe summaries, explicit UI allocation flow, and bounded cleanup behavior remain unchanged。 + - Browser Worker spawn now prevalidates selected working directory allocation + `relative_cwd` through the materializer before spawn。 + - `WorkingDirectoryDiagnostic` is mapped directly into Browser API error diagnostics, preserving typed codes such as `working_directory_relative_cwd_invalid`, `working_directory_relative_cwd_unavailable`, and `working_directory_relative_cwd_escape_rejected`。 + - Added a scoped Browser API regression test asserting invalid `relative_cwd: "../escape"` returns typed `working_directory_relative_cwd_invalid` diagnostic。 + - Existing scoped working directory routes, safe summaries, explicit UI allocation flow, and bounded cleanup behavior remain unchanged。 - Orchestrator reran validation in the implementation worktree: - `git diff --check`: pass - `cd web/workspace && deno task check`: pass(0 errors / 0 warnings) @@ -259,13 +259,13 @@ Blockers: none. Evidence reviewed in implementation worktree only: - Ticket records: `.yoi/tickets/00001KWY8EHEJ/item.md`, `thread.md`, and orchestration artifact。 - Fix diff: `9adb0fae` stat/check and `684b19e8..9adb0fae` focused diff for `crates/workspace-server/src/server.rs`。 -- Relevant files: `workspace-server/src/server.rs`, `worker-runtime/src/execution_workspace.rs`, `worker_backend.rs`, scoped host/runtime API pieces, and worker launch UI/model/tests。 +- Relevant files: `workspace-server/src/server.rs`, `worker-runtime/src/working_directory.rs`, `worker_backend.rs`, scoped host/runtime API pieces, and worker launch UI/model/tests。 Findings: -- Prior blocker is resolved. Browser Worker spawn now pre-validates `execution_workspace.relative_cwd` through the execution workspace materializer before calling the runtime spawn path, and maps `ExecutionWorkspaceError` into API diagnostics via `execution_workspace_api_error`。 -- Invalid Browser spawn `relative_cwd` now returns typed diagnostics with the expected codes, covered by the added test asserting `execution_workspace_relative_cwd_invalid` appears both in diagnostics and message。 -- Browser-facing spawn payload remains allocation-based (`allocation_id` + optional `relative_cwd`) rather than exposing raw `ExecutionWorkspaceRequest` or host paths. UI request construction sends allocation id and relative cwd only。 -- Scoped create/list/detail/cleanup routes remain under `/api/w/:workspace_id/execution-workspaces`; worker creation uses the scoped workspace API path from the Browser UI。 +- Prior blocker is resolved. Browser Worker spawn now pre-validates `working_directory.relative_cwd` through the working directory materializer before calling the runtime spawn path, and maps `WorkingDirectoryError` into API diagnostics via `working_directory_api_error`。 +- Invalid Browser spawn `relative_cwd` now returns typed diagnostics with the expected codes, covered by the added test asserting `working_directory_relative_cwd_invalid` appears both in diagnostics and message。 +- Browser-facing spawn payload remains allocation-based (`allocation_id` + optional `relative_cwd`) rather than exposing raw `WorkingDirectoryRequest` or host paths. UI request construction sends allocation id and relative cwd only。 +- Scoped create/list/detail/cleanup routes remain under `/api/w/:workspace_id/working-directories`; worker creation uses the scoped workspace API path from the Browser UI。 - Cleanup remains bounded to the allocation root: cleanup re-derives allocation root from the allocation id/root, canonicalizes workspace root, rejects escape, and removes only the recorded workspace root。 - Runtime/internal trusted path still performs canonical materialization/bind at Worker launch, while Browser gets a safer scoped allocation API。 - Reported validation was comprehensive and green; reviewer did not rerun tests as this was a read-only follow-up review。 @@ -283,7 +283,7 @@ No merge-blocking issues found. Merge/validation acceptance: - Follow-up external review approved the implementation after blocker fix。 -- Implementation branch `work/00001KWY8EHEJ-browser-execution-workspaces` was merged into the orchestration branch with merge commit `b52986e5 merge: browser execution workspaces`。 +- Implementation branch `work/00001KWY8EHEJ-browser-working-directories` was merged into the orchestration branch with merge commit `b52986e5 merge: browser working directories`。 - Final validation in Orchestrator worktree passed: - `git diff --check`: pass - `cd web/workspace && deno task check`: pass(0 errors / 0 warnings) @@ -295,8 +295,8 @@ Merge/validation acceptance: - `nix build .#yoi --no-link`: pass Implementation commits: -- `684b19e8 feat: add browser execution workspaces` -- `9adb0fae fix: preserve execution workspace cwd diagnostics` +- `684b19e8 feat: add browser working directories` +- `9adb0fae fix: preserve working directory cwd diagnostics` No merge-blocking issues remain. @@ -318,24 +318,24 @@ Ticket を closed にしました。 完了。 実装内容: -- Browser-managed Execution Workspace API を scoped Workspace API に追加した。 - - `POST /api/w//execution-workspaces` - - `GET /api/w//execution-workspaces` +- Browser-managed working directory API を scoped Workspace API に追加した。 + - `POST /api/w//working-directories` + - `GET /api/w//working-directories` - detail / cleanup endpoint -- Browser create payload は configured `repository_id` / selector / policy を使い、raw host path / raw internal `ExecutionWorkspaceRequest` / internal runtime path を受け取らないようにした。 +- Browser create payload は configured `repository_id` / selector / policy を使い、raw host path / raw internal `WorkingDirectoryRequest` / internal runtime path を受け取らないようにした。 - Browser response は allocation id、repository id、selector/commit evidence、status、cleanup policy など safe summary のみを返すようにした。 -- Worker launch UI で Execution Workspace を create/select できるようにした。 +- Worker launch UI で working directory を create/select できるようにした。 - Worker spawn は selected allocation id + optional `relative_cwd` を使い、Runtime materialized workspace に接続するようにした。 - `relative_cwd` は materialized root 相対として検証し、absolute path、`..` escape、symlink escape、nonexistent / non-directory を拒否するようにした。 -- invalid `relative_cwd` は Browser-facing API で typed `execution_workspace_relative_cwd_*` diagnostic として返るようにした。 -- Worker list/detail に execution workspace safe summary を含めた。 +- invalid `relative_cwd` は Browser-facing API で typed `working_directory_relative_cwd_*` diagnostic として返るようにした。 +- Worker list/detail に working directory safe summary を含めた。 - cleanup は allocation root に bounded され、typed/sanitized diagnostic を返すようにした。 - Focused backend/runtime/web tests を追加した。 主な commit / merge: -- implementation: `684b19e8 feat: add browser execution workspaces` -- review fix: `9adb0fae fix: preserve execution workspace cwd diagnostics` -- merge into orchestration: `b52986e5 merge: browser execution workspaces` +- implementation: `684b19e8 feat: add browser working directories` +- review fix: `9adb0fae fix: preserve working directory cwd diagnostics` +- merge into orchestration: `b52986e5 merge: browser working directories` Review: - 初回 external review は request_changes。 diff --git a/crates/worker-runtime/src/catalog.rs b/crates/worker-runtime/src/catalog.rs index a68d972f..ec4eeace 100644 --- a/crates/worker-runtime/src/catalog.rs +++ b/crates/worker-runtime/src/catalog.rs @@ -56,7 +56,7 @@ impl std::ops::Deref for RepositorySelector { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceRepository { +pub struct WorkingDirectoryRepository { pub id: String, pub provider: String, pub uri: String, @@ -81,8 +81,8 @@ pub enum DirtyStatePolicy { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceRequest { - pub repository: ExecutionWorkspaceRepository, +pub struct WorkingDirectoryRequest { + pub repository: WorkingDirectoryRepository, #[serde(default)] pub materializer: MaterializerKind, #[serde(default)] @@ -90,7 +90,7 @@ pub struct ExecutionWorkspaceRequest { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceAllocationClaim { +pub struct WorkingDirectoryAllocationClaim { pub allocation_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub relative_cwd: Option, @@ -98,21 +98,21 @@ pub struct ExecutionWorkspaceAllocationClaim { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] -pub enum ExecutionWorkspaceStatusKind { +pub enum WorkingDirectoryStatusKind { Active, Removed, CleanupPending, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceCleanupTarget { +pub struct WorkingDirectoryCleanupTarget { pub kind: String, pub allocation_id: String, pub repository_id: String, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceSummary { +pub struct WorkingDirectorySummary { pub allocation_id: String, pub repository_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -124,15 +124,15 @@ pub struct ExecutionWorkspaceSummary { #[serde(default, skip_serializing_if = "Option::is_none")] pub resolved_tree: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub cleanup_target: Option, + pub cleanup_target: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub cleanup_policy: Option, - pub status: ExecutionWorkspaceStatusKind, + pub status: WorkingDirectoryStatusKind, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceStatus { - pub summary: ExecutionWorkspaceSummary, +pub struct WorkingDirectoryStatus { + pub summary: WorkingDirectorySummary, } /// Canonical Runtime Worker creation request. @@ -141,9 +141,9 @@ pub struct ExecutionWorkspaceStatus { /// request is built. The request contains only durable Runtime identity inputs: /// a backend-decided profile selector, a previously synced ConfigBundle identity, /// optional initial user input that is committed in the same transaction as -/// Worker catalog/transcript persistence, and an optional execution workspace +/// Worker catalog/transcript persistence, and an optional working directory /// request that preserves RepositoryPoint-style semantics for runtime-side -/// materialization. Browser-facing status for materialized workspaces is +/// materialization. Browser-facing status for materialized working directories is /// summarized without exposing raw host paths. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CreateWorkerRequest { @@ -152,9 +152,9 @@ pub struct CreateWorkerRequest { #[serde(default, skip_serializing_if = "Option::is_none")] pub initial_input: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub execution_workspace: Option, + pub working_directory: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub execution_workspace_allocation: Option, + pub working_directory_allocation: Option, } /// Worker lifecycle status for the in-memory embedded runtime. diff --git a/crates/worker-runtime/src/execution.rs b/crates/worker-runtime/src/execution.rs index ac1fa6c8..d31eebc2 100644 --- a/crates/worker-runtime/src/execution.rs +++ b/crates/worker-runtime/src/execution.rs @@ -1,10 +1,10 @@ -use crate::catalog::{CreateWorkerRequest, ExecutionWorkspaceStatus}; +use crate::catalog::{CreateWorkerRequest, WorkingDirectoryStatus}; use crate::error::RuntimeError; -use crate::execution_workspace::ExecutionWorkspaceBinding; use crate::identity::WorkerRef; use crate::interaction::WorkerInput; #[cfg(feature = "ws-server")] use crate::observation::WorkerObservationEvent; +use crate::working_directory::WorkingDirectoryBinding; use serde::{Deserialize, Serialize}; use std::fmt; use std::sync::Arc; @@ -154,7 +154,7 @@ pub struct WorkerExecutionStatus { #[serde(default, skip_serializing_if = "Option::is_none")] pub binding: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub execution_workspace: Option, + pub working_directory: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub last_result: Option, } @@ -169,7 +169,7 @@ impl WorkerExecutionStatus { backend: WorkerExecutionBackendKind::Connected, run_state, binding: None, - execution_workspace: None, + working_directory: None, last_result: None, } } @@ -185,8 +185,8 @@ impl WorkerExecutionStatus { self } - pub fn with_execution_workspace(mut self, status: ExecutionWorkspaceStatus) -> Self { - self.execution_workspace = Some(status); + pub fn with_working_directory(mut self, status: WorkingDirectoryStatus) -> Self { + self.working_directory = Some(status); self } @@ -286,7 +286,7 @@ pub struct WorkerExecutionSpawnRequest { pub worker_ref: WorkerRef, pub request: CreateWorkerRequest, pub context: WorkerExecutionContext, - pub execution_workspace: Option, + pub working_directory: Option, } /// Result of backend Worker spawn/initialization. @@ -295,7 +295,7 @@ pub enum WorkerExecutionSpawnResult { Connected { handle: WorkerExecutionHandle, run_state: WorkerExecutionRunState, - execution_workspace: Option, + working_directory: Option, }, Rejected(WorkerExecutionResult), Errored(WorkerExecutionResult), diff --git a/crates/worker-runtime/src/http_server.rs b/crates/worker-runtime/src/http_server.rs index daf5d6d9..16cef36d 100644 --- a/crates/worker-runtime/src/http_server.rs +++ b/crates/worker-runtime/src/http_server.rs @@ -863,8 +863,8 @@ mod tests { digest: bundle.metadata.digest, }, initial_input: None, - execution_workspace: None, - execution_workspace_allocation: None, + working_directory: None, + working_directory_allocation: None, } } @@ -879,8 +879,8 @@ mod tests { WorkerExecutionSpawnResult::Connected { handle: WorkerExecutionHandle::new(request.worker_ref, self.backend_id()), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } @@ -1098,8 +1098,8 @@ mod ws_tests { WorkerExecutionSpawnResult::Connected { handle: WorkerExecutionHandle::new(request.worker_ref, self.backend_id()), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } @@ -1148,8 +1148,8 @@ mod ws_tests { digest: bundle.metadata.digest, }, initial_input: None, - execution_workspace: None, - execution_workspace_allocation: None, + working_directory: None, + working_directory_allocation: None, } } diff --git a/crates/worker-runtime/src/lib.rs b/crates/worker-runtime/src/lib.rs index 70bd09c7..686983fb 100644 --- a/crates/worker-runtime/src/lib.rs +++ b/crates/worker-runtime/src/lib.rs @@ -11,7 +11,6 @@ pub mod config_bundle; pub mod diagnostics; pub mod error; pub mod execution; -pub mod execution_workspace; #[cfg(feature = "fs-store")] pub mod fs_store; #[cfg(feature = "http-server")] @@ -22,6 +21,7 @@ pub mod management; pub mod observation; mod runtime; pub mod worker_backend; +pub mod working_directory; #[cfg(feature = "fs-store")] pub use fs_store::{FsRuntimeStore, FsRuntimeStoreOptions}; diff --git a/crates/worker-runtime/src/main.rs b/crates/worker-runtime/src/main.rs index 44062c83..54112219 100644 --- a/crates/worker-runtime/src/main.rs +++ b/crates/worker-runtime/src/main.rs @@ -14,13 +14,13 @@ use std::process::ExitCode; use std::sync::Arc; use worker_runtime::error::RuntimeError; -use worker_runtime::execution_workspace::LocalGitWorktreeMaterializer; use worker_runtime::fs_store::FsRuntimeStoreOptions; use worker_runtime::http_server::{ RuntimeHttpServerConfig, RuntimeHttpServerError, RuntimeHttpStoreSelection, }; use worker_runtime::identity::RuntimeId; use worker_runtime::worker_backend::{ProfileRuntimeWorkerFactory, WorkerRuntimeExecutionBackend}; +use worker_runtime::working_directory::LocalGitWorktreeMaterializer; use worker_runtime::{Runtime, RuntimeOptions}; fn main() -> ExitCode { @@ -81,12 +81,12 @@ fn build_runtime(config: &ProcessConfig) -> Result { if let Some(profile) = config.profile.clone() { factory = factory.with_profile(profile); } - let execution_workspace_root = execution_workspace_runtime_root(config); + let working_directory_root = working_directory_runtime_root(config); let backend = Arc::new( WorkerRuntimeExecutionBackend::new(factory) .map_err(ProcessError::WorkerAdapter)? - .with_execution_workspace_materializer(LocalGitWorktreeMaterializer::new( - execution_workspace_root, + .with_working_directory_materializer(LocalGitWorktreeMaterializer::new( + working_directory_root, )), ); @@ -109,7 +109,7 @@ fn build_runtime(config: &ProcessConfig) -> Result { } } -fn execution_workspace_runtime_root(config: &ProcessConfig) -> PathBuf { +fn working_directory_runtime_root(config: &ProcessConfig) -> PathBuf { match &config.http.store { RuntimeHttpStoreSelection::Fs { root } => root.clone(), _ => config diff --git a/crates/worker-runtime/src/runtime.rs b/crates/worker-runtime/src/runtime.rs index 5821098e..097567f1 100644 --- a/crates/worker-runtime/src/runtime.rs +++ b/crates/worker-runtime/src/runtime.rs @@ -1,7 +1,6 @@ use crate::catalog::{ - ConfigBundleRef, CreateWorkerRequest, - ExecutionWorkspaceStatus as CatalogExecutionWorkspaceStatus, ProfileSelector, WorkerDetail, - WorkerLifecycleAck, WorkerStatus, WorkerSummary, + ConfigBundleRef, CreateWorkerRequest, ProfileSelector, WorkerDetail, WorkerLifecycleAck, + WorkerStatus, WorkerSummary, WorkingDirectoryStatus as CatalogWorkingDirectoryStatus, }; use crate::config_bundle::{ ConfigBundle, ConfigBundleAvailability, ConfigBundleSummary, validate_config_bundle, @@ -289,18 +288,18 @@ impl Runtime { worker_ref: worker_ref.clone(), request, context: self.execution_context(worker_ref.clone()), - execution_workspace: None, + working_directory: None, }; (backend, worker_ref, spawn_request) }; let spawn_result = backend.spawn_worker(spawn_request); - let (handle, run_state, execution_workspace) = match spawn_result { + let (handle, run_state, working_directory) = match spawn_result { WorkerExecutionSpawnResult::Connected { handle, run_state, - execution_workspace, - } => (handle, run_state, execution_workspace), + working_directory, + } => (handle, run_state, working_directory), WorkerExecutionSpawnResult::Rejected(result) | WorkerExecutionSpawnResult::Errored(result) => { self.rollback_failed_create(&worker_ref)?; @@ -334,7 +333,7 @@ impl Runtime { &worker_ref, handle, WorkerExecutionRunState::Busy, - execution_workspace, + working_directory, WorkerExecutionResult::accepted( WorkerExecutionOperation::Input, WorkerExecutionRunState::Busy, @@ -345,7 +344,7 @@ impl Runtime { &worker_ref, handle, run_state, - execution_workspace, + working_directory, WorkerExecutionResult::accepted(WorkerExecutionOperation::Spawn, run_state), ) } @@ -441,7 +440,7 @@ impl Runtime { backend: WorkerExecutionBackendKind::Connected, run_state: dispatch_result.run_state, binding: worker.execution.binding.clone(), - execution_workspace: worker.execution.execution_workspace.clone(), + working_directory: worker.execution.working_directory.clone(), last_result: Some(dispatch_result), }; worker.transcript.push(TranscriptEntry { @@ -491,7 +490,7 @@ impl Runtime { worker_ref: &WorkerRef, handle: WorkerExecutionHandle, run_state: WorkerExecutionRunState, - execution_workspace: Option, + working_directory: Option, result: WorkerExecutionResult, ) -> Result { let mut state = self.lock()?; @@ -501,8 +500,8 @@ impl Runtime { let worker = state.worker_mut(worker_ref)?; worker.execution_handle = Some(handle); let mut execution = WorkerExecutionStatus::connected(run_state).with_binding(binding); - if let Some(status) = execution_workspace { - execution = execution.with_execution_workspace(status); + if let Some(status) = working_directory { + execution = execution.with_working_directory(status); } worker.execution = execution.with_result(result); worker.detail(&runtime_id) @@ -538,7 +537,7 @@ impl Runtime { backend: WorkerExecutionBackendKind::Connected, run_state: result.run_state, binding: worker.execution.binding.clone(), - execution_workspace: worker.execution.execution_workspace.clone(), + working_directory: worker.execution.working_directory.clone(), last_result: Some(result), }; state.persist_worker(&worker_ref.worker_id)?; @@ -1593,8 +1592,8 @@ mod tests { digest: bundle.metadata.digest, }, initial_input: None, - execution_workspace: None, - execution_workspace_allocation: None, + working_directory: None, + working_directory_allocation: None, } } @@ -1660,8 +1659,8 @@ mod tests { WorkerExecutionSpawnResult::Connected { handle: WorkerExecutionHandle::new(request.worker_ref, self.backend_id()), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } @@ -1935,8 +1934,8 @@ mod tests { WorkerExecutionSpawnResult::Connected { handle: WorkerExecutionHandle::new(request.worker_ref, self.backend_id()), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } diff --git a/crates/worker-runtime/src/worker_backend.rs b/crates/worker-runtime/src/worker_backend.rs index 0183dbbf..8480c1d4 100644 --- a/crates/worker-runtime/src/worker_backend.rs +++ b/crates/worker-runtime/src/worker_backend.rs @@ -18,8 +18,8 @@ use crate::execution::{ WorkerExecutionBackend, WorkerExecutionHandle, WorkerExecutionOperation, WorkerExecutionResult, WorkerExecutionRunState, WorkerExecutionSpawnRequest, WorkerExecutionSpawnResult, }; -use crate::execution_workspace::{ExecutionWorkspaceBinding, ExecutionWorkspaceMaterializer}; use crate::interaction::{WorkerInput, WorkerInputKind}; +use crate::working_directory::{WorkingDirectoryBinding, WorkingDirectoryMaterializer}; use async_trait::async_trait; use manifest::paths; use protocol::{Method, Segment, WorkerStatus}; @@ -162,12 +162,12 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory { let worker_name = Self::runtime_worker_name(&request); let profile = self.runtime_profile(&request); let workspace_root = request - .execution_workspace + .working_directory .as_ref() .map(|binding| binding.workspace_root().to_path_buf()) .unwrap_or_else(|| self.workspace_root.clone()); let cwd = request - .execution_workspace + .working_directory .as_ref() .map(|binding| binding.cwd().to_path_buf()) .unwrap_or_else(|| self.cwd.clone()); @@ -210,14 +210,14 @@ impl RuntimeWorkerFactory for ProfileRuntimeWorkerFactory { struct RuntimeWorkerExecution { handle: WorkerHandle, busy: Arc, - execution_workspace: Option, + working_directory: Option, } /// `worker-runtime` execution backend backed by real `worker` crate Workers. pub struct WorkerRuntimeExecutionBackend { backend_id: String, factory: Arc, - execution_workspace_materializer: Option>, + working_directory_materializer: Option>, runtime: Mutex>, workers: Mutex>, } @@ -241,7 +241,7 @@ where Ok(Self { backend_id: DEFAULT_BACKEND_ID.to_string(), factory: Arc::new(factory), - execution_workspace_materializer: None, + working_directory_materializer: None, runtime: Mutex::new(Some(runtime)), workers: Mutex::new(HashMap::new()), }) @@ -252,11 +252,11 @@ where self } - pub fn with_execution_workspace_materializer( + pub fn with_working_directory_materializer( mut self, - materializer: impl ExecutionWorkspaceMaterializer, + materializer: impl WorkingDirectoryMaterializer, ) -> Self { - self.execution_workspace_materializer = Some(Arc::new(materializer)); + self.working_directory_materializer = Some(Arc::new(materializer)); self } @@ -374,26 +374,26 @@ where } let mut request = request; - let execution_workspace = match ( - request.request.execution_workspace.as_ref(), - request.request.execution_workspace_allocation.as_ref(), + let working_directory = match ( + request.request.working_directory.as_ref(), + request.request.working_directory_allocation.as_ref(), ) { (Some(_), Some(_)) => { return WorkerExecutionSpawnResult::Rejected(WorkerExecutionResult::rejected( WorkerExecutionOperation::Spawn, - "worker spawn cannot specify both execution_workspace and execution_workspace_allocation", + "worker spawn cannot specify both working_directory and working_directory_allocation", )); } (Some(workspace_request), None) => { - let Some(materializer) = self.execution_workspace_materializer.as_ref() else { + let Some(materializer) = self.working_directory_materializer.as_ref() else { return WorkerExecutionSpawnResult::Rejected(WorkerExecutionResult::rejected( WorkerExecutionOperation::Spawn, - "execution workspace materialization requested, but no materializer is configured for this runtime backend", + "working directory materialization requested, but no materializer is configured for this runtime backend", )); }; match materializer.materialize(&request.worker_ref, workspace_request) { Ok(binding) => { - request.execution_workspace = Some(binding.clone()); + request.working_directory = Some(binding.clone()); Some(binding) } Err(error) => { @@ -407,10 +407,10 @@ where } } (None, Some(allocation)) => { - let Some(materializer) = self.execution_workspace_materializer.as_ref() else { + let Some(materializer) = self.working_directory_materializer.as_ref() else { return WorkerExecutionSpawnResult::Rejected(WorkerExecutionResult::rejected( WorkerExecutionOperation::Spawn, - "execution workspace allocation requested, but no materializer is configured for this runtime backend", + "working directory allocation requested, but no materializer is configured for this runtime backend", )); }; match materializer.bind_allocation( @@ -418,7 +418,7 @@ where allocation.relative_cwd.as_deref(), ) { Ok(binding) => { - request.execution_workspace = Some(binding.clone()); + request.working_directory = Some(binding.clone()); Some(binding) } Err(error) => { @@ -444,8 +444,8 @@ where Ok(handle) => handle, Err(message) => { if let (Some(materializer), Some(binding)) = ( - self.execution_workspace_materializer.as_ref(), - execution_workspace.as_ref(), + self.working_directory_materializer.as_ref(), + working_directory.as_ref(), ) { let _ = materializer.cleanup(binding); } @@ -494,14 +494,14 @@ where RuntimeWorkerExecution { handle, busy, - execution_workspace: execution_workspace.clone(), + working_directory: working_directory.clone(), }, ); WorkerExecutionSpawnResult::Connected { handle: WorkerExecutionHandle::new(worker_ref, self.backend_id()), run_state: WorkerExecutionRunState::Idle, - execution_workspace: execution_workspace.map(|binding| binding.status()), + working_directory: working_directory.map(|binding| binding.status()), } } @@ -592,8 +592,8 @@ where WorkerExecutionRunState::Stopped, ); if let (Some(materializer), Some(binding)) = ( - self.execution_workspace_materializer.as_ref(), - execution.execution_workspace.as_ref(), + self.working_directory_materializer.as_ref(), + execution.working_directory.as_ref(), ) { let _ = materializer.cleanup(binding); } @@ -627,13 +627,13 @@ mod tests { use crate::Runtime as EmbeddedRuntime; use crate::catalog::{ - ConfigBundleRef, CreateWorkerRequest, DirtyStatePolicy, ExecutionWorkspaceRepository, - ExecutionWorkspaceRequest, MaterializerKind, ProfileSelector, RepositorySelector, + ConfigBundleRef, CreateWorkerRequest, DirtyStatePolicy, MaterializerKind, ProfileSelector, + RepositorySelector, WorkingDirectoryRepository, WorkingDirectoryRequest, }; - use crate::execution_workspace::LocalGitWorktreeMaterializer; use crate::identity::RuntimeId; use crate::management::RuntimeOptions; use crate::observation::{TranscriptQuery, TranscriptRole}; + use crate::working_directory::LocalGitWorktreeMaterializer; use async_trait::async_trait; use futures::Stream; use llm_engine::Engine; @@ -715,7 +715,7 @@ mod tests { FsWorkerStore::new(&self.worker_metadata_dir).map_err(|err| err.to_string())?, ); let cwd = request - .execution_workspace + .working_directory .as_ref() .map(|binding| binding.cwd().to_path_buf()) .unwrap_or_else(|| self.cwd.clone()); @@ -780,8 +780,8 @@ mod tests { digest: bundle.metadata.digest, }, initial_input: None, - execution_workspace: None, - execution_workspace_allocation: None, + working_directory: None, + working_directory_allocation: None, } } @@ -809,9 +809,9 @@ mod tests { dir } - fn execution_workspace_request(repo: &std::path::Path) -> ExecutionWorkspaceRequest { - ExecutionWorkspaceRequest { - repository: ExecutionWorkspaceRepository { + fn working_directory_request(repo: &std::path::Path) -> WorkingDirectoryRequest { + WorkingDirectoryRequest { + repository: WorkingDirectoryRepository { id: "repo-main".to_string(), provider: "git".to_string(), uri: ".".to_string(), @@ -920,7 +920,7 @@ mod tests { }; let backend = WorkerRuntimeExecutionBackend::new(factory) .unwrap() - .with_execution_workspace_materializer(LocalGitWorktreeMaterializer::new( + .with_working_directory_materializer(LocalGitWorktreeMaterializer::new( runtime_base.path(), )); let runtime = EmbeddedRuntime::with_execution_backend( @@ -933,11 +933,11 @@ mod tests { .unwrap(); runtime.store_config_bundle(test_bundle()).unwrap(); let mut request = create_request("chat"); - request.execution_workspace = Some(execution_workspace_request(repo.path())); + request.working_directory = Some(working_directory_request(repo.path())); let detail = runtime.create_worker(request).unwrap(); - assert!(detail.execution.execution_workspace.is_some()); + assert!(detail.execution.working_directory.is_some()); let cwds = observed_cwds.lock().unwrap(); assert_eq!(cwds.len(), 1); let cwd = &cwds[0]; diff --git a/crates/worker-runtime/src/execution_workspace.rs b/crates/worker-runtime/src/working_directory.rs similarity index 70% rename from crates/worker-runtime/src/execution_workspace.rs rename to crates/worker-runtime/src/working_directory.rs index 0de00de4..8885823a 100644 --- a/crates/worker-runtime/src/execution_workspace.rs +++ b/crates/worker-runtime/src/working_directory.rs @@ -1,7 +1,6 @@ use crate::catalog::{ - DirtyStatePolicy, ExecutionWorkspaceCleanupTarget, ExecutionWorkspaceRequest, - ExecutionWorkspaceStatus, ExecutionWorkspaceStatusKind, ExecutionWorkspaceSummary, - MaterializerKind, + DirtyStatePolicy, MaterializerKind, WorkingDirectoryCleanupTarget, WorkingDirectoryRequest, + WorkingDirectoryStatus, WorkingDirectoryStatusKind, WorkingDirectorySummary, }; use crate::identity::WorkerRef; use serde::{Deserialize, Serialize}; @@ -11,12 +10,12 @@ use std::process::Command; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; -const EXECUTION_WORKSPACES_DIR: &str = "execution-workspaces"; +const WORKING_DIRECTORIES_DIR: &str = "working-directories"; const MATERIALIZATION_RECORD: &str = "materialization.json"; static NEXT_ALLOCATION_SEQUENCE: AtomicU64 = AtomicU64::new(0); #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceEvidence { +pub struct WorkingDirectoryEvidence { pub repository_id: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub requested_selector: Option, @@ -28,20 +27,20 @@ pub struct ExecutionWorkspaceEvidence { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ExecutionWorkspaceAllocation { +pub struct WorkingDirectoryAllocation { pub id: String, pub repository_id: String, pub materializer_kind: MaterializerKind, pub dirty_state_policy: DirtyStatePolicy, - pub evidence: ExecutionWorkspaceEvidence, - pub cleanup_target: ExecutionWorkspaceCleanupTarget, + pub evidence: WorkingDirectoryEvidence, + pub cleanup_target: WorkingDirectoryCleanupTarget, pub cleanup_policy: String, - pub status: ExecutionWorkspaceStatusKind, + pub status: WorkingDirectoryStatusKind, } -impl ExecutionWorkspaceAllocation { - pub fn status_summary(&self) -> ExecutionWorkspaceSummary { - ExecutionWorkspaceSummary { +impl WorkingDirectoryAllocation { + pub fn status_summary(&self) -> WorkingDirectorySummary { + WorkingDirectorySummary { allocation_id: self.id.clone(), repository_id: self.repository_id.clone(), requested_selector: self.evidence.requested_selector.clone(), @@ -57,15 +56,15 @@ impl ExecutionWorkspaceAllocation { } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ExecutionWorkspaceBinding { - pub allocation: ExecutionWorkspaceAllocation, +pub struct WorkingDirectoryBinding { + pub allocation: WorkingDirectoryAllocation, pub workspace_root: PathBuf, pub cwd: PathBuf, allocation_root: PathBuf, source_repository_path: PathBuf, } -impl ExecutionWorkspaceBinding { +impl WorkingDirectoryBinding { pub fn workspace_root(&self) -> &Path { &self.workspace_root } @@ -82,20 +81,20 @@ impl ExecutionWorkspaceBinding { &self.source_repository_path } - pub fn status(&self) -> ExecutionWorkspaceStatus { - ExecutionWorkspaceStatus { + pub fn status(&self) -> WorkingDirectoryStatus { + WorkingDirectoryStatus { summary: self.allocation.status_summary(), } } } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ExecutionWorkspaceDiagnostic { +pub struct WorkingDirectoryDiagnostic { pub code: String, pub message: String, } -impl ExecutionWorkspaceDiagnostic { +impl WorkingDirectoryDiagnostic { fn new(code: impl Into, message: impl Into) -> Self { Self { code: code.into(), @@ -104,50 +103,45 @@ impl ExecutionWorkspaceDiagnostic { } } -impl std::fmt::Display for ExecutionWorkspaceDiagnostic { +impl std::fmt::Display for WorkingDirectoryDiagnostic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}: {}", self.code, self.message) } } -impl std::error::Error for ExecutionWorkspaceDiagnostic {} +impl std::error::Error for WorkingDirectoryDiagnostic {} -pub trait ExecutionWorkspaceMaterializer: Send + Sync + 'static { +pub trait WorkingDirectoryMaterializer: Send + Sync + 'static { fn materialize( &self, worker_ref: &WorkerRef, - request: &ExecutionWorkspaceRequest, - ) -> Result; + request: &WorkingDirectoryRequest, + ) -> Result; fn preallocate( &self, - request: &ExecutionWorkspaceRequest, - ) -> Result; + request: &WorkingDirectoryRequest, + ) -> Result; fn bind_allocation( &self, allocation_id: &str, relative_cwd: Option<&str>, - ) -> Result; + ) -> Result; - fn list_allocations( - &self, - ) -> Result, ExecutionWorkspaceDiagnostic>; + fn list_allocations(&self) -> Result, WorkingDirectoryDiagnostic>; fn allocation_status( &self, allocation_id: &str, - ) -> Result; + ) -> Result; fn cleanup_allocation( &self, allocation_id: &str, - ) -> Result; + ) -> Result; - fn cleanup( - &self, - binding: &ExecutionWorkspaceBinding, - ) -> Result<(), ExecutionWorkspaceDiagnostic>; + fn cleanup(&self, binding: &WorkingDirectoryBinding) -> Result<(), WorkingDirectoryDiagnostic>; } #[derive(Clone, Debug)] @@ -177,30 +171,30 @@ impl LocalGitWorktreeMaterializer { fn allocation_root(&self, allocation_id: &str) -> PathBuf { self.runtime_root - .join(EXECUTION_WORKSPACES_DIR) + .join(WORKING_DIRECTORIES_DIR) .join(allocation_id) } fn write_record( &self, - binding: &ExecutionWorkspaceBinding, - ) -> Result<(), ExecutionWorkspaceDiagnostic> { - let record = ExecutionWorkspaceMaterializationRecord { + binding: &WorkingDirectoryBinding, + ) -> Result<(), WorkingDirectoryDiagnostic> { + let record = WorkingDirectoryMaterializationRecord { allocation: binding.allocation.clone(), workspace_root: binding.workspace_root.clone(), source_repository_path: binding.source_repository_path.clone(), }; let path = binding.allocation_root.join(MATERIALIZATION_RECORD); let raw = serde_json::to_vec_pretty(&record).map_err(|error| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_record_serialize_failed", - format!("failed to serialize execution workspace record: {error}"), + WorkingDirectoryDiagnostic::new( + "working_directory_record_serialize_failed", + format!("failed to serialize working directory record: {error}"), ) })?; fs::write(&path, raw).map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_record_write_failed", - "failed to write execution workspace record; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_record_write_failed", + "failed to write working directory record; backend-private path details were omitted", ) }) } @@ -208,22 +202,22 @@ impl LocalGitWorktreeMaterializer { fn read_binding( &self, allocation_id: &str, - ) -> Result { + ) -> Result { let allocation_root = self.allocation_root(allocation_id); let path = allocation_root.join(MATERIALIZATION_RECORD); let raw = fs::read(&path).map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_allocation_not_found", - "execution workspace allocation was not found", + WorkingDirectoryDiagnostic::new( + "working_directory_allocation_not_found", + "working directory allocation was not found", ) })?; - let record: ExecutionWorkspaceMaterializationRecord = serde_json::from_slice(&raw).map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_record_invalid", - "execution workspace allocation record is invalid; backend-private path details were omitted", + let record: WorkingDirectoryMaterializationRecord = serde_json::from_slice(&raw).map_err(|_| { + WorkingDirectoryDiagnostic::new( + "working_directory_record_invalid", + "working directory allocation record is invalid; backend-private path details were omitted", ) })?; - Ok(ExecutionWorkspaceBinding { + Ok(WorkingDirectoryBinding { allocation: record.allocation, workspace_root: record.workspace_root.clone(), cwd: record.workspace_root, @@ -235,34 +229,34 @@ impl LocalGitWorktreeMaterializer { fn materialize_with_allocation_id( &self, allocation_id: String, - request: &ExecutionWorkspaceRequest, + request: &WorkingDirectoryRequest, cleanup_policy: &str, - ) -> Result { + ) -> Result { validate_allocation_id(&allocation_id)?; if request.materializer != MaterializerKind::LocalGitWorktree { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_materializer_unsupported", - "only local_git_worktree execution workspace materialization is supported in v0", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_materializer_unsupported", + "only local_git_worktree working directory materialization is supported in v0", )); } if request.repository.provider != "git" { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_repository_provider_unsupported", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_repository_provider_unsupported", format!( - "repository provider `{}` is not supported by the v0 execution workspace materializer", + "repository provider `{}` is not supported by the v0 working directory materializer", request.repository.provider ), )); } if request.dirty_state_policy != DirtyStatePolicy::CleanPointOnly { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_dirty_policy_unsupported", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_dirty_policy_unsupported", "only clean_point_only dirty-state policy is supported in v0", )); } if is_remote_uri(&request.repository.uri) { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_remote_repository_unsupported", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_remote_repository_unsupported", "remote repository URI materialization is not implemented in v0", )); } @@ -275,17 +269,17 @@ impl LocalGitWorktreeMaterializer { let source_root = git_stdout(&source_path, ["rev-parse", "--show-toplevel"]) .map(|value| PathBuf::from(value.trim())) .map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_git_repository_unavailable", + WorkingDirectoryDiagnostic::new( + "working_directory_git_repository_unavailable", "configured local repository is not an available Git worktree; backend-private path details were omitted", ) })?; let status = git_stdout(&source_root, ["status", "--porcelain"])?; if !status.trim().is_empty() { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_dirty_source_rejected", - "clean_point_only execution workspace materialization rejects dirty source repository state", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_dirty_source_rejected", + "clean_point_only working directory materialization rejects dirty source repository state", )); } @@ -310,21 +304,21 @@ impl LocalGitWorktreeMaterializer { .join("root") .join(sanitize_path_component(&request.repository.id)); if workspace_root.exists() { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_allocation_exists", - "execution workspace allocation target already exists; cleanup or choose a new allocation", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_allocation_exists", + "working directory allocation target already exists; cleanup or choose a new allocation", )); } fs::create_dir_all(workspace_root.parent().ok_or_else(|| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_invalid_target", - "execution workspace allocation target has no parent directory", + WorkingDirectoryDiagnostic::new( + "working_directory_invalid_target", + "working directory allocation target has no parent directory", ) })?) .map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_create_failed", - "failed to create execution workspace allocation directory; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_create_failed", + "failed to create working directory allocation directory; backend-private path details were omitted", ) })?; @@ -340,12 +334,12 @@ impl LocalGitWorktreeMaterializer { ], )?; - let allocation = ExecutionWorkspaceAllocation { + let allocation = WorkingDirectoryAllocation { id: allocation_id.clone(), repository_id: request.repository.id.clone(), materializer_kind: MaterializerKind::LocalGitWorktree, dirty_state_policy: DirtyStatePolicy::CleanPointOnly, - evidence: ExecutionWorkspaceEvidence { + evidence: WorkingDirectoryEvidence { repository_id: request.repository.id.clone(), requested_selector: request .repository @@ -357,15 +351,15 @@ impl LocalGitWorktreeMaterializer { materializer_kind: MaterializerKind::LocalGitWorktree, dirty_state_policy: DirtyStatePolicy::CleanPointOnly, }, - cleanup_target: ExecutionWorkspaceCleanupTarget { + cleanup_target: WorkingDirectoryCleanupTarget { kind: "git_worktree".to_string(), allocation_id, repository_id: request.repository.id.clone(), }, cleanup_policy: cleanup_policy.to_string(), - status: ExecutionWorkspaceStatusKind::Active, + status: WorkingDirectoryStatusKind::Active, }; - let binding = ExecutionWorkspaceBinding { + let binding = WorkingDirectoryBinding { allocation, workspace_root: workspace_root.clone(), cwd: workspace_root, @@ -377,20 +371,20 @@ impl LocalGitWorktreeMaterializer { } } -impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { +impl WorkingDirectoryMaterializer for LocalGitWorktreeMaterializer { fn materialize( &self, worker_ref: &WorkerRef, - request: &ExecutionWorkspaceRequest, - ) -> Result { + request: &WorkingDirectoryRequest, + ) -> Result { let allocation_id = Self::allocation_id(worker_ref, &request.repository.id); self.materialize_with_allocation_id(allocation_id, request, "remove_on_worker_stop") } fn preallocate( &self, - request: &ExecutionWorkspaceRequest, - ) -> Result { + request: &WorkingDirectoryRequest, + ) -> Result { let allocation_id = next_allocation_id(&request.repository.id); self.materialize_with_allocation_id(allocation_id, request, "manual_or_worker_stop") } @@ -399,30 +393,28 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { &self, allocation_id: &str, relative_cwd: Option<&str>, - ) -> Result { + ) -> Result { validate_allocation_id(allocation_id)?; let binding = self.read_binding(allocation_id)?; - if binding.allocation.status != ExecutionWorkspaceStatusKind::Active { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_not_active", - "execution workspace allocation is not active", + if binding.allocation.status != WorkingDirectoryStatusKind::Active { + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_not_active", + "working directory allocation is not active", )); } let cwd = validate_relative_cwd(binding.workspace_root(), relative_cwd)?; - Ok(ExecutionWorkspaceBinding { cwd, ..binding }) + Ok(WorkingDirectoryBinding { cwd, ..binding }) } - fn list_allocations( - &self, - ) -> Result, ExecutionWorkspaceDiagnostic> { - let root = self.runtime_root.join(EXECUTION_WORKSPACES_DIR); + fn list_allocations(&self) -> Result, WorkingDirectoryDiagnostic> { + let root = self.runtime_root.join(WORKING_DIRECTORIES_DIR); let entries = match fs::read_dir(&root) { Ok(entries) => entries, Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()), Err(_) => { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_list_failed", - "failed to list execution workspace allocations; backend-private path details were omitted", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_list_failed", + "failed to list working directory allocations; backend-private path details were omitted", )); } }; @@ -451,7 +443,7 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { fn allocation_status( &self, allocation_id: &str, - ) -> Result { + ) -> Result { validate_allocation_id(allocation_id)?; Ok(self.read_binding(allocation_id)?.status()) } @@ -459,34 +451,31 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { fn cleanup_allocation( &self, allocation_id: &str, - ) -> Result { + ) -> Result { validate_allocation_id(allocation_id)?; let binding = self.read_binding(allocation_id)?; self.cleanup(&binding)?; self.allocation_status(allocation_id) } - fn cleanup( - &self, - binding: &ExecutionWorkspaceBinding, - ) -> Result<(), ExecutionWorkspaceDiagnostic> { + fn cleanup(&self, binding: &WorkingDirectoryBinding) -> Result<(), WorkingDirectoryDiagnostic> { let mut allocation = binding.allocation.clone(); let allocation_root = binding.allocation_root.canonicalize().map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_cleanup_target_invalid", - "execution workspace allocation root is unavailable; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_cleanup_target_invalid", + "working directory allocation root is unavailable; backend-private path details were omitted", ) })?; let workspace_root = binding.workspace_root.canonicalize().map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_cleanup_target_invalid", - "execution workspace root is unavailable; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_cleanup_target_invalid", + "working directory root is unavailable; backend-private path details were omitted", ) })?; if !workspace_root.starts_with(&allocation_root) { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_cleanup_escape_rejected", - "execution workspace cleanup target is outside the allocation root", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_cleanup_escape_rejected", + "working directory cleanup target is outside the allocation root", )); } let workspace_root_arg = path_str(&workspace_root)?; @@ -497,9 +486,9 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { .or_else(|_| { if workspace_root.exists() { fs::remove_dir_all(&workspace_root).map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_cleanup_failed", - "failed to remove execution workspace; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_cleanup_failed", + "failed to remove working directory; backend-private path details were omitted", ) }) } else { @@ -507,11 +496,11 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { } }); allocation.status = if remove_result.is_ok() { - ExecutionWorkspaceStatusKind::Removed + WorkingDirectoryStatusKind::Removed } else { - ExecutionWorkspaceStatusKind::CleanupPending + WorkingDirectoryStatusKind::CleanupPending }; - let updated = ExecutionWorkspaceBinding { + let updated = WorkingDirectoryBinding { allocation, workspace_root: binding.workspace_root.clone(), cwd: binding.cwd.clone(), @@ -524,16 +513,13 @@ impl ExecutionWorkspaceMaterializer for LocalGitWorktreeMaterializer { } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -struct ExecutionWorkspaceMaterializationRecord { - allocation: ExecutionWorkspaceAllocation, +struct WorkingDirectoryMaterializationRecord { + allocation: WorkingDirectoryAllocation, workspace_root: PathBuf, source_repository_path: PathBuf, } -fn git_stdout<'a, I>( - repository_path: &Path, - args: I, -) -> Result +fn git_stdout<'a, I>(repository_path: &Path, args: I) -> Result where I: IntoIterator, { @@ -543,32 +529,32 @@ where .args(args) .output() .map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_git_unavailable", + WorkingDirectoryDiagnostic::new( + "working_directory_git_unavailable", "Git command could not be executed; backend-private path details were omitted", ) })?; if !output.status.success() { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_git_failed", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_git_failed", "Git command failed; backend-private path details were omitted", )); } Ok(String::from_utf8_lossy(&output.stdout).trim().to_string()) } -fn git_status<'a, I>(repository_path: &Path, args: I) -> Result<(), ExecutionWorkspaceDiagnostic> +fn git_status<'a, I>(repository_path: &Path, args: I) -> Result<(), WorkingDirectoryDiagnostic> where I: IntoIterator, { git_stdout(repository_path, args).map(|_| ()) } -fn path_str(path: &Path) -> Result { +fn path_str(path: &Path) -> Result { path.to_str().map(ToString::to_string).ok_or_else(|| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_non_utf8_path", - "execution workspace path is not valid UTF-8; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_non_utf8_path", + "working directory path is not valid UTF-8; backend-private path details were omitted", ) }) } @@ -612,7 +598,7 @@ fn next_allocation_id(repository_id: &str) -> String { ) } -fn validate_allocation_id(allocation_id: &str) -> Result<(), ExecutionWorkspaceDiagnostic> { +fn validate_allocation_id(allocation_id: &str) -> Result<(), WorkingDirectoryDiagnostic> { let sanitized = sanitize_path_component(allocation_id); if allocation_id.is_empty() || allocation_id != sanitized @@ -620,9 +606,9 @@ fn validate_allocation_id(allocation_id: &str) -> Result<(), ExecutionWorkspaceD || allocation_id.contains('/') || allocation_id.contains('\\') { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_allocation_id_invalid", - "execution workspace allocation id is invalid", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_allocation_id_invalid", + "working directory allocation id is invalid", )); } Ok(()) @@ -631,11 +617,11 @@ fn validate_allocation_id(allocation_id: &str) -> Result<(), ExecutionWorkspaceD fn validate_relative_cwd( workspace_root: &Path, relative_cwd: Option<&str>, -) -> Result { +) -> Result { let workspace_root = workspace_root.canonicalize().map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_root_unavailable", - "execution workspace root is unavailable; backend-private path details were omitted", + WorkingDirectoryDiagnostic::new( + "working_directory_root_unavailable", + "working directory root is unavailable; backend-private path details were omitted", ) })?; let relative = relative_cwd.unwrap_or(".").trim(); @@ -651,24 +637,24 @@ fn validate_relative_cwd( ) }) { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_relative_cwd_invalid", - "execution workspace relative_cwd must be a relative path inside the allocation root", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_relative_cwd_invalid", + "working directory relative_cwd must be a relative path inside the allocation root", )); } let target = workspace_root .join(relative_path) .canonicalize() .map_err(|_| { - ExecutionWorkspaceDiagnostic::new( - "execution_workspace_relative_cwd_unavailable", - "execution workspace relative_cwd does not identify an existing directory", + WorkingDirectoryDiagnostic::new( + "working_directory_relative_cwd_unavailable", + "working directory relative_cwd does not identify an existing directory", ) })?; if !target.starts_with(&workspace_root) || !target.is_dir() { - return Err(ExecutionWorkspaceDiagnostic::new( - "execution_workspace_relative_cwd_escape_rejected", - "execution workspace relative_cwd must resolve to a directory inside the allocation root", + return Err(WorkingDirectoryDiagnostic::new( + "working_directory_relative_cwd_escape_rejected", + "working directory relative_cwd must resolve to a directory inside the allocation root", )); } Ok(target) @@ -677,7 +663,7 @@ fn validate_relative_cwd( #[cfg(test)] mod tests { use super::*; - use crate::catalog::{ExecutionWorkspaceRepository, RepositorySelector}; + use crate::catalog::{RepositorySelector, WorkingDirectoryRepository}; use crate::identity::{RuntimeId, WorkerId}; fn git(path: &Path, args: &[&str]) { @@ -704,9 +690,9 @@ mod tests { dir } - fn request(repo: &Path) -> ExecutionWorkspaceRequest { - ExecutionWorkspaceRequest { - repository: ExecutionWorkspaceRepository { + fn request(repo: &Path) -> WorkingDirectoryRequest { + WorkingDirectoryRequest { + repository: WorkingDirectoryRepository { id: "repo-main".to_string(), provider: "git".to_string(), uri: ".".to_string(), @@ -787,7 +773,7 @@ mod tests { .materialize(&worker_ref(1), &request(repo.path())) .unwrap_err(); - assert_eq!(error.code, "execution_workspace_dirty_source_rejected"); + assert_eq!(error.code, "working_directory_dirty_source_rejected"); assert!(error.message.contains("clean_point_only")); } @@ -803,7 +789,7 @@ mod tests { .unwrap_err(); assert_eq!( error.code, - "execution_workspace_remote_repository_unsupported" + "working_directory_remote_repository_unsupported" ); let mut non_git = remote; @@ -814,7 +800,7 @@ mod tests { .unwrap_err(); assert_eq!( error.code, - "execution_workspace_repository_provider_unsupported" + "working_directory_repository_provider_unsupported" ); } @@ -858,28 +844,28 @@ mod tests { .bind_allocation(&allocation.allocation.id, Some("/tmp")) .unwrap_err() .code, - "execution_workspace_relative_cwd_invalid" + "working_directory_relative_cwd_invalid" ); assert_eq!( materializer .bind_allocation(&allocation.allocation.id, Some("../outside")) .unwrap_err() .code, - "execution_workspace_relative_cwd_invalid" + "working_directory_relative_cwd_invalid" ); assert_eq!( materializer .bind_allocation(&allocation.allocation.id, Some("missing")) .unwrap_err() .code, - "execution_workspace_relative_cwd_unavailable" + "working_directory_relative_cwd_unavailable" ); assert_eq!( materializer .bind_allocation(&allocation.allocation.id, Some("inside/file.txt")) .unwrap_err() .code, - "execution_workspace_relative_cwd_escape_rejected" + "working_directory_relative_cwd_escape_rejected" ); #[cfg(unix)] @@ -890,7 +876,7 @@ mod tests { .bind_allocation(&allocation.allocation.id, Some("escape")) .unwrap_err() .code, - "execution_workspace_relative_cwd_escape_rejected" + "working_directory_relative_cwd_escape_rejected" ); } } diff --git a/crates/workspace-server/src/companion.rs b/crates/workspace-server/src/companion.rs index ba69e5f4..88708336 100644 --- a/crates/workspace-server/src/companion.rs +++ b/crates/workspace-server/src/companion.rs @@ -387,9 +387,9 @@ fn spawn_companion_worker(runtime: &RuntimeRegistry) -> CompanionWorkerState { }, profile: Some(selector), initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, }, ); @@ -578,8 +578,8 @@ mod tests { "deterministic-companion-test", ), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } diff --git a/crates/workspace-server/src/hosts.rs b/crates/workspace-server/src/hosts.rs index 9f11ef60..2749b8a7 100644 --- a/crates/workspace-server/src/hosts.rs +++ b/crates/workspace-server/src/hosts.rs @@ -12,9 +12,9 @@ use std::{ time::Duration, }; use worker_runtime::catalog::{ - ConfigBundleRef, CreateWorkerRequest, ExecutionWorkspaceAllocationClaim, - ExecutionWorkspaceRequest, ExecutionWorkspaceSummary, ProfileSelector, - WorkerDetail as EmbeddedWorkerDetail, WorkerStatus as EmbeddedWorkerStatus, + ConfigBundleRef, CreateWorkerRequest, ProfileSelector, WorkerDetail as EmbeddedWorkerDetail, + WorkerStatus as EmbeddedWorkerStatus, WorkingDirectoryAllocationClaim, WorkingDirectoryRequest, + WorkingDirectorySummary, }; use worker_runtime::config_bundle::{ ConfigBundle, ConfigBundleAvailability, ConfigBundleMetadata, ConfigBundleProvenance, @@ -240,7 +240,7 @@ pub struct WorkerSummary { pub implementation: WorkerImplementationSummary, pub capabilities: WorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Option::is_none")] - pub execution_workspace: Option, + pub working_directory: Option, pub diagnostics: Vec, } @@ -268,14 +268,14 @@ pub struct WorkerLookupResult { /// The request carries Browser-facing launch semantics only: workspace intent, /// optional display identity, acceptance policy, optional profile selector, /// optional initial input, and optional configured Repository selector for -/// execution-workspace materialization. Runtime execution authority is resolved by the host +/// working-directory materialization. Runtime execution authority is resolved by the host /// into a synced ConfigBundle before the canonical Runtime create request is /// built. Raw workspace roots, child cwd, executable paths, tool scope, /// credentials, raw config stores, sockets, sessions, and storage paths are not /// accepted from Workspace API callers. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(deny_unknown_fields)] -pub struct WorkerSpawnExecutionWorkspaceRequest { +pub struct WorkerSpawnWorkingDirectoryRequest { /// Safe configured Repository id. The host resolves this id to repository /// authority from server-side config; browser callers cannot provide raw /// source paths or runtime-internal storage paths. @@ -295,15 +295,15 @@ pub struct WorkerSpawnRequest { pub profile: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub initial_input: Option, - /// Optional safe execution-workspace selector. The Workspace server resolves - /// this into a runtime-internal `ExecutionWorkspaceRequest` from configured + /// Optional safe working-directory selector. The Workspace server resolves + /// this into a runtime-internal `WorkingDirectoryRequest` from configured /// repositories before calling a host. #[serde(default, skip_serializing_if = "Option::is_none")] - pub execution_workspace: Option, + pub working_directory: Option, #[serde(skip, default)] - pub resolved_execution_workspace: Option, + pub resolved_working_directory: Option, #[serde(skip, default)] - pub resolved_execution_workspace_allocation: Option, + pub resolved_working_directory_allocation: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -1143,9 +1143,9 @@ impl EmbeddedWorkerRuntime { can_stop: self.can_stop_embedded_worker(summary.status, &summary.execution), can_spawn_followup: false, }, - execution_workspace: summary + working_directory: summary .execution - .execution_workspace + .working_directory .clone() .map(|status| status.summary), diagnostics: embedded_worker_projection_diagnostics(&summary.execution), @@ -1177,9 +1177,9 @@ impl EmbeddedWorkerRuntime { can_stop: self.can_stop_embedded_worker(detail.status, &detail.execution), can_spawn_followup: false, }, - execution_workspace: detail + working_directory: detail .execution - .execution_workspace + .working_directory .clone() .map(|status| status.summary), diagnostics: embedded_worker_projection_diagnostics(&detail.execution), @@ -1356,8 +1356,8 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { profile, config_bundle, initial_input: request.initial_input.clone(), - execution_workspace: request.resolved_execution_workspace.clone(), - execution_workspace_allocation: request.resolved_execution_workspace_allocation.clone(), + working_directory: request.resolved_working_directory.clone(), + working_directory_allocation: request.resolved_working_directory_allocation.clone(), }; match self.runtime.create_worker(create_request) { Ok(detail) => { @@ -1833,7 +1833,7 @@ impl RemoteWorkerRuntime { can_stop: runtime_worker_can_stop(true, summary.status, &summary.execution), can_spawn_followup: false, }, - execution_workspace: summary.execution.execution_workspace.clone().map(|status| status.summary), + working_directory: summary.execution.working_directory.clone().map(|status| status.summary), diagnostics: vec![diagnostic( "remote_runtime_projection", DiagnosticSeverity::Info, @@ -1867,7 +1867,7 @@ impl RemoteWorkerRuntime { can_stop: runtime_worker_can_stop(true, detail.status, &detail.execution), can_spawn_followup: false, }, - execution_workspace: detail.execution.execution_workspace.clone().map(|status| status.summary), + working_directory: detail.execution.working_directory.clone().map(|status| status.summary), diagnostics: vec![diagnostic( "remote_runtime_projection", DiagnosticSeverity::Info, @@ -2032,8 +2032,8 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { profile, config_bundle, initial_input: request.initial_input.clone(), - execution_workspace: request.resolved_execution_workspace.clone(), - execution_workspace_allocation: request.resolved_execution_workspace_allocation.clone(), + working_directory: request.resolved_working_directory.clone(), + working_directory_allocation: request.resolved_working_directory_allocation.clone(), }; match self.post_json::<_, RuntimeHttpWorkerResponse>("/v1/workers", &create) { Ok(response) => WorkerSpawnResult { @@ -2844,7 +2844,7 @@ pub fn placeholder_worker(host_id: impl Into) -> WorkerSummary { can_stop: false, can_spawn_followup: false, }, - execution_workspace: None, + working_directory: None, diagnostics: vec![diagnostic( "runtime_capability_unsupported", DiagnosticSeverity::Info, @@ -2958,8 +2958,8 @@ mod tests { self.backend_id(), ), run_state: WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } @@ -3040,7 +3040,7 @@ mod tests { can_stop: false, can_spawn_followup: false, }, - execution_workspace: None, + working_directory: None, diagnostics: Vec::new(), }], } @@ -3191,9 +3191,9 @@ mod tests { }, profile: None, initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, } } @@ -3323,9 +3323,9 @@ mod tests { }, profile: None, initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, }, ) .unwrap(); @@ -3425,9 +3425,9 @@ mod tests { }, profile: Some(ProfileSelector::Builtin("builtin:coder".to_string())), initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, }, ) .unwrap(); @@ -3456,9 +3456,9 @@ mod tests { acceptance: WorkerSpawnAcceptanceRequirement::SocketReady, profile: None, initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, }, ) .unwrap(); diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 9595d2f4..c3e6c219 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -12,10 +12,10 @@ use chrono::{SecondsFormat, Utc}; use futures::StreamExt; use serde::{Deserialize, Serialize}; use tokio::net::TcpListener; -use worker_runtime::execution_workspace::{ - ExecutionWorkspaceDiagnostic, ExecutionWorkspaceMaterializer, LocalGitWorktreeMaterializer, -}; use worker_runtime::worker_backend::WorkerRuntimeExecutionBackend; +use worker_runtime::working_directory::{ + LocalGitWorktreeMaterializer, WorkingDirectoryDiagnostic, WorkingDirectoryMaterializer, +}; use crate::companion::{ CompanionCancelRequest, CompanionConsole, CompanionMessageRequest, CompanionMessageResponse, @@ -27,8 +27,8 @@ use crate::hosts::{ HostSummary, RemoteRuntimeConfig, RemoteWorkerRuntime, RuntimeDiagnostic, RuntimeRegistry, RuntimeRegistryUnregisterResult, RuntimeSummary, WorkerInputRequest, WorkerInputResult, WorkerLifecycleRequest, WorkerLifecycleResult, WorkerOperationState, - WorkerSpawnAcceptanceRequirement, WorkerSpawnExecutionWorkspaceRequest, WorkerSpawnIntent, - WorkerSpawnRequest, WorkerSpawnResult, WorkerSummary, WorkerTranscriptProjection, + WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, WorkerSpawnResult, + WorkerSpawnWorkingDirectoryRequest, WorkerSummary, WorkerTranscriptProjection, }; use crate::identity::WorkspaceIdentity; use crate::observation::{ @@ -45,9 +45,9 @@ use crate::repositories::{ use crate::store::{ControlPlaneStore, WorkspaceRecord}; use crate::{Error, Result}; use worker_runtime::catalog::{ - ConfigBundleRef, DirtyStatePolicy, ExecutionWorkspaceAllocationClaim, - ExecutionWorkspaceRepository, ExecutionWorkspaceRequest, ExecutionWorkspaceSummary, - MaterializerKind, ProfileSelector, RepositorySelector as RuntimeRepositorySelector, + ConfigBundleRef, DirtyStatePolicy, MaterializerKind, ProfileSelector, + RepositorySelector as RuntimeRepositorySelector, WorkingDirectoryAllocationClaim, + WorkingDirectoryRepository, WorkingDirectoryRequest, WorkingDirectorySummary, }; use worker_runtime::config_bundle::ConfigBundle; use worker_runtime::http_server::{ @@ -154,7 +154,7 @@ pub struct WorkspaceApi { runtime: Arc, companion: Arc, observation_proxy: BackendObservationProxy, - execution_workspace_materializer: Arc, + working_directory_materializer: Arc, } impl WorkspaceApi { @@ -169,7 +169,7 @@ impl WorkspaceApi { "failed to initialize embedded Worker backend: {err}" )) })? - .with_execution_workspace_materializer((*materializer).clone()); + .with_working_directory_materializer((*materializer).clone()); Self::new_with_execution_backend(config, store, Arc::new(execution_backend)).await } @@ -204,7 +204,7 @@ impl WorkspaceApi { let runtime = Arc::new(runtime); let companion = Arc::new(CompanionConsole::new(runtime.clone())); let observation_proxy = BackendObservationProxy::new(config.runtime_event_sources.clone()); - let execution_workspace_materializer = Arc::new(LocalGitWorktreeMaterializer::new( + let working_directory_materializer = Arc::new(LocalGitWorktreeMaterializer::new( config.embedded_runtime_store_root.clone(), )); Ok(Self { @@ -214,7 +214,7 @@ impl WorkspaceApi { runtime, companion, observation_proxy, - execution_workspace_materializer, + working_directory_materializer, }) } @@ -271,12 +271,12 @@ pub fn build_router(api: WorkspaceApi) -> Router { .route("/api/hosts", get(list_hosts)) .route("/api/w/{workspace_id}/hosts", get(scoped_list_hosts)) .route( - "/api/w/{workspace_id}/execution-workspaces", - get(scoped_list_execution_workspaces).post(scoped_create_execution_workspace), + "/api/w/{workspace_id}/working-directories", + get(scoped_list_working_directories).post(scoped_create_working_directory), ) .route( - "/api/w/{workspace_id}/execution-workspaces/{allocation_id}", - get(scoped_execution_workspace_detail).delete(scoped_cleanup_execution_workspace), + "/api/w/{workspace_id}/working-directories/{allocation_id}", + get(scoped_working_directory_detail).delete(scoped_cleanup_working_directory), ) .route("/api/runtimes", get(list_runtimes)) .route("/api/w/{workspace_id}/runtimes", get(scoped_list_runtimes)) @@ -547,8 +547,8 @@ pub struct WorkerLaunchOptionsResponse { pub workspace_id: String, pub runtimes: Vec, pub profiles: Vec, - pub repositories: Vec, - pub execution_workspaces: Vec, + pub repositories: Vec, + pub working_directories: Vec, pub diagnostics: Vec, } @@ -570,7 +570,7 @@ pub struct WorkerLaunchProfileCandidate { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ExecutionWorkspaceRepositoryOption { +pub struct WorkingDirectoryRepositoryOption { pub id: String, pub display_name: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -578,54 +578,54 @@ pub struct ExecutionWorkspaceRepositoryOption { } #[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct BrowserExecutionWorkspaceCreatePolicy { +pub struct BrowserWorkingDirectoryCreatePolicy { #[serde(default)] - pub dirty_state: BrowserExecutionWorkspaceDirtyStatePolicy, + pub dirty_state: BrowserWorkingDirectoryDirtyStatePolicy, #[serde(default)] - pub cleanup: BrowserExecutionWorkspaceCleanupPolicy, + pub cleanup: BrowserWorkingDirectoryCleanupPolicy, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] -pub enum BrowserExecutionWorkspaceDirtyStatePolicy { +pub enum BrowserWorkingDirectoryDirtyStatePolicy { #[default] CleanPointOnly, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] -pub enum BrowserExecutionWorkspaceCleanupPolicy { +pub enum BrowserWorkingDirectoryCleanupPolicy { #[default] ManualOrWorkerStop, } #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct BrowserExecutionWorkspaceCreateRequest { +pub struct BrowserWorkingDirectoryCreateRequest { pub repository_id: String, #[serde(default)] pub selector: Option, #[serde(default)] - pub policy: BrowserExecutionWorkspaceCreatePolicy, + pub policy: BrowserWorkingDirectoryCreatePolicy, } #[derive(Debug, Serialize, Deserialize)] -pub struct BrowserExecutionWorkspaceListResponse { +pub struct BrowserWorkingDirectoryListResponse { pub workspace_id: String, - pub items: Vec, + pub items: Vec, pub diagnostics: Vec, } #[derive(Debug, Serialize, Deserialize)] -pub struct BrowserExecutionWorkspaceDetailResponse { +pub struct BrowserWorkingDirectoryDetailResponse { pub workspace_id: String, - pub item: ExecutionWorkspaceSummary, + pub item: WorkingDirectorySummary, pub diagnostics: Vec, } #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] -pub struct BrowserWorkerExecutionWorkspaceSelection { +pub struct BrowserWorkerWorkingDirectorySelection { pub allocation_id: String, #[serde(default)] pub relative_cwd: Option, @@ -639,7 +639,7 @@ pub struct BrowserCreateWorkerRequest { pub profile: String, pub initial_text: String, #[serde(default)] - pub execution_workspace: Option, + pub working_directory: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -742,7 +742,7 @@ struct ScopedRuntimePath { } #[derive(Debug, Deserialize)] -struct ScopedExecutionWorkspacePath { +struct ScopedWorkingDirectoryPath { workspace_id: String, allocation_id: String, } @@ -884,28 +884,28 @@ async fn scoped_get_worker_launch_options( get_worker_launch_options(State(api)).await } -async fn scoped_list_execution_workspaces( +async fn scoped_list_working_directories( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; - let items = execution_workspace_summaries(&api)?; - Ok(Json(BrowserExecutionWorkspaceListResponse { + let items = working_directory_summaries(&api)?; + Ok(Json(BrowserWorkingDirectoryListResponse { workspace_id: api.config.workspace_id.clone(), items, diagnostics: Vec::new(), })) } -async fn scoped_create_execution_workspace( +async fn scoped_create_working_directory( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; - let workspace_request = execution_workspace_request_for_browser(&api, request)?; + let workspace_request = working_directory_request_for_browser(&api, request)?; let binding = api - .execution_workspace_materializer + .working_directory_materializer .preallocate(&workspace_request) .map_err(|diagnostic| { ApiError::from(Error::RuntimeOperationFailed { @@ -914,20 +914,20 @@ async fn scoped_create_execution_workspace( message: diagnostic.message, }) })?; - Ok(Json(BrowserExecutionWorkspaceDetailResponse { + Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), item: binding.status().summary, diagnostics: Vec::new(), })) } -async fn scoped_execution_workspace_detail( +async fn scoped_working_directory_detail( State(api): State, - AxumPath(path): AxumPath, -) -> ApiResult> { + AxumPath(path): AxumPath, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let status = api - .execution_workspace_materializer + .working_directory_materializer .allocation_status(&path.allocation_id) .map_err(|diagnostic| { ApiError::from(Error::RuntimeOperationFailed { @@ -936,20 +936,20 @@ async fn scoped_execution_workspace_detail( message: diagnostic.message, }) })?; - Ok(Json(BrowserExecutionWorkspaceDetailResponse { + Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), item: status.summary, diagnostics: Vec::new(), })) } -async fn scoped_cleanup_execution_workspace( +async fn scoped_cleanup_working_directory( State(api): State, - AxumPath(path): AxumPath, -) -> ApiResult> { + AxumPath(path): AxumPath, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let status = api - .execution_workspace_materializer + .working_directory_materializer .cleanup_allocation(&path.allocation_id) .map_err(|diagnostic| { ApiError::from(Error::RuntimeOperationFailed { @@ -958,7 +958,7 @@ async fn scoped_cleanup_execution_workspace( message: diagnostic.message, }) })?; - Ok(Json(BrowserExecutionWorkspaceDetailResponse { + Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), item: status.summary, diagnostics: Vec::new(), @@ -1551,12 +1551,12 @@ async fn get_worker_launch_options( Ok(Json(worker_launch_options_response(&api))) } -fn execution_workspace_request_from_repository( +fn working_directory_request_from_repository( repository: &ConfiguredRepository, selector: Option<&str>, -) -> ExecutionWorkspaceRequest { - ExecutionWorkspaceRequest { - repository: ExecutionWorkspaceRepository { +) -> WorkingDirectoryRequest { + WorkingDirectoryRequest { + repository: WorkingDirectoryRepository { id: repository.id.clone(), provider: repository.provider.clone(), uri: repository.uri.clone(), @@ -1576,21 +1576,21 @@ fn execution_workspace_request_from_repository( } } -fn configured_execution_workspace_request( +fn configured_working_directory_request( config: &ServerConfig, - request: &WorkerSpawnExecutionWorkspaceRequest, -) -> Result { + request: &WorkerSpawnWorkingDirectoryRequest, +) -> Result { let repository = config .repositories .iter() .find(|repository| repository.id == request.repository_id) .ok_or_else(|| { Error::Config(format!( - "unknown repository id `{}` for Worker execution workspace", + "unknown repository id `{}` for Worker working directory", request.repository_id )) })?; - Ok(execution_workspace_request_from_repository( + Ok(working_directory_request_from_repository( repository, request.selector.as_deref(), )) @@ -1621,31 +1621,31 @@ async fn create_workspace_worker( content: initial_text, }) }; - let resolved_execution_workspace_allocation = + let resolved_working_directory_allocation = request - .execution_workspace - .map(|selection| ExecutionWorkspaceAllocationClaim { + .working_directory + .map(|selection| WorkingDirectoryAllocationClaim { allocation_id: selection.allocation_id, relative_cwd: selection.relative_cwd, }); - if resolved_execution_workspace_allocation.is_some() + if resolved_working_directory_allocation.is_some() && request.runtime_id != EMBEDDED_WORKER_RUNTIME_ID { return Err(Error::RuntimeOperationFailed { runtime_id: request.runtime_id.clone(), - code: "execution_workspace_runtime_unsupported".to_string(), - message: "preallocated execution workspaces are only supported by the embedded local runtime in v0".to_string(), + code: "working_directory_runtime_unsupported".to_string(), + message: "preallocated working directories are only supported by the embedded local runtime in v0".to_string(), } .into()); } - if let Some(allocation) = resolved_execution_workspace_allocation.as_ref() { - api.execution_workspace_materializer + if let Some(allocation) = resolved_working_directory_allocation.as_ref() { + api.working_directory_materializer .bind_allocation( &allocation.allocation_id, allocation.relative_cwd.as_deref(), ) .map_err(|diagnostic| { - execution_workspace_api_error(request.runtime_id.clone(), diagnostic) + working_directory_api_error(request.runtime_id.clone(), diagnostic) })?; } let result = api @@ -1660,9 +1660,9 @@ async fn create_workspace_worker( }, profile: Some(profile_selector), initial_input, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation, }, ) .map_err(|err| err.into_error())?; @@ -1751,11 +1751,11 @@ async fn create_runtime_worker( AxumPath(runtime_id): AxumPath, Json(mut request): Json, ) -> ApiResult> { - request.resolved_execution_workspace = request - .execution_workspace + request.resolved_working_directory = request + .working_directory .as_ref() - .map(|execution_workspace| { - configured_execution_workspace_request(&api.config, execution_workspace) + .map(|working_directory| { + configured_working_directory_request(&api.config, working_directory) }) .transpose()?; let result = api @@ -2688,19 +2688,19 @@ fn worker_launch_options_response(api: &WorkspaceApi) -> WorkerLaunchOptionsResp workspace_id: api.config.workspace_id.clone(), runtimes, profiles: worker_profile_candidates(), - repositories: execution_workspace_repository_options(api), - execution_workspaces: execution_workspace_summaries(api).unwrap_or_default(), + repositories: working_directory_repository_options(api), + working_directories: working_directory_summaries(api).unwrap_or_default(), diagnostics: Vec::new(), } } -fn execution_workspace_repository_options( +fn working_directory_repository_options( api: &WorkspaceApi, -) -> Vec { +) -> Vec { api.config .repositories .iter() - .map(|repository| ExecutionWorkspaceRepositoryOption { + .map(|repository| WorkingDirectoryRepositoryOption { id: repository.id.clone(), display_name: repository .display_name @@ -2711,8 +2711,8 @@ fn execution_workspace_repository_options( .collect() } -fn execution_workspace_summaries(api: &WorkspaceApi) -> ApiResult> { - api.execution_workspace_materializer +fn working_directory_summaries(api: &WorkspaceApi) -> ApiResult> { + api.working_directory_materializer .list_allocations() .map(|items| items.into_iter().map(|status| status.summary).collect()) .map_err(|diagnostic| { @@ -2724,10 +2724,10 @@ fn execution_workspace_summaries(api: &WorkspaceApi) -> ApiResult ApiResult { + request: BrowserWorkingDirectoryCreateRequest, +) -> ApiResult { let repository = api .config .repositories @@ -2739,13 +2739,13 @@ fn execution_workspace_request_for_browser( .or_else(|| repository.default_selector.clone()) .filter(|selector| !selector.trim().is_empty()); match request.policy.dirty_state { - BrowserExecutionWorkspaceDirtyStatePolicy::CleanPointOnly => {} + BrowserWorkingDirectoryDirtyStatePolicy::CleanPointOnly => {} } match request.policy.cleanup { - BrowserExecutionWorkspaceCleanupPolicy::ManualOrWorkerStop => {} + BrowserWorkingDirectoryCleanupPolicy::ManualOrWorkerStop => {} } - Ok(ExecutionWorkspaceRequest { - repository: ExecutionWorkspaceRepository { + Ok(WorkingDirectoryRequest { + repository: WorkingDirectoryRepository { id: repository.id.clone(), provider: "git".to_string(), uri: repository.path.to_string_lossy().to_string(), @@ -2822,9 +2822,9 @@ fn worker_create_not_accepted_error( ) } -fn execution_workspace_api_error( +fn working_directory_api_error( runtime_id: String, - diagnostic: ExecutionWorkspaceDiagnostic, + diagnostic: WorkingDirectoryDiagnostic, ) -> ApiError { ApiError::with_diagnostics( Error::RuntimeOperationFailed { @@ -3168,7 +3168,7 @@ impl IntoResponse for ApiError { if code.starts_with("workspace_settings_") || code.starts_with("invalid_") || code.starts_with("unsupported_worker_profile") - || code.starts_with("execution_workspace_") + || code.starts_with("working_directory_") || code.ends_with("_already_exists") || code.ends_with("_not_config_managed") || code.ends_with("_unsupported") => @@ -3284,8 +3284,8 @@ mod tests { self.backend_id(), ), run_state: worker_runtime::execution::WorkerExecutionRunState::Idle, - execution_workspace: request - .execution_workspace + working_directory: request + .working_directory .as_ref() .map(|binding| binding.status()), } @@ -3418,8 +3418,8 @@ mod tests { digest: bundle.metadata.digest, }, initial_input: None, - execution_workspace: None, - execution_workspace_allocation: None, + working_directory: None, + working_directory_allocation: None, } } @@ -3435,11 +3435,11 @@ mod tests { } #[tokio::test] - async fn browser_execution_workspace_preallocate_list_detail_and_cleanup_are_path_safe() { + async fn browser_working_directory_preallocate_list_detail_and_cleanup_are_path_safe() { let dir = tempfile::tempdir().unwrap(); init_clean_git_workspace(dir.path()); let app = test_app(dir.path()).await; - let workspace_path = format!("/api/w/{TEST_WORKSPACE_ID}/execution-workspaces"); + let workspace_path = format!("/api/w/{TEST_WORKSPACE_ID}/working-directories"); let created = post_json( app.clone(), @@ -3478,10 +3478,10 @@ mod tests { let dir = tempfile::tempdir().unwrap(); init_clean_git_workspace(dir.path()); let app = test_app(dir.path()).await; - let workspaces_path = format!("/api/w/{TEST_WORKSPACE_ID}/execution-workspaces"); + let working_directories_path = format!("/api/w/{TEST_WORKSPACE_ID}/working-directories"); let created = post_json( app.clone(), - &workspaces_path, + &working_directories_path, serde_json::json!({ "repository_id": TEST_REPOSITORY_ID, "selector": "HEAD", @@ -3500,7 +3500,7 @@ mod tests { "display_name": "Coding Worker", "profile": "builtin:coder", "initial_text": "", - "execution_workspace": { + "working_directory": { "allocation_id": allocation_id, "relative_cwd": "../escape" } @@ -3513,14 +3513,14 @@ mod tests { .as_array() .unwrap() .iter() - .any(|diagnostic| diagnostic["code"] == "execution_workspace_relative_cwd_invalid"), + .any(|diagnostic| diagnostic["code"] == "working_directory_relative_cwd_invalid"), "expected typed relative_cwd diagnostic, got {response}" ); assert!( response["message"] .as_str() .unwrap_or_default() - .contains("execution_workspace_relative_cwd_invalid") + .contains("working_directory_relative_cwd_invalid") ); let projected = serde_json::to_string(&response).unwrap(); assert!(!projected.contains(dir.path().to_string_lossy().as_ref())); @@ -3840,7 +3840,7 @@ mod tests { } #[tokio::test] - async fn runtime_worker_spawn_rejects_raw_execution_workspace_fields() { + async fn runtime_worker_spawn_rejects_raw_working_directory_fields() { let dir = tempfile::tempdir().unwrap(); let app = test_app(dir.path()).await; let response = request_json( @@ -3857,7 +3857,7 @@ mod tests { "kind": "run_accepted", "expected_segments": 0 }, - "execution_workspace": { + "working_directory": { "repository_id": TEST_REPOSITORY_ID, "local_path": dir.path().display().to_string() } @@ -3870,7 +3870,7 @@ mod tests { .as_str() .unwrap_or_default() .contains("unknown field"), - "raw execution workspace field should be rejected: {response}" + "raw working directory field should be rejected: {response}" ); } @@ -3892,7 +3892,7 @@ mod tests { "kind": "run_accepted", "expected_segments": 0 }, - "execution_workspace": { + "working_directory": { "repository_id": TEST_REPOSITORY_ID, "selector": "HEAD" } @@ -4379,9 +4379,9 @@ mod tests { }, profile: None, initial_input: None, - execution_workspace: None, - resolved_execution_workspace: None, - resolved_execution_workspace_allocation: None, + working_directory: None, + resolved_working_directory: None, + resolved_working_directory_allocation: None, }, ) .expect("spawn worker"); diff --git a/web/workspace/src/lib/workspace-sidebar/WorkersNavSection.svelte b/web/workspace/src/lib/workspace-sidebar/WorkersNavSection.svelte index 1aeb1a97..4a9acbf3 100644 --- a/web/workspace/src/lib/workspace-sidebar/WorkersNavSection.svelte +++ b/web/workspace/src/lib/workspace-sidebar/WorkersNavSection.svelte @@ -4,7 +4,7 @@ import { buildBrowserCreateWorkerRequest, defaultWorkerLaunchForm } from './worker-launch'; import type { BrowserCreateWorkerResponse, - BrowserExecutionWorkspaceCreateResponse, + BrowserWorkingDirectoryCreateResponse, ListResponse, Worker, WorkerLaunchOptionsResponse, @@ -36,11 +36,11 @@ let runtimeId = $state(''); let profile = $state('builtin:coder'); let initialText = $state(''); - let executionWorkspaceAllocationId = $state(''); - let executionWorkspaceRepositoryId = $state(''); - let executionWorkspaceSelector = $state('HEAD'); + let workingDirectoryAllocationId = $state(''); + let workingDirectoryRepositoryId = $state(''); + let workingDirectorySelector = $state('HEAD'); let relativeCwd = $state(''); - let creatingWorkspace = $state(false); + let creatingWorkingDirectory = $state(false); $effect(() => { if (!workspaceId) { @@ -102,17 +102,17 @@ display_name: displayName, profile, initial_text: initialText, - execution_workspace_allocation_id: executionWorkspaceAllocationId, - execution_workspace_repository_id: executionWorkspaceRepositoryId, - execution_workspace_selector: executionWorkspaceSelector, + working_directory_allocation_id: workingDirectoryAllocationId, + working_directory_repository_id: workingDirectoryRepositoryId, + working_directory_selector: workingDirectorySelector, relative_cwd: relativeCwd, }); runtimeId = form.runtime_id; displayName = form.display_name; profile = form.profile; - executionWorkspaceAllocationId = form.execution_workspace_allocation_id; - executionWorkspaceRepositoryId = form.execution_workspace_repository_id; - executionWorkspaceSelector = form.execution_workspace_selector; + workingDirectoryAllocationId = form.working_directory_allocation_id; + workingDirectoryRepositoryId = form.working_directory_repository_id; + workingDirectorySelector = form.working_directory_selector; relativeCwd = form.relative_cwd; } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') { @@ -122,36 +122,36 @@ } } - async function createExecutionWorkspace() { - if (!executionWorkspaceRepositoryId) { - submitError = 'select a repository before creating an execution workspace'; + async function createWorkingDirectory() { + if (!workingDirectoryRepositoryId) { + submitError = 'select a repository before creating a working directory'; return; } - creatingWorkspace = true; + creatingWorkingDirectory = true; submitError = null; try { - const response = await fetch(workerApiPath('/execution-workspaces'), { + const response = await fetch(workerApiPath('/working-directories'), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ - repository_id: executionWorkspaceRepositoryId, - selector: executionWorkspaceSelector || null, + repository_id: workingDirectoryRepositoryId, + selector: workingDirectorySelector || null, policy: { dirty_state: 'clean_point_only', cleanup: 'manual_or_worker_stop' }, }), }); if (!response.ok) { - throw new Error(await responseErrorMessage(response, 'execution workspace create failed')); + throw new Error(await responseErrorMessage(response, 'working directory create failed')); } - const payload = (await response.json()) as BrowserExecutionWorkspaceCreateResponse; - const items = options?.execution_workspaces ?? []; + const payload = (await response.json()) as BrowserWorkingDirectoryCreateResponse; + const items = options?.working_directories ?? []; options = options - ? { ...options, execution_workspaces: [...items.filter((item) => item.allocation_id !== payload.item.allocation_id), payload.item] } + ? { ...options, working_directories: [...items.filter((item) => item.allocation_id !== payload.item.allocation_id), payload.item] } : options; - executionWorkspaceAllocationId = payload.item.allocation_id; + workingDirectoryAllocationId = payload.item.allocation_id; } catch (err) { - submitError = err instanceof Error ? err.message : 'execution workspace create failed'; + submitError = err instanceof Error ? err.message : 'working directory create failed'; } finally { - creatingWorkspace = false; + creatingWorkingDirectory = false; } } @@ -172,9 +172,9 @@ display_name: displayName, profile, initial_text: initialText, - execution_workspace_allocation_id: executionWorkspaceAllocationId, - execution_workspace_repository_id: executionWorkspaceRepositoryId, - execution_workspace_selector: executionWorkspaceSelector, + working_directory_allocation_id: workingDirectoryAllocationId, + working_directory_repository_id: workingDirectoryRepositoryId, + working_directory_selector: workingDirectorySelector, relative_cwd: relativeCwd, })), }); @@ -251,22 +251,22 @@ {/if} -
- Execution workspace +
+ Working directory -