diff --git a/.github/workflows/feature-smoke.yml b/.github/workflows/feature-smoke.yml index b24e2d6c6..07d967525 100644 --- a/.github/workflows/feature-smoke.yml +++ b/.github/workflows/feature-smoke.yml @@ -201,10 +201,10 @@ jobs: cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace --all-targets - - name: Build substrate + world-agent (debug) + - name: Build substrate + world-service (debug) run: | cargo build --bin substrate --bin substrate-shim - cargo build -p world-agent --bin world-agent + cargo build -p world-service --bin world-service - name: Add substrate to PATH run: echo "$GITHUB_WORKSPACE/target/debug" >> "$GITHUB_PATH" @@ -239,11 +239,11 @@ jobs: exit 1 fi - manual_sock="${RUNNER_TEMP:-/tmp}/substrate-world-agent.sock" - manual_log="${RUNNER_TEMP:-/tmp}/substrate-world-agent.log" + manual_sock="${RUNNER_TEMP:-/tmp}/substrate-world-service.sock" + manual_log="${RUNNER_TEMP:-/tmp}/substrate-world-service.log" rm -f "${manual_sock}" "${manual_log}" - "${socket_activate}" --listen="${manual_sock}" --fdname=substrate-world-agent "$GITHUB_WORKSPACE/target/debug/world-agent" >"${manual_log}" 2>&1 & + "${socket_activate}" --listen="${manual_sock}" --fdname=substrate-world-service "$GITHUB_WORKSPACE/target/debug/world-service" >"${manual_log}" 2>&1 & agent_pid=$! for _ in $(seq 1 50); do @@ -254,13 +254,13 @@ jobs: done if [[ ! -S "${manual_sock}" ]]; then - echo "ERROR: failed to start user-scoped world-agent socket fallback" >&2 + echo "ERROR: failed to start user-scoped world-service socket fallback" >&2 [[ -f "${manual_log}" ]] && cat "${manual_log}" >&2 exit 1 fi export SUBSTRATE_WORLD_SOCKET="${manual_sock}" - echo "Using user-scoped world-agent socket fallback: ${manual_sock}" + echo "Using user-scoped world-service socket fallback: ${manual_sock}" fi feature_dir="${{ inputs.feature_dir }}" @@ -309,15 +309,15 @@ jobs: echo "dir=." >> "$GITHUB_OUTPUT" fi - - name: Provision world-agent (Linux self-hosted; WAPS) - if: startsWith(inputs.feature_dir, 'docs/project_management/packs/draft/world-deps-apt-provisioning') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/next/world-sync') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-agent-policy-snapshot') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/full-isolation-landlock-overlayfs-compat') + - name: Provision world-service (Linux self-hosted; WAPS) + if: startsWith(inputs.feature_dir, 'docs/project_management/packs/draft/world-deps-apt-provisioning') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/next/world-sync') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-service-policy-snapshot') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/full-isolation-landlock-overlayfs-compat') working-directory: ${{ steps.checkout_dir.outputs.dir }} run: | set -euo pipefail if [[ -S /run/substrate.sock && -w /run/substrate.sock ]]; then - echo "Using existing system world-agent socket" + echo "Using existing system world-service socket" elif sudo -n true >/dev/null 2>&1; then - cargo build -p world-agent --release + cargo build -p world-service --release scripts/linux/world-provision.sh --profile release --skip-build --sudo-noninteractive else echo "WARN: sudo -n unavailable on this runner; smoke will fall back to a user-scoped socket if needed" >&2 @@ -333,7 +333,7 @@ jobs: exit 0 fi - echo "WARN: system world-agent socket unavailable; smoke will try a user-scoped socket fallback" >&2 + echo "WARN: system world-service socket unavailable; smoke will try a user-scoped socket fallback" >&2 if [[ -e "${sock}" ]]; then ls -l "${sock}" >&2 || true stat "${sock}" >&2 || true @@ -385,11 +385,11 @@ jobs: cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace --all-targets - - name: Build substrate + world-agent (debug) + - name: Build substrate + world-service (debug) working-directory: ${{ steps.checkout_dir.outputs.dir }} run: | cargo build --bin substrate --bin substrate-shim - cargo build -p world-agent --bin world-agent + cargo build -p world-service --bin world-service - name: Add substrate to PATH shell: bash @@ -433,11 +433,11 @@ jobs: exit 1 fi - manual_sock="${RUNNER_TEMP:-/tmp}/substrate-world-agent.sock" - manual_log="${RUNNER_TEMP:-/tmp}/substrate-world-agent.log" + manual_sock="${RUNNER_TEMP:-/tmp}/substrate-world-service.sock" + manual_log="${RUNNER_TEMP:-/tmp}/substrate-world-service.log" rm -f "${manual_sock}" "${manual_log}" - "${socket_activate}" --listen="${manual_sock}" --fdname=substrate-world-agent "$PWD/target/debug/world-agent" >"${manual_log}" 2>&1 & + "${socket_activate}" --listen="${manual_sock}" --fdname=substrate-world-service "$PWD/target/debug/world-service" >"${manual_log}" 2>&1 & agent_pid=$! for _ in $(seq 1 50); do @@ -448,13 +448,13 @@ jobs: done if [[ ! -S "${manual_sock}" ]]; then - echo "ERROR: failed to start user-scoped world-agent socket fallback" >&2 + echo "ERROR: failed to start user-scoped world-service socket fallback" >&2 [[ -f "${manual_log}" ]] && cat "${manual_log}" >&2 exit 1 fi export SUBSTRATE_WORLD_SOCKET="${manual_sock}" - echo "Using user-scoped world-agent socket fallback: ${manual_sock}" + echo "Using user-scoped world-service socket fallback: ${manual_sock}" fi feature_dir="${{ inputs.feature_dir }}" @@ -581,7 +581,7 @@ jobs: run: caffeinate -dimsu & - name: Warm Lima world (macOS self-hosted; WAPS) - if: startsWith(inputs.feature_dir, 'docs/project_management/packs/draft/world-deps-apt-provisioning') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/next/world-sync') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-agent-policy-snapshot') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/full-isolation-landlock-overlayfs-compat') + if: startsWith(inputs.feature_dir, 'docs/project_management/packs/draft/world-deps-apt-provisioning') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/next/world-sync') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-service-policy-snapshot') || startsWith(inputs.feature_dir, 'docs/project_management/_archived/full-isolation-landlock-overlayfs-compat') working-directory: ${{ steps.checkout_dir.outputs.dir }} run: bash scripts/mac/lima-warm.sh "$PWD" @@ -751,7 +751,7 @@ jobs: cargo test --workspace --all-targets - name: Warm WSL world (Windows self-hosted; WAPS) - if: startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-agent-policy-snapshot') + if: startsWith(inputs.feature_dir, 'docs/project_management/_archived/world-service-policy-snapshot') shell: pwsh env: # The Windows WSL backend is expected to use the named pipe forwarder; disable the TCP bridge @@ -828,7 +828,7 @@ jobs: if [[ ! -S "${sock}" ]]; then echo "SUBSTRATE_RUNNER_MISPROVISIONED=1" echo "SUBSTRATE_RUNNER_MISPROVISIONED_REASON=substrate_sock_missing" - echo "WSL runner is missing required world-agent socket: ${sock}" >&2 + echo "WSL runner is missing required world-service socket: ${sock}" >&2 echo "Remediation: provision the world agent + ensure the runner user can access ${sock} (root:substrate 0660)." >&2 echo "Diagnostics: id=$(id -a || true)" >&2 exit 1 @@ -837,7 +837,7 @@ jobs: if [[ ! -w "${sock}" ]]; then echo "SUBSTRATE_RUNNER_MISPROVISIONED=1" echo "SUBSTRATE_RUNNER_MISPROVISIONED_REASON=substrate_sock_permission_denied" - echo "WSL runner cannot access required world-agent socket: ${sock} (need write permission to connect)" >&2 + echo "WSL runner cannot access required world-service socket: ${sock} (need write permission to connect)" >&2 echo "Remediation: ensure runner user is in substrate group + restart runner service; confirm ${sock} is root:substrate 0660." >&2 ls -l "${sock}" >&2 || true stat "${sock}" >&2 || true diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 6d0f8c4f7..71e9f7a58 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -161,8 +161,8 @@ jobs: - name: Targeted replay tests run: cargo test -p substrate-replay -- --nocapture - - name: Targeted world-agent tests - run: cargo test -p world-agent -- --nocapture + - name: Targeted world-service tests + run: cargo test -p world-service -- --nocapture - name: Install dist CLI run: | diff --git a/AGENTS.md b/AGENTS.md index 40c8af46d..4b1600db3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,8 +8,8 @@ Substrate is the secure execution layer that sits between AI agents and a develo - `crates/shell`: interactive REPL, PTY plumbing, shim deployment, and world orchestration. - `crates/shim`: PATH interception, depth tracking (`SHIM_DEPTH`), and structured logging of every spawned process. - `crates/common`: shared log schema, redaction helpers, filesystem diff types. -- `crates/world*`: platform-specific backends. `world` uses namespaces/cgroups/nftables on Linux; `world-mac-lima` boots a Lima VM and tunnels to `world-agent`; `world-windows-wsl` warms a WSL distro and brokers named-pipe/TCP access (functional but experimental). -- `crates/world-agent`: the in-world daemon exposing REST (`/v1/execute`) and WebSocket (`/v1/stream`) APIs with fs diff collection. +- `crates/world*`: platform-specific backends. `world` uses namespaces/cgroups/nftables on Linux; `world-mac-lima` boots a Lima VM and tunnels to `world-service`; `world-windows-wsl` warms a WSL distro and brokers named-pipe/TCP access (functional but experimental). +- `crates/world-service`: the in-world daemon exposing REST (`/v1/execute`) and WebSocket (`/v1/stream`) APIs with fs diff collection. - `crates/agent-api-*`: shared request/response models, async client, and host proxy helpers for third-party agents. - `crates/broker`, `forwarder`, `host-proxy`, `telemetry-lib`, `replay`, `substrate-graph`: policy decisions, transport glue, telemetry pipelines, replay tooling, and optional graph analytics. - `docs/`: deep dives (`WORLD.md`, `TRACE.md`, `REPLAY.md`, `TELEMETRY.md`, `CONFIGURATION.md`, `cross-platform/…`). @@ -19,8 +19,8 @@ Substrate is the secure execution layer that sits between AI agents and a develo ## Execution Architecture 1. Shell loads policy via the broker (`substrate_broker::evaluate`), ensures shims are deployed (`ShimDeployer`), and initializes tracing (`substrate_trace::init_trace`). 2. The shim intercepts PATH lookups, writes structured execution events (via `logger::log_execution`), performs fast policy checks, and forwards invocation metadata to trace spans. -3. The shell consults the world backend selected by `world-backend-factory`, issuing `/v1/execute` for non-PTY runs or upgrading to `/v1/stream` for interactive workloads. On Linux/macOS this depends on the `substrate-world-agent` service (systemd on Linux, Lima guest on macOS); provisioning scripts install and start that service. -4. `world-agent` executes inside a per-session world: Linux uses cgroup v2 + netns + overlayfs; macOS proxies into a Lima VM via VSock/SSH; Windows calls into WSL using named pipes or a TCP forwarder. +3. The shell consults the world backend selected by `world-backend-factory`, issuing `/v1/execute` for non-PTY runs or upgrading to `/v1/stream` for interactive workloads. On Linux/macOS this depends on the `substrate-world-service` service (systemd on Linux, Lima guest on macOS); provisioning scripts install and start that service. +4. `world-service` executes inside a per-session world: Linux uses cgroup v2 + netns + overlayfs; macOS proxies into a Lima VM via VSock/SSH; Windows calls into WSL using named pipes or a TCP forwarder. 5. Results, filesystem diffs, and policy decisions are recorded by `crates/trace`, producing JSONL spans in `~/.substrate/trace.jsonl` (or `SHIM_TRACE_LOG`). Replay (`crates/replay`) re-runs spans through the same world backend (Linux replays require CAP_SYS_ADMIN for namespaces/overlay; use `--no-world` / `SUBSTRATE_REPLAY_USE_WORLD=disabled` only if you want to opt out). ## Local Environment & Core Tools @@ -55,14 +55,14 @@ cargo bench # exercise hotspots when touching pe - Replay and diff analysis live in `docs/REPLAY.md` and `docs/TRACE.md`; update those docs when schema changes. See `docs/REPLAY.md` for privileged requirements (`SUBSTRATE_REPLAY_USE_WORLD`) and how to use `--no-world` when isolation isn’t available. ## Platform Worlds & Provisioning -- **Linux**: Run `scripts/linux/world-provision.sh --profile release` to install `world-agent` plus the matching `.service`/`.socket` units so systemd manages `/run/substrate.sock`. Dev installs invoke this script automatically (sudo prompts expected). The installers now create the `substrate` group, add the invoking user, and reload the socket units so `/run/substrate.sock` is `root:substrate 0660` just like the provisioning script. After provisioning, run `id -nG "$USER"` to confirm the new membership and `loginctl enable-linger "$USER"` so the socket-activated service is available after logout/reboot. Validate with `systemctl status substrate-world-agent.socket`, `systemctl status substrate-world-agent.service`, and `substrate world doctor --json | jq '.world_socket'`. +- **Linux**: Run `scripts/linux/world-provision.sh --profile release` to install `world-service` plus the matching `.service`/`.socket` units so systemd manages `/run/substrate.sock`. Dev installs invoke this script automatically (sudo prompts expected). The installers now create the `substrate` group, add the invoking user, and reload the socket units so `/run/substrate.sock` is `root:substrate 0660` just like the provisioning script. After provisioning, run `id -nG "$USER"` to confirm the new membership and `loginctl enable-linger "$USER"` so the socket-activated service is available after logout/reboot. Validate with `systemctl status substrate-world-service.socket`, `systemctl status substrate-world-service.service`, and `substrate world doctor --json | jq '.world_socket'`. - **macOS**: `scripts/mac/lima-warm.sh` provisions the `substrate` Lima VM, installs dependencies, and ensures forwarding. `scripts/mac/lima-doctor.sh` and `scripts/mac/smoke.sh` validate PTY, REST, and replay flows. - **Windows**: Warm the WSL backend via `pwsh -File scripts/windows/wsl-warm.ps1 -DistroName substrate-wsl -ProjectPath (Resolve-Path .)`; verify with `scripts/windows/wsl-smoke.ps1`. The backend prefers named-pipe transport but can fall back to TCP (`SUBSTRATE_FORWARDER_PORT`). - Always capture doctor/smoke output in PRs touching `world-*`, `forwarder`, or `host-proxy` crates. ## Testing & Validation Expectations - Unit tests live alongside sources (`#[cfg(test)] mod tests`); integration suites sit in `crates/*/tests/` (e.g., `crates/shell/tests/integration.rs`, `crates/replay/tests/`). Use `test_` naming. -- Run `cargo test -p world-agent`, `cargo test -p substrate-shim`, and targeted `cargo test --test shim_deployment` when editing those components. +- Run `cargo test -p world-service`, `cargo test -p substrate-shim`, and targeted `cargo test --test shim_deployment` when editing those components. - When touching shell/world/shim logic, capture `substrate world doctor --json`, `substrate shim doctor --json`, and `substrate health --json` output in the PR. - Privileged checks (netns, nftables, cgroups) require `docs/HOWTO_PRIVILEGED_TESTS.md`; document platform coverage in PR descriptions. - Validate shim lifecycle on every OS touched: `substrate --shim-status`, `substrate --shim-deploy`, `substrate --shim-remove`. @@ -95,7 +95,7 @@ cargo bench # exercise hotspots when touching pe # GitNexus — Code Intelligence -This project is indexed by GitNexus as **substrate** (49012 symbols, 75513 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **substrate** (49108 symbols, 75625 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. diff --git a/AGENT_ORCHESTRATION_GAP_MATRIX.md b/AGENT_ORCHESTRATION_GAP_MATRIX.md index a05fb1d7c..9728719b0 100644 --- a/AGENT_ORCHESTRATION_GAP_MATRIX.md +++ b/AGENT_ORCHESTRATION_GAP_MATRIX.md @@ -59,17 +59,17 @@ There are two different API layers in this repository and they should not be con - Default Rust import name in code: `agent_api` - Purpose: run and control Codex / Claude Code / other CLI agents through one capability-gated contract - Substrate-local host/world transport: - - `crates/agent-api-types` - - `crates/agent-api-core` - - `crates/agent-api-client` - - Purpose: Substrate host components talking to `world-agent` + - `crates/transport-api-types` + - `crates/transport-api-core` + - `crates/transport-api-client` + - Purpose: Substrate host components talking to `world-service` The local `agent-api-*` crates are not the same thing as Unified Agent API. They currently represent Substrate’s transport layer for host-to-world execution, not the canonical CLI-agent wrapper contract. Terminology rule for this repository: - Reserve `Unified Agent API`, `UAA`, and the external Rust import name `agent_api` for the CLI-agent runtime abstraction. -- Refer to `crates/agent-api-types`, `crates/agent-api-core`, and `crates/agent-api-client` as the Substrate-local host/world transport layer or world-agent transport layer. +- Refer to `crates/transport-api-types`, `crates/transport-api-core`, and `crates/transport-api-client` as the Substrate-local host/world transport layer or world-service transport layer. ## Current State Summary @@ -102,13 +102,13 @@ Terminology rule for this repository: | Area | Status | What exists now | What is missing for the intended v1 path | |---|---|---|---| | Product intent | `Now explicit` | This file now records the intended v1 and later product shape | Keep downstream docs aligned to this statement | -| Unified Agent API adoption | `Landed for host orchestrator and supported Linux/macOS world member runtime` | `crates/shell/Cargo.toml` now pulls `unified-agent-api`, `agent_runtime/registry.rs` registers Codex / Claude Code backends, the REPL keeps the host orchestrator under attached control, and supported world-scoped members now launch through `world-agent` over the existing host↔world transport on Linux and the macOS/Lima forwarded path | Remaining v1 work is no longer basic UAA runtime adoption; it is on operator-facing controls, status resilience, Windows/WSL posture, and broader caller-surface productization | -| UAA vs local `agent-api-*` boundary | `Partially clarified` | Code now distinguishes shell-owned UAA runtime from local host/world transport; `PURE_AGENT_PROTOCOL` comments explicitly say `uaa.agent.session` is a Substrate-local normalized label, not an upstream protocol claim | Local crates are still named `agent-api-*`, and the `uaa.agent.session` label still exists, so naming confusion has been reduced but not removed | +| Unified Agent API adoption | `Landed for host orchestrator and supported Linux/macOS world member runtime` | `crates/shell/Cargo.toml` now pulls `unified-agent-api`, `agent_runtime/registry.rs` registers Codex / Claude Code backends, the REPL keeps the host orchestrator under attached control, and supported world-scoped members now launch through `world-service` over the existing host↔world transport on Linux and the macOS/Lima forwarded path | Remaining v1 work is no longer basic UAA runtime adoption; it is on operator-facing controls, status resilience, Windows/WSL posture, and broader caller-surface productization | +| UAA vs local `agent-api-*` boundary | `Partially clarified` | Code now distinguishes shell-owned UAA runtime from local host/world transport; `PURE_AGENT_PROTOCOL` comments explicitly say `substrate.agent.session` is a Substrate-local normalized label, not an upstream protocol claim | Local crates are still named `agent-api-*`, and the `substrate.agent.session` label still exists, so naming confusion has been reduced but not removed | | Agent config and inventory | `Landed` | Inventory schema, capability gates, derived `backend_id`, scope resolution, and backend selection are live in `agent_inventory.rs` and `agent_runtime/validator.rs` | No major v1 inventory gap remains; future work is mostly on runtime breadth rather than selection modeling | | Agent CLI inspection surface | `Landed for inspection plus narrow public control` | `substrate agent list|status|doctor|toolbox status|env` is live, `agent status` now merges live runtime-state snapshots with trace fallback and surfaces durable `parked_resumable` / `awaiting_attention` host sessions, and the namespace now exposes first-class `start|turn|reattach|fork|stop` actions with exact selectors plus one-of prompt-source validation for `start` and `turn` | Remaining gaps are broader productization only: there is still no default-agent routing, no member-level public selector surface beyond exact `(orchestration_session_id, backend_id)`, and no public world-root start | | REPL agent invocation grammar | `Landed, Linux-first and REPL-first` | The interactive shell now accepts exact single-line targeted follow-up turns as `:: `, routes them before shell fallback, preserves `:host` / `:pty`, and continues to treat plain REPL input as shell execution | Remaining work is broader product-surface breadth only: no default-agent routing exists, and non-REPL surfaces still need their own explicit caller contract | | Non-interactive agent invocation surface | `Landed narrowly` | `substrate agent start --backend (--prompt | --prompt-file | --prompt-file -) [--json]` and `substrate agent turn --session --backend ...` now provide a real public prompt-taking caller surface with helper-owned streaming, completion-time `session_posture`, explicit fail-closed exact selector coverage, Linux world-member follow-up proof through typed `/v1/member_turn/stream`, and an explicit non-regression guard that keeps `substrate -c` / `--command` in shell-wrap mode | Remaining gaps are broader caller-surface breadth and parity only: there is still no default-agent routing, no public world-root start, and Windows/WSL world-sensitive follow-up still fails closed | -| Terminology guardrails from the proposed v1 slice | `Partially landed` | Backend-kind mapping for supported UAA agents exists in `agent_runtime/mapping.rs`; runtime selection fails closed for unsupported kinds; `agent_events.rs` documents the local meaning of `uaa.agent.session` | Local `agent-api-*` crate names are still in place, and the local trace/session label has not been renamed | +| Terminology guardrails from the proposed v1 slice | `Partially landed` | Backend-kind mapping for supported UAA agents exists in `agent_runtime/mapping.rs`; runtime selection fails closed for unsupported kinds; `agent_events.rs` documents the local meaning of `substrate.agent.session` | Local `agent-api-*` crate names are still in place, and the local trace/session label has not been renamed | | Shell-owned UAA runtime | `Landed` | The REPL prepares orchestrator startup, validates allowlisting, builds a shell-owned UAA gateway, starts the orchestrator, retains cancel/event/completion handles, and persists lifecycle transitions in `repl/async_repl.rs` | This is still REPL-owned, not a general hub service or reusable daemonized control plane | | Live agent session registry | `Landed` | `AgentRuntimeStateStore` persists authoritative parent sessions and participant records under `~/.substrate/run/agent-hub/sessions//session.json` plus per-session `participants/`, `leases/`, and durable `inbox/` artifacts; host sessions now persist explicit posture/attachment truth instead of inferring validity from attached-control diagnostics alone | Only a future product-surface rename to something like `agent-sessions` remains undecided; there is no current runtime gap here | | Agent session control plane | `Landed for narrow public control, Linux-source with supported macOS/Lima world-sensitive parity` | Internal runtime paths now support attached-control `start`, public `reattach` / `fork`, prompt-taking `start` / `turn`, authoritative persistence, clean host detach parking into `parked_resumable`, pending-inbox escalation into `awaiting_attention`, exact-session `reattach` recovery back to durable attached truth, `stop`/cancel as the canonical closeout path for attached and parked durable sessions, replacement-member creation across world-generation rollover, the hidden owner-helper launch seam, the same private stop-owner plane for helper-owned and REPL-owned sessions, and explicit post-`Accepted` terminal envelopes | Remaining gaps are broader surface breadth and parity: no public world-root start, no member-level public selectors beyond exact session/backend targeting, and Windows/WSL world-sensitive control posture still fails closed | @@ -116,9 +116,9 @@ Terminology rule for this repository: | Session handles | `Partial` | The runtime extracts and persists surfaced UAA session ids (`internal.uaa_session_id`) and correlates them with orchestration/participant metadata; parent sessions also track `active_session_handle_id` | The Substrate registry still mixes local participant ids, orchestration ids, and optional surfaced UAA ids rather than exposing a clean public session-handle contract | | Host orchestrator process management | `Landed` | A real host-scoped orchestrator backend is launched via UAA, tracked through persisted lifecycle states, and stopped authoritatively on teardown | No major thin-slice blocker remains here | | Explicit backend/member targeting | `Landed for the exact REPL and narrow public caller surfaces` | Effective inventory entries derive canonical `backend_id` values, targeted REPL follow-up turns route by exact `backend_id`, public `turn` requires exact `(orchestration_session_id, backend_id)` routing, host follow-up targeting fails closed unless it names the active orchestrator backend, Linux world reuse/relaunch is keyed by exact `backend_id` rather than `agent_id`, distinct retained members such as `cli:codex` and `cli:claude_code` can coexist in one orchestration session / world generation, and duplicate retained members for the same `backend_id` still fail closed | Remaining work is broader productization rather than runtime correctness: there is still no default-agent routing, and policy/default-selection decisions outside these exact caller paths are still open | -| Explicit shared-world ownership | `Landed on Linux and supported macOS/Lima; Linux remains source-of-truth` | Persistent world startup now sends `SharedWorldOwnerSpec`, world-agent echoes `SharedWorldBindingSnapshot`, session metadata persists `orchestration_session_id` / `world_generation`, shared-world replacement rolls back correctly on failure, and the shell invalidates stale world members after rollover on Linux plus the supported macOS/Lima forwarded path | Remaining work is broader platform scope and operator maturity, not missing shared-owner proof on the supported macOS/Lima path. Windows/WSL still does not implement this contract | +| Explicit shared-world ownership | `Landed on Linux and supported macOS/Lima; Linux remains source-of-truth` | Persistent world startup now sends `SharedWorldOwnerSpec`, world-service echoes `SharedWorldBindingSnapshot`, session metadata persists `orchestration_session_id` / `world_generation`, shared-world replacement rolls back correctly on failure, and the shell invalidates stale world members after rollover on Linux plus the supported macOS/Lima forwarded path | Remaining work is broader platform scope and operator maturity, not missing shared-owner proof on the supported macOS/Lima path. Windows/WSL still does not implement this contract | | Shared-world replacement ordering and world metadata durability | `Landed` | Linux replacement already uses a two-phase `Active -> Replacing -> Replaced` flow with rollback on creation failure, and world `session.json` persistence is atomic-by-rename with failure-preserving behavior | No current correctness gap was confirmed here; only future hardening beyond the current contract would remain | -| In-world member dispatch over existing host↔world transport | `Landed on Linux and supported macOS/Lima` | The shell now emits typed `member_dispatch` requests over `/v1/execute/stream`, `world-agent` validates the authoritative shared-world binding and launches the member UAA runtime inside the active world, `/v1/execute/cancel` reaches retained in-world control ownership, and regression tests cover lazy launch, reuse, replacement, and fail-closed startup behavior on Linux plus the supported macOS/Lima path | Remaining work is broader platform scope and follow-on hardening, not missing member-dispatch transport on the supported macOS/Lima path | +| In-world member dispatch over existing host↔world transport | `Landed on Linux and supported macOS/Lima` | The shell now emits typed `member_dispatch` requests over `/v1/execute/stream`, `world-service` validates the authoritative shared-world binding and launches the member UAA runtime inside the active world, `/v1/execute/cancel` reaches retained in-world control ownership, and regression tests cover lazy launch, reuse, replacement, and fail-closed startup behavior on Linux plus the supported macOS/Lima path | Remaining work is broader platform scope and follow-on hardening, not missing member-dispatch transport on the supported macOS/Lima path | | macOS/Lima parity for agent orchestration | `Landed in the supported Lima-backed posture` | `world-mac-lima` now forwards the same shared-owner/member-runtime contract as Linux for the supported backend-detected path: `SharedWorldOwnerSpec` reaches the guest, `SharedWorldBindingSnapshot` returns through `WorldHandle.shared_binding`, `member_dispatch` and typed `/v1/member_turn/stream` follow-up are preserved, and cancel remains guest-owned through `/v1/execute/cancel` | Remaining work is outside this slice: Windows/WSL parity, broader public caller-surface breadth, and any future live-guest coverage beyond the documented macOS/Lima smoke plus regression suite | | Replacement-member semantics across world-generation rollover | `Landed` | Restart/drift handling invalidates stale members, advances `world_generation`, creates a distinct replacement participant with preserved lineage, and relaunches the replacement member through the in-world transport path on Linux | Remaining work is mainly parity and operator-surface breadth rather than replacement correctness | | Agent event schema and trace flattening | `Landed` | Tuple-aware `AgentEvent` schema is live, runtime events are emitted from orchestrator/member lifecycle code, and status surfaces validate world identity + nested parent correlation | Remaining work is narrower read-side cleanup and producer breadth, not missing support for the now-landed in-world member launch path | @@ -128,7 +128,7 @@ Terminology rule for this repository: | Gateway lifecycle | `Landed for nested LLM runtime` | `status|sync|restart` are live end-to-end for the nested gateway lifecycle | This should still not be conflated with pure-agent session orchestration | | Status ambiguity handling | `Partially landed` | `substrate agent status` now keeps valid rows visible while surfacing degraded warnings for torn parent/session state such as missing `active_session_handle_id`, missing parent-session metadata, and incomplete selected participants; durable parked and attention-needed host sessions remain visible on the read surface instead of collapsing into terminal absence | Strict selectors still fail closed by design on the control-plane surfaces (`toolbox status`, `toolbox env`, doctor/member selection), and status still needs a clearer operator-facing ambiguity policy for participant-less trace-only rows | | Trace-only participant-aware fallback | `Partially landed` | Trace rows that carry `participant_id` now stay sibling-distinct, sibling-specific suppression is keyed by `participant_id`, and nested correlation honors `parent_participant_id` when same-agent siblings coexist in one `orchestration_session_id` | Participant-less trace rows still warn and fall back to coarse matching, so the fallback is not yet fully participant-aware unless the trace carries `participant_id` / `parent_participant_id` | -| Secret handoff into the world gateway | `Landed` | Host-side policy/auth selection still owns auth-source precedence, `world-agent` now delivers a read-once `GatewayAuthBundleV1` over inherited FD via `SUBSTRATE_LLM_AUTH_BUNDLE_FD`, and `gateway` integrated startup overlays that bundle in memory before provider construction | Remaining work is follow-on hardening and broader parity, not the default carrier: integrated delivery no longer depends on secret-bearing child env vars | +| Secret handoff into the world gateway | `Landed` | Host-side policy/auth selection still owns auth-source precedence, `world-service` now delivers a read-once `GatewayAuthBundleV1` over inherited FD via `SUBSTRATE_LLM_AUTH_BUNDLE_FD`, and `gateway` integrated startup overlays that bundle in memory before provider construction | Remaining work is follow-on hardening and broader parity, not the default carrier: integrated delivery no longer depends on secret-bearing child env vars | | Toolbox surface | `Partial` | Config, `toolbox status`, `toolbox env`, live-session endpoint derivation, and world-binding projection are present | The internal MCP server, mutation tools, and auth/audit plumbing are still unimplemented | | Toolbox role in orchestration | `Constrained by design` | ADR-0045 is introspection-only in [ADR-0045](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0045-orchestration-toolbox-internal-mcp-identity-trace-contract.md:77) | Nothing here should be treated as a substitute for member launch or control-plane execution | | Custom Substrate harness | `Deferred by intent` | Product intent allows for a future Substrate-native harness layer | Do not block v1 on this; revisit only after CLI-agent orchestration through UAA is working cleanly | @@ -146,15 +146,15 @@ These are the decisions that still need to be made to keep the path forward clea - The REPL now has an explicit targeted-turn contract: `:: ` on a single line, with no default-agent routing and with plain REPL input still reserved for shell execution. - The remaining decision is how that explicit caller contract should extend beyond the REPL, because `substrate -c` still means shell wrap mode today and any non-interactive agent caller must be introduced deliberately rather than implied. -3. Whether `uaa.agent.session` should remain the Substrate-local normalized protocol-family label, or be renamed now that real UAA runtime integration is live. +3. Whether `substrate.agent.session` should remain the Substrate-local normalized protocol-family label, or be renamed now that real UAA runtime integration is live. - The recent runtime landing increases the cost of leaving this ambiguous, because the repo now contains both real external `agent_api` usage and local `uaa.*` labeling. - The discussion above did not resolve this; it only reinforces that the label is still local and still easy to misread as an upstream protocol claim. 4. How aggressively to deconflict the local `agent-api-*` crate names from external `agent_api` before more runtime code accumulates on both sides of that boundary. -- This is closely related to the `uaa.agent.session` decision above, but it is a crate/module/package naming question rather than a protocol/identity-label question. +- This is closely related to the `substrate.agent.session` decision above, but it is a crate/module/package naming question rather than a protocol/identity-label question. - The recent runtime landing makes this more pressing from a governance/clarity standpoint, even though it is not a direct runtime-correctness blocker. -5. Whether to stop at the newly landed hidden shell↔world-agent helper over the existing execute/stream transport, or continue on to a broader reusable world-owned UAA service surface. +5. Whether to stop at the newly landed hidden shell↔world-service helper over the existing execute/stream transport, or continue on to a broader reusable world-owned UAA service surface. - The current repo state has now landed the thinner option first: host orchestrator on the shell, world member launched through the existing world transport seam, without building a broad new hub service up front. - The remaining question is product breadth and reuse, not whether member placement can work through the existing transport seam. diff --git a/CLAUDE.md b/CLAUDE.md index 12febe02e..2abe38f42 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # GitNexus — Code Intelligence -This project is indexed by GitNexus as **substrate** (49012 symbols, 75513 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **substrate** (49108 symbols, 75625 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. diff --git a/Cargo.lock b/Cargo.lock index 7c5cfbd3d..0659f5263 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,52 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "agent-api-client" -version = "0.2.8" -dependencies = [ - "agent-api-types", - "anyhow", - "async-trait", - "dirs 5.0.1", - "hex", - "http-body-util", - "hyper 1.9.0", - "hyper-util", - "serde", - "serde_json", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "agent-api-core" -version = "0.2.8" -dependencies = [ - "agent-api-types", - "async-trait", - "axum 0.7.9", - "http 1.4.0", - "serde", - "serde_json", - "thiserror 1.0.69", - "tower 0.5.3", -] - -[[package]] -name = "agent-api-types" -version = "0.2.8" -dependencies = [ - "serde", - "serde_json", - "substrate-common", - "thiserror 1.0.69", - "world-api", -] - [[package]] name = "ahash" version = "0.8.12" @@ -2598,9 +2552,6 @@ dependencies = [ name = "host-proxy" version = "0.2.8" dependencies = [ - "agent-api-client", - "agent-api-core", - "agent-api-types", "anyhow", "async-trait", "axum 0.7.9", @@ -2620,6 +2571,9 @@ dependencies = [ "tower-http 0.5.2", "tracing", "tracing-subscriber", + "transport-api-client", + "transport-api-core", + "transport-api-types", "uuid", ] @@ -5142,8 +5096,6 @@ dependencies = [ name = "shell" version = "0.2.8" dependencies = [ - "agent-api-client", - "agent-api-types", "anyhow", "assert_cmd", "base64 0.22.1", @@ -5189,6 +5141,8 @@ dependencies = [ "tokio-tungstenite 0.21.0", "toml", "tracing", + "transport-api-client", + "transport-api-types", "unified-agent-api", "url", "uuid", @@ -5516,8 +5470,6 @@ dependencies = [ name = "substrate-replay" version = "0.1.0" dependencies = [ - "agent-api-client", - "agent-api-types", "anyhow", "base64 0.22.1", "chrono", @@ -5535,6 +5487,8 @@ dependencies = [ "tempfile", "tokio", "tracing", + "transport-api-client", + "transport-api-types", "uuid", "world", "world-api", @@ -6197,6 +6151,52 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "transport-api-client" +version = "0.2.8" +dependencies = [ + "anyhow", + "async-trait", + "dirs 5.0.1", + "hex", + "http-body-util", + "hyper 1.9.0", + "hyper-util", + "serde", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tower-service", + "tracing", + "transport-api-types", +] + +[[package]] +name = "transport-api-core" +version = "0.2.8" +dependencies = [ + "async-trait", + "axum 0.7.9", + "http 1.4.0", + "serde", + "serde_json", + "thiserror 1.0.69", + "tower 0.5.3", + "transport-api-types", +] + +[[package]] +name = "transport-api-types" +version = "0.2.8" +dependencies = [ + "serde", + "serde_json", + "substrate-common", + "thiserror 1.0.69", + "world-api", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -7255,42 +7255,6 @@ dependencies = [ "world-api", ] -[[package]] -name = "world-agent" -version = "0.2.8" -dependencies = [ - "agent-api-core", - "agent-api-types", - "anyhow", - "axum 0.6.20", - "base64 0.22.1", - "chrono", - "futures-util", - "hyper 0.14.32", - "libc", - "once_cell", - "portable-pty 0.8.1", - "rand 0.8.6", - "serde", - "serde_json", - "sha2", - "substrate-broker", - "substrate-common", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.21.0", - "tokio-util", - "tower 0.4.13", - "tracing", - "tracing-subscriber", - "unified-agent-api", - "uuid", - "world", - "world-api", -] - [[package]] name = "world-api" version = "0.1.1" @@ -7318,8 +7282,6 @@ dependencies = [ name = "world-mac-lima" version = "0.1.1" dependencies = [ - "agent-api-client", - "agent-api-types", "anyhow", "base64 0.22.1", "dirs 6.0.0", @@ -7330,24 +7292,62 @@ dependencies = [ "thiserror 1.0.69", "tokio", "tracing", + "transport-api-client", + "transport-api-types", "ureq", "uuid", "which 4.4.2", "world-api", ] +[[package]] +name = "world-service" +version = "0.2.8" +dependencies = [ + "anyhow", + "axum 0.6.20", + "base64 0.22.1", + "chrono", + "futures-util", + "hyper 0.14.32", + "libc", + "once_cell", + "portable-pty 0.8.1", + "rand 0.8.6", + "serde", + "serde_json", + "sha2", + "substrate-broker", + "substrate-common", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "tokio-tungstenite 0.21.0", + "tokio-util", + "tower 0.4.13", + "tracing", + "tracing-subscriber", + "transport-api-core", + "transport-api-types", + "unified-agent-api", + "uuid", + "world", + "world-api", +] + [[package]] name = "world-windows-wsl" version = "0.1.0" dependencies = [ - "agent-api-client", - "agent-api-types", "anyhow", "base64 0.22.1", "serde_json", "substrate-common", "tokio", "tracing", + "transport-api-client", + "transport-api-types", "uuid", "world-api", ] diff --git a/Cargo.toml b/Cargo.toml index e323a0773..bc029af4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,11 +50,11 @@ members = [ "crates/world", "crates/world-mac-lima", "crates/world-backend-factory", - "crates/world-agent", + "crates/world-service", # Agent API (shared contract + proxy) - "crates/agent-api-types", - "crates/agent-api-core", - "crates/agent-api-client", + "crates/transport-api-types", + "crates/transport-api-core", + "crates/transport-api-client", "crates/host-proxy", "crates/broker", "crates/trace", # "crates/trace", # TODO: Implement after broker diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index 9729508d6..6842f815e 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,650 +1,959 @@ -# ORCH_PLAN: Async Persistent-Session Bootstrap Readiness Split +# ORCH_PLAN: UAA Boundary And Naming Cleanup Execution Controller Authoritative plan source: [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) -Primary protocol doc: [persistent_session.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/internals/repl/persistent_session.md) -World backend doc: [WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) -Stale note: the existing durable-session orchestration content is out of scope for this run and is replaced by this controller. +Source SOW: [27-uaa-boundary-and-naming-cleanup.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/llm-last-mile/27-uaa-boundary-and-naming-cleanup.md) +Style reference: [ORCH_PLAN-25.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/llm-last-mile/ORCH_PLAN-25.md) +Current workspace branch: `chore/uaa-boundary-and-naming-cleanup` +Execution type: direct-cutover naming correction, parent-frozen contract, one serialized foundation code lane, two follow-on parallel lanes, one parent-only validation wall Live repo root: `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate` -Fresh worktree root: `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split` -Baseline branch: `feat/host-orchestrator-durable-session` -Run id: `slice-26-async-persistent-session-bootstrap-readiness` -Max concurrent workers: `2` -Parent role: only integrator, only gate authority, only writer of `.runs` artifacts +Fresh worktree root: `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup` +Run id: `plan-uaa-boundary-and-naming-cleanup` +Worker model: `GPT-5.4` with `reasoning_effort=high` +Initial concurrent worker cap: `1` +Peak concurrent worker cap: `2` +Parent role: sole integrator, sole approval authority, sole writer of `.runs/**` artifacts ## Summary -This controller operationalizes [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) for the async persistent-session bootstrap readiness split. +This controller executes [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md). It is not a restatement of the plan. -Execution shape is fixed: +The run shape is frozen: -1. Parent source-locks the plan and creates a clean integration branch from the current slice branch. -2. Worker A lands `A1`, which freezes the shell-facing async readiness contract without widening sync surfaces. -3. Workers B and C run in the only parallel window, on disjoint backend crates. -4. Worker A returns for `A2` to wire the shell caller migration and sync-regression proof against the merged backend seams. -5. Worker D updates docs late. -6. Parent runs the full validation wall and decides final acceptance. +1. Parent freezes the rename contract, the live-surface grep wall, the historical allowlist, and the worker ownership map. +2. One foundation code lane lands the whole shared-hotspot cutover in order: + - `uaa.agent.session` -> `substrate.agent.session` + - `world-agent*` -> `world-service*` + - `agent-api*` -> `transport-api*` +3. Only after the foundation lane is accepted does the parent open the parallel window: + - scripts, CI, release, and operator helper lane + - docs, ADR, and repo-truth lane +4. Parent integrates the parallel lanes, runs the full validation wall from [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md), and decides final acceptance. -This preserves the slice contract: +This run is honest only if the merged tree proves all of the following together: -1. no async `WorldBackend` rewrite -2. sync `ensure_ready` preserved for sync callers -3. macOS async persistent-session startup stops using the sync bridge -4. socket override bypass remains exact -5. backend-owned readiness stays backend-owned -6. WSL work is internal parity only -7. tests, docs, and validation are mandatory -8. concurrency stays honest +1. upstream `agent_api`, adopted `agent_api.*`, `uaa_session_id`, and unchanged `world-api` remain intact, +2. `substrate.agent.session` is the only canonical supported local protocol-family label on live surfaces, +3. the local daemon family is `world-service*` everywhere live, +4. the local typed host↔world contract family is `transport-api*` everywhere live, +5. world-required routing still fails closed if renamed service discovery breaks, +6. the live-surface grep wall is green outside the explicit historical allowlist, +7. code, scripts, CI, release bundles, docs, ADRs, fixtures, tests, and operator guidance tell one coherent boundary story. ## Hard Guards -1. `WorldBackend` remains synchronous for this slice. -2. `PlatformWorldContext.ensure_ready` remains available and unchanged for sync callers. -3. The macOS persistent-session async startup path must stop calling the sync bridge. -4. `SUBSTRATE_WORLD_SOCKET` override behavior must remain exact, including bypass semantics. -5. Shell code may dispatch to backend-owned readiness. It may not duplicate VM, forwarding, client, or capability logic. -6. Linux behavior stays structurally unchanged unless a tiny compile-only adapter is needed. -7. WSL hardening is internal-only and must not create a shipped Windows persistent-session shell caller. -8. Readiness failures stay fail closed and return normal errors, never a Tokio panic and never best-effort degraded startup. -9. The `A1` shell contract must preserve a concrete backend-owned async-ready access path. It is not enough to add a free shell helper if `PlatformWorldContext` still erases the backend behind `Arc` with no async-ready access surface. -10. The no-panic regression must exercise a no-override path that previously crossed `ctx.ensure_ready()`. `SUBSTRATE_WORLD_SOCKET` override tests remain required, but they are not valid evidence for the original panic fix. -11. Docs ship only after merged behavior is stable. -12. Workers do not edit [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md), [ORCH_PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/ORCH_PLAN.md), or `.runs/**`. -13. Every symbol edit requires prior GitNexus impact analysis. -14. Any `HIGH` or `CRITICAL` GitNexus impact result is a parent-only escalation point. -15. Every worker handoff must include `gitnexus_detect_changes()` status before the parent considers merge. -16. Parent runs a final `gitnexus_detect_changes()` on the merged branch before closeout. - -## Worktree Creation Order And Commands - -Parent creates worktrees in this exact order. - -### 1. Create parent integration worktree +These are run-stopping invariants. -```bash -mkdir -p /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split +1. This slice is a naming correction and boundary cleanup only. It is not a runtime redesign, not a public contract redesign, and not a compatibility-migration project. +2. Upstream `agent_api` and adopted `agent_api.*` ids remain untouched. +3. `world-api` remains untouched. +4. `uaa_session_id` and `internal.uaa_session_id` remain untouched. +5. `substrate.agent.session` is the only canonical supported local protocol-family label after cutover. +6. Old supported live names do not remain as aliases on canonical live surfaces just to avoid repo work. +7. Stale `uaa.agent.session` configs, fixtures, or persisted runtime rows fail closed with explicit operator-readable errors after cutover. +8. World-required routing must still hard-fail if renamed binary, socket, or unit discovery breaks. No rename drift may silently fall back to host execution. +9. No parallel worker may touch shared foundation hotspots before the foundation lane is accepted: + - [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) + - `dist-workspace.toml` + - `crates/shell/**` + - mixed-boundary imports that mention upstream `agent_api` plus local transport or service crates + - local protocol validation and persistence seams +10. Parent is the only integrator, the only gate authority, and the only writer of `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.runs/plan-uaa-boundary-and-naming-cleanup/**`. +11. Workers do not edit [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md), [ORCH_PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/ORCH_PLAN.md), or `.runs/**`. +12. Every symbol edit requires prior GitNexus impact analysis. Any `HIGH` or `CRITICAL` result is a parent-only escalation point. +13. Every worker handoff must include `gitnexus_detect_changes()` status before the parent considers merge. +14. Parent runs a final `gitnexus_detect_changes()` on the merged tree before closeout. -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate fetch origin +Stop the run and write `blocked.json` if any of these become true: -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/parent \ - -b codex/slice-26-async-persistent-session-readiness \ - feat/host-orchestrator-durable-session -``` +1. The foundation cutover needs to be split across multiple concurrent code owners to make progress. +2. The rename cannot be landed without changing public selector semantics, `world-api`, upstream `agent_api.*`, or `uaa_session_id`. +3. The only path to green is a long-lived compatibility alias layer for old live names. +4. Fail-closed routing cannot be proven after the `world-service` rename. +5. Parallel lanes need to reopen shared manifests, shell runtime seams, or rename decisions that should have been frozen by the foundation lane. +6. Docs or ADRs can only be made truthful by contradicting the merged code and scripts tree. +7. The final validation wall cannot prove grep, cargo, operator, release, and fail-closed checks on the same merged tree. -### 2. Create Worker A worktree for `A1` +## Fresh Worktrees / Branches / Worker Model / Concurrency Cap -```bash -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane \ - -b codex/slice-26-shell-readiness-seam \ - codex/slice-26-async-persistent-session-readiness -``` +Fresh worktree root: -### 3. After `G1`, create Worker B and Worker C worktrees +- `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup` -```bash -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/mac-lima-lane \ - -b codex/slice-26-mac-lima-readiness-split \ - codex/slice-26-async-persistent-session-readiness +Authoritative integration checkout: -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/wsl-lane \ - -b codex/slice-26-wsl-readiness-parity \ - codex/slice-26-async-persistent-session-readiness -``` +- `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate` +- branch: `chore/uaa-boundary-and-naming-cleanup` -### 4. After `G2`, refresh Worker A for `A2` +Worker worktrees: -If the original Worker A worktree is still clean and easy to reuse, rebase it on the parent branch. Otherwise, remove and recreate it. +- `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/foundation-code` +- `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/scripts-release` +- `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/docs-adrs` -```bash -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane fetch origin +Worker branches: -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane rebase \ - codex/slice-26-async-persistent-session-readiness -``` +- `codex/chore-uaa-boundary-and-naming-cleanup-foundation` +- `codex/chore-uaa-boundary-and-naming-cleanup-scripts-release` +- `codex/chore-uaa-boundary-and-naming-cleanup-docs-adrs` -If recreate is cleaner: +Exact setup order: ```bash -git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree remove \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane +mkdir -p /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup + +git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate fetch origin git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane \ - -B codex/slice-26-shell-readiness-seam \ - codex/slice-26-async-persistent-session-readiness + /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/foundation-code \ + -b codex/chore-uaa-boundary-and-naming-cleanup-foundation \ + chore/uaa-boundary-and-naming-cleanup ``` -### 5. After `G3`, create Worker D worktree +Do not create the parallel worktrees until `G1` is accepted. ```bash git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ - /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/docs-lane \ - -b codex/slice-26-docs-readiness-split \ - codex/slice-26-async-persistent-session-readiness + /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/scripts-release \ + -b codex/chore-uaa-boundary-and-naming-cleanup-scripts-release \ + chore/uaa-boundary-and-naming-cleanup + +git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate worktree add \ + /Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-uaa-boundary-cleanup/docs-adrs \ + -b codex/chore-uaa-boundary-and-naming-cleanup-docs-adrs \ + chore/uaa-boundary-and-naming-cleanup ``` -## Parent-Owned Run-State Surface / Artifact Ledger +Concurrency contract: + +1. `P0` and `A1` / `A2` are serialized. +2. `B1` and `C1` are the only honest parallel window. +3. Peak concurrency is `2`, not `3`, because there is one shared foundation hotspot lane and two isolated follow-on lanes. + +## Parent-Owned Run-State Surface And Required Artifacts Canonical run root: -- `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.runs/slice-26-async-persistent-session-bootstrap-readiness/` - -Required directory tree: - -```text -.runs/slice-26-async-persistent-session-bootstrap-readiness/ - run-state.json - source-lock.json - branch-map.json - ownership-boundaries.json - task-ledger.json - merge-log.md - final-summary.md - blocked.json # only if run blocks - sentinels/ - RUN_OPEN - RUN_BLOCKED # only if blocked - RUN_COMPLETE # only if complete - gates/ - G0-source-lock/ - gate.json - evidence.md - OPEN - G1-shell-contract-freeze/ - gate.json - evidence.md - OPEN - REOPENED # touched only if contract drift forces reset - G2-backend-parallel-window/ - gate.json - evidence.md - OPEN - G3-shell-migration/ - gate.json - evidence.md - OPEN - G4-docs-closeout/ - gate.json - evidence.md - OPEN - G5-final-acceptance/ - gate.json - evidence.md - OPEN - tasks/ - P0-source-lock/ - P1-parent-branch-init/ - A1-shell-contract-freeze/ - G1-shell-contract-accept/ - B1-mac-lima-readiness-split/ - C1-wsl-parity-hardening/ - G2-backend-accept/ - A2-shell-migration-and-sync-regressions/ - G3-shell-accept/ - D1-docs-closeout/ - G4-docs-accept/ - P2-final-validation-wall/ - P3-final-gitnexus-closeout/ - P4-final-summary/ -``` +- `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.runs/plan-uaa-boundary-and-naming-cleanup/` + +Required parent-owned top-level artifacts: + +- `run-state.json` +- `source-lock.json` +- `contract-freeze.json` +- `branch-map.json` +- `lane-ownership.json` +- `merge-order.json` +- `validation-wall.md` +- `session-log.md` +- `final-summary.md` +- `blocked.json` on blocked runs only +- `sentinels/` +- `tasks/` +- `gates/` + +Required sentinels: + +- `sentinels/RUN_OPEN` +- `sentinels/RUN_BLOCKED` on blocked runs only +- `sentinels/RUN_COMPLETE` on successful closeout only + +Frozen gate directories: + +- `gates/G0-parent-contract-freeze/` +- `gates/G1-foundation-accept-and-parallel-launch/` +- `gates/G2-parallel-window-integration/` +- `gates/G3-validation-launch/` +- `gates/G4-final-acceptance/` + +Every gate directory must contain: + +- `gate.json` +- `evidence.md` +- one sentinel exactly one of: + - `OPEN` + - `PASSED` + - `FAILED` + - `REOPENED` + +Task map: + +- `tasks/P0-parent-contract-freeze-and-run-init/` +- `tasks/A1-protocol-label-cutover/` +- `tasks/A2-family-rename-foundation-cutover/` +- `tasks/G1-foundation-accept-and-parallel-launch/` +- `tasks/B1-scripts-ci-release-cutover/` +- `tasks/C1-docs-adr-truth-convergence/` +- `tasks/G2-parallel-window-integration/` +- `tasks/P1-parent-parallel-integration/` +- `tasks/G3-validation-launch/` +- `tasks/P2-parent-validation-wall/` +- `tasks/P3-parent-closeout/` + +Every task directory must contain: + +- `task.json` +- `owner.txt` +- `status.txt` +- `scope.txt` +- `deliverable.txt` +- `dependencies.json` +- `changed-files.txt` +- `commands.txt` +- `exit-codes.json` +- `impact-analysis-summary.md` +- `gitnexus-detect-changes.txt` +- `handoff-notes.md` +- `summary.md` +- `HEAD_SHA.txt` +- `blocker-notes.md` if blocked +- one sentinel exactly one of: + - `READY_FOR_REVIEW` + - `ACCEPTED` + - `REJECTED` + - `BLOCKED` + +Worker artifact rule: + +1. Workers do not write `.runs/**`. +2. Workers return handoff content to the parent. +3. Parent transcribes the handoff into `.runs/**`, including changed files, commands, exit codes, impact notes, detect-changes output, acceptance notes, and blocker notes. +4. Parent creates or replaces the task sentinel after transcription. Workers never create task sentinels. +5. No task is complete until the parent writes the artifacts and marks the task `ACCEPTED`. + +`contract-freeze.json` must record at minimum: + +1. `authoritative_branch: "chore/uaa-boundary-and-naming-cleanup"` +2. locked rename families: + - `uaa.agent.session -> substrate.agent.session` + - `world-agent* -> world-service*` + - `agent-api* -> transport-api*` +3. preserved names: + - upstream `agent_api` + - upstream `agent_api.*` + - `uaa_session_id` + - unchanged `world-api` +4. the fail-closed rule for renamed service discovery and stale protocol labels +5. the live rename boundary +6. the historical allowlist +7. the exact validation wall commands +8. the initial worker cap of `1` and peak worker cap of `2` + +## GitNexus Workflow And Required Impact Targets + +GitNexus is a required run control, not a best-effort check. + +### Source-Lock Stage + +1. Parent checks GitNexus availability and index freshness before any worker edits. +2. If the index is stale, parent runs `npx gitnexus analyze` from the authoritative checkout before launching `A1`. +3. Parent records the freshness result in `tasks/P0-parent-contract-freeze-and-run-init/impact-analysis-summary.md`. + +### Minimum Required Targets By Task + +`A1` protocol-label cutover + +1. Concrete symbol targets if present: + - `LOCAL_AGENT_PROTOCOL_FAMILY` + - the validator entrypoint that accepts or rejects local protocol labels in `crates/shell/src/execution/agent_runtime/validator.rs` + - the trace-emission seam in `crates/common/src/agent_events.rs` +2. File-level seam targets that must be analyzed if symbol names drift: + - `crates/shell/src/execution/agent_runtime/mapping.rs` + - `crates/shell/src/execution/agent_runtime/validator.rs` + - `crates/shell/src/execution/agent_runtime/session.rs` + - `crates/shell/src/execution/agent_runtime/orchestration_session.rs` + - `crates/shell/src/execution/agent_runtime/state_store.rs` + - `crates/shell/src/execution/routing/dispatch/world_ops.rs` + +`A2` family-rename foundation cutover + +1. Concrete symbol targets if present: + - the Linux world-routing or socket-activation resolution path that locates the in-world daemon + - the `world-agent` binary entrypoint crate target being renamed to `world-service` +2. File-level seam targets that must be analyzed if symbol names drift: + - [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) + - `crates/shell/src/repl/async_repl.rs` + - `crates/shell/src/execution/socket_activation.rs` + - `crates/shell/src/execution/platform/linux.rs` + - `crates/shell/src/execution/routing/world.rs` + - `crates/world-agent/**` or renamed equivalents + - `crates/agent-api-types/**`, `crates/agent-api-core/**`, and `crates/agent-api-client/**` or renamed equivalents + +`B1` scripts / CI / release cutover + +1. File-level seam targets: + - `scripts/linux/world-provision.sh` + - `scripts/substrate/install-substrate.sh` + - `scripts/substrate/dev-install-substrate.sh` + - `scripts/substrate/uninstall-substrate.sh` + - `scripts/mac/lima-warm.sh` + - `scripts/mac/smoke.sh` + - `scripts/windows/wsl-warm.ps1` + - `scripts/windows/wsl-smoke.ps1` + - `scripts/windows/wsl-doctor.ps1` + - `dist/scripts/assemble-release-bundles.sh` + - `.github/workflows/feature-smoke.yml` + - `.github/workflows/nightly.yml` +2. If GitNexus has script or workflow indexing for callable entrypoints in these files, analyze those entrypoints before editing; otherwise the file-level seam analysis is sufficient and must be stated explicitly in the handoff. + +`C1` docs / ADR / truth convergence + +1. File-level seam targets: + - `AGENT_ORCHESTRATION_GAP_MATRIX.md` + - `docs/WORLD.md` + - `docs/TRACE.md` + - `docs/CONFIGURATION.md` + - `docs/INSTALLATION.md` + - `docs/UNINSTALL.md` + - `docs/USAGE.md` + - `README.md` + - `AGENTS.md` + - relevant ADRs under `docs/project_management/adrs/**` +2. GitNexus impact is docs-truth only here. If no symbol-level targets are relevant, the worker must state that the lane is file-level and docs-only. + +### Escalation Rule + +1. Any `HIGH` or `CRITICAL` impact result stops that worker before edits. +2. The worker returns a blocker handoff instead of proceeding. +3. Parent records the blocker, decides whether the run still fits the frozen contract, and either relaunches with a narrower brief or blocks the run. + +## Workstream Plan With Parent-Owned Gates And Worker-Owned Lanes + +### Workstream Map + +| PLAN.md workstream | Orchestration tasks | Ownership | +| --- | --- | --- | +| Freeze vocabulary, rename matrix, grep wall, historical allowlist | `P0`, `G0` | Parent only | +| Cut over `substrate.agent.session` on runtime, validation, trace, config, and persistence surfaces | `A1` | Foundation lane | +| Rename `world-agent*` and `agent-api*` families on code and manifests; keep mixed-boundary imports clear | `A2` | Foundation lane | +| Sweep scripts, CI, release bundles, install/warm/smoke/uninstall, and operator helper text | `B1` | Scripts/release lane | +| Sweep docs, ADRs, repo truth docs, examples, operator guidance, and matrix language | `C1` | Docs/ADR lane | +| Integrate, validate, and close out | `G2`, `P1`, `G3`, `P2`, `G4`, `P3` | Parent only | + +### Parent-Owned Gates + +`G0`: Contract freeze + +1. Parent locks the rename matrix, preserved-name list, live boundary, historical allowlist, validation wall, and branch map. +2. Parent confirms that no worker will need to guess names, scope, or final grep commands. -Required per-task artifact files under each `tasks//` directory: +`G1`: Foundation acceptance and parallel launch -1. `task.json` -2. `owner.txt` -3. `status.txt` -4. `dependency-status.json` -5. `scope.txt` -6. `deliverable.txt` -7. `acceptance-notes.md` -8. `changed-files.txt` -9. `commands.txt` -10. `exit-codes.json` -11. `impact-analysis-summary.md` -12. `gitnexus-detect-changes.txt` -13. `handoff-notes.md` -14. `blocker-notes.md` if blocked -15. `HEAD_SHA.txt` -16. sentinel file exactly one of: - - `READY_FOR_REVIEW` - - `ACCEPTED` - - `REJECTED` - - `BLOCKED` +1. `A1` and `A2` are both merged or otherwise accepted into the authoritative branch. +2. Parent confirms that shared hotspots are stable: + - manifests + - `crates/shell/**` + - local protocol validation and persistence seams + - mixed-boundary imports +3. Parent confirms the new canonical names are real enough for downstream scripts and docs work. +4. Only after `G1` is accepted may `B1` and `C1` start. + +`G2`: Parallel-window integration + +1. Parent receives both worker handoffs. +2. Parent verifies each lane stayed inside its ownership boundary. +3. Parent merges accepted outputs into `chore/uaa-boundary-and-naming-cleanup`. +4. Parent quarantines any lane that reopens foundation hotspots without approval. + +`G3`: Validation launch + +1. Parent confirms the merged tree is the validation candidate. +2. Parent confirms no pending naming disputes remain. +3. Parent freezes the validation command list from the plan and starts `P2`. + +`G4`: Final acceptance -Rules: +1. Grep wall is green outside the historical allowlist. +2. Cargo wall is green. +3. Operator surface gates are green. +4. Release gates are green. +5. Fail-closed checks are green. +6. Parent runs final GitNexus scope verification and writes closeout. + +### Worker-Owned Lanes -1. Workers do not write these artifacts directly. -2. Workers return the content in their handoff. -3. Parent transcribes the handoff into `.runs/**`. -4. No task is considered complete until the parent has written the task artifacts and touched the correct sentinel. +`A` Foundation code lane: serialized under one owner -## GitNexus Workflow +1. `A1` protocol-label cutover +2. `A2` family rename cutover -GitNexus is an active part of the run, not a passive guard. +`B` Scripts / CI / release lane: launch only after `G1` -### Source-lock stage +1. install and provision scripts +2. dev-install and uninstall scripts +3. Lima and WSL warm/smoke/doctor helpers +4. release bundle assembly +5. CI workflow package invocations and smoke checks +6. operator helper, remediation, and script output text in those surfaces -1. Parent checks GitNexus availability and index freshness. -2. If stale, parent runs `npx gitnexus analyze` from the parent worktree before any worker starts. -3. Parent records GitNexus freshness status in `tasks/P0-source-lock/impact-analysis-summary.md`. +`C` Docs / ADR / truth lane: launch only after `G1` -### Before each worker edits symbols +1. live docs +2. repo truth docs +3. ADRs +4. README and AGENTS guidance +5. examples, diagrams, and operator remediation wording in docs -The worker must run impact analysis on the symbols it expects to touch and include a concise summary in handoff. Minimum required targets by lane: +## Task Execution Contracts -- `A1` - - `PlatformWorldContext` - - `build_ws_and_start_session_frame` -- `B1` - - `MacLimaBackend::ensure_session` - - any backend-local readiness helper being split -- `C1` - - `WindowsWslBackend::ensure_session` - - any backend-local readiness helper being split -- `A2` - - `ReplPersistentSessionClient::start_with` - - `open_world_session` - - any sync shell caller or regression surface being touched -- `D1` - - no GitNexus impact required if docs-only, but worker still reports docs-only status +### `P0-parent-contract-freeze-and-run-init` -Escalation rule: +Primary owned surfaces: -1. If any impact analysis returns `HIGH` or `CRITICAL`, the worker stops before editing and returns a blocker handoff. -2. Parent records the blocker, decides whether the slice still fits [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md), and either relaunches with narrower scope or blocks the run. +- [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) +- [ORCH_PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/ORCH_PLAN.md) +- `.runs/plan-uaa-boundary-and-naming-cleanup/**` -### Before each worker handoff +Required actions: -Each coding lane runs `gitnexus_detect_changes()` and reports: +1. Freeze the rename matrix, preserved-name list, live rename boundary, historical allowlist, and validation wall exactly from the current plan. +2. Create `branch-map.json`, `lane-ownership.json`, `merge-order.json`, and `contract-freeze.json`. +3. Record GitNexus freshness status and the concurrency contract. +4. Write the initial gate and task directories with `OPEN` and `READY_FOR_REVIEW` only where appropriate for parent-owned setup completion. -1. pass or fail -2. changed symbols -3. changed execution flows -4. whether the result matches the intended lane scope +Verification commands: -### Before final closeout +```bash +test -f /Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md +test -f /Users/spensermcconnell/__Active_Code/atomize-hq/substrate/ORCH_PLAN.md +``` -Parent runs `gitnexus_detect_changes()` on the merged parent branch and records the result under `tasks/P3-final-gitnexus-closeout/`. +Acceptance conditions: -The run cannot close green if final GitNexus detection shows unexpected symbol drift that the parent cannot explain from the task ledger. +1. No worker would need to guess names, ownership, or final validation commands. +2. `.runs/**` skeleton exists on paper with required artifacts and sentinel rules. +3. Parent has recorded whether GitNexus freshness work is needed before `A1`. -## Frozen Ownership Boundaries +### `A1-protocol-label-cutover` -### Worker A, phase `A1`: shell contract freeze only +Primary owned surfaces or file families: -Owned files: +- `crates/common/src/agent_events.rs` +- `crates/shell/src/execution/agent_runtime/**` +- `crates/shell/src/execution/routing/dispatch/world_ops.rs` +- `config/**` +- code-adjacent tests and fixtures for local protocol validation, trace emission, and persisted runtime state -1. [crates/shell/src/execution/platform_world/mod.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/platform_world/mod.rs) -2. [crates/shell/src/execution/routing/dispatch/world_persistent_session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs) -3. [crates/shell/src/execution/platform_world/windows.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/platform_world/windows.rs) if the context shape changes require the Windows detector to populate the same async-ready access surface +Required actions: -Purpose: +1. Replace the canonical local protocol-family label from `uaa.agent.session` to `substrate.agent.session`. +2. Update validator success paths, rejection text, config examples, emitted traces, transport payloads, durable writes, and durable reload paths to the new label. +3. Rewrite supported fixtures and checked-in examples that still treat `uaa.agent.session` as supported canonical input. +4. Make stale old-label configs or persisted rows fail closed with explicit operator-readable errors. +5. Preserve upstream `agent_api.*` ids and `uaa_session_id` semantics unchanged. -1. add the shell-facing async readiness seam -2. preserve sync `ensure_ready` -3. preserve a backend-owned async-ready access path despite backend type erasure in `PlatformWorldContext` -4. freeze helper names, dispatch shape, and error-shape expectations for backend lanes +Verification commands: -Not allowed in `A1`: +```bash +cargo test -p shell agents_validate -- --nocapture +cargo test -p shell agent_successor_contract_ahcsitc0 -- --nocapture +rg -n "substrate\.agent\.session|uaa\.agent\.session" crates/common crates/shell config +``` -1. no edits to `async_repl.rs` -2. no backend crate edits -3. no docs -4. no attempt to prove the macOS no-panic regression through `SUBSTRATE_WORLD_SOCKET` override-only tests -5. no sync regression additions outside minimal compile coverage +Acceptance conditions: -### Worker B, phase `B1`: macOS backend split +1. `substrate.agent.session` is the only supported canonical local protocol-family label on live code and config surfaces owned by `A1`. +2. `uaa.agent.session` no longer succeeds silently on supported live paths. +3. Trace, status, validation, and persistence seams owned by `A1` all reflect the new label. +4. No upstream capability or session-correlation semantics changed. -Owned files: +### `A2-family-rename-foundation-cutover` -1. [crates/world-mac-lima/src/lib.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-mac-lima/src/lib.rs) +Primary owned surfaces or file families: -Purpose: +- [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) +- `dist-workspace.toml` +- `crates/world-agent/**` to be renamed +- `crates/agent-api-types/**`, `crates/agent-api-core/**`, `crates/agent-api-client/**` to be renamed +- mixed-boundary code under `crates/shell/**` +- code-level service discovery and doctor logic coupled to renamed package, binary, socket, or unit names -1. split shared setup and async verification from the sync wrapper -2. keep sync `ensure_session(...)` working -3. harden `block_on_compat(...)` as defense-in-depth +Required actions: -### Worker C, phase `C1`: WSL parity hardening +1. Rename `world-agent*` to `world-service*` across workspace members, package names, Rust crate ids, and binary names. +2. Rename `agent-api*` to `transport-api*` across workspace members, package names, Rust crate ids, and imports. +3. Update mixed-boundary consumers so upstream `agent_api` remains visually distinct from local `transport_api_*`. +4. Preserve `world-api` unchanged. +5. Keep service discovery fail closed when renamed binary, unit, or socket lookup breaks. -Owned files: +Verification commands: -1. [crates/world-windows-wsl/src/backend.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-windows-wsl/src/backend.rs) -2. [crates/world-windows-wsl/src/tests.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-windows-wsl/src/tests.rs) +```bash +cargo build --workspace +cargo test -p world-service -- --nocapture +cargo test -p transport-api-types -- --nocapture +cargo test -p transport-api-core -- --nocapture +cargo test -p transport-api-client -- --nocapture +rg -n "world-agent|world_agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client|agent_api_types|agent_api_core|agent_api_client" Cargo.toml dist-workspace.toml crates +``` -Purpose: +Acceptance conditions: -1. mirror the readiness split internally -2. preserve existing sync behavior -3. explicitly avoid shipping a Windows persistent-session caller +1. Workspace manifests and crate imports compile with `world-service` and `transport-api*`. +2. Mixed-boundary consumers remain readable: upstream `agent_api` untouched, local transport crates clearly renamed. +3. `world-api` is unchanged. +4. Code-level service discovery and doctor logic no longer depend on stale canonical `world-agent` naming. -### Worker A, phase `A2`: shell caller migration and sync regressions +### `B1-scripts-ci-release-cutover` -Owned files: +Primary owned surfaces or file families: -1. [crates/shell/src/execution/routing/dispatch/world_persistent_session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs) -2. [crates/shell/src/repl/async_repl.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/repl/async_repl.rs) -3. [crates/shell/src/execution/routing/world.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/world.rs) -4. [crates/shell/src/execution/routing/dispatch/world_ops.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/dispatch/world_ops.rs) -5. [crates/shell/src/execution/workspace_cmd.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/workspace_cmd.rs) -6. [crates/shell/tests/repl_world_first_routing_v1.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/tests/repl_world_first_routing_v1.rs) -7. any directly related shell-side tests required by the slice +- `scripts/**` +- `.github/**` +- `dist/**` +- live packaging or launch helpers under `macos-hardening/**` if applicable -Purpose: +Required actions: -1. replace the macOS sync bridge with the async seam -2. preserve exact socket override behavior -3. land the honest no-override no-panic regression separately from the override-path regression -4. prove sync callers still use sync `ensure_ready` +1. Rename package, binary, alias, socket, and systemd unit references to `world-service` and `substrate-world-service`. +2. Update install, dev-install, uninstall, provision, warm, smoke, and doctor flows to the new daemon and transport family names. +3. Update release bundle assembly and release template payload naming. +4. Update CI workflow package invocations and smoke steps. +5. Ensure upgrade-capable paths remove or stop legacy `substrate-world-agent` assets where the plan requires cleanup. -Reason `A1` and `A2` are serialized: +Verification commands: -1. `A1` defines the shell-to-backend seam consumed by `B1` and `C1` -2. `A2` cannot honestly finish until the backend adapters are merged and stable -3. merging them into one long shell lane would either block the backend window or force backend workers to guess the final shell contract +```bash +rg -n "world-agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client" scripts .github dist macos-hardening +rg -n "world-service|substrate-world-service|transport-api-types|transport-api-core|transport-api-client" scripts .github dist macos-hardening +./dist/scripts/assemble-release-bundles.sh +``` -### Worker D, phase `D1`: docs late +Acceptance conditions: -Owned files: +1. Live scripts, workflows, and release surfaces use the renamed service and transport families consistently. +2. No operator helper script or release script points canonical usage back to `substrate-world-agent`. +3. Release bundle assembly and CI naming surfaces prove the cutover beyond code compilation. -1. [docs/internals/repl/persistent_session.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/internals/repl/persistent_session.md) -2. [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) -3. optional [llm-last-mile/README.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/llm-last-mile/README.md) only if needed for index hygiene +### `C1-docs-adr-truth-convergence` -Purpose: +Primary owned surfaces or file families: -1. document the shipped caller-shape split -2. document the Windows parity decision as internal only +- `docs/**` +- `README.md` +- `AGENTS.md` +- `AGENT_ORCHESTRATION_GAP_MATRIX.md` +- relevant ADRs under `docs/project_management/adrs/**` -## Detailed Task Ledger +Required actions: -| Task ID | Owner | Worktree / Branch | Depends on | Deliverable | Acceptance notes | -| --- | --- | --- | --- | --- | --- | -| `P0` | Parent | `parent` / `codex/slice-26-async-persistent-session-readiness` | none | source lock, GitNexus freshness check, run-state init | `source-lock.json` exists, `RUN_OPEN` touched | -| `P1` | Parent | `parent` / `codex/slice-26-async-persistent-session-readiness` | `P0` | parent branch ready, Worker A launched | clean parent branch exists, `branch-map.json` written | -| `A1` | Worker A | `shell-lane` / `codex/slice-26-shell-readiness-seam` | `P1` | shell-facing async readiness seam only | sync `ensure_ready` preserved, no backend or `async_repl.rs` edits | -| `G1` | Parent | `parent` / same | `A1` | shell contract accepted | contract artifact written, gate `G1` opened | -| `B1` | Worker B | `mac-lima-lane` / `codex/slice-26-mac-lima-readiness-split` | `G1` | macOS backend shared sync/async readiness split | crate tests green, no shell edits | -| `C1` | Worker C | `wsl-lane` / `codex/slice-26-wsl-readiness-parity` | `G1` | WSL parity hardening and tests | crate tests green, no product-surface expansion | -| `G2` | Parent | `parent` / same | `B1`, `C1` | backend window accepted | both backend merges recorded, gate `G2` opened | -| `A2` | Worker A | `shell-lane` / `codex/slice-26-shell-readiness-seam` rebased | `G2` | shell caller migration plus sync regressions | macOS path off sync bridge, no-override panic regression present, socket override exact, shell tests green | -| `G3` | Parent | `parent` / same | `A2` | shell migration accepted | merged tree satisfies code-path split, gate `G3` opened | -| `D1` | Worker D | `docs-lane` / `codex/slice-26-docs-readiness-split` | `G3` | docs updates only | docs match merged code, no code edits | -| `G4` | Parent | `parent` / same | `D1` | docs accepted | gate `G4` opened | -| `P2` | Parent | `parent` / same | `G4` | final validation wall | all commands executed and logged | -| `P3` | Parent | `parent` / same | `P2` | final GitNexus detect-changes closeout | output matches expected scope | -| `P4` | Parent | `parent` / same | `P3` | final summary and run completion | `RUN_COMPLETE` touched | +1. Rewrite docs, ADRs, and truth docs so upstream UAA, local `transport-api*`, local `world-service*`, unchanged `world-api`, and local `substrate.agent.session` are clearly separated. +2. Update operator examples and remediation guidance to the new canonical names. +3. Remove live doc guidance that still teaches `uaa.agent.session` or `substrate-world-agent` as supported canonical names. +4. Keep historical references only where explicitly allowable as historical. -## Lane Handoff Requirements +Verification commands: -Every worker handoff must include, at minimum: +```bash +rg -n "world-agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client|uaa\.agent\.session" docs README.md AGENTS.md AGENT_ORCHESTRATION_GAP_MATRIX.md +rg -n "world-service|substrate-world-service|transport-api-types|transport-api-core|transport-api-client|substrate\.agent\.session|world-api|agent_api\." docs README.md AGENTS.md AGENT_ORCHESTRATION_GAP_MATRIX.md +``` + +Acceptance conditions: -1. `HEAD` commit SHA -2. exact changed file list -3. exact commands run -4. per-command exit codes -5. concise GitNexus impact summary -6. whether any impact call returned `HIGH` or `CRITICAL` -7. whether `gitnexus_detect_changes()` passed -8. concise explanation of any unexpected file drift -9. blocker notes if not green -10. statement of whether the lane stayed within ownership boundaries +1. Live docs and ADRs tell the same boundary story as the merged code and scripts tree. +2. Canonical examples use `substrate.agent.session`, `world-service*`, and `transport-api*`. +3. No live operator doc instructs readers to inspect `substrate-world-agent` or configure `uaa.agent.session`. -Parent records this under the matching `tasks//` artifact directory. +### `P1-parent-parallel-integration` -### Minimum handoff packet format for coding lanes +Primary owned surfaces: -1. `changed-files.txt` -2. `commands.txt` -3. `exit-codes.json` -4. `impact-analysis-summary.md` -5. `gitnexus-detect-changes.txt` -6. `handoff-notes.md` -7. `HEAD_SHA.txt` +- authoritative branch `chore/uaa-boundary-and-naming-cleanup` +- `.runs/**` -### Required lane-specific notes +Required actions: -`A1` must additionally state: +1. Review `B1` and `C1` handoffs against ownership boundaries. +2. Merge accepted lane outputs in the frozen order. +3. Replay or quarantine lanes when overlap breaks the ownership contract. +4. Record integration outcomes, replays, and quarantines in `.runs/**`. -1. the exact new shell-facing async helper name -2. which backend-facing contract points were frozen -3. how the platform-world layer preserved concrete backend-owned async-ready access -4. whether any shell test additions were deferred to `A2` +Verification commands: -`B1` must additionally state: - -1. which sync and async backend-local helpers now exist -2. whether `block_on_compat(...)` behavior changed only as hardening -3. whether any shell contract mismatch was discovered - -`C1` must additionally state: - -1. which backend-local parity helpers now exist -2. whether any Windows product-surface change was avoided explicitly -3. whether any shell contract mismatch was discovered - -`A2` must additionally state: - -1. which call site stopped using the sync bridge -2. how socket override bypass was preserved -3. where the no-override current-thread no-panic regression lives -4. which sync caller regressions were tested - -`D1` must additionally state: - -1. docs touched -2. exact statements added for macOS async split and Windows internal-only scope - -## Task Graph And Control Points - -```mermaid -graph TD - P0["P0 Source lock + GitNexus freshness"] --> P1["P1 Parent branch init"] - P1 --> A1["A1 Shell contract freeze"] - A1 --> G1["G1 Parent accepts shell contract"] - G1 --> B1["B1 macOS backend split"] - G1 --> C1["C1 WSL parity hardening"] - B1 --> G2["G2 Parent accepts backend window"] - C1 --> G2 - G2 --> A2["A2 Shell migration + sync regressions"] - A2 --> G3["G3 Parent accepts shell migration"] - G3 --> D1["D1 Docs closeout"] - D1 --> G4["G4 Parent accepts docs"] - G4 --> P2["P2 Final validation wall"] - P2 --> P3["P3 Final GitNexus closeout"] - P3 --> P4["P4 Final summary"] +```bash +git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate status --short ``` -## Validation Commands By Task +Acceptance conditions: -These are the required commands, not examples. +1. The authoritative branch contains exactly the accepted outputs from `B1` and `C1`. +2. Any overlap is resolved by explicit replay or quarantine, not silent manual blending. +3. `.runs/**` reflects the actual merge and replay history. -### `A1` validation commands +### `P2-parent-validation-wall` -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane`: +Primary owned surfaces: -```bash -cargo fmt --all -cargo test -p shell --lib -- --nocapture -``` +- merged authoritative tree +- `.runs/**` -### `B1` validation commands +Required actions: -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/mac-lima-lane`: +1. Run the grep gates. +2. Run the cargo gates. +3. Run the operator surface gates. +4. Run the release gates. +5. Record fail-closed proof points and environment blockers, if any. -```bash -cargo fmt --all -cargo test -p world-mac-lima -- --nocapture -``` +Verification commands: -### `C1` validation commands +1. The exact grep, cargo, operator, release, and fail-closed commands already frozen in `Validation Wall`. -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/wsl-lane`: +Acceptance conditions: -```bash -cargo fmt --all -cargo test -p world-windows-wsl -- --nocapture -``` +1. The validation wall is green or explicitly blocked by environment availability with no ambiguity about follow-up. +2. The merged tree proves the rename across code, scripts, docs, release surfaces, and operator flows. +3. Parent records exact commands, exit codes, and evidence in `.runs/**`. -### `A2` validation commands +### `P3-parent-closeout` -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/shell-lane` after rebase to the parent branch: +Primary owned surfaces: -```bash -cargo fmt --all -cargo test -p shell --lib -- --nocapture -cargo test -p shell --test repl_world_first_routing_v1 -- --nocapture -cargo test -p shell -- --nocapture -``` +- `.runs/**` +- final authoritative branch state -### `D1` validation commands +Required actions: -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/docs-lane`: +1. Run final `gitnexus_detect_changes()` on the merged tree. +2. Confirm the changed scope matches the frozen plan. +3. Write `final-summary.md`, task and gate outcomes, and `RUN_COMPLETE`. +4. If the run cannot close honestly, write `blocked.json` instead. + +Verification commands: ```bash -git diff -- docs/internals/repl/persistent_session.md docs/WORLD.md llm-last-mile/README.md +git -C /Users/spensermcconnell/__Active_Code/atomize-hq/substrate status --short ``` -### Parent final validation wall in `P2` +Acceptance conditions: -Run from `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/substrate-slice-26-readiness-split/parent`: +1. Final run-state artifacts match the actual merged result. +2. Parent has recorded final scope verification and residual risks. +3. The run ends with exactly one terminal sentinel: `RUN_COMPLETE` or `RUN_BLOCKED`. -```bash -cargo fmt --all -- --check -cargo clippy --workspace --all-targets -- -D warnings -cargo test -p shell -- --nocapture -cargo test -p world-mac-lima -- --nocapture -cargo test -p world-windows-wsl -- --nocapture -script -q /dev/null zsh -lc 'RUST_BACKTRACE=1 ~/.substrate/bin/substrate' -``` +## Exact Lane Ownership Boundaries By Directories / Modules + +### Lane A: Foundation Code Ownership + +Owns these surfaces end to end: + +- [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) +- `dist-workspace.toml` +- `crates/common/**` where protocol-family trace identity is emitted +- `crates/shell/**` +- `crates/world-agent/**` to be renamed to `crates/world-service/**` +- `crates/agent-api-types/**` to be renamed to `crates/transport-api-types/**` +- `crates/agent-api-core/**` to be renamed to `crates/transport-api-core/**` +- `crates/agent-api-client/**` to be renamed to `crates/transport-api-client/**` +- code-adjacent tests and fixtures under those crates +- `config/**` for supported live protocol examples + +Lane A owns both of these subproblems: + +1. protocol-label cutover: + - `uaa.agent.session -> substrate.agent.session` + - validators + - trace emission + - transport payloads + - durable writes and reload paths + - fail-closed handling for stale rows and configs +2. family renames: + - `world-agent* -> world-service*` + - `agent-api* -> transport-api*` + - workspace members + - package names + - Rust crate ids + - mixed-boundary imports + - in-code service discovery and doctor logic that depends on the renamed family + +Lane A may also touch: + +- code-level doctor or remediation strings that are directly coupled to renamed runtime or service discovery logic + +Lane A may not defer any shared-hotspot name choice to later lanes. + +### Lane B: Scripts / CI / Release Ownership + +Owns these surfaces after `G1`: + +- `scripts/**` +- `.github/**` +- `dist/**` +- `macos-hardening/**` if the file is part of live packaging, launch, or operator setup + +Lane B specifically owns: + +1. package invocation updates +2. binary, alias, socket, and systemd unit references in scripts +3. install, warm, smoke, doctor, provision, and uninstall flows +4. release payload staging and release-template references +5. upgrade cleanup of legacy `substrate-world-agent` units or binaries where required by the plan -## Conflict-Resolution Rule For Cross-Lane Contract Drift +Lane B may not edit: -Contract drift after `G1` is handled explicitly. +- crate code +- workspace manifests +- local protocol validation logic +- docs or ADR narrative except tiny inline comments inside its own scripts if needed -If `B1` or `C1` discovers that the `A1` shell contract is insufficient or wrong: +### Lane C: Docs / ADR / Truth Ownership -1. The worker stops immediately. -2. The worker does not patch shell files locally. -3. The worker returns a blocker handoff describing the exact contract mismatch. -4. Parent writes `BLOCKED` for that task and touches `REOPENED` under `gates/G1-shell-contract-freeze/`. -5. Parent closes downstream acceptance for unmerged work based on the stale contract. -6. Parent either: - - launches a narrow `A1R1` correction on the shell lane, or - - blocks the run if the required change violates [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) -7. If `A1R1` lands, parent updates: - - `source-lock.json` only if the plan authority itself changed - - `gates/G1-shell-contract-freeze/gate.json` - - `ownership-boundaries.json` if file boundaries changed - - `task-ledger.json` -8. Parent then relaunches affected backend lanes from the new parent base. Old backend commits against the stale contract are not merged opportunistically. +Owns these surfaces after `G1`: -## Gate Rules And Stop Conditions +- `docs/**` +- `README.md` +- `AGENTS.md` +- `AGENT_ORCHESTRATION_GAP_MATRIX.md` +- `docs/project_management/adrs/**` -### Gate `G1` acceptance rule +Lane C specifically owns: -Open only when: +1. upstream-vs-local boundary wording +2. examples using `substrate.agent.session` +3. docs and ADR references to `world-service*` +4. docs and ADR references to `transport-api*` +5. truth-table cleanup so no live doc implies local transport equals upstream UAA -1. `A1` handoff is complete -2. parent confirms sync `ensure_ready` still exists for sync callers -3. parent confirms the frozen shell contract preserves a backend-owned async-ready access path for downstream backend lanes -4. parent confirms `A1` did not spill into backend crates or `async_repl.rs` -5. parent has a written shell contract artifact under `tasks/G1-shell-contract-accept/` +Lane C may not edit: -### Gate `G2` acceptance rule +- crate code +- scripts +- CI files +- release bundle assembly +- manifests -Open only when: +### Hotspot No-Split List -1. `B1` and `C1` are both merged into the parent branch -2. their required crate tests passed -3. neither lane expanded public Windows persistent-session surface -4. neither lane forced shell changes after `G1` without reopening the gate +These surfaces must not be split across concurrent workers: -### Gate `G3` acceptance rule +1. [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) +2. `dist-workspace.toml` +3. `crates/shell/**` +4. any file importing upstream `agent_api` and local transport crates in the same module +5. any validator, mapping, trace, routing, or persistence file that interprets the local protocol-family label +6. any code path that resolves the renamed `world-service` binary, unit, or socket -Open only when: +## Merge / Integration Order -1. `A2` is merged -2. macOS persistent-session startup no longer uses the sync bridge -3. socket override bypass is still exact -4. a no-override current-thread regression covers the former panic path without relying on `SUBSTRATE_WORLD_SOCKET` -5. sync caller regressions are covered and green +Frozen integration order: -### Gate `G4` acceptance rule +1. `P0-parent-contract-freeze-and-run-init` +2. `A1-protocol-label-cutover` +3. `A2-family-rename-foundation-cutover` +4. `G1-foundation-accept-and-parallel-launch` +5. parallel launch: + - `B1-scripts-ci-release-cutover` + - `C1-docs-adr-truth-convergence` +6. `G2-parallel-window-integration` +7. `P1-parent-parallel-integration` +8. `G3-validation-launch` +9. `P2-parent-validation-wall` +10. `G4-final-acceptance` +11. `P3-parent-closeout` -Open only when: +Integration rule inside the parallel window: -1. docs are based on the merged parent branch -2. docs explicitly describe: - - async persistent-session startup using a dedicated async readiness seam - - sync request-builders continuing to use sync `ensure_ready` - - WSL work in this slice being internal-only parity hardening +1. `B1` merges before `C1` unless the parent explicitly records that there is zero overlap. +2. Reason: script, release, and operator helper names are execution surfaces; docs and ADRs should match the final merged operator vocabulary. +3. Parent must diff the `B1` and `C1` file sets before merge. This is mandatory, not optional. +4. If `B1` and `C1` overlap unexpectedly on any file outside an explicitly parent-approved shared file list, parent immediately rejects the later lane handoff and records `REJECTED` pending replay. +5. If `C1` was prepared before the final `B1` operator-visible naming settled, parent must replay or rebase `C1` on the accepted `B1` tree before `C1` can become `ACCEPTED`. +6. Parent may not manually cherry-pick prose fragments from a rejected `C1` handoff into the authoritative tree. The lane must be replayed as a coherent docs pass. +7. If `B1` reopens any foundation hotspot, parent quarantines the lane and either narrows the brief or blocks the run. -### Run stop conditions +## Validation Wall -Write `blocked.json`, touch `RUN_BLOCKED`, and stop if any are true: +Parent owns and runs the full validation wall on the merged tree only. -1. the slice requires an async `WorldBackend` -2. sync `ensure_ready` cannot be preserved -3. exact socket override bypass cannot be preserved -4. shell code must duplicate backend readiness logic to succeed -5. a backend lane requires a shipped Windows persistent-session caller to validate the design -6. final manual smoke still produces a Tokio panic -7. final GitNexus detect-changes shows unexpected scope the parent cannot justify -8. required macOS smoke cannot be run honestly and the user has not explicitly approved an evidence waiver +### Grep Gates -## Final Acceptance Wall +Zero-hit wall on live surfaces: -The run passes only if all of the following are true on the merged parent branch: +```bash +rg -n "world-agent|world_agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client|agent_api_types|agent_api_core|agent_api_client|uaa\.agent\.session" \ + Cargo.toml dist-workspace.toml crates scripts docs .github dist README.md AGENTS.md config macos-hardening +``` -1. Persistent-session startup no longer calls the sync readiness bridge on the macOS async path. -2. macOS current-thread REPL startup no longer panics on `block_in_place`. -3. Readiness failures surface as normal `Result` errors. -4. Existing sync bootstrap and request-builder callers still use sync `ensure_ready`. -5. Shared readiness rules remain backend-owned across sync and async entrypoints. -6. `SUBSTRATE_WORLD_SOCKET` override bypass remains exact. -7. WSL parity hardening stayed internal-only. -8. Required tests are present and pass, including a no-override current-thread regression for the former panic path and a separate override-path regression. -9. Docs clearly encode the caller-shape split and Windows scope decision. -10. Final GitNexus detect-changes matches expected scope. +Positive guardrails that must still succeed: -### Manual smoke expectation +```bash +rg -n "agent_api\.run|agent_api\.session\.resume\.v1|agent_api\.session\.handle\.v1" crates docs config +rg -n "world-api" Cargo.toml crates docs +rg -n "world-service|substrate-world-service|transport-api-types|transport-api-core|transport-api-client|transport_api_types|transport_api_core|transport_api_client|substrate\.agent\.session" \ + Cargo.toml dist-workspace.toml crates scripts docs .github dist README.md AGENTS.md config macos-hardening +``` -Primary honest smoke: +### Cargo Gates -1. Run the parent `script -q /dev/null zsh -lc 'RUST_BACKTRACE=1 ~/.substrate/bin/substrate'` check in a macOS environment where the current-thread REPL path is meaningful for this slice. -2. Pass if startup succeeds without panic. -3. Pass if startup fails as a normal readiness error without Tokio runtime panic. -4. Fail if a Tokio panic or `block_in_place` runtime invariant panic is observed. +```bash +cargo fmt --all -- --check +cargo clippy --workspace --all-targets -- -D warnings +cargo build --workspace +cargo test --workspace -- --nocapture +cargo test -p shell -- --nocapture +cargo test -p world-service -- --nocapture +cargo test -p transport-api-types -- --nocapture +cargo test -p transport-api-core -- --nocapture +cargo test -p transport-api-client -- --nocapture +cargo test -p world-mac-lima -- --nocapture +cargo test -p world-windows-wsl -- --nocapture +cargo test -p substrate-replay -- --nocapture +``` -### If the environment cannot run the smoke honestly +### Operator Surface Gates -This does not default to pass. +Linux: -Rules: +```bash +systemctl status substrate-world-service.socket --no-pager +systemctl status substrate-world-service.service --no-pager +systemctl list-unit-files | rg "substrate-world-agent" +substrate host doctor --json | jq . +substrate world doctor --json | jq . +``` + +macOS Lima: + +```bash +scripts/mac/lima-warm.sh --check-only +scripts/mac/smoke.sh +limactl shell substrate systemctl status substrate-world-service.socket +limactl shell substrate systemctl status substrate-world-service.service +``` + +WSL: -1. Parent records `SMOKE_UNAVAILABLE` in `tasks/P2-final-validation-wall/acceptance-notes.md`. -2. Parent explains exactly why the environment is not honest for this smoke. -3. Parent marks the run blocked unless the user explicitly accepts a smoke-evidence waiver. -4. If a waiver is granted, parent still records the gap in `final-summary.md`. The run is “accepted with waived manual smoke,” not “fully validated.” +```powershell +pwsh -File scripts/windows/wsl-warm.ps1 -DistroName substrate-wsl -ProjectPath (Resolve-Path .) +pwsh -File scripts/windows/wsl-smoke.ps1 +pwsh -File scripts/windows/wsl-doctor.ps1 +``` -## Tests And Acceptance Mapping To PLAN.md +### Release Gates -The parent should use this direct mapping when deciding final acceptance: +```bash +./dist/scripts/assemble-release-bundles.sh +rg -n "world-service|substrate-world-service" dist/release-template.md dist/scripts/assemble-release-bundles.sh +``` -1. PLAN completion point `1`: covered by `A2`, `G3`, and final code inspection -2. PLAN completion point `2`: covered by the no-override current-thread regression in `A2` plus manual smoke -3. PLAN completion point `3`: covered by shell and backend error-path tests and smoke failure mode -4. PLAN completion point `4`: covered by `A1`, `A2`, and sync shell regressions -5. PLAN completion point `5`: covered by `B1`, `C1`, `A2`, and docs -6. PLAN completion point `6`: covered by `C1` scope notes and docs -7. PLAN completion point `7`: covered by `D1` and parent doc review +### Fail-Closed Checks + +1. `protocol: substrate.agent.session` validates. +2. Old `uaa.agent.session` live configs or persisted rows are rejected clearly after cutover. +3. World-required routing still hard-fails if renamed service discovery breaks. +4. No doctor, help, or remediation string points operators back to `substrate-world-agent`. + +### Manual Validation Proof Points + +Parent closeout must explicitly record proof of: + +1. canonical config and docs use `protocol: substrate.agent.session`, +2. live trace identity emits `substrate.agent.session`, +3. local host↔world contract crates are `transport-api-*`, +4. local in-world daemon surfaces are `world-service*`, +5. `world-api` remains unchanged, +6. stale old-name live surfaces are gone outside the historical allowlist, +7. world-required rename drift still fails closed, +8. upstream `agent_api.*` wording remains unchanged. + +## Blocked-Run Contract + +If the run blocks, parent writes: + +- `/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.runs/plan-uaa-boundary-and-naming-cleanup/blocked.json` + +Required fields: + +- `run_id` +- `authoritative_branch` +- `timestamp` +- `current_task_id` +- `gate_state` +- `summary` +- `stop_condition_id` +- `worker_lane` +- `blocking_files` +- `accepted_outputs` +- `quarantined_outputs` +- `next_required_parent_action` + +Blocked-run rules: + +1. `blocked.json` is parent-written only. +2. It is written exactly once at the stop point. +3. No further worker launches occur after it is written. +4. Existing worker outputs are either accepted and recorded or quarantined and named explicitly. + +## Context-Control Rules For Parent And Workers + +### Parent Rules + +1. Parent keeps the canonical plan state in `.runs/**` only. +2. Parent is the only actor allowed to reopen a gate or reinterpret the rename contract. +3. Parent must keep the frozen rename matrix and live boundary visible in every worker brief. +4. Parent owns all cross-lane rebases, merges, and acceptance decisions. +5. Parent records GitNexus findings, handoffs, validation commands, and final acceptance artifacts. + +### Worker Rules + +1. Read [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md), this controller, and only the lane-relevant files before editing. +2. Do not widen scope beyond the lane boundary. +3. Run GitNexus impact analysis before editing symbols in the owned lane. +4. Stop and escalate on `HIGH` or `CRITICAL` impact. +5. Do not write `.runs/**`. +6. Do not edit [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) or [ORCH_PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/ORCH_PLAN.md). +7. Handoffs must include: + - changed files + - commands run + - exit codes + - GitNexus impact summary + - `gitnexus_detect_changes()` output + - blockers or residual risks + +### Context Hygiene + +1. No worker loads broad unrelated surfaces just because the rename is repo-wide. +2. Lane A reads only the code, manifests, config, and code-adjacent tests it owns. +3. Lane B reads only scripts, CI, release, and packaging surfaces after `G1`. +4. Lane C reads only docs, ADRs, truth docs, and guidance surfaces after `G1`. +5. Archived or historical files are read only when needed to prove allowlist rationale. + +## Acceptance / Completion Criteria + +The run is complete only when all of the following are true on the merged tree: + +1. `P0`, `A1`, `A2`, `B1`, `C1`, `P1`, `P2`, and `P3` are accepted by the parent. +2. The rename contract in `.runs/**` matches [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) exactly. +3. The foundation lane completed before the parallel lanes started. +4. The live-surface grep wall is green outside the historical allowlist. +5. Positive guardrails still show upstream `agent_api.*`, unchanged `world-api`, and the new canonical local names. +6. Cargo fmt, clippy, build, workspace tests, and targeted renamed-package tests are green. +7. Linux, Lima, and WSL operator surface gates are green or explicitly recorded as environment-blocked with no ambiguity about required follow-up. +8. Release bundle assembly is green and stages the renamed payloads. +9. Fail-closed checks prove stale `uaa.agent.session` and broken renamed service discovery do not silently succeed. +10. Code, scripts, release bundles, CI, docs, ADRs, fixtures, and operator guidance all tell one coherent boundary story. +11. Parent runs final `gitnexus_detect_changes()` and confirms the changed scope matches the plan. +12. Parent writes `final-summary.md` and marks `sentinels/RUN_COMPLETE`. + +## Task Acceptance Checklist + +| Task | Done means | +| --- | --- | +| `P0` | Rename contract, live boundary, historical allowlist, validation wall, branch map, lane ownership, and `.runs` artifact contract are frozen in parent-owned artifacts. | +| `A1` | `substrate.agent.session` is canonical on owned live code and config surfaces, stale `uaa.agent.session` fails closed, and owned tests or greps show the cutover. | +| `A2` | `world-service*` and `transport-api*` compile and test on owned code surfaces, `world-api` stays unchanged, and mixed-boundary imports remain clear. | +| `B1` | Scripts, CI, release, and operator helpers use the renamed service and transport families with no stale canonical `substrate-world-agent` guidance. | +| `C1` | Docs, ADRs, and truth docs teach the same boundary story as the merged code and script surfaces and use the new canonical names. | +| `P1` | Parent has integrated accepted parallel outputs, replayed or quarantined overlaps, and recorded the real integration history in `.runs/**`. | +| `P2` | Grep, cargo, operator, release, and fail-closed checks are executed and recorded against the merged validation candidate. | +| `P3` | Final GitNexus scope verification, final summary, terminal sentinel, and any residual-risk notes are written by the parent. | ## Assumptions -1. The parent will create a clean integration worktree from the current slice branch, `feat/host-orchestrator-durable-session`, and treat the current [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) text as the locked slice authority in run-state artifacts. -2. If the manual smoke environment is unavailable, that is a real acceptance gap, not a silent skip. -3. The parent can access GitNexus through the required MCP workflow or equivalent repo-supported invocation before worker edits begin. +1. The authoritative execution branch remains `chore/uaa-boundary-and-naming-cleanup`. +2. Fresh worker worktrees can be created under `/Users/spensermcconnell/__Active_Code/atomize-hq/.worktrees/`. +3. GitNexus is available or can be refreshed before worker edits begin. +4. The repo can tolerate a direct live-name cutover with fail-closed handling and no supported migration layer for stale `uaa.agent.session` runtime state. +5. The final validation wall may require Linux, Lima, and WSL prerequisites already described in repo docs; if a platform environment is unavailable, the parent records that explicitly instead of silently skipping it. +6. Historical files outside the live-surface wall may retain old tokens only when the parent records the allowlist rationale. +7. This controller supersedes the stale async persistent-session orchestration topic previously present in this file. diff --git a/PLAN.md b/PLAN.md index aa13163b4..8c0c2c0de 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,34 +1,39 @@ # PLAN: UAA Boundary and Naming Cleanup Source SOW: [27-uaa-boundary-and-naming-cleanup.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/llm-last-mile/27-uaa-boundary-and-naming-cleanup.md) -Related validation plan: [spensermcconnell-testing-uaa-boundary-hardening-test-plan-20260521-111202.md](/Users/spensermcconnell/.gstack/projects/atomize-hq-substrate/spensermcconnell-testing-uaa-boundary-hardening-test-plan-20260521-111202.md) -Primary boundary anchors: [AGENT_ORCHESTRATION_GAP_MATRIX.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md), [ADR-0042](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0042-llm-and-agent-identity-tuple-and-deployment-posture.md), [ADR-0044](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0044-agent-hub-core-successor-identity-tuple-compatible.md), [ADR-0047](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0047-host-orchestrator-durable-session-and-parked-resumable-ownership.md), [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md), [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md) -Current workspace branch: `testing` +Primary boundary anchors: [AGENT_ORCHESTRATION_GAP_MATRIX.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md:53), [ADR-0042](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0042-llm-and-agent-identity-tuple-and-deployment-posture.md:120), [ADR-0044](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0044-agent-hub-core-successor-identity-tuple-compatible.md:177), [ADR-0047](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/project_management/adrs/draft/ADR-0047-host-orchestrator-durable-session-and-parked-resumable-ownership.md:202), [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md:235), [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md:184) +Current workspace branch: `chore/uaa-boundary-and-naming-cleanup` Base branch: `main` -Plan type: repo-wide naming and boundary correction, no UI scope, developer-facing runtime and operator scope -Status: unified implementation plan, 2026-05-21 -Supersedes: previous root `PLAN.md`, which tracked the async persistent-session bootstrap readiness split instead of the UAA boundary cleanup slice +Plan type: repo-wide naming and boundary cleanup, no runtime-model redesign +Status: rewritten execution plan, unified to `/plan-eng-review` rigor on 2026-05-21 ## Objective -Finish the boundary cleanup so the repo no longer blurs: +Finish the naming cleanup so the repo tells one stable story and operators do not have to infer boundary meaning from context. -1. the external Unified Agent API runtime abstraction, -2. the Substrate-local host<->world typed transport, -3. the Substrate-local in-world daemon and service boundary, -4. and the Substrate-local pure-agent protocol-family label used in config, persistence, and trace identity. +After this slice: -This is a naming and supportability correction, not a runtime-model rewrite. +1. `Unified Agent API`, `UAA`, Rust `agent_api`, and adopted `agent_api.*` capability or schema ids mean the upstream CLI-agent runtime abstraction only. +2. `world-api` still means the abstract world backend contract only. +3. `transport-api-*` means the Substrate-local typed host↔world contract layer only. +4. `world-service*` means the Substrate-local in-world daemon or service boundary only. +5. `substrate.agent.session` is the only canonical local pure-agent protocol-family label on supported live surfaces. -The exact choices in this plan are fixed: +This is a direct-cutover naming correction. It is not a runtime redesign, not a public contract redesign, and not a compatibility-migration project. -1. upstream `agent_api` keeps its current meaning and does not rename, -2. `world-api` keeps its current meaning and does not rename, -3. canonical local pure-agent protocol-family becomes `substrate.agent.session`, -4. `world-agent*` renames to `world-service*`, -5. `agent-api*` renames to `transport-api*`, -6. the slice is a direct cutover on live supported surfaces, not a long-lived compatibility program, -7. and world-required flows must remain fail closed if rename drift breaks service discovery. +## Locked Decisions + +These choices are already made. Implementation does not reopen them. + +| Topic | Locked decision | Why | +| --- | --- | --- | +| Upstream UAA naming | Keep upstream `agent_api` and adopted `agent_api.*` ids unchanged | Those names are already correct and already mean the upstream runtime layer | +| Abstract world contract | Keep `world-api` unchanged | It already names the abstraction, not the in-world daemon | +| Local protocol-family label | Rename `uaa.agent.session` to `substrate.agent.session` | The current name reads like an upstream claim when it is actually a local protocol-family | +| Local daemon family | Rename `world-agent*` to `world-service*` | The service is a daemon boundary, not an AI-agent identity | +| Local transport family | Rename `agent-api-*` to `transport-api-*` | The current family collides conceptually with the real upstream UAA | +| Compatibility posture | No long-lived alias layer on supported live surfaces | Partial rename keeps the ambiguity alive and increases support burden | +| Failure posture | Old live names fail closed with explicit operator-readable errors | Silent fallback would hide broken routing and broken state interpretation | ## Acceptance Criteria @@ -36,116 +41,167 @@ This plan is complete only when all of the following are true: 1. `Unified Agent API`, `UAA`, Rust `agent_api`, and adopted `agent_api.*` capability and schema ids have one stable repo meaning: the upstream CLI-agent runtime abstraction only. 2. `substrate.agent.session` is the only canonical local pure-agent protocol-family label on supported live surfaces. -3. `world-agent*` has been replaced by `world-service*` across code, package names, binaries, systemd units, install helpers, bundle payloads, docs, and operator remediation text on live surfaces. +3. `world-agent*` has been replaced by `world-service*` across code, package names, binaries, install aliases, systemd units, release payloads, CI, and live docs. 4. `agent-api-types`, `agent-api-core`, and `agent-api-client` have been replaced by `transport-api-types`, `transport-api-core`, and `transport-api-client`, including the Rust crate ids `transport_api_types`, `transport_api_core`, and `transport_api_client`. -5. `world-api` remains unchanged as the abstract world backend contract. -6. Mixed-boundary crates that import both upstream `agent_api` and local transport crates read clearly at the dependency and import layer after the rename. -7. Old `uaa.agent.session` live configs, fixtures, and persisted rows are no longer treated as supported canonical inputs after cutover. If encountered, they fail closed with explicit operator-readable errors. +5. `world-api` remains unchanged everywhere. +6. Mixed-boundary crates that import both upstream `agent_api` and local transport crates read clearly after the rename. +7. Old `uaa.agent.session` configs, fixtures, or persisted rows are not treated as supported canonical inputs after cutover. They fail closed with explicit operator-readable errors. 8. The live-surface grep wall passes with zero stale hits for `world-agent`, `substrate-world-agent`, `agent-api-*`, `agent_api_*`, and `uaa.agent.session` outside the explicit historical allowlist. -9. Linux, macOS Lima, and WSL provision, warm, smoke, doctor, uninstall, and release-bundle flows all use the renamed `world-service` family consistently. +9. Linux, macOS Lima, and WSL install, provision, warm, smoke, doctor, uninstall, and release flows all use the renamed `world-service` family consistently. 10. World-required routing still hard-fails if renamed service discovery breaks. No rename drift path silently falls back to host execution. +11. Docs, ADRs, tests, fixtures, comments, and operator remediation strings all tell the same boundary story. +12. No public-handle, session-selector, `uaa_session_id`, or upstream capability semantics regress. + +## Scope + +In scope: + +1. freezing one canonical vocabulary for upstream UAA versus local transport versus local service naming, +2. renaming the canonical local protocol-family label from `uaa.agent.session` to `substrate.agent.session`, +3. renaming `world-agent*` to `world-service*`, +4. renaming `agent-api*` to `transport-api*`, +5. freezing one exact rename matrix for crate paths, package names, Rust crate ids, binaries, install aliases, service units, socket names, bundle paths, and doctor or remediation strings, +6. updating code, tests, fixtures, docs, ADRs, scripts, CI, bundle assembly, and operator playbooks together, +7. defining the live-surface grep boundary and the explicit historical allowlist, +8. preserving fail-closed behavior anywhere the repo already requires world isolation or protocol validation. + +## NOT in scope + +1. Renaming true upstream `agent_api` imports or any upstream `agent_api.*` capability or schema ids. +2. Renaming `world-api`. +3. Changing public session selectors, widening public control surfaces, or revisiting public handle semantics. +4. Changing `uaa_session_id` or `internal.uaa_session_id`. +5. Adding a migration command, startup rewrite, compatibility alias, or persisted-state upgrader for old `uaa.agent.session` rows. +6. Reworking HTTP path schemas unless a rename is directly required by the chosen service family. +7. Bulk-rewriting archived evidence packs, old planning logs, or historical artifacts just to purge old tokens. +8. Renaming generic `SUBSTRATE_*AGENT*` environment variables solely because they contain the word `agent`. + +## Current Repo Truth + +These facts are already true. This plan builds on them and does not reopen them. + +1. The real upstream UAA boundary already exists in this repo. `crates/shell` and the current `crates/world-agent` already consume upstream `agent_api`, and adopted `agent_api.*` ids are already live. +2. The local host↔world contract is also already real. The rename target is the local `agent-api-*` family, not the upstream runtime abstraction. +3. `world-api` already names the abstract world backend contract. It is not part of the rename and should stay visually stable throughout the slice. +4. `uaa.agent.session` is not just a doc token. It is wired into config, validation, trace emission, transport payloads, and durable state. That is why this plan uses a direct cutover with fail-closed handling instead of a docs-only cleanup. ## Step 0: Scope Challenge ### What already exists -| Sub-problem | Existing code or surface | Plan decision | +| Sub-problem | Existing code or surface | Reuse decision | | --- | --- | --- | -| Upstream UAA runtime boundary | [`crates/shell/Cargo.toml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/Cargo.toml), [`crates/world-agent/Cargo.toml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/Cargo.toml), [`docs/contracts/substrate-gateway-backend-adapter-schema.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/contracts/substrate-gateway-backend-adapter-schema.md) | Reuse. Upstream `agent_api` stays untouched. | -| Local pure-agent protocol label | [`mapping.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/mapping.rs), [`validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs), [`agent_events.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs), [`state_store.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/state_store.rs) | Reuse central seams. Cut over to `substrate.agent.session` in one supported pass. | -| Local typed host<->world contract | [`crates/agent-api-types/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-types/), [`crates/agent-api-core/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-core/), [`crates/agent-api-client/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-client/) | Reuse the actual contract. Rename the local family to `transport-api-*` without touching upstream `agent_api`. | -| In-world daemon and control surface | [`crates/world-agent/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/), [`scripts/linux/world-provision.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/linux/world-provision.sh), [`docs/WORLD.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) | Reuse the implementation. Rename the service family to `world-service*`. | -| Install, warm, release, and CI harnesses | [`scripts/substrate/install-substrate.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/install-substrate.sh), [`scripts/mac/lima-warm.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/mac/lima-warm.sh), [`scripts/windows/wsl-warm.ps1`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/windows/wsl-warm.ps1), [`dist/scripts/assemble-release-bundles.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/scripts/assemble-release-bundles.sh), [`.github/workflows/feature-smoke.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/feature-smoke.yml) | Reuse as the real validation wall. These are first-class implementation scope, not polish. | -| Boundary wording | [`AGENT_ORCHESTRATION_GAP_MATRIX.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md), [`docs/WORLD.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md), [`docs/TRACE.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md) | Reuse as source-of-truth anchors, then rewrite so the same story appears everywhere. | +| Upstream UAA runtime boundary | [`crates/shell/Cargo.toml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/Cargo.toml:61), [`crates/world-agent/Cargo.toml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/Cargo.toml:43), [docs/contracts/substrate-gateway-backend-adapter-schema.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/contracts/substrate-gateway-backend-adapter-schema.md:21) | Reuse exactly. Upstream `agent_api` stays untouched. | +| Local pure-agent protocol label | [`mapping.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/mapping.rs:3), [`validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs:76), [`agent_events.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs:17), [`state_store.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/state_store.rs:2093) | Reuse the seam, rename the constant and every supported live consumer in one pass. | +| Local typed host↔world contract | [`crates/agent-api-types/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-types/), [`crates/agent-api-core/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-core/), [`crates/agent-api-client/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-client/) | Reuse the contract, rename the family to `transport-api-*`. | +| In-world daemon and control surface | [`crates/world-agent/`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/), [`scripts/linux/world-provision.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/linux/world-provision.sh:527), [`docs/WORLD.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md:237) | Reuse the implementation, rename the family to `world-service*`. | +| Install, warm, release, and CI harnesses | [`scripts/substrate/install-substrate.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/install-substrate.sh:2041), [`scripts/mac/lima-warm.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/mac/lima-warm.sh:686), [`scripts/windows/wsl-warm.ps1`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/windows/wsl-warm.ps1:57), [`dist/scripts/assemble-release-bundles.sh`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/scripts/assemble-release-bundles.sh:261), [`.github/workflows/feature-smoke.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/feature-smoke.yml:204) | First-class implementation scope, not follow-up polish. | +| Boundary wording and gap framing | [`AGENT_ORCHESTRATION_GAP_MATRIX.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md:53), [`docs/WORLD.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md:237), [`docs/TRACE.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md:184) | Reuse as truth anchors, then rewrite all live wording to match. | -### Minimum honest diff +### Minimum viable change -The minimum honest implementation touches these module and surface groups: +The minimum honest change is still broad: -1. workspace manifests and crate directories under `Cargo.toml` and `crates/` -2. runtime label and validator seams under `crates/common/` and `crates/shell/src/execution/agent_runtime/` -3. host<->world routing and payload seams under `crates/shell/src/execution/routing/` and the renamed transport crates -4. the in-world daemon crate and its tests under the renamed `crates/world-service/` -5. installers, provisioners, warm scripts, uninstallers, bundle builders, and CI under `scripts/`, `dist/`, and `.github/` -6. operator and developer docs under `docs/`, `README.md`, and `AGENTS.md` -7. targeted tests in `crates/shell/tests/`, the renamed transport crates, `world-mac-lima`, `world-windows-wsl`, and `substrate-replay` +1. rename the local protocol-family label, +2. rename the local daemon and transport families, +3. update all service discovery and packaging surfaces that reference those names, +4. update docs and operator guidance in the same slice, +5. prove the rename with tests, grep gates, and platform validation. -Anything smaller is a shortcut. It would leave drift in install surfaces, service discovery, validation, or mixed-boundary imports. +Anything smaller creates a half-renamed repo. That is worse than the current state because operators would now have two partially valid vocabularies instead of one bad one. -### Repo-truth findings +### Complexity and distribution check -1. This is not greenfield. `world-agent`, `substrate-world-agent`, `agent-api-*`, and `uaa.agent.session` already appear in code, package names, binaries, units, docs, CI, and persisted state. -2. The biggest product risk is not runtime speed. It is supportability drift. If rename work misses systemd units, bundle paths, or doctor text, users get broken worlds and misleading remediation. -3. The highest-severity technical risk is fail-open behavior. If renamed service discovery breaks and world-required flows quietly fall back to host execution, the cleanup weakens the isolation model. -4. The highest-severity migration seam is `uaa.agent.session`. It is exact-matched in validator paths, config examples, traces, transport payloads, and durable runtime/session state. -5. Mixed-boundary consumers already import both upstream `agent_api` and local `agent-api-*` names. The rename must make those files clearer, not just different. -6. `world-agent` is not just transport. It owns execute, cancel, gateway, member-runtime, and world-fs behavior. `world-service` is the truthful replacement. `world-transport` is not. +1. This slice touches far more than 8 files and more than 2 modules. That is a smell in most features. Here it is justified because the ambiguity already spans manifests, crates, scripts, CI, release packaging, and docs. +2. No new artifact type is introduced. But distribution still matters because install scripts, release bundles, and systemd units are user-facing delivery surfaces. If they are not renamed in the same slice, users cannot actually consume the cleanup. +3. There is no built-in framework feature that can do this cleanup for us. This is a repo-owned naming and packaging correction, not a library toggle. +4. The right scope posture is "broad but boring": direct rename, explicit validation, no extra abstraction, no migration subsystem, no alias layer. -### Complexity, completeness, and distribution verdict +### Target architecture after rename -1. This is a large slice. It touches more than eight files and crosses crates, scripts, CI, release, docs, and operator guidance. That is justified because the ambiguity is already cross-cutting. -2. The complete fix is still the right fix. A code-only rename without install, bundle, and grep walls is fake completeness and will produce support debt immediately. -3. No new binary class or distribution pipeline is introduced, but existing distribution surfaces are part of the implementation scope because the renamed daemon already ships through them. -4. Direct cutover is approved for supported live surfaces. That means supported examples, fixtures, and docs get rewritten now, and unsupported stale rows fail closed with clear errors after the cutover. +```text +Upstream CLI-agent runtime + Unified Agent API / agent_api / agent_api.* + | + v + substrate shell runtime + | + +-------------+-------------+ + | | + v v +transport-api-* world-api +(typed host<->world (abstract backend + contract layer) contract) + | + v +world-service +(in-world daemon / REST + WS) + | + v +linux world / Lima guest / WSL guest + +Local pure-agent protocol-family: + substrate.agent.session +``` -### Scope ruling +### Exact remaining gap -The scope is accepted as-is. +The architecture already exists. The gap is naming drift across live surfaces: -This plan is intentionally broader than a few crate renames and intentionally narrower than a public runtime or protocol redesign. That is the right middle. +1. the repo contains both real upstream `agent_api` integration and local `agent-api-*` transport crates, +2. the repo contains both abstract `world-api` and concrete `world-agent` daemon naming, +3. the local protocol-family is still `uaa.agent.session` in config, validators, traces, transport payloads, and durable state, +4. installers, bundle scripts, and doctor text still expose the old daemon family names, +5. docs and ADRs still risk implying the local transport layer is itself upstream UAA. -## Architecture Review +## Frozen Execution Contract -### Problem statement +If implementation wants to violate any rule below, stop and revise the plan first. -The repo already has three real boundaries: +1. Upstream `agent_api` and `agent_api.*` remain untouched. +2. `world-api` remains untouched. +3. `substrate.agent.session` is the only canonical supported local protocol-family label after cutover. +4. Supported live surfaces cut over directly. Do not add permanent compatibility aliases just to avoid touching code, docs, or scripts. +5. Unsupported stale rows and configs fail closed with explicit error text, not silent acceptance and not silent downgrade. +6. World-required flows must not fall back to host execution because a renamed binary, socket, or unit was missed. +7. Operator-facing unit names, install aliases, release payload names, and doctor text must match the code rename in the same slice. +8. Mixed-boundary files must read more clearly after the rename than before. +9. The plan lands in this order: freeze names first, rename live code second, sweep scripts and docs third, validate last. -1. upstream `agent_api` for external Unified Agent API semantics, -2. local typed host<->world contracts, -3. local in-world daemon and control surfaces. +## Architecture Review -The bug is not missing architecture. The bug is that the names still blur those boundaries. +This slice should stay boring. -### Boundary thesis +1. No new runtime boundary, migration subsystem, compatibility daemon, or abstraction layer is introduced. +2. The only intentional live-name changes are `uaa.agent.session` -> `substrate.agent.session`, `world-agent*` -> `world-service*`, and `agent-api*` -> `transport-api*`. +3. The critical dependency chain after cutover is: -Responsibility decides the noun. +```text +shell runtime + |\ + | +--> upstream agent_api (unchanged) + | + +----> transport-api-* (renamed local host<->world contract) + | + +----> world-api (unchanged abstract backend contract) + | + +----> world-service (renamed in-world daemon) +``` -- upstream runtime semantics keep `agent_api` -- local contract crates use `transport` -- the in-world daemon uses `service` -- the abstract world backend layer keeps `world-api` -- the local pure-agent protocol-family uses `substrate.agent.session` +4. Distribution architecture is part of the implementation, not follow-up polish. Binaries, units, sockets, install aliases, release bundles, and CI package invocations must cut over in the same slice as the code rename. +5. The one unacceptable architectural regression is silent downgrade from world-required routing to host execution because a renamed binary, unit, or socket was missed. -### Target boundary map +## Code Quality Review -```text -TARGET BOUNDARY MAP -=================== -upstream Unified Agent API - -> Rust crate/import: agent_api - -> capability/schema ids: agent_api.* - -> meaning: external CLI-agent runtime abstraction only - -Substrate local world contract - -> crates: transport-api-types / transport-api-core / transport-api-client - -> Rust crate ids: transport_api_types / transport_api_core / transport_api_client - -> meaning: typed host<->world transport contract only - -Substrate local in-world daemon - -> crate/package/binary/service: world-service - -> systemd/socket family: substrate-world-service.* - -> meaning: in-world execution and control daemon - -Abstract world backend layer - -> crate/package: world-api - -> meaning: backend contract only - -Local pure-agent protocol-family - -> canonical label: substrate.agent.session - -> meaning: Substrate-local pure-agent identity only -``` +The implementation should be judged against these quality rules: -### Canonical rename contract +1. Prefer direct rename over alias shims. A compatibility layer here preserves ambiguity instead of reducing it. +2. Mixed-boundary files must become easier to read, not harder. Upstream runtime imports stay `agent_api`; local contract crates become `transport_api_*`; the in-world daemon becomes `world_service`. +3. Update comments, fixtures, examples, and operator text in the same slice as the code change. No live surface should require a reader to remember both old and new names. +4. Update nearby ASCII diagrams when touched. A stale boundary diagram is worse than no diagram because it teaches the wrong model. +5. Do not add helper abstractions unless they eliminate real duplication across multiple live surfaces. This slice wants explicit rename mechanics, not clever indirection. + +## Canonical Rename Matrix | Current canonical name | New canonical name | Decision | | --- | --- | --- | @@ -154,245 +210,223 @@ Local pure-agent protocol-family | package `world-agent` | package `world-service` | rename | | binary `world-agent` | binary `world-service` | rename | | installed alias `substrate-world-agent` | `substrate-world-service` | rename | -| `substrate-world-agent.service` / `.socket` | `substrate-world-service.service` / `.socket` | rename | +| units `substrate-world-agent.service` / `.socket` | `substrate-world-service.service` / `.socket` | rename | +| socket or fd names containing `substrate-world-agent` | matching `substrate-world-service` names | rename | +| `scripts/mac/substrate-world-agent.service` | matching `world-service` filename and `ExecStart` | rename | +| release payload `bin/world-agent` and `bin/linux/world-agent` | `bin/world-service` and `bin/linux/world-service` | rename | | `crates/agent-api-types` | `crates/transport-api-types` | rename | | `crates/agent-api-core` | `crates/transport-api-core` | rename | | `crates/agent-api-client` | `crates/transport-api-client` | rename | | `agent_api_types` / `agent_api_core` / `agent_api_client` | `transport_api_types` / `transport_api_core` / `transport_api_client` | rename | +| workspace dependency keys `agent-api-*` | `transport-api-*` | rename | +| live embedded file or module identifiers such as `world_agent` | rename when they remain live support code | rename | | `world-api` | `world-api` | keep | | upstream `agent_api` | upstream `agent_api` | keep | | upstream `agent_api.*` ids | upstream `agent_api.*` ids | keep | -### Live rename boundary and historical allowlist +## Live Rename Boundary And Historical Allowlist -Live rename scope must go green on: +### Live surfaces that must go green 1. `Cargo.toml` -2. `crates/**` -3. `scripts/**` -4. `.github/**` -5. `dist/**` -6. `README.md` -7. `AGENTS.md` -8. `config/**` -9. operator and developer docs under `docs/**` - -Historical or evidence-bearing files may retain old tokens only when intentionally preserved, for example: - -1. `docs/project_management/_archived/**` -2. older planning packs or captured artifacts that are not normative runtime or operator guidance +2. `dist-workspace.toml` +3. `crates/**` +4. `scripts/**` +5. `.github/**` +6. `dist/**` +7. `README.md` +8. `AGENTS.md` +9. `config/**` +10. `docs/**` +11. `macos-hardening/**` -This plan does not allow a vague "repo-wide rename." It requires the live-surface command wall and the historical allowlist to be named explicitly. +### Historical allowlist -### Frozen contract +Old tokens may remain only when the file is intentionally historical or archival, for example: -If implementation wants to violate any rule below, stop and revise the plan first. - -1. Upstream `agent_api` and `agent_api.*` remain untouched. -2. `world-api` remains untouched. -3. `substrate.agent.session` is the only canonical supported local protocol-family label after cutover. -4. Direct cutover applies to supported live surfaces. Do not add permanent compatibility aliases just to avoid touching docs or scripts. -5. Unsupported stale rows and configs must fail closed with explicit error text, not silent acceptance and not silent downgrade. -6. World-required flows must not fall back to host execution because a renamed binary, socket, or unit was missed. -7. Operator-facing unit names, install aliases, release payload names, and doctor text must match the code rename in the same slice. -8. Mixed-boundary files must read more clearly after the rename than before. - -## Code Quality Review - -### Explicit design choices - -1. Rename only the local transport and daemon families. Do not widen this slice into a public runtime or protocol redesign. -2. Treat scripts, CI, bundles, and docs as product surfaces. Do not dump them into "truth sync later." -3. Centralize the protocol-family cutover through the current validator, mapping, trace, and persistence seams instead of scattering custom translation code. -4. Keep the direct-cutover rule honest: all supported examples and checked-in live fixtures update now; unsupported stale rows fail clearly after cutover. -5. Use one exact rename matrix and one exact grep wall. No ad hoc exceptions. - -### Naming and ownership expectations +1. `docs/project_management/_archived/**` +2. historical planning packets and evidence packs that are not normative runtime or operator guidance +3. older `llm-last-mile/**` artifacts other than this root `PLAN.md` -1. `world-service` means "the in-world daemon and control surface." It is not just a transport pipe. -2. `transport-api-*` means "the local typed host<->world contract layer." It does not mean upstream UAA. -3. `world-api` keeps the abstract backend meaning and should be explicitly called out as unchanged anywhere the rename is explained. -4. Mixed-boundary consumers should preserve explicit wording in comments and tests: upstream runtime on one side, local transport on the other. +### Grep policy -### DRY guardrails +This slice does not allow vague language like "repo-wide rename." It requires both of these to be explicit: -1. There must be one authoritative rename matrix, not slightly different rename lists in code comments, docs, and scripts. -2. There must be one grep wall for old-name detection across live surfaces, not one command per contributor with different scopes. -3. There must be one canonical protocol-family constant after cutover. Do not leave stale helper constants lying around under alternate names. -4. Error and remediation copy should be sourced from renamed truth, not duplicated with half-updated old examples. +1. the live-surface command wall that must be green before the slice is done, +2. the historical allowlist that explains why any remaining old-name hits still exist. ## Implementation Plan ### Phase summary -| Phase | Purpose | Modules or surfaces touched | Hard dependency | -| --- | --- | --- | --- | -| 1 | Freeze vocabulary, rename matrix, and live-surface boundary | `Cargo.toml`, `AGENT_ORCHESTRATION_GAP_MATRIX.md`, `docs/`, `README.md`, `AGENTS.md` | — | -| 2 | Cut over local protocol-family label | `crates/common/`, `crates/shell/src/execution/agent_runtime/`, `config/`, relevant fixtures and tests | 1 | -| 3 | Rename local transport and daemon families | `crates/world-agent/`, `crates/agent-api-*/`, `Cargo.toml`, mixed-boundary consumers | 1 | -| 4 | Sweep installers, release bundles, platform helpers, and CI | `scripts/`, `dist/`, `.github/` | 1, 3 | -| 5 | Sweep operator docs, ADRs, and developer guidance | `docs/`, `README.md`, `AGENTS.md`, ADRs | 1, 2, 3, 4 | -| 6 | Run grep wall, package tests, operator validation, and closeout | repo root, platform scripts, release scripts | 2, 3, 4, 5 | +| Phase | Purpose | Modules or surfaces touched | Hard dependency | Exit gate | +| --- | --- | --- | --- | --- | +| 1 | Freeze vocabulary, rename matrix, and grep boundary | `PLAN.md`, `AGENT_ORCHESTRATION_GAP_MATRIX.md`, top-level truth docs | — | Exact rename matrix, live boundary, historical allowlist, and grep wall are frozen in writing | +| 2 | Cut over the local protocol-family label | `crates/common/`, `crates/shell/src/execution/agent_runtime/`, `config/`, relevant fixtures and tests | 1 | `substrate.agent.session` validates and emits; stale `uaa.agent.session` fails closed with explicit guidance | +| 3 | Rename local transport and daemon families | `Cargo.toml`, `crates/world-agent/`, `crates/agent-api-*/`, mixed-boundary consumers | 1, 2 | Workspace builds with `world-service` and `transport-api-*`; mixed-boundary imports stay clear | +| 4 | Sweep installers, platform helpers, release bundles, and CI | `scripts/`, `dist/`, `.github/`, doctor or remediation text | 3 | Install, warm, release, and CI surfaces all use renamed package, binary, unit, and socket names | +| 5 | Sweep docs, ADRs, and developer guidance | `docs/`, `README.md`, `AGENTS.md`, ADRs, truth docs | 2, 3 | Live docs and ADRs tell the same boundary story as the code and scripts | +| 6 | Run grep wall, package tests, operator validation, and closeout | repo root, platform helpers, release scripts | 2, 3, 4, 5 | Grep, cargo, operator, release, and fail-closed gates all pass together | -### Phase 1: Freeze vocabulary, rename matrix, and live-surface boundary +### Phase 1: Freeze vocabulary, rename matrix, and grep boundary -Files and surfaces: +Primary surfaces: -1. [AGENT_ORCHESTRATION_GAP_MATRIX.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md) -2. [README.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/README.md) -3. [AGENTS.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENTS.md) -4. [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) -5. [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md) +1. [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/PLAN.md) +2. [AGENT_ORCHESTRATION_GAP_MATRIX.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENT_ORCHESTRATION_GAP_MATRIX.md) +3. [README.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/README.md) +4. [AGENTS.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENTS.md) +5. [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) +6. [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md) -Actions: +Required actions: -1. Freeze the vocabulary contract in one place: upstream `agent_api`, local `transport-api-*`, local `world-service`, unchanged `world-api`, local `substrate.agent.session`. -2. Freeze the exact rename matrix for crate directories, package names, Rust crate ids, binaries, install aliases, unit names, and bundle paths before file moves start. +1. Freeze one authoritative vocabulary rule: upstream `agent_api`, local `transport-api-*`, local `world-service`, unchanged `world-api`, local `substrate.agent.session`. +2. Freeze one exact rename matrix for crate paths, package names, Rust crate ids, binaries, install aliases, service units, socket names, bundle paths, and doctor or remediation strings. 3. Freeze the live rename boundary and historical allowlist before grep work starts. -4. Record the exact grep wall commands in the plan so contributors are checking the same thing. +4. Record the exact grep wall commands in this plan so contributors are checking the same surfaces. -Exit criteria: +Done when: -1. Every later phase can point back to one exact rename matrix. -2. No implementation phase has to guess which surfaces are live and which are historical. +1. every later phase can point back to one exact rename matrix, +2. no implementation phase has to guess which surfaces are live and which are historical, +3. no later phase invents new names or new exceptions. ### Phase 2: Cut over the local protocol-family label to `substrate.agent.session` -Files and surfaces: +Primary surfaces: -1. [crates/common/src/agent_events.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs) -2. [crates/shell/src/execution/agent_runtime/mapping.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/mapping.rs) -3. [crates/shell/src/execution/agent_runtime/validator.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs) -4. [crates/shell/src/execution/agent_runtime/session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/session.rs) -5. [crates/shell/src/execution/agent_runtime/orchestration_session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/orchestration_session.rs) -6. [crates/shell/src/execution/agent_runtime/state_store.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/state_store.rs) -7. [config/agents/codex.yaml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/config/agents/codex.yaml) +1. [crates/common/src/agent_events.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs:17) +2. [crates/shell/src/execution/agent_runtime/mapping.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/mapping.rs:3) +3. [crates/shell/src/execution/agent_runtime/validator.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs:76) +4. [crates/shell/src/execution/agent_runtime/session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/session.rs:57) +5. [crates/shell/src/execution/agent_runtime/orchestration_session.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/orchestration_session.rs:80) +6. [crates/shell/src/execution/agent_runtime/state_store.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/state_store.rs:2093) +7. [crates/shell/src/execution/routing/dispatch/world_ops.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/dispatch/world_ops.rs:203) +8. [config/agents/codex.yaml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/config/agents/codex.yaml:6) -Actions: +Required actions: 1. Replace the canonical local protocol-family constant from `uaa.agent.session` to `substrate.agent.session`. 2. Update validator success paths, error text, config examples, emitted traces, transport payloads, and durable writes to the new canonical label. 3. Rewrite supported fixtures, checked-in examples, and test inventories in the same cutover. -4. Make stale old-label rows fail closed with explicit operator-readable errors after cutover. Do not silently accept them as canonical. +4. Make stale old-label rows fail closed with explicit operator-readable errors after cutover, including the exact remediation path operators should take instead of automatic rewrite. 5. Preserve all true upstream `agent_api.*` ids and `uaa_session_id` semantics unchanged. -Exit criteria: +Done when: -1. `protocol: substrate.agent.session` validates and shows up in emitted live surfaces. -2. `uaa.agent.session` is no longer documented or accepted as the preferred supported local label. -3. No public selector or upstream capability id semantics changed. +1. `protocol: substrate.agent.session` validates and shows up in emitted live surfaces, +2. `uaa.agent.session` is no longer documented or accepted as the preferred supported local label, +3. old rows or configs fail with explicit guidance instead of being silently interpreted, +4. no public selector or upstream capability semantics changed. ### Phase 3: Rename local transport and daemon families directly -Files and surfaces: +Primary surfaces: 1. [Cargo.toml](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/Cargo.toml) 2. [crates/world-agent/](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/) 3. [crates/agent-api-types/](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-types/) 4. [crates/agent-api-core/](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-core/) 5. [crates/agent-api-client/](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/agent-api-client/) -6. mixed-boundary consumers such as [`crates/shell/src/repl/async_repl.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/repl/async_repl.rs) and [`member_runtime.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/src/member_runtime.rs) +6. mixed-boundary consumers such as [`crates/shell/src/repl/async_repl.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/repl/async_repl.rs:12) and [`crates/world-agent/src/member_runtime.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/world-agent/src/member_runtime.rs:1) -Actions: +Required actions: 1. Rename `crates/world-agent` to `crates/world-service`, package `world-agent` to `world-service`, and binary `world-agent` to `world-service`. -2. Rename installed alias and service family from `substrate-world-agent` to `substrate-world-service`. +2. Rename the installed alias and service family from `substrate-world-agent` to `substrate-world-service`. 3. Rename the local contract crates to `transport-api-types`, `transport-api-core`, and `transport-api-client`, including their Rust crate ids. -4. Update all imports, dependency keys, workspace member entries, crate references, and tests to the new names. +4. Update all imports, dependency keys, workspace members, crate references, tests, helper filenames, and live module identifiers to the new names. 5. Preserve `world-api` unchanged. -6. Tighten comments and tests in mixed-boundary files so the upstream-vs-local distinction is explicit. +6. Tighten comments and mixed-boundary docs in touched files so the upstream-versus-local distinction is explicit. -Exit criteria: +Done when: -1. The workspace builds with the renamed local families. -2. Mixed-boundary files read cleanly with upstream `agent_api` alongside local `transport_api_*`. -3. No supported live surface still treats `world-agent` or `agent-api-*` as canonical names. +1. the workspace builds with the renamed local families, +2. mixed-boundary files read cleanly with upstream `agent_api` alongside local `transport_api_*`, +3. no supported live surface still treats `world-agent` or `agent-api-*` as canonical names. ### Phase 4: Sweep installers, platform helpers, release bundles, and CI -Files and surfaces: +Primary surfaces: -1. [scripts/linux/world-provision.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/linux/world-provision.sh) -2. [scripts/substrate/install-substrate.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/install-substrate.sh) +1. [scripts/linux/world-provision.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/linux/world-provision.sh:527) +2. [scripts/substrate/install-substrate.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/install-substrate.sh:2041) 3. [scripts/substrate/dev-install-substrate.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/dev-install-substrate.sh) 4. [scripts/substrate/uninstall-substrate.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/substrate/uninstall-substrate.sh) -5. [scripts/mac/lima-warm.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/mac/lima-warm.sh) -6. [scripts/windows/wsl-warm.ps1](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/windows/wsl-warm.ps1) +5. [scripts/mac/lima-warm.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/mac/lima-warm.sh:686) +6. [scripts/windows/wsl-warm.ps1](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/windows/wsl-warm.ps1:57) 7. [scripts/windows/uninstall-substrate.ps1](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/scripts/windows/uninstall-substrate.ps1) -8. [dist/scripts/assemble-release-bundles.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/scripts/assemble-release-bundles.sh) -9. [dist/release-template.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/release-template.md) -10. [`.github/workflows/feature-smoke.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/feature-smoke.yml) and [`.github/workflows/nightly.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/nightly.yml) +8. [dist/scripts/assemble-release-bundles.sh](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/scripts/assemble-release-bundles.sh:261) +9. [dist/release-template.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/dist/release-template.md:33) +10. [`.github/workflows/feature-smoke.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/feature-smoke.yml:204) +11. [`.github/workflows/nightly.yml`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/.github/workflows/nightly.yml:164) -Actions: +Required actions: 1. Update install, warm, provision, uninstall, and smoke flows to the renamed binary, socket, and unit names. 2. Update release bundle assembly so host and guest payloads stage `world-service` in the right locations. 3. Update CI package invocations, smoke checks, and release checks to the renamed package and service family names. 4. Update doctor, remediation, and helper output so operators are never pointed back at `substrate-world-agent`. +5. Make upgrade-capable paths remove any legacy `substrate-world-agent.service`, `substrate-world-agent.socket`, and `substrate-world-agent` binary before enabling or probing the new family. -Exit criteria: +Done when: -1. Linux, macOS Lima, and WSL helpers all target `world-service`. -2. Release bundle and CI surfaces prove the rename beyond code compilation. -3. Operator guidance and helper text are consistent with the new service family. +1. Linux, macOS Lima, and WSL helpers all target `world-service`, +2. release bundle and CI surfaces prove the rename beyond code compilation, +3. operator guidance and helper text are consistent with the new service family. ### Phase 5: Sweep boundary docs, ADRs, and developer guidance -Files and surfaces: - -1. [docs/CONFIGURATION.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/CONFIGURATION.md) -2. [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md) -3. [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md) -4. [docs/INSTALLATION.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/INSTALLATION.md) -5. [docs/UNINSTALL.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/UNINSTALL.md) -6. [docs/COMMANDS.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/COMMANDS.md) -7. [docs/USAGE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/USAGE.md) -8. [docs/REPLAY.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/REPLAY.md) -9. [docs/reference/env/contract.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/reference/env/contract.md) -10. [docs/manual_verification/linux_world_socket.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/manual_verification/linux_world_socket.md) -11. [docs/cross-platform/mac_world_setup.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/cross-platform/mac_world_setup.md) -12. [docs/cross-platform/wsl_world_troubleshooting.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/cross-platform/wsl_world_troubleshooting.md) +Primary surfaces: + +1. [docs/CONFIGURATION.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/CONFIGURATION.md:42) +2. [docs/TRACE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/TRACE.md:184) +3. [docs/WORLD.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/WORLD.md:237) +4. [docs/INSTALLATION.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/INSTALLATION.md:13) +5. [docs/UNINSTALL.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/UNINSTALL.md:43) +6. [docs/COMMANDS.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/COMMANDS.md:81) +7. [docs/USAGE.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/USAGE.md:169) +8. [docs/REPLAY.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/REPLAY.md:82) +9. [docs/reference/env/contract.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/reference/env/contract.md:63) +10. [docs/manual_verification/linux_world_socket.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/manual_verification/linux_world_socket.md:12) +11. [docs/cross-platform/mac_world_setup.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/cross-platform/mac_world_setup.md:108) +12. [docs/cross-platform/wsl_world_troubleshooting.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/docs/cross-platform/wsl_world_troubleshooting.md:163) 13. [README.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/README.md) 14. [AGENTS.md](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/AGENTS.md) -15. relevant ADRs including ADR-0042, ADR-0044, ADR-0045, and ADR-0021 +15. ADRs including ADR-0042, ADR-0044, ADR-0045, and ADR-0021 -Actions: +Required actions: 1. Rewrite operator and developer docs so upstream UAA and local transport or service layers are never described as the same thing. 2. Rewrite doc examples to `substrate.agent.session` and the renamed `world-service` and `transport-api-*` families. 3. Keep historical references only where explicitly marked as historical or archived. -4. Update the gap matrix so the boundary row is no longer "partially clarified." +4. Update the gap matrix so the boundary row is no longer partially clarified or open-ended. -Exit criteria: +Done when: -1. The same exact boundary story appears in docs, ADRs, code comments, and install guides. -2. No live operator doc still tells someone to inspect `substrate-world-agent.service` or use `uaa.agent.session`. +1. the same exact boundary story appears in docs, ADRs, code comments, and install guides, +2. no live operator doc still tells someone to inspect `substrate-world-agent.service` or use `uaa.agent.session`. ### Phase 6: Validation and closeout -Files and surfaces: - -1. repo root grep wall -2. target package tests -3. platform helper and smoke commands -4. release bundle assembly - -Actions: +Required actions: 1. Run the grep wall on live surfaces and confirm zero stale hits outside the historical allowlist. 2. Run workspace tests and the targeted renamed-package tests. 3. Run Linux, Lima, and WSL validation commands against the renamed surfaces. -4. Run release bundle assembly and verify renamed payloads. +4. Run release-bundle assembly and verify renamed payloads. 5. Confirm explicit fail-closed behavior for rename drift and old local protocol label usage. -Exit criteria: +Done when: -1. The rename is proven across code, scripts, docs, bundles, and operator flows. -2. Old-name drift is either gone or explicitly historical. +1. the rename is proven across code, scripts, docs, bundles, and operator flows, +2. old-name drift is either gone or explicitly historical, +3. the repo reads as if the boundary was intentional from the start. ## Test Review -Rust and shell or operator validation are the authoritative test layers for this slice. +Rust tests, shell or operator validation, and grep gates are the authoritative test layers for this slice. ### Code path coverage @@ -403,10 +437,12 @@ CODE PATH COVERAGE mapping.rs -> canonical label constant validator.rs - -> config validation + -> config validation + failure text agent_events.rs / agents_cmd.rs / state_store.rs -> emitted trace + persisted state + status grouping - Required tests: + world_ops.rs and typed payloads + -> transport propagation + Required proof: - substrate.agent.session validates - emitted trace rows use substrate.agent.session - stale uaa.agent.session input fails clearly after cutover @@ -418,7 +454,7 @@ CODE PATH COVERAGE -> package names + Rust crate ids mixed-boundary consumers -> upstream agent_api stays unchanged - Required tests: + Required proof: - mixed-boundary crates compile cleanly - imports remain unambiguous @@ -427,7 +463,7 @@ CODE PATH COVERAGE -> package + binary + tests socket activation / world routing / doctor paths -> renamed binary + socket + unit discovery - Required tests: + Required proof: - service discovery uses new names - world-required routing still fails closed if service is missing @@ -436,61 +472,39 @@ CODE PATH COVERAGE Lima/WSL warm and smoke flows release bundle assembly feature-smoke and nightly workflows - Required tests: + Required proof: - renamed payloads are staged - - renamed unit names are enabled and queried + - renamed unit names are enabled, queried, and cleaned up on upgrade paths - CI invokes renamed packages and binaries ``` -### User-flow and error-state coverage - -```text -USER FLOW COVERAGE -================== -[+] New config or inventory author - - writes protocol: substrate.agent.session - - validator accepts it - - docs show the same label - -[+] Operator provisions or warms a world - - install or provision script stages world-service - - systemd unit names are substrate-world-service.* - - doctor and remediation text point at world-service, not world-agent - -[+] World-required execution path - - renamed service discovery succeeds - - if it fails, command errors explicitly - - no host fallback when require_world=true - -[+] Contributor runs CI or bundle commands - - cargo package names are renamed - - feature-smoke and nightly still pass - - release bundles contain world-service payloads - -[+] Legacy unsupported old-name input - - uaa.agent.session row or config is encountered post-cutover - - validator or load path rejects it clearly - - user sees explicit remediation, not silent drift -``` - ### Concrete test additions | Target | Test requirement | Type | | --- | --- | --- | -| [`crates/shell/tests/agents_validate.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/tests/agents_validate.rs) and [`validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs) | Validate `protocol: substrate.agent.session`; reject `uaa.agent.session` with explicit post-cutover error text. | focused unit and integration | -| [`crates/common/src/agent_events.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs), [`agents_cmd.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agents_cmd.rs), [`crates/shell/tests/agent_successor_contract_ahcsitc0.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/tests/agent_successor_contract_ahcsitc0.rs) | Prove trace, status, and grouping emit and consume `substrate.agent.session` without regression. | focused unit and integration | -| renamed `transport-api-*` crates plus mixed-boundary consumers such as [`async_repl.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/repl/async_repl.rs) | Compile and runtime tests that prove upstream `agent_api` remains distinct from renamed local `transport_api_*` crates. | compile and focused unit | +| [`crates/shell/tests/agents_validate.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/tests/agents_validate.rs:82) and [`validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agent_runtime/validator.rs:76) | Validate `protocol: substrate.agent.session`; reject `uaa.agent.session` with explicit post-cutover error text that tells operators the old label is unsupported. | focused unit and integration | +| [`crates/common/src/agent_events.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs:299), [`crates/shell/tests/agent_successor_contract_ahcsitc0.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/tests/agent_successor_contract_ahcsitc0.rs:12), [`agents_cmd.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agents_cmd.rs:2429) | Prove trace, status, and grouping emit and consume `substrate.agent.session` without regression. | focused unit and integration | +| renamed `transport-api-*` crates plus mixed-boundary consumers such as [`async_repl.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/repl/async_repl.rs:12) | Compile and runtime tests that prove upstream `agent_api` remains distinct from renamed local `transport_api_*` crates. | compile and focused unit | | renamed `world-service` crate tests, including current `world-agent` test families | Keep daemon execute, stream, cancel, and member-runtime behavior intact under the new package and binary names. | crate unit and integration | -| [`socket_activation.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/socket_activation.rs), [`platform/linux.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/platform/linux.rs), [`routing/world.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/world.rs) | Prove rename drift still fails closed when service or socket discovery is broken. | focused integration | +| [`socket_activation.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/socket_activation.rs:13), [`platform/linux.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/platform/linux.rs:897), [`routing/world.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/routing/world.rs:200) | Prove rename drift still fails closed when service or socket discovery is broken. | focused integration | | install, warm, smoke, and bundle helpers under `scripts/`, `dist/`, and `.github/` | Prove renamed package invocations, renamed payload staging, and renamed service or socket names across Linux, Lima, and WSL. | shell smoke and CI validation | -| repo-root grep wall | Prove no stale old-name hits remain on live surfaces, while positive guardrails still find upstream `agent_api.*` and unchanged `world-api`. | static validation | +| repo-root grep wall | Prove no stale old-name hits remain on live surfaces while positive guardrails still find upstream `agent_api.*` and unchanged `world-api`. | static validation | + +### QA handoff artifact + +Implementation is not done when the tests compile. The PR or implementation notes must also leave one compact test handoff artifact that names: + +1. affected operator commands and flows, +2. Linux, Lima, and WSL paths that must be exercised, +3. fail-closed checks for old protocol labels and missing renamed services, +4. and the exact grep wall commands that prove the rename is complete on live surfaces. ### Regression rule for this slice Two regressions are mandatory blockers: -1. any path that causes world-required routing to stop failing closed because a renamed binary, socket, or unit was missed -2. any path that leaves supported live surfaces still advertising or emitting `uaa.agent.session` +1. any path that causes world-required routing to stop failing closed because a renamed binary, socket, or unit was missed, +2. any path that leaves supported live surfaces still advertising or emitting `uaa.agent.session`. No defer. No "docs follow-up." These are slice-completion gates. @@ -505,32 +519,20 @@ No defer. No "docs follow-up." These are slice-completion gates. | Old `uaa.agent.session` rows or configs drift through silently | validator and persistence load paths | explicit rejection after cutover | validator and state tests | Yes | | Docs or remediation text still point operators to `substrate-world-agent` | docs, doctor output, helper strings | rewrite all live guidance | grep wall plus manual validation | No, but unacceptable for ship | -Any failure mode with no test, no explicit handling, and a silent or misleading user experience is a release blocker for this slice. - -## Performance Review +Any failure mode with no test, no explicit handling, and a silent or misleading operator experience is a release blocker for this slice. -This slice is not driven by runtime throughput, but there are still real performance and operational guardrails: +## Performance And Complexity Review -1. No repeated service-discovery loops should be added just to paper over rename drift. +1. This is a wide slice because the ambiguity is already cross-cutting. The breadth is justified. 2. No compatibility shim should sit on the hot path for supported live flows after cutover. -3. No extra validation pass should be added to the steady-state execution path beyond the existing semantics required to enforce the renamed label. -4. CI and bundle validation may get broader, but runtime execution should remain unchanged in steady state. -5. The dominant cost center is support churn, not CPU time. The plan should optimize for zero operator ambiguity. +3. No repeated service-discovery retries should be added just to paper over rename drift. +4. The dominant cost center here is support churn and operator confusion, not CPU time. Optimize for zero ambiguity, explicit failure, and minimal moving parts. -## NOT in scope - -1. Renaming true upstream `agent_api` imports or any upstream `agent_api.*` capability or schema ids. -2. Renaming `world-api`. -3. Changing public session selectors or widening public control surfaces. -4. Reworking HTTP path schemas unless a rename is explicitly required by the chosen service family. -5. Bulk-rewriting archived evidence packs, old planning logs, or historical artifacts just to purge old tokens. -6. Renaming generic `SUBSTRATE_*AGENT*` environment variables solely because they contain the word `agent`. +## Deferred Follow-Ups / TODO Candidates -## Deferred Follow-Ups - -1. Optional archival cleanup for historical documents after the live-surface wall is green. -2. Any future naming cleanup for non-blocking environment variable families, only if it earns its own scope and rollout story. -3. Any later public contract cleanup beyond the local transport and service boundary, only if separately justified. +1. Optional archival cleanup for historical documents after the live-surface wall is green. Rationale: useful hygiene, but not part of the cutover proof. +2. Any later naming cleanup for non-blocking environment variable families, only if it earns its own rollout story. Rationale: do not expand scope just because the token contains `agent`. +3. Any future public contract cleanup beyond the local transport and service boundary, only if separately justified. Rationale: this plan is a local naming correction, not a public API redesign. ## Worktree Parallelization Strategy @@ -538,50 +540,56 @@ This slice is not driven by runtime throughput, but there are still real perform | Step | Modules touched | Depends on | | --- | --- | --- | -| 1. Freeze rename contract and grep boundary | `Cargo.toml`, `docs/`, `README.md`, `AGENTS.md` | — | -| 2. Cut over `substrate.agent.session` | `crates/common/`, `crates/shell/src/execution/agent_runtime/`, `config/` | 1 | -| 3. Rename `world-agent*` and `agent-api*` families | `crates/world-service/`, `crates/transport-api-*/`, `Cargo.toml`, mixed-boundary consumers | 1 | -| 4. Sweep scripts, CI, and release bundles | `scripts/`, `.github/`, `dist/` | 1, 3 | -| 5. Sweep docs and ADRs | `docs/`, `README.md`, `AGENTS.md` | 1, 2, 3, 4 | -| 6. Run validation wall and closeout | repo root, platform helpers, release scripts | 2, 3, 4, 5 | +| A0. Freeze rename contract and grep boundary | `PLAN.md`, `docs/`, `README.md`, `AGENTS.md`, top-level truth docs | — | +| A1. Cut over `substrate.agent.session` | `crates/common/`, `crates/shell/src/execution/agent_runtime/`, `config/` | A0 | +| A2. Rename `world-agent*` and `agent-api*` families | `crates/world-service/`, `crates/transport-api-*/`, `Cargo.toml`, mixed-boundary consumers | A0, A1 | +| B. Sweep scripts, CI, and release bundles | `scripts/`, `.github/`, `dist/` | A2 | +| C. Sweep docs and ADRs | `docs/`, `README.md`, `AGENTS.md`, ADRs | A2 | +| D. Run validation wall and closeout | repo root, platform helpers, release scripts | A1, A2, B, C | ### Parallel lanes -Lane A: Step 2 -> Step 3 -Reason: shared crate and Cargo naming work, one owner, sequential +Lane A: A0 -> A1 -> A2 +Reason: these steps share manifests, protocol validation, mixed-boundary imports, and `crates/shell/`. Splitting them creates merge-conflict bait and naming churn. -Lane B: Step 4 -Reason: scripts, CI, and bundle surfaces are mostly isolated once Step 3 names are frozen +Lane B: B +Reason: scripts, CI, and release surfaces are mostly isolated once the final package, binary, socket, and unit names are real. -Lane C: Step 5 -Reason: docs and ADR rewrites can proceed in parallel once the final names are frozen, but they should trail the code rename enough to avoid doc churn +Lane C: C +Reason: docs and ADR rewrites can run in parallel with Lane B after implementation names are stable, but they should not start before code naming settles. -Lane D: Step 6 -Reason: validation must run after A, B, and C converge +Lane D: D +Reason: validation is the merge gate. It must run after A, B, and C converge. ### Execution order -1. Land Step 1 first. No parallel work before the rename matrix is frozen. -2. Launch Lane A first. The protocol cutover and code-family rename define the real names everyone else must reference. -3. Launch Lane B once the renamed package, binary, and unit names are stable enough to wire into scripts and CI. -4. Launch Lane C once the same names are stable enough for docs. It can run in parallel with Lane B. -5. Run Lane D last. Validation is the merge gate for the whole slice. +1. Land A0 first. No parallel work before the rename matrix and grep boundary are frozen. +2. Run A1 next. The protocol label cutover touches the same runtime and persistence seams that later code work depends on. +3. Run A2 immediately after A1, ideally under the same owner or worktree lane, because both steps touch `Cargo.toml`, `crates/shell/`, and mixed-boundary imports. +4. Once A2 stabilizes the final code names, launch B and C in parallel. +5. Run D last. Validation is the only honest merge gate for this slice. ### Conflict flags -1. Lane A and Lane B both indirectly depend on exact package, binary, and unit names. Freeze those before parallelizing. -2. Lane B and Lane C both touch operator command strings and remediation copy. Keep one canonical rename matrix open while both lanes run. -3. Do not split Step 2 and Step 3 across different owners if they both touch the same mixed-boundary consumers in `crates/shell/`. +1. A1 and A2 both touch `crates/shell/`, runtime validation, and mixed-boundary imports. Do not split them across independent owners. +2. B depends on exact package, binary, socket, and unit names from A2. Starting B early creates rework. +3. C shares operator command strings and remediation copy with B. Keep the rename matrix open and visible while both lanes run. + +### Parallelization verdict + +This slice has one required foundation lane, two safe follow-on parallel lanes, and one final validation lane. + +Peak honest parallelism is B + C. Everything before that is sequential because the same names and the same files are still moving. ## Validation Commands -### Grep Gates +### Grep gates Run after the rename and expect zero live-surface hits unless the hit is in the explicit historical allowlist. ```bash -rg -n "world-agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client|agent_api_types|agent_api_core|agent_api_client|uaa\.agent\.session" \ - Cargo.toml crates scripts docs .github dist README.md AGENTS.md config +rg -n "world-agent|world_agent|substrate-world-agent|agent-api-types|agent-api-core|agent-api-client|agent_api_types|agent_api_core|agent_api_client|uaa\.agent\.session" \ + Cargo.toml dist-workspace.toml crates scripts docs .github dist README.md AGENTS.md config macos-hardening ``` Positive guardrails that must still return expected upstream and unchanged-boundary hits: @@ -589,9 +597,11 @@ Positive guardrails that must still return expected upstream and unchanged-bound ```bash rg -n "agent_api\.run|agent_api\.session\.resume\.v1|agent_api\.session\.handle\.v1" crates docs config rg -n "world-api" Cargo.toml crates docs +rg -n "world-service|substrate-world-service|transport-api-types|transport-api-core|transport-api-client|transport_api_types|transport_api_core|transport_api_client|substrate\.agent\.session" \ + Cargo.toml dist-workspace.toml crates scripts docs .github dist README.md AGENTS.md config macos-hardening ``` -### Cargo Gates +### Cargo gates ```bash cargo fmt --all -- --check @@ -608,13 +618,14 @@ cargo test -p world-windows-wsl -- --nocapture cargo test -p substrate-replay -- --nocapture ``` -### Operator Surface Gates +### Operator surface gates Linux: ```bash systemctl status substrate-world-service.socket --no-pager systemctl status substrate-world-service.service --no-pager +systemctl list-unit-files | rg "substrate-world-agent" substrate host doctor --json | jq . substrate world doctor --json | jq . ``` @@ -636,17 +647,17 @@ pwsh -File scripts/windows/wsl-smoke.ps1 pwsh -File scripts/windows/wsl-doctor.ps1 ``` -### Release Gates +### Release gates ```bash ./dist/scripts/assemble-release-bundles.sh rg -n "world-service|substrate-world-service" dist/release-template.md dist/scripts/assemble-release-bundles.sh ``` -### Fail-Closed Checks +### Fail-closed checks 1. `protocol: substrate.agent.session` validates. -2. Old `uaa.agent.session` live configs or fixtures are rejected clearly after cutover. +2. Old `uaa.agent.session` live configs or persisted rows are rejected clearly after cutover. 3. World-required routing still hard-fails if renamed service discovery breaks. 4. No doctor, help, or remediation string points operators back to `substrate-world-agent`. @@ -654,26 +665,34 @@ rg -n "world-service|substrate-world-service" dist/release-template.md dist/scri Manual validation for this slice should explicitly prove: -1. a canonical new config entry using `protocol: substrate.agent.session` -2. the local in-world daemon is renamed to the `world-service` family through package, binary, installed alias, bundle path, and systemd unit naming -3. the local host<->world contract crates are renamed to the `transport-api-*` family -4. `world-api` remains unchanged -5. pure-agent trace output now emits `substrate.agent.session` -6. stale old-name live surfaces are gone outside the historical allowlist -7. world-required rename drift still fails closed -8. true upstream `agent_api.*` capability and session-handle wording is unchanged +1. a canonical new config entry using `protocol: substrate.agent.session`, +2. the local in-world daemon is renamed to the `world-service` family through package, binary, installed alias, bundle path, and systemd unit naming, +3. the local host↔world contract crates are renamed to the `transport-api-*` family, +4. `world-api` remains unchanged, +5. pure-agent trace output now emits `substrate.agent.session`, +6. stale old-name live surfaces are gone outside the historical allowlist, +7. world-required rename drift still fails closed, +8. true upstream `agent_api.*` capability and session-handle wording is unchanged. ## Completion Summary -This plan is ready to implement when the work lands with this end state: - -1. Step 0: scope accepted as-is, direct cutover on supported live surfaces, no upstream `agent_api` rename, no `world-api` rename. -2. Architecture: one stable boundary story, one rename matrix, one live-surface wall. -3. Code quality: local transport and service families are renamed truthfully, mixed-boundary code is clearer, and no duplicated rename rules remain. -4. Tests: protocol cutover, mixed-boundary compile paths, service discovery, fail-closed routing, install or warm flows, bundle staging, and grep gates are all covered. -5. Performance: no new hot-path compatibility shims, no extra fallback loops, no runtime ambiguity. -6. `NOT in scope`: written. -7. `What already exists`: written. -8. Failure modes: six named production failures accounted for, four of them critical if left untested. -9. Parallelization: four lanes, with one code lane and two safe side lanes after the contract freeze. -10. Lake score: the complete rename and validation wall wins over the fake-cheap partial rename. +This plan is implementation-ready because it now fixes the full execution contract: + +1. Step 0: scope is accepted as intentionally broad because the ambiguity already spans code, packaging, install flows, CI, release surfaces, and docs. +2. Architecture review: the target boundary graph, unchanged seams, direct-cutover rule, and fail-closed posture are frozen in this document. +3. Code quality review: no alias layer, no mixed vocabulary on live surfaces, and no stale diagram or comment debt is allowed at ship time. +4. Test review: the code-path coverage map, concrete test additions, regression blockers, QA handoff artifact, and validation commands are all defined. +5. Performance and complexity review: the slice stays boring, explicit, and free of hot-path compatibility machinery. +6. NOT in scope: explicitly written. +7. What already exists: explicitly written. +8. Deferred follow-ups / TODO candidates: explicitly written. +9. Failure modes: the critical gaps are named and treated as release blockers. +10. Parallelization: one foundation lane, two honest parallel follow-on lanes, one final validation lane. + +After this slice lands, the repo should read as if the boundary was intentional from the start: + +1. upstream UAA is upstream UAA, +2. `world-api` is the abstract world backend contract, +3. local `transport-api-*` is the typed host↔world contract layer, +4. local `world-service` is the in-world daemon and service boundary, +5. and the local pure-agent protocol-family label is unmistakably Substrate-local. diff --git a/README.md b/README.md index c79425166..93be800b4 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ cargo build --release - **Security Worlds Enhancements**: Default-on worlds ship for Linux, macOS (via Lima), and Windows (via WSL **functional but experimental**); active work includes tightening isolation policies and expanding telemetry around world sessions. - **Policy Engine Evolution**: The broker today enforces allow/deny/isolate rules in observe mode by default; future milestones include opt-in enforcement flows and richer restriction types. -- **Agent API Maturation**: `world-agent` already exposes REST/WebSocket endpoints for shell integration—planned work adds per-agent budgets and scope negotiation for third-party assistants. +- **Agent API Maturation**: `world-service` already exposes REST/WebSocket endpoints for shell integration—planned work adds per-agent budgets and scope negotiation for third-party assistants. - **Graph Intelligence Roadmap**: The `substrate-graph` crate ships with a mock backend; Kuzu-powered ingestion and query tooling remain active development areas. ### Future Features Pipeline @@ -221,7 +221,7 @@ macOS report highlights: - Lima tooling (`limactl`), Virtualization.framework (`sysctl kern.hv_support`), and optional `vsock-proxy` - Lima VM `substrate` status plus SSH connectivity -- Guest `substrate-world-agent` service, socket, and `/v1/capabilities` response +- Guest `substrate-world-service` service, socket, and `/v1/capabilities` response - nftables availability and root filesystem usage inside the guest - JSON output nests these fields under `lima.{installed, vm_status, service_active, agent_socket, agent_caps_ok, vsock_proxy, ssh, nft, disk_usage}` diff --git a/config/agents/codex.yaml b/config/agents/codex.yaml index 9c7939198..f2d9ab2b6 100644 --- a/config/agents/codex.yaml +++ b/config/agents/codex.yaml @@ -3,7 +3,7 @@ id: codex config: kind: cli enabled: true - protocol: uaa.agent.session + protocol: substrate.agent.session execution: scope: host cli: diff --git a/crates/common/src/agent_events.rs b/crates/common/src/agent_events.rs index 094135b70..21494964c 100644 --- a/crates/common/src/agent_events.rs +++ b/crates/common/src/agent_events.rs @@ -16,7 +16,7 @@ pub const AGENT_EVENT_CHANNEL_MAX_BYTES: usize = 64; const PURE_AGENT_ROUTER: &str = "agent_hub"; // Substrate-local normalized protocol-family id for current pure-agent records. // This label is not, by itself, a claim of upstream UAA wire/API compatibility. -const PURE_AGENT_PROTOCOL: &str = "uaa.agent.session"; +const PURE_AGENT_PROTOCOL: &str = "substrate.agent.session"; /// Canonical set of agent event categories. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] diff --git a/crates/host-proxy/Cargo.toml b/crates/host-proxy/Cargo.toml index c6d441968..3e4ba65d9 100644 --- a/crates/host-proxy/Cargo.toml +++ b/crates/host-proxy/Cargo.toml @@ -32,9 +32,9 @@ chrono = "0.4" http-body-util = "0.1" # Local dependencies -agent-api-client = { version = "0.2.0-beta.1", path = "../agent-api-client" } -agent-api-core = { version = "0.2.0-beta.1", path = "../agent-api-core" } -agent-api-types = { version = "0.2.0-beta.1", path = "../agent-api-types" } +transport-api-client = { version = "0.2.0-beta.1", path = "../transport-api-client" } +transport-api-core = { version = "0.2.0-beta.1", path = "../transport-api-core" } +transport-api-types = { version = "0.2.0-beta.1", path = "../transport-api-types" } [dev-dependencies] tempfile = "3" diff --git a/crates/host-proxy/src/auth.rs b/crates/host-proxy/src/auth.rs index 9cfa0d10b..b9c82826f 100644 --- a/crates/host-proxy/src/auth.rs +++ b/crates/host-proxy/src/auth.rs @@ -4,11 +4,11 @@ use std::collections::HashMap; use std::path::Path; use std::sync::Arc; -use agent_api_types::ApiError; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use tracing::{debug, warn}; +use transport_api_types::ApiError; use crate::AuthConfig; diff --git a/crates/host-proxy/src/config.rs b/crates/host-proxy/src/config.rs index ab8c7bcd1..d24c2e346 100644 --- a/crates/host-proxy/src/config.rs +++ b/crates/host-proxy/src/config.rs @@ -9,7 +9,7 @@ use crate::transport::AgentTransportConfig; pub struct ProxyConfig { /// Path to the host-side Unix socket. pub host_socket: PathBuf, - /// Transport used to reach world-agent. + /// Transport used to reach world-service. #[serde(default)] pub agent: AgentTransportConfig, /// Maximum request body size in bytes. diff --git a/crates/host-proxy/src/lib.rs b/crates/host-proxy/src/lib.rs index 047422948..e52ade16c 100644 --- a/crates/host-proxy/src/lib.rs +++ b/crates/host-proxy/src/lib.rs @@ -2,9 +2,9 @@ //! //! This crate provides the host-side HTTP/WebSocket server that: //! - Binds to ~/.substrate/sock/agent.sock -//! - Forwards requests to world-agent via agent-api-client +//! - Forwards requests to world-service via transport-api-client //! - Adds middleware for auth, rate limiting, and budgets -//! - Uses the same schema from agent-api-types +//! - Uses the same schema from transport-api-types pub mod auth; pub mod middleware; diff --git a/crates/host-proxy/src/runtime.rs b/crates/host-proxy/src/runtime.rs index 60fb6cd40..5e792e55c 100644 --- a/crates/host-proxy/src/runtime.rs +++ b/crates/host-proxy/src/runtime.rs @@ -4,15 +4,15 @@ use std::path::PathBuf; use std::sync::Arc; use std::time::{Duration, Instant}; -use agent_api_client::AgentClient; -use agent_api_core::AgentService; -use agent_api_types::{ApiError, ExecuteRequest, ExecuteResponse}; use anyhow::{Context, Result}; use async_trait::async_trait; use axum::{body::Body, response::Response}; use http_body_util::BodyExt; use tokio::sync::RwLock; use tracing::debug; +use transport_api_client::AgentClient; +use transport_api_core::AgentService; +use transport_api_types::{ApiError, ExecuteRequest, ExecuteResponse}; use crate::auth::AuthService; use crate::config::ProxyConfig; @@ -23,7 +23,7 @@ use crate::transport::AgentTransportConfig; #[cfg(unix)] use crate::transport::DEFAULT_AGENT_TCP_PORT; -/// Host proxy service that forwards requests to world-agent. +/// Host proxy service that forwards requests to world-service. #[derive(Clone)] pub struct HostProxyService { client: Arc, @@ -203,13 +203,13 @@ pub async fn cleanup_socket(socket_path: &Path) -> Result<()> { #[cfg(unix)] pub async fn run_host_proxy() -> Result<()> { - use agent_api_core::build_router; use axum::routing::get; use tower::ServiceBuilder; use tower::ServiceExt; use tower_http::limit::RequestBodyLimitLayer; use tracing::info; use tracing_subscriber::prelude::*; + use transport_api_core::build_router; tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) @@ -255,7 +255,7 @@ pub async fn run_host_proxy() -> Result<()> { } info!("Host proxy listening on: {:?}", socket_path); - info!("Ready to forward requests to world-agent"); + info!("Ready to forward requests to world-service"); loop { let (stream, _addr) = listener diff --git a/crates/replay/Cargo.toml b/crates/replay/Cargo.toml index 281308536..a886a6418 100644 --- a/crates/replay/Cargo.toml +++ b/crates/replay/Cargo.toml @@ -21,8 +21,8 @@ regex = "1.10" base64 = "0.22" # Internal dependencies -agent-api-client = { path = "../agent-api-client", version = "0.2.3" } -agent-api-types = { path = "../agent-api-types", version = "0.2.3" } +transport-api-client = { path = "../transport-api-client", version = "0.2.3" } +transport-api-types = { path = "../transport-api-types", version = "0.2.3" } substrate-common = { path = "../common" } substrate-trace = { path = "../trace" } substrate-broker = { path = "../broker" } diff --git a/crates/replay/src/replay/executor.rs b/crates/replay/src/replay/executor.rs index 6e00741d6..3909e65a8 100644 --- a/crates/replay/src/replay/executor.rs +++ b/crates/replay/src/replay/executor.rs @@ -11,19 +11,12 @@ use tokio::time::{timeout, Duration}; use super::{ExecutionResult, ExecutionState}; use crate::replay::helpers::replay_verbose; -use agent_api_types::{ExecuteRequest, PolicySnapshotV3}; #[cfg(target_os = "linux")] use substrate_common::FsDiff; use substrate_common::{log_schema, WorldRootMode}; use substrate_trace::append_to_trace; +use transport_api_types::{ExecuteRequest, PolicySnapshotV3}; -#[cfg(target_os = "linux")] -use agent_api_client::AgentClient; -#[cfg(target_os = "linux")] -use agent_api_types::{ - ExecuteResponse, PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, - PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, WorldFsDenyEnforcementV3, -}; #[cfg(target_os = "linux")] use base64::engine::general_purpose::STANDARD as BASE64; #[cfg(target_os = "linux")] @@ -33,6 +26,13 @@ use std::process as stdprocess; #[cfg(target_os = "linux")] use std::sync::OnceLock; #[cfg(target_os = "linux")] +use transport_api_client::AgentClient; +#[cfg(target_os = "linux")] +use transport_api_types::{ + ExecuteResponse, PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, + PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, WorldFsDenyEnforcementV3, +}; +#[cfg(target_os = "linux")] use world::{copydiff, overlayfs}; use world_api::WorldReuseMode; @@ -1043,12 +1043,12 @@ fn agent_socket_path(state: &ExecutionState) -> std::path::PathBuf { #[cfg(test)] mod tests { use super::*; - use agent_api_types::{ + use std::collections::HashMap; + use std::sync::{Mutex, OnceLock}; + use transport_api_types::{ PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, }; - use std::collections::HashMap; - use std::sync::{Mutex, OnceLock}; fn env_lock() -> std::sync::MutexGuard<'static, ()> { static LOCK: OnceLock> = OnceLock::new(); diff --git a/crates/replay/src/state.rs b/crates/replay/src/state.rs index ac52444a1..0359b767b 100644 --- a/crates/replay/src/state.rs +++ b/crates/replay/src/state.rs @@ -63,17 +63,25 @@ pub async fn load_span_from_trace(trace_file: &Path, span_id: &str) -> Result String { script } -fn run_world_command_for_deps(cmd: &str) -> Result { +fn run_world_command_for_deps(cmd: &str) -> Result { run_world_command_for_deps_at(cmd, None, None) } @@ -3052,7 +3052,7 @@ fn run_world_command_for_deps_at( cmd: &str, cwd_override: Option<&str>, profile_override: Option<&str>, -) -> Result { +) -> Result { let (client, mut request, _) = build_agent_client_and_request(cmd)?; // Runtime world-deps execution uses a dedicated internal profile so read-only probes and // world-deps wrapper installs can run on runners that block unprivileged user namespaces, @@ -3077,7 +3077,7 @@ fn run_world_command_for_deps_at( client .execute(request) .await - .context("world-agent /v1/execute request failed") + .context("world-service /v1/execute request failed") })?; Ok(response) } @@ -3121,7 +3121,7 @@ fn looks_like_world_backend_unavailable(err: &anyhow::Error) -> bool { let mut current: Option<&(dyn StdError + 'static)> = Some(err.as_ref()); while let Some(err) = current { let message = err.to_string(); - if message.contains("world-agent") + if message.contains("world-service") || message.contains("platform world context") || message.contains("world backend") || message.contains("connect UDS") diff --git a/crates/shell/src/builtins/world_enable/runner.rs b/crates/shell/src/builtins/world_enable/runner.rs index 2176dc970..9eeb6b566 100644 --- a/crates/shell/src/builtins/world_enable/runner.rs +++ b/crates/shell/src/builtins/world_enable/runner.rs @@ -14,7 +14,7 @@ use log_ops::{append_log_line, initialize_log_file, print_dry_run_plan}; use manager_env::update_manager_env_exports; use paths::{ locate_helper_script, next_log_path, resolve_version_dir, resolve_world_socket_path, - select_accepted_staged_world_agent, + select_accepted_staged_world_service, }; use provision_deps::{ ensure_supported_backend_or_exit, exit_probe_result_not_supported, print_pacman_requirements, @@ -58,27 +58,27 @@ fn enforce_standard_version_dir_preflight( return; }; - if select_accepted_staged_world_agent(version_dir).is_none() { + if select_accepted_staged_world_service(version_dir).is_none() { eprintln!( "{}", - render_missing_accepted_staged_world_agent_remediation(version_dir) + render_missing_accepted_staged_world_service_remediation(version_dir) ); std::process::exit(3); } } -fn render_missing_accepted_staged_world_agent_remediation(version_dir: &Path) -> String { - let root_candidate = version_dir.join("bin").join("world-agent"); - let linux_candidate = version_dir.join("bin").join("linux").join("world-agent"); +fn render_missing_accepted_staged_world_service_remediation(version_dir: &Path) -> String { + let root_candidate = version_dir.join("bin").join("world-service"); + let linux_candidate = version_dir.join("bin").join("linux").join("world-service"); format!( - "substrate: accepted staged world-agent artifact missing under {}\n\ + "substrate: accepted staged world-service artifact missing under {}\n\ substrate: expected one of:\n\ - {}\n\ - {}\n\ substrate: remediation:\n\ - scripts/substrate/dev-install-substrate.sh --no-world\n\ - - cargo build -p world-agent", + - cargo build -p world-service", version_dir.display(), root_candidate.display(), linux_candidate.display(), @@ -174,7 +174,7 @@ pub fn run_enable(args: &WorldEnableArgs) -> Result<()> { .socket_unit .as_ref() .map(|u| u.name) - .unwrap_or("substrate-world-agent.socket"), + .unwrap_or("substrate-world-service.socket"), activation_report .socket_unit .as_ref() @@ -349,7 +349,7 @@ fn run_enable_with_provision_deps(args: &WorldEnableArgs) -> Result<()> { .socket_unit .as_ref() .map(|u| u.name) - .unwrap_or("substrate-world-agent.socket"), + .unwrap_or("substrate-world-service.socket"), activation_report .socket_unit .as_ref() @@ -459,16 +459,16 @@ mod tests { use tempfile::tempdir; #[test] - fn render_missing_accepted_staged_world_agent_remediation_includes_paths_and_commands() { + fn render_missing_accepted_staged_world_service_remediation_includes_paths_and_commands() { let temp = tempdir().unwrap(); let version_dir = temp.path().join("version"); - let message = render_missing_accepted_staged_world_agent_remediation(&version_dir); + let message = render_missing_accepted_staged_world_service_remediation(&version_dir); assert!(message.contains( &version_dir .join("bin") - .join("world-agent") + .join("world-service") .display() .to_string() )); @@ -476,11 +476,11 @@ mod tests { &version_dir .join("bin") .join("linux") - .join("world-agent") + .join("world-service") .display() .to_string() )); assert!(message.contains("scripts/substrate/dev-install-substrate.sh --no-world")); - assert!(message.contains("cargo build -p world-agent")); + assert!(message.contains("cargo build -p world-service")); } } diff --git a/crates/shell/src/builtins/world_enable/runner/paths.rs b/crates/shell/src/builtins/world_enable/runner/paths.rs index 47bc54cfe..104768c7a 100644 --- a/crates/shell/src/builtins/world_enable/runner/paths.rs +++ b/crates/shell/src/builtins/world_enable/runner/paths.rs @@ -30,13 +30,13 @@ pub(super) fn resolve_version_dir(prefix: &Path) -> Result { Ok(version_dir.to_path_buf()) } -pub(super) fn select_accepted_staged_world_agent(version_dir: &Path) -> Option { - let root_candidate = version_dir.join("bin").join("world-agent"); +pub(super) fn select_accepted_staged_world_service(version_dir: &Path) -> Option { + let root_candidate = version_dir.join("bin").join("world-service"); if root_candidate.exists() { return Some(root_candidate); } - let linux_candidate = version_dir.join("bin").join("linux").join("world-agent"); + let linux_candidate = version_dir.join("bin").join("linux").join("world-service"); if linux_candidate.exists() { return Some(linux_candidate); } @@ -247,38 +247,38 @@ mod tests { } #[test] - fn select_accepted_staged_world_agent_prefers_root_path() { + fn select_accepted_staged_world_service_prefers_root_path() { let temp = tempdir().unwrap(); let version_dir = temp.path().join("version"); - let root_candidate = version_dir.join("bin").join("world-agent"); - let linux_candidate = version_dir.join("bin").join("linux").join("world-agent"); + let root_candidate = version_dir.join("bin").join("world-service"); + let linux_candidate = version_dir.join("bin").join("linux").join("world-service"); std::fs::create_dir_all(root_candidate.parent().unwrap()).unwrap(); std::fs::create_dir_all(linux_candidate.parent().unwrap()).unwrap(); std::fs::write(&root_candidate, "root").unwrap(); std::fs::write(&linux_candidate, "linux").unwrap(); - let selected = select_accepted_staged_world_agent(&version_dir).expect("world-agent"); + let selected = select_accepted_staged_world_service(&version_dir).expect("world-service"); assert_eq!(selected, root_candidate); } #[test] - fn select_accepted_staged_world_agent_falls_back_to_linux_path() { + fn select_accepted_staged_world_service_falls_back_to_linux_path() { let temp = tempdir().unwrap(); let version_dir = temp.path().join("version"); - let linux_candidate = version_dir.join("bin").join("linux").join("world-agent"); + let linux_candidate = version_dir.join("bin").join("linux").join("world-service"); std::fs::create_dir_all(linux_candidate.parent().unwrap()).unwrap(); std::fs::write(&linux_candidate, "linux").unwrap(); - let selected = select_accepted_staged_world_agent(&version_dir).expect("world-agent"); + let selected = select_accepted_staged_world_service(&version_dir).expect("world-service"); assert_eq!(selected, linux_candidate); } #[test] - fn select_accepted_staged_world_agent_returns_none_when_missing() { + fn select_accepted_staged_world_service_returns_none_when_missing() { let temp = tempdir().unwrap(); let version_dir = temp.path().join("version"); std::fs::create_dir_all(version_dir.join("bin")).unwrap(); - assert!(select_accepted_staged_world_agent(&version_dir).is_none()); + assert!(select_accepted_staged_world_service(&version_dir).is_none()); } } diff --git a/crates/shell/src/builtins/world_enable/runner/provision_deps.rs b/crates/shell/src/builtins/world_enable/runner/provision_deps.rs index 32f84caac..98cd2ae79 100644 --- a/crates/shell/src/builtins/world_enable/runner/provision_deps.rs +++ b/crates/shell/src/builtins/world_enable/runner/provision_deps.rs @@ -379,7 +379,7 @@ fn probe_requirements(requirements: &[AptSpecV1]) -> Result Result { +) -> Result { let (client, mut request, _) = build_agent_client_and_request(cmd).map_err(|err| format!("{err:#}"))?; request.profile = Some(profile.to_string()); @@ -392,7 +392,7 @@ fn execute_with_profile( client .execute(request) .await - .map_err(|err| format!("world-agent /v1/execute request failed: {err}")) + .map_err(|err| format!("world-service /v1/execute request failed: {err}")) }) } @@ -546,7 +546,7 @@ fn build_pacman_install_command(requirements: &[String]) -> String { format!("sh -lc {}", sh_quote(&script)) } -fn response_snippet(response: &agent_api_types::ExecuteResponse) -> String { +fn response_snippet(response: &transport_api_types::ExecuteResponse) -> String { let stderr = decode_output(&response.stderr_b64); if !stderr.trim().is_empty() { return stderr.trim().to_string(); diff --git a/crates/shell/src/builtins/world_gateway.rs b/crates/shell/src/builtins/world_gateway.rs index bfcf49328..71a9e8d86 100644 --- a/crates/shell/src/builtins/world_gateway.rs +++ b/crates/shell/src/builtins/world_gateway.rs @@ -6,12 +6,6 @@ use crate::execution::policy_snapshot::{ #[cfg(target_os = "windows")] use crate::execution::pw; use crate::execution::{WorldGatewayAction, WorldGatewayCmd, WorldGatewayStatusArgs}; -use agent_api_client::AgentClient; -use agent_api_types::{ - GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, GatewayIntegratedAuthPayloadV1, - GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, GatewayStatusV1, IdentityTuple, - PlacementExecution, PlacementPosture, -}; use serde_json::Value; use std::collections::HashMap; use std::fs; @@ -22,6 +16,12 @@ use std::os::unix::net::UnixStream; use std::path::PathBuf; #[cfg(target_os = "macos")] use std::time::Duration; +use transport_api_client::AgentClient; +use transport_api_types::{ + GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, GatewayIntegratedAuthPayloadV1, + GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, GatewayStatusV1, IdentityTuple, + PlacementExecution, PlacementPosture, +}; #[cfg(target_os = "linux")] const DEFAULT_WORLD_SOCKET_PATH: &str = "/run/substrate.sock"; diff --git a/crates/shell/src/execution/agent_runtime/control.rs b/crates/shell/src/execution/agent_runtime/control.rs index d5e6ce6f2..52db8bef2 100644 --- a/crates/shell/src/execution/agent_runtime/control.rs +++ b/crates/shell/src/execution/agent_runtime/control.rs @@ -11,8 +11,6 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -#[cfg(target_os = "linux")] -use agent_api_types::{ExecuteStreamFrame, MemberTurnSubmitRequestV1}; use anyhow::{Context, Result}; #[cfg(target_os = "linux")] use base64::engine::general_purpose::STANDARD as BASE64; @@ -26,6 +24,8 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; #[cfg(unix)] use tokio::net::{UnixListener, UnixStream}; use tokio::sync::{mpsc, oneshot}; +#[cfg(target_os = "linux")] +use transport_api_types::{ExecuteStreamFrame, MemberTurnSubmitRequestV1}; use uuid::Uuid; #[cfg(unix)] diff --git a/crates/shell/src/execution/agent_runtime/mapping.rs b/crates/shell/src/execution/agent_runtime/mapping.rs index a775677d6..caf672660 100644 --- a/crates/shell/src/execution/agent_runtime/mapping.rs +++ b/crates/shell/src/execution/agent_runtime/mapping.rs @@ -1,6 +1,7 @@ use anyhow::Result; -pub(crate) const PURE_AGENT_PROTOCOL: &str = "uaa.agent.session"; +pub(crate) const PURE_AGENT_PROTOCOL: &str = "substrate.agent.session"; +pub(crate) const LEGACY_PURE_AGENT_PROTOCOL: &str = concat!("uaa.agent", ".session"); pub(crate) const PURE_AGENT_ROUTER: &str = "agent_hub"; pub(crate) const NESTED_ROUTER: &str = "substrate_gateway"; pub(crate) const ORCHESTRATOR_ROLE: &str = "orchestrator"; @@ -31,3 +32,12 @@ pub(crate) fn orchestrator_backend_kind(agent_id: &str) -> Result) -> String { + match actual { + Some(LEGACY_PURE_AGENT_PROTOCOL) => format!( + "{subject} advertises legacy unsupported protocol '{LEGACY_PURE_AGENT_PROTOCOL}'; rename it to '{PURE_AGENT_PROTOCOL}'" + ), + _ => format!("{subject} does not advertise protocol '{PURE_AGENT_PROTOCOL}'"), + } +} diff --git a/crates/shell/src/execution/agent_runtime/orchestration_session.rs b/crates/shell/src/execution/agent_runtime/orchestration_session.rs index f6bd9821c..f37bdb5df 100644 --- a/crates/shell/src/execution/agent_runtime/orchestration_session.rs +++ b/crates/shell/src/execution/agent_runtime/orchestration_session.rs @@ -1,6 +1,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use super::mapping::{protocol_validation_error, PURE_AGENT_PROTOCOL}; use super::session::AgentRuntimeSessionManifest; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -226,6 +227,13 @@ impl OrchestrationSessionRecord { } pub(crate) fn validate_persisted_invariants(&self) -> anyhow::Result<()> { + if self.orchestrator_protocol != PURE_AGENT_PROTOCOL { + anyhow::bail!( + "orchestration session {}", + protocol_validation_error("it", Some(self.orchestrator_protocol.as_str())) + ); + } + match self.posture { OrchestrationSessionPosture::ActiveAttached => { if self.attached_participant_id.is_none() { @@ -373,7 +381,7 @@ mod tests { agent_id: "codex".to_string(), backend_id: "cli:codex".to_string(), backend_kind: AgentRuntimeBackendKind::Codex, - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), execution_scope: AgentExecutionScope::Host, binary_path: PathBuf::from("/usr/bin/codex"), }, diff --git a/crates/shell/src/execution/agent_runtime/session.rs b/crates/shell/src/execution/agent_runtime/session.rs index 737fe5844..c981dc792 100644 --- a/crates/shell/src/execution/agent_runtime/session.rs +++ b/crates/shell/src/execution/agent_runtime/session.rs @@ -3,7 +3,9 @@ use serde::{Deserialize, Deserializer, Serialize}; use crate::execution::config_model::AgentExecutionScope; -use super::mapping::{MEMBER_ROLE, ORCHESTRATOR_ROLE}; +use super::mapping::{ + protocol_validation_error, MEMBER_ROLE, ORCHESTRATOR_ROLE, PURE_AGENT_PROTOCOL, +}; use super::orchestration_session::OrchestrationSessionRecord; use super::validator::RuntimeSelectionDescriptor; @@ -375,6 +377,12 @@ impl AgentRuntimeParticipantRecord { if self.handle.orchestration_session_id.trim().is_empty() { anyhow::bail!("orchestration_session_id must not be empty"); } + if self.handle.protocol != PURE_AGENT_PROTOCOL { + anyhow::bail!( + "participant record {}", + protocol_validation_error("it", Some(self.handle.protocol.as_str())) + ); + } let world_fields_present = self.handle.world_id.is_some() || self.handle.world_generation.is_some(); @@ -689,14 +697,16 @@ mod tests { use serde_json::json; use super::*; - use crate::execution::agent_runtime::mapping::AgentRuntimeBackendKind; + use crate::execution::agent_runtime::mapping::{ + AgentRuntimeBackendKind, LEGACY_PURE_AGENT_PROTOCOL, + }; fn descriptor(scope: AgentExecutionScope) -> RuntimeSelectionDescriptor { RuntimeSelectionDescriptor { agent_id: "codex".to_string(), backend_id: "cli:codex".to_string(), backend_kind: AgentRuntimeBackendKind::Codex, - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), execution_scope: scope, binary_path: PathBuf::from("/usr/bin/codex"), } @@ -913,7 +923,7 @@ mod tests { "agent_id": "codex", "backend_id": "cli:codex", "role": "orchestrator", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "execution": { "scope": "host" }, "state": "allocating", "opened_at": "2026-04-24T18:30:00Z", @@ -970,7 +980,7 @@ mod tests { "agent_id": "codex", "backend_id": "cli:codex", "role": "orchestrator", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "execution": { "scope": "world" }, "state": "allocating", "opened_at": "2026-04-24T18:30:00Z", @@ -995,4 +1005,41 @@ mod tests { .to_string() .contains("orchestrator participants must use execution.scope=host")); } + + #[test] + fn deserialize_rejects_legacy_protocol_row() { + let payload = json!({ + "participant_id": "ash_001", + "orchestration_session_id": "sess_001", + "agent_id": "codex", + "backend_id": "cli:codex", + "role": ORCHESTRATOR_ROLE, + "protocol": LEGACY_PURE_AGENT_PROTOCOL, + "execution": { "scope": "host" }, + "state": "ready", + "opened_at": "2026-05-21T00:00:00Z", + "last_transition_at": "2026-05-21T00:00:00Z", + "internal": { + "resolved_agent_kind": "codex", + "resolved_binary_path": "/usr/bin/codex", + "shell_owner_pid": 1, + "lease_token": "lease_001", + "cancel_supported": true, + "control_owner_retained": false, + "event_stream_active": false, + "completion_observer_retained": false, + "ownership_valid": false, + "attached_client_present": false, + "resume_eligible": false + } + }); + + let error = serde_json::from_value::(payload) + .expect_err("legacy protocol rows must fail closed"); + assert!( + error.to_string().contains(LEGACY_PURE_AGENT_PROTOCOL) + && error.to_string().contains(PURE_AGENT_PROTOCOL), + "unexpected error: {error}" + ); + } } diff --git a/crates/shell/src/execution/agent_runtime/state_store.rs b/crates/shell/src/execution/agent_runtime/state_store.rs index ba99b089f..061ffb02d 100644 --- a/crates/shell/src/execution/agent_runtime/state_store.rs +++ b/crates/shell/src/execution/agent_runtime/state_store.rs @@ -2463,7 +2463,7 @@ mod tests { agent_id: agent_id.to_string(), backend_id: format!("cli:{agent_id}"), backend_kind: AgentRuntimeBackendKind::Codex, - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), execution_scope: scope, binary_path: PathBuf::from("/usr/bin/codex"), } @@ -2578,7 +2578,7 @@ mod tests { "agent_id": agent_id, "backend_id": format!("cli:{agent_id}"), "role": "orchestrator", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "execution": { "scope": "host" }, "state": "ready", "opened_at": "2026-04-24T18:30:00Z", @@ -4338,7 +4338,7 @@ mod tests { "agent_id": "codex", "backend_id": "cli:codex", "role": "member", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "execution": { "scope": "world" }, "state": "ready", "opened_at": "2026-04-24T18:30:00Z", diff --git a/crates/shell/src/execution/agent_runtime/validator.rs b/crates/shell/src/execution/agent_runtime/validator.rs index 48ed05546..19932c20d 100644 --- a/crates/shell/src/execution/agent_runtime/validator.rs +++ b/crates/shell/src/execution/agent_runtime/validator.rs @@ -9,7 +9,10 @@ use crate::execution::agent_inventory::{ }; use crate::execution::config_model::{AgentCliMode, AgentExecutionScope, SubstrateConfig}; -use super::mapping::{orchestrator_backend_kind, AgentRuntimeBackendKind, PURE_AGENT_PROTOCOL}; +use super::mapping::{ + orchestrator_backend_kind, protocol_validation_error, AgentRuntimeBackendKind, + PURE_AGENT_PROTOCOL, +}; #[derive(Clone, Debug)] pub(crate) struct RuntimeSelectionDescriptor { @@ -74,9 +77,9 @@ pub(crate) fn validate_orchestrator_selection<'a>( } if entry.file.config.protocol.as_deref() != Some(PURE_AGENT_PROTOCOL) { - return Err(format!( - "orchestrator agent '{}' does not advertise protocol '{}'", - orchestrator_agent_id, PURE_AGENT_PROTOCOL + return Err(protocol_validation_error( + &format!("orchestrator agent '{orchestrator_agent_id}'"), + entry.file.config.protocol.as_deref(), )); } @@ -196,8 +199,9 @@ pub(crate) fn validate_member_selection( return Err(MemberSelectionError { exit_code: 2, reason: format!( - "world-scoped member '{}' is not eligible because it does not advertise protocol '{}'", - entry.file.id, PURE_AGENT_PROTOCOL + "world-scoped member '{}' is not eligible because {}", + entry.file.id, + protocol_validation_error("it", entry.file.config.protocol.as_deref()) ), }); } @@ -292,11 +296,15 @@ fn validate_exact_backend_entry( return Err(ExactBackendSelectionError { exit_code: 2, reason: format!( - "selected {} runtime '{}' for backend '{}' does not advertise protocol '{}'", + "selected {} runtime '{}' for backend '{}' {}", runtime_scope_label(scope), entry.file.id, backend_id, - PURE_AGENT_PROTOCOL, + protocol_validation_error( + "does not advertise", + entry.file.config.protocol.as_deref() + ) + .replacen("does not advertise ", "", 1), ), }); } @@ -359,6 +367,7 @@ mod tests { AgentCapabilitiesV1, AgentCliConfigV1, AgentConfigKind, AgentConfigV1, AgentExecutionConfigV1, AgentFileV1, AgentInventoryEntryV1, }; + use crate::execution::agent_runtime::mapping::LEGACY_PURE_AGENT_PROTOCOL; use crate::execution::config_model::{AgentCliMode, AgentExecutionScope, SubstrateConfig}; use std::collections::BTreeMap; use std::path::PathBuf; @@ -522,7 +531,32 @@ mod tests { assert!( error .reason - .contains("does not advertise protocol 'uaa.agent.session'"), + .contains("does not advertise protocol 'substrate.agent.session'"), + "unexpected reason: {}", + error.reason + ); + } + + #[test] + fn validate_member_selection_rejects_legacy_protocol_with_rename_guidance() { + let config = SubstrateConfig::default(); + let mut inventory = BTreeMap::new(); + inventory.insert( + "codex".to_string(), + make_entry( + "codex", + AgentExecutionScope::World, + Some(LEGACY_PURE_AGENT_PROTOCOL), + AgentCliMode::Persistent, + required_capabilities(), + ), + ); + + let error = validate_member_selection(&config, &inventory).expect_err("must fail closed"); + assert_eq!(error.exit_code, 2); + assert!( + error.reason.contains(LEGACY_PURE_AGENT_PROTOCOL) + && error.reason.contains(PURE_AGENT_PROTOCOL), "unexpected reason: {}", error.reason ); @@ -614,7 +648,7 @@ mod tests { assert!( error .reason - .contains("selected host-scoped runtime 'claude_code' for backend 'cli:claude_code' does not advertise protocol 'uaa.agent.session'"), + .contains("selected host-scoped runtime 'claude_code' for backend 'cli:claude_code' does not advertise protocol 'substrate.agent.session'"), "unexpected reason: {}", error.reason ); diff --git a/crates/shell/src/execution/invocation/plan.rs b/crates/shell/src/execution/invocation/plan.rs index c49ed2677..74fb1bd7c 100644 --- a/crates/shell/src/execution/invocation/plan.rs +++ b/crates/shell/src/execution/invocation/plan.rs @@ -755,7 +755,7 @@ fn shim_status_socket_json() -> serde_json::Value { "substrate", "systemctl", "is-active", - "substrate-world-agent.socket", + "substrate-world-service.socket", ]) .output() .ok() @@ -767,7 +767,7 @@ fn shim_status_socket_json() -> serde_json::Value { "substrate", "systemctl", "is-active", - "substrate-world-agent", + "substrate-world-service", ]) .output() .ok() @@ -812,7 +812,7 @@ fn print_socket_activation_summary() { .socket_unit .as_ref() .map(|u| u.name) - .unwrap_or("substrate-world-agent.socket"), + .unwrap_or("substrate-world-service.socket"), report .socket_unit .as_ref() @@ -826,7 +826,7 @@ fn print_socket_activation_summary() { .socket_unit .as_ref() .map(|u| u.name) - .unwrap_or("substrate-world-agent.socket"), + .unwrap_or("substrate-world-service.socket"), report .socket_unit .as_ref() @@ -889,7 +889,7 @@ fn print_socket_activation_summary() { "substrate", "systemctl", "is-active", - "substrate-world-agent.socket", + "substrate-world-service.socket", ]) .output() .ok() @@ -897,7 +897,7 @@ fn print_socket_activation_summary() { .unwrap_or(false); if socket_unit_active { - println!("World socket: socket activation (substrate-world-agent.socket active in Lima)"); + println!("World socket: socket activation (substrate-world-service.socket active in Lima)"); } else if socket_exists { println!("World socket: manual forwarded listener present"); } else { diff --git a/crates/shell/src/execution/platform/linux.rs b/crates/shell/src/execution/platform/linux.rs index 93a916375..25892146b 100644 --- a/crates/shell/src/execution/platform/linux.rs +++ b/crates/shell/src/execution/platform/linux.rs @@ -1,10 +1,4 @@ use crate::execution::socket_activation; -use agent_api_client::AgentClient; -use agent_api_types::{ - ExecuteRequest, WorldDoctorLandlockV1, WorldDoctorNetfilterStatusV1, WorldDoctorReportV1, - WorldDoctorWorldFsStrategyKindV1, WorldDoctorWorldFsStrategyProbeResultV1, - WorldDoctorWorldFsStrategyProbeV1, WorldDoctorWorldFsStrategyV1, WorldFsMode, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use serde_json::json; @@ -16,6 +10,12 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::Duration; use substrate_broker::{detect_profile, world_fs_policy}; +use transport_api_client::AgentClient; +use transport_api_types::{ + ExecuteRequest, WorldDoctorLandlockV1, WorldDoctorNetfilterStatusV1, WorldDoctorReportV1, + WorldDoctorWorldFsStrategyKindV1, WorldDoctorWorldFsStrategyProbeResultV1, + WorldDoctorWorldFsStrategyProbeV1, WorldDoctorWorldFsStrategyV1, WorldFsMode, +}; use which::which; pub(crate) fn host_doctor_main( @@ -168,7 +168,7 @@ pub(crate) fn host_doctor_main( "socket_exists": activation_report.socket_exists, "authorization_boundary": { "kind": "unix_socket_acl", - "description": "Socket ACL is the caller authorization boundary; any local user that can open the socket can issue world-agent requests." + "description": "Socket ACL is the caller authorization boundary; any local user that can open the socket can issue world-service requests." }, "socket_acl": socket_acl.as_ref().map(|acl| json!({ "is_socket": acl.is_socket, @@ -287,7 +287,7 @@ pub(crate) fn host_doctor_main( None }; - info("authorization boundary: socket ACL (local users with RW access to the socket can issue world-agent requests)"); + info("authorization boundary: socket ACL (local users with RW access to the socket can issue world-service requests)"); if activation_report.socket_exists { if let Some(acl) = &socket_acl { let owner = acl @@ -299,19 +299,19 @@ pub(crate) fn host_doctor_main( .clone() .unwrap_or_else(|| acl.group_gid.to_string()); info(&format!( - "world-agent socket ACL: owner={owner} group={group} mode={}", + "world-service socket ACL: owner={owner} group={group} mode={}", acl.mode_octal, )); } match (activation_report.is_socket_activated(), socket_probe_ok) { - (true, true) => pass("world-agent socket: systemd-managed and reachable"), - (true, false) => fail("world-agent socket: systemd-managed but unreachable"), - (false, true) => pass("world-agent socket: reachable"), - (false, false) => fail("world-agent socket: present but unreachable"), + (true, true) => pass("world-service socket: systemd-managed and reachable"), + (true, false) => fail("world-service socket: systemd-managed but unreachable"), + (false, true) => pass("world-service socket: reachable"), + (false, false) => fail("world-service socket: present but unreachable"), } } else { fail(&format!( - "world-agent socket: missing at {}", + "world-service socket: missing at {}", activation_report.socket_path )); } @@ -477,7 +477,7 @@ pub(crate) fn world_doctor_main( "socket_exists": activation_report.socket_exists, "authorization_boundary": { "kind": "unix_socket_acl", - "description": "Socket ACL is the caller authorization boundary; any local user that can open the socket can issue world-agent requests." + "description": "Socket ACL is the caller authorization boundary; any local user that can open the socket can issue world-service requests." }, "socket_acl": socket_acl.as_ref().map(|acl| json!({ "is_socket": acl.is_socket, @@ -533,11 +533,11 @@ pub(crate) fn world_doctor_main( if json_mode { let detail = socket_probe_error .as_deref() - .unwrap_or("world-agent socket probe failed"); + .unwrap_or("world-service socket probe failed"); if activation_report.is_socket_activated() { - eprintln!("world-agent readiness (socket activation) probe failed: {detail}"); + eprintln!("world-service readiness (socket activation) probe failed: {detail}"); } else { - eprintln!("world-agent readiness probe failed: {detail}"); + eprintln!("world-service readiness probe failed: {detail}"); } } exit_code = 3; @@ -587,9 +587,9 @@ pub(crate) fn world_doctor_main( Err(_) => { if json_mode { if activation_report.is_socket_activated() { - eprintln!("world-agent readiness (socket activation) request failed"); + eprintln!("world-service readiness (socket activation) request failed"); } else { - eprintln!("world-agent readiness request failed"); + eprintln!("world-service readiness request failed"); } } exit_code = 3; @@ -680,7 +680,7 @@ pub(crate) fn world_doctor_main( None }; - info("authorization boundary: socket ACL (local users with RW access to the socket can issue world-agent requests)"); + info("authorization boundary: socket ACL (local users with RW access to the socket can issue world-service requests)"); if activation_report.socket_exists { if let Some(acl) = &socket_acl { let owner = acl @@ -692,19 +692,19 @@ pub(crate) fn world_doctor_main( .clone() .unwrap_or_else(|| acl.group_gid.to_string()); info(&format!( - "world-agent socket ACL: owner={owner} group={group} mode={}", + "world-service socket ACL: owner={owner} group={group} mode={}", acl.mode_octal )); } match (activation_report.is_socket_activated(), socket_probe_ok) { - (true, true) => pass("world-agent socket: systemd-managed and reachable"), - (true, false) => fail("world-agent socket: systemd-managed but unreachable"), - (false, true) => pass("world-agent socket: reachable"), - (false, false) => fail("world-agent socket: present but unreachable"), + (true, true) => pass("world-service socket: systemd-managed and reachable"), + (true, false) => fail("world-service socket: systemd-managed but unreachable"), + (false, true) => pass("world-service socket: reachable"), + (false, false) => fail("world-service socket: present but unreachable"), } } else { fail(&format!( - "world-agent socket: missing at {}", + "world-service socket: missing at {}", activation_report.socket_path )); } diff --git a/crates/shell/src/execution/platform/macos.rs b/crates/shell/src/execution/platform/macos.rs index 3166f91f6..a8a81a9f4 100644 --- a/crates/shell/src/execution/platform/macos.rs +++ b/crates/shell/src/execution/platform/macos.rs @@ -34,7 +34,6 @@ pub(crate) fn world_doctor_main( mod world_doctor_macos { use super::*; - use agent_api_client::AgentClient; use chrono::SecondsFormat; use serde_json::Value; use std::io::{Read, Write}; @@ -44,6 +43,7 @@ mod world_doctor_macos { use std::path::PathBuf; use std::process::Command; use std::time::Duration; + use transport_api_client::AgentClient; pub(super) trait CommandRunner { fn run(&self, program: &str, args: &[&str]) -> CommandOutput; @@ -186,7 +186,7 @@ cat /sys/kernel/security/landlock/abi_version let combined = format!("{}{}", landlock_output.stdout, landlock_output.stderr); // Some Lima guests don't expose the Landlock ABI via securityfs, even when the // Landlock syscalls are present. Prefer a best-effort "supported" fallback so macOS - // doctor scopes remain usable when the deployed world-agent is behind this CLI. + // doctor scopes remain usable when the deployed world-service is behind this CLI. if combined.contains("landlock/abi_version") && (combined.contains("No such file") || combined.contains("not found")) { @@ -360,7 +360,7 @@ echo pass &vm_name, "systemctl", "is-active", - "substrate-world-agent", + "substrate-world-service", ], ) .success @@ -628,7 +628,7 @@ echo pass &vm_name, "systemctl", "is-active", - "substrate-world-agent", + "substrate-world-service", ], ) .success @@ -638,9 +638,9 @@ echo pass if !json_mode && can_probe_vm { if service_active { - pass("substrate-world-agent service active"); + pass("substrate-world-service service active"); } else { - fail("substrate-world-agent service not active"); + fail("substrate-world-service service not active"); } } @@ -659,9 +659,9 @@ echo pass if !json_mode && can_probe_vm && service_active { if agent_caps_ok { - pass("world-agent reachable (capabilities probe)"); + pass("world-service reachable (capabilities probe)"); } else { - fail("world-agent unreachable (capabilities probe)"); + fail("world-service unreachable (capabilities probe)"); } } @@ -783,17 +783,17 @@ echo pass if !json_mode && can_probe_vm { if assessment.service_active { - pass("substrate-world-agent service active"); + pass("substrate-world-service service active"); } else { - fail("substrate-world-agent service not active"); + fail("substrate-world-service service not active"); } } if !json_mode && can_probe_vm && assessment.service_active { if assessment.agent_caps_ok { - pass("world-agent reachable (capabilities probe)"); + pass("world-service reachable (capabilities probe)"); } else { - fail("world-agent unreachable (capabilities probe)"); + fail("world-service unreachable (capabilities probe)"); } } @@ -947,7 +947,7 @@ echo pass if first_line.starts_with("GET /v1/capabilities") { write_response( &mut stream, - r#"{"version":"v1","features":["execute"],"backend":"world-agent","platform":"linux"}"#, + r#"{"version":"v1","features":["execute"],"backend":"world-service","platform":"linux"}"#, ); } else if first_line.starts_with("GET /v1/doctor/world") { write_response( @@ -1041,7 +1041,7 @@ echo pass "substrate".into(), "systemctl".into(), "is-active".into(), - "substrate-world-agent".into(), + "substrate-world-service".into(), ], success_out("active\n"), ), @@ -1108,7 +1108,7 @@ echo pass vm_name.into(), "systemctl".into(), "is-active".into(), - "substrate-world-agent".into(), + "substrate-world-service".into(), ], success_out("active\n"), ), @@ -1157,7 +1157,7 @@ echo pass vm_name.into(), "systemctl".into(), "is-active".into(), - "substrate-world-agent".into(), + "substrate-world-service".into(), ], success_out("active\n"), ), @@ -1239,7 +1239,7 @@ echo pass "substrate".into(), "systemctl".into(), "is-active".into(), - "substrate-world-agent".into(), + "substrate-world-service".into(), ], failure_out(), ), diff --git a/crates/shell/src/execution/platform_world/mod.rs b/crates/shell/src/execution/platform_world/mod.rs index 861e1acca..e66de8ef1 100644 --- a/crates/shell/src/execution/platform_world/mod.rs +++ b/crates/shell/src/execution/platform_world/mod.rs @@ -11,7 +11,6 @@ pub fn detect() -> Result { use crate::execution::policy_snapshot::bootstrap_world_spec; #[cfg(not(target_os = "windows"))] use crate::execution::settings; -use agent_api_types::SharedWorldOwnerSpec; #[cfg(target_os = "linux")] use anyhow::Context; use anyhow::Result; @@ -21,6 +20,7 @@ use std::path::PathBuf; use std::pin::Pin; use std::sync::Arc; use std::sync::OnceLock; +use transport_api_types::SharedWorldOwnerSpec; use world_api::WorldBackend; #[derive(Clone, Debug)] diff --git a/crates/shell/src/execution/platform_world/windows.rs b/crates/shell/src/execution/platform_world/windows.rs index 549fce925..14c9dd1ea 100644 --- a/crates/shell/src/execution/platform_world/windows.rs +++ b/crates/shell/src/execution/platform_world/windows.rs @@ -3,11 +3,11 @@ use crate::execution::policy_snapshot::bootstrap_world_spec as build_bootstrap_w use crate::execution::settings; #[cfg(test)] use crate::execution::world_env_guard; -use agent_api_client::{AgentClient, Transport}; use anyhow::{Context, Result}; use std::path::PathBuf; use std::sync::{Arc, OnceLock}; use substrate_broker::world_fs_mode; +use transport_api_client::{AgentClient, Transport}; use world_api::{SharedWorldOwnerSpec, WorldBackend, WorldSpec}; use world_windows_wsl::WindowsWslBackend; diff --git a/crates/shell/src/execution/policy_snapshot.rs b/crates/shell/src/execution/policy_snapshot.rs index ffe9ea05a..18048a08a 100644 --- a/crates/shell/src/execution/policy_snapshot.rs +++ b/crates/shell/src/execution/policy_snapshot.rs @@ -1,8 +1,3 @@ -use agent_api_types::{ - validate_net_allowed_for_enforcement, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsDenyEnforcementV3, -}; use anyhow::{anyhow, Context, Result}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; @@ -14,6 +9,11 @@ use std::path::{Path, PathBuf}; use std::sync::{Mutex, OnceLock}; use std::time::SystemTime; use substrate_common::WorldFsMode; +use transport_api_types::{ + validate_net_allowed_for_enforcement, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsDenyEnforcementV3, +}; use world_api::{ResourceLimits, WorldReuseMode, WorldSpec}; const WORLD_FS_ENFORCEMENT_PLAN_B64_ENV: &str = "SUBSTRATE_WORLD_FS_ENFORCEMENT_PLAN_B64"; @@ -184,8 +184,8 @@ pub(crate) fn bootstrap_world_spec(project_dir: PathBuf, fs_mode: WorldFsMode) - pub(crate) fn request_world_network_routing( network_policy: &ResolvedWorldNetworkPolicy, -) -> agent_api_types::WorldNetworkRoutingV1 { - agent_api_types::WorldNetworkRoutingV1 { +) -> transport_api_types::WorldNetworkRoutingV1 { + transport_api_types::WorldNetworkRoutingV1 { isolate_network: network_policy.isolate_network, allowed_domains: network_policy.allowed_domains.clone(), } diff --git a/crates/shell/src/execution/pty/mod.rs b/crates/shell/src/execution/pty/mod.rs index 331ea909a..bca4ea6b9 100644 --- a/crates/shell/src/execution/pty/mod.rs +++ b/crates/shell/src/execution/pty/mod.rs @@ -4,6 +4,6 @@ mod io; #[allow(unused_imports)] pub use io::{execute_with_pty, PtyExitStatus}; -// Shared helpers used by non-local PTY transports (e.g. world-agent WS PTY). +// Shared helpers used by non-local PTY transports (e.g. world-service WS PTY). #[allow(unused_imports)] pub(crate) use io::{get_terminal_size, MinimalTerminalGuard}; diff --git a/crates/shell/src/execution/repl_persistent_session.rs b/crates/shell/src/execution/repl_persistent_session.rs index e7200ad07..5aafe6480 100644 --- a/crates/shell/src/execution/repl_persistent_session.rs +++ b/crates/shell/src/execution/repl_persistent_session.rs @@ -1,7 +1,7 @@ //! Pure protocol core for the world-first REPL persistent session (PROTOCOL v1). //! //! This is intentionally transport-agnostic so we can unit test fail-closed behavior without -//! standing up a world-agent WebSocket. +//! standing up a world-service WebSocket. #![allow(dead_code)] @@ -279,7 +279,7 @@ impl PersistentSessionClientCore { )); } let seq_note = seq.map(|s| format!(" seq={s}")).unwrap_or_default(); - Err(self.latch_fatal(format!("world-agent error ({code}{seq_note}): {message}"))) + Err(self.latch_fatal(format!("world-service error ({code}{seq_note}): {message}"))) } ServerFrame::Unknown => Err(self .latch_fatal("protocol error: unknown server frame type (protocol fail-closed)")), diff --git a/crates/shell/src/execution/routing/dispatch/exec.rs b/crates/shell/src/execution/routing/dispatch/exec.rs index 9cf3467f7..4269226bb 100644 --- a/crates/shell/src/execution/routing/dispatch/exec.rs +++ b/crates/shell/src/execution/routing/dispatch/exec.rs @@ -50,7 +50,7 @@ use substrate_trace::{create_span_builder, ExecutionOrigin, PolicyDecision}; #[cfg(target_os = "linux")] const WORLD_BACKEND_UNAVAILABLE_HINT: &str = - "hint: run 'substrate world doctor --json' and check 'systemctl status substrate-world-agent.socket'"; + "hint: run 'substrate world doctor --json' and check 'systemctl status substrate-world-service.socket'"; #[cfg(not(target_os = "linux"))] const WORLD_BACKEND_UNAVAILABLE_HINT: &str = "hint: run 'substrate world doctor --json'"; @@ -491,7 +491,7 @@ pub(crate) fn execute_command( let start_time = std::time::Instant::now(); if should_use_pty { - // Attempt world-agent PTY WS route on Linux when world is enabled or agent socket exists + // Attempt world-service PTY WS route on Linux when world is enabled or agent socket exists #[cfg(target_os = "linux")] { let socket_path = world_socket_path(); @@ -688,7 +688,7 @@ pub(crate) fn execute_command( } } - // Attempt world-agent PTY WS route on mac when world is enabled + // Attempt world-service PTY WS route on mac when world is enabled #[cfg(target_os = "macos")] { let context = pw::get_context(); @@ -1000,7 +1000,7 @@ pub(crate) fn execute_command( Err(e) => { if world_required { anyhow::bail!( - "world execution required ({}); world-agent exec failed: {}", + "world execution required ({}); world-service exec failed: {}", world_required_reason, e ); @@ -1082,7 +1082,7 @@ pub(crate) fn execute_command( Err(e) => { if world_required { anyhow::bail!( - "world execution required ({}); world-agent exec failed: {}", + "world execution required ({}); world-service exec failed: {}", world_required_reason, e ); @@ -1276,7 +1276,7 @@ pub(crate) fn execute_command( } else { if world_required { anyhow::bail!( - "world execution required ({}); world-agent exec failed: {}", + "world execution required ({}); world-service exec failed: {}", world_required_reason, e ); diff --git a/crates/shell/src/execution/routing/dispatch/shim_ops.rs b/crates/shell/src/execution/routing/dispatch/shim_ops.rs index a131c93de..c05a04aa8 100644 --- a/crates/shell/src/execution/routing/dispatch/shim_ops.rs +++ b/crates/shell/src/execution/routing/dispatch/shim_ops.rs @@ -51,7 +51,7 @@ pub(crate) fn build_world_env_map_for_cwd( env_map.insert("XDG_CACHE_HOME".to_string(), "/root/.cache".to_string()); env_map.insert("TERM".to_string(), "xterm-256color".to_string()); - // Preserve Substrate/WORLD control-plane env so policy/config state is visible to world-agent + // Preserve Substrate/WORLD control-plane env so policy/config state is visible to world-service // and downstream shims, without inheriting arbitrary host user environment. for (key, value) in std::env::vars() { let keep = key.starts_with("SUBSTRATE_") || key.starts_with("WORLD_"); diff --git a/crates/shell/src/execution/routing/dispatch/tests/telemetry.rs b/crates/shell/src/execution/routing/dispatch/tests/telemetry.rs index 38475f480..d7aac751d 100644 --- a/crates/shell/src/execution/routing/dispatch/tests/telemetry.rs +++ b/crates/shell/src/execution/routing/dispatch/tests/telemetry.rs @@ -1,14 +1,14 @@ use super::*; use crate::execution::agent_events::{self, clear_agent_event_sender, init_event_channel}; use crate::execution::routing::dispatch::world_ops::consume_agent_stream_buffer_with_meta; -use agent_api_types::{ - ExecuteStreamFrame, ProcessEvent, ProcessEventType, ProcessEventsStatus, ProcessTelemetry, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use serde_json::Value as JsonValue; use substrate_common::FsDiff; use tokio::runtime::Runtime; +use transport_api_types::{ + ExecuteStreamFrame, ProcessEvent, ProcessEventType, ProcessEventsStatus, ProcessTelemetry, +}; // Telemetry stream handling #[test] diff --git a/crates/shell/src/execution/routing/dispatch/world_ops.rs b/crates/shell/src/execution/routing/dispatch/world_ops.rs index be5710586..17d9796f8 100644 --- a/crates/shell/src/execution/routing/dispatch/world_ops.rs +++ b/crates/shell/src/execution/routing/dispatch/world_ops.rs @@ -16,14 +16,6 @@ use crate::execution::routing::{get_term_size, RawModeGuard}; use crate::execution::world_env_guard; #[cfg(target_os = "linux")] use crate::execution::{policy_snapshot::bootstrap_world_spec, socket_activation}; -#[cfg(any(target_os = "linux", target_os = "macos"))] -use agent_api_client::AgentClient; -#[cfg(not(target_os = "windows"))] -use agent_api_types::ExecuteCancelRequestV1; -use agent_api_types::{ - ExecuteRequest, ExecuteStreamFrame, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, - ProcessTelemetry, ResolvedMemberRuntimeDescriptorV1, WorldFsMode, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use std::env; @@ -36,6 +28,14 @@ use tokio::net::UnixStream; use tokio::signal::unix::{signal, SignalKind}; #[cfg(any(target_os = "linux", target_os = "macos"))] use tokio_tungstenite as tungs; +#[cfg(any(target_os = "linux", target_os = "macos"))] +use transport_api_client::AgentClient; +#[cfg(not(target_os = "windows"))] +use transport_api_types::ExecuteCancelRequestV1; +use transport_api_types::{ + ExecuteRequest, ExecuteStreamFrame, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, + ProcessTelemetry, ResolvedMemberRuntimeDescriptorV1, WorldFsMode, +}; #[cfg(target_os = "linux")] use world::LinuxLocalBackend; #[cfg(target_os = "linux")] @@ -119,7 +119,7 @@ pub(super) fn normalize_env_for_linux_guest( } // Note: SUBSTRATE_WORLD_DEPS_GUEST_BIN_DIR is set above and may be overridden by tests/fixtures - // that use a host-exec world-agent stub. + // that use a host-exec world-service stub. } #[cfg(any(target_os = "linux", target_os = "macos"))] @@ -239,8 +239,8 @@ struct ExecuteRequestInput { cwd: String, env_map: std::collections::HashMap, agent_id: String, - policy_snapshot: agent_api_types::PolicySnapshotV3, - world_network: agent_api_types::WorldNetworkRoutingV1, + policy_snapshot: transport_api_types::PolicySnapshotV3, + world_network: transport_api_types::WorldNetworkRoutingV1, world_fs_mode: WorldFsMode, member_dispatch: Option, } @@ -280,7 +280,7 @@ pub(super) fn execute_world_pty_over_ws( use futures::{SinkExt, StreamExt}; // Ensure agent is ready - ensure_world_agent_ready()?; + ensure_world_service_ready()?; // Connect UDS and do WS handshake let rt = tokio::runtime::Runtime::new()?; @@ -299,7 +299,7 @@ pub(super) fn execute_world_pty_over_ws( let sink = std::sync::Arc::new(tokio::sync::Mutex::new(sink)); if std::env::var("SUBSTRATE_WS_DEBUG").ok().as_deref() == Some("1") { - eprintln!("using world-agent PTY WS"); + eprintln!("using world-service PTY WS"); } // Prepare start frame (strip optional ":pty " prefix used in REPL to force PTY) @@ -523,9 +523,9 @@ pub(super) fn execute_world_pty_over_ws( }, )); } - return Err(anyhow::anyhow!("world-agent error: {}", message)); + return Err(anyhow::anyhow!("world-service error: {}", message)); } - return Err(anyhow::anyhow!("world-agent error")); + return Err(anyhow::anyhow!("world-service error")); } _ => {} } @@ -554,7 +554,7 @@ pub(super) fn execute_world_pty_over_ws( } #[cfg(target_os = "linux")] -pub(super) fn ensure_world_agent_ready() -> anyhow::Result<()> { +pub(super) fn ensure_world_service_ready() -> anyhow::Result<()> { use std::path::Path; use std::path::PathBuf; use std::thread; @@ -609,8 +609,8 @@ pub(super) fn ensure_world_agent_ready() -> anyhow::Result<()> { thread::sleep(Duration::from_millis(100)); } anyhow::bail!( - "world-agent socket activation detected but {} did not respond. \ - Run 'systemctl status substrate-world-agent.socket' for details.", + "world-service socket activation detected but {} did not respond. \ + Run 'systemctl status substrate-world-service.socket' for details.", socket_path.display() ); } @@ -636,24 +636,24 @@ pub(super) fn ensure_world_agent_ready() -> anyhow::Result<()> { // Try to spawn agent let candidate_bins = [ std::env::var("SUBSTRATE_WORLD_AGENT_BIN").ok(), - which::which("substrate-world-agent") + which::which("substrate-world-service") .ok() .map(|p| p.display().to_string()), - Some("target/release/world-agent".to_string()), - Some("target/debug/world-agent".to_string()), + Some("target/release/world-service".to_string()), + Some("target/debug/world-service".to_string()), ]; let bin = candidate_bins .into_iter() .flatten() .find(|p| std::path::Path::new(p).exists()) - .ok_or_else(|| anyhow::anyhow!("world-agent binary not found"))?; + .ok_or_else(|| anyhow::anyhow!("world-service binary not found"))?; std::process::Command::new(&bin) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) .spawn() - .map_err(|e| anyhow::anyhow!("spawn world-agent: {}", e))?; + .map_err(|e| anyhow::anyhow!("spawn world-service: {}", e))?; // Wait up to ~1s for readiness let deadline = std::time::Instant::now() + std::time::Duration::from_millis(1000); @@ -663,7 +663,7 @@ pub(super) fn ensure_world_agent_ready() -> anyhow::Result<()> { } std::thread::sleep(std::time::Duration::from_millis(50)); } - anyhow::bail!("world-agent readiness probe failed") + anyhow::bail!("world-service readiness probe failed") } #[cfg(target_os = "linux")] @@ -677,7 +677,7 @@ pub(crate) enum LinuxWorldInit { #[cfg(target_os = "linux")] pub(crate) fn init_linux_world(world_disabled: bool) -> LinuxWorldInit { - init_linux_world_with_probe(world_disabled, ensure_world_agent_ready) + init_linux_world_with_probe(world_disabled, ensure_world_service_ready) } #[cfg(target_os = "linux")] @@ -912,11 +912,11 @@ pub(super) fn execute_world_pty_over_ws_macos( )); } return Err(anyhow::anyhow!( - "world-agent error: {}", + "world-service error: {}", message )); } - return Err(anyhow::anyhow!("world-agent error")); + return Err(anyhow::anyhow!("world-service error")); } _ => {} } @@ -1055,8 +1055,8 @@ fn extract_process_telemetry_from_ws_exit(value: &serde_json::Value) -> ProcessT pub(crate) fn build_agent_client_and_request( cmd: &str, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { build_agent_client_and_request_with_trace_metadata(cmd, None, None) @@ -1067,8 +1067,8 @@ pub(crate) fn build_agent_client_and_request_with_trace_metadata( parent_span_id: Option<&str>, parent_cmd_id: Option<&str>, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { build_agent_client_and_request_impl(cmd, parent_span_id, parent_cmd_id) @@ -1078,16 +1078,16 @@ pub(crate) fn build_agent_client_and_request_with_trace_metadata( pub(crate) fn build_agent_client_and_member_dispatch_request( request: &MemberDispatchTransportRequest, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { build_agent_client_and_member_dispatch_request_impl(request) } pub(crate) fn build_agent_client_and_pending_diff_request() -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::PendingDiffRequestV1, + transport_api_client::AgentClient, + transport_api_types::PendingDiffRequestV1, String, )> { build_agent_client_and_pending_diff_request_impl() @@ -1110,8 +1110,8 @@ fn current_world_request_profile() -> Option { #[cfg(target_os = "windows")] fn validate_execute_response_shared_world( - requested: Option<&agent_api_types::SharedWorldOwnerSpec>, - response: &agent_api_types::ExecuteResponse, + requested: Option<&transport_api_types::SharedWorldOwnerSpec>, + response: &transport_api_types::ExecuteResponse, ) -> anyhow::Result<()> { crate::execution::repl_persistent_session::validate_shared_world_echo( requested, @@ -1129,11 +1129,11 @@ fn build_agent_client_and_request_impl( parent_span_id: Option<&str>, parent_cmd_id: Option<&str>, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { - ensure_world_agent_ready()?; + ensure_world_service_ready()?; let socket_path = std::env::var_os("SUBSTRATE_WORLD_SOCKET") .map(std::path::PathBuf::from) @@ -1178,8 +1178,8 @@ fn build_agent_client_and_request_impl( fn build_agent_client_and_member_dispatch_request_impl( dispatch: &MemberDispatchTransportRequest, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { let socket_path = std::env::var_os("SUBSTRATE_WORLD_SOCKET") @@ -1229,11 +1229,11 @@ fn preserve_world_project_dir_override(env_map: &mut std::collections::HashMap anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::PendingDiffRequestV1, + transport_api_client::AgentClient, + transport_api_types::PendingDiffRequestV1, String, )> { - ensure_world_agent_ready()?; + ensure_world_service_ready()?; let socket_path = std::env::var_os("SUBSTRATE_WORLD_SOCKET") .map(std::path::PathBuf::from) @@ -1253,7 +1253,7 @@ fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( )?; ensure_world_deps_bin_on_path(&mut env_map); - let request = agent_api_types::PendingDiffRequestV1 { + let request = transport_api_types::PendingDiffRequestV1 { profile: current_world_request_profile(), cwd: Some(cwd), env: Some(env_map), @@ -1271,8 +1271,8 @@ fn build_agent_client_and_request_impl( parent_span_id: Option<&str>, parent_cmd_id: Option<&str>, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { // Allow explicit socket overrides (used by tests/fixtures and advanced setups). @@ -1370,8 +1370,8 @@ fn build_agent_client_and_request_impl( fn build_agent_client_and_member_dispatch_request_impl( dispatch: &MemberDispatchTransportRequest, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { if let Some(socket_path) = std::env::var_os("SUBSTRATE_WORLD_SOCKET") { @@ -1458,8 +1458,8 @@ fn build_agent_client_and_member_dispatch_request_impl( #[cfg(target_os = "macos")] fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::PendingDiffRequestV1, + transport_api_client::AgentClient, + transport_api_types::PendingDiffRequestV1, String, )> { // Allow explicit socket overrides (used by tests/fixtures and advanced setups). @@ -1481,7 +1481,7 @@ fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( &mut env_map, )?; - let request = agent_api_types::PendingDiffRequestV1 { + let request = transport_api_types::PendingDiffRequestV1 { profile: current_world_request_profile(), cwd: Some(cwd), env: Some(env_map), @@ -1524,7 +1524,7 @@ fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( &mut env_map, )?; - let request = agent_api_types::PendingDiffRequestV1 { + let request = transport_api_types::PendingDiffRequestV1 { profile: current_world_request_profile(), cwd: Some(cwd), env: Some(env_map), @@ -1542,8 +1542,8 @@ fn build_agent_client_and_request_impl( parent_span_id: Option<&str>, parent_cmd_id: Option<&str>, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { use crate::execution::platform_world::windows; @@ -1609,8 +1609,8 @@ fn build_agent_client_and_request_impl( fn build_agent_client_and_member_dispatch_request_impl( dispatch: &MemberDispatchTransportRequest, ) -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::ExecuteRequest, + transport_api_client::AgentClient, + transport_api_types::ExecuteRequest, String, )> { use crate::execution::platform_world::windows; @@ -1671,8 +1671,8 @@ fn build_agent_client_and_member_dispatch_request_impl( #[cfg(target_os = "windows")] fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( - agent_api_client::AgentClient, - agent_api_types::PendingDiffRequestV1, + transport_api_client::AgentClient, + transport_api_types::PendingDiffRequestV1, String, )> { use crate::execution::platform_world::windows; @@ -1700,7 +1700,7 @@ fn build_agent_client_and_pending_diff_request_impl() -> anyhow::Result<( &mut env_map, )?; - let request = agent_api_types::PendingDiffRequestV1 { + let request = transport_api_types::PendingDiffRequestV1 { profile, cwd: Some(cwd), env: Some(env_map), @@ -1770,12 +1770,12 @@ pub(crate) fn stream_non_pty_via_agent( client .execute(request) .await - .context("world-agent /v1/execute request failed") + .context("world-service /v1/execute request failed") }) .await .with_context(|| { format!( - "Timed out after {}s waiting for world-agent /v1/execute (transport: {}).\nHint: ensure the Windows named-pipe forwarder is running (\\\\.\\pipe\\substrate-agent) and the WSL agent is healthy (try `pwsh -File scripts/windows/wsl-warm.ps1 -DistroName substrate-wsl`).", + "Timed out after {}s waiting for world-service /v1/execute (transport: {}).\nHint: ensure the Windows named-pipe forwarder is running (\\\\.\\pipe\\substrate-agent) and the WSL agent is healthy (try `pwsh -File scripts/windows/wsl-warm.ps1 -DistroName substrate-wsl`).", timeout.as_secs(), client.transport().description() ) @@ -1813,7 +1813,7 @@ pub(crate) fn stream_non_pty_via_agent( #[cfg(not(target_os = "windows"))] { - use agent_api_types::ApiError; + use transport_api_types::ApiError; use http_body_util::BodyExt; let response = client.execute_stream(request).await?; @@ -2127,7 +2127,7 @@ fn consume_agent_stream_buffer_with_context( fallback_reason: parse_world_fs_strategy_unavailable_reason(&message), })); } - eprintln!("world-agent error: {}", message); + eprintln!("world-service error: {}", message); anyhow::bail!(message); } } @@ -2212,11 +2212,6 @@ mod tests { acquire_event_test_guard, clear_agent_event_sender, init_event_channel, ShellCommandEventContext, ShellEventEmissionContext, }; - use agent_api_types::{ - ExecuteStreamFrame, MemberRuntimeBackendKindV1, PolicySnapshotV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - ResolvedMemberRuntimeDescriptorV1, WorldFsMode, WorldNetworkRoutingV1, - }; use base64::Engine; use futures::stream; use http_body_util::StreamBody; @@ -2225,6 +2220,11 @@ mod tests { use std::sync::{Arc, Mutex, OnceLock}; use std::time::Duration; use substrate_common::agent_events::AgentEventKind; + use transport_api_types::{ + ExecuteStreamFrame, MemberRuntimeBackendKindV1, PolicySnapshotV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + ResolvedMemberRuntimeDescriptorV1, WorldFsMode, WorldNetworkRoutingV1, + }; fn with_env_var(key: &str, value: &str, f: impl FnOnce() -> T) -> T { let _guard = test_env_lock().lock().expect("test env mutex poisoned"); @@ -2351,7 +2351,7 @@ mod tests { parent_participant_id: Some("ash_parent_123".to_string()), resumed_from_participant_id: Some("ash_prev_123".to_string()), backend_id: "cli:codex".to_string(), - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), run_id: "run_123".to_string(), world_id: "world_123".to_string(), world_generation: 9, @@ -2373,7 +2373,7 @@ mod tests { Some("ash_prev_123") ); assert_eq!(payload.backend_id, "cli:codex"); - assert_eq!(payload.protocol, "uaa.agent.session"); + assert_eq!(payload.protocol, "substrate.agent.session"); assert_eq!(payload.run_id, "run_123"); assert_eq!(payload.world_id, "world_123"); assert_eq!(payload.world_generation, 9); @@ -2425,7 +2425,7 @@ mod tests { parent_participant_id: None, resumed_from_participant_id: None, backend_id: "cli:codex".to_string(), - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), run_id: "run_123".to_string(), world_id: "world_123".to_string(), world_generation: 9, @@ -2484,7 +2484,7 @@ mod tests { span_id: "spn_interrupt".to_string(), scopes_used: Vec::new(), fs_diff: None, - process_telemetry: agent_api_types::ProcessTelemetry::default(), + process_telemetry: transport_api_types::ProcessTelemetry::default(), }), ]; @@ -2596,7 +2596,7 @@ mod tests { span_id: "spn-world".to_string(), scopes_used: Vec::new(), fs_diff: None, - process_telemetry: agent_api_types::ProcessTelemetry::default(), + process_telemetry: transport_api_types::ProcessTelemetry::default(), }), ]; let body = StreamBody::new(stream::iter( diff --git a/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs b/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs index c9eaf3dc7..ac9f1aca6 100644 --- a/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs +++ b/crates/shell/src/execution/routing/dispatch/world_persistent_session.rs @@ -1,4 +1,4 @@ -//! Host-side persistent REPL session client for world-agent `/v1/stream` (PROTOCOL v1). +//! Host-side persistent REPL session client for world-service `/v1/stream` (PROTOCOL v1). #[cfg(any(target_os = "linux", target_os = "macos"))] #[allow(dead_code)] @@ -62,7 +62,7 @@ mod imp { enum SessionState { Starting { ready_tx: tokio::sync::oneshot::Sender, - requested_shared_world: Option, + requested_shared_world: Option, }, Ready { next_seq: u64, @@ -82,16 +82,16 @@ mod imp { pub(crate) world_id: String, pub(crate) cwd: String, pub(crate) protocol_version: u32, - pub(crate) shared_world: Option, + pub(crate) shared_world: Option, } #[derive(Debug, Clone)] pub(crate) struct ReplSessionStartParams { pub(crate) cwd: String, pub(crate) env: HashMap, - pub(crate) policy_snapshot: agent_api_types::PolicySnapshotV3, - pub(crate) shared_world: Option, - pub(crate) world_network: agent_api_types::WorldNetworkRoutingV1, + pub(crate) policy_snapshot: transport_api_types::PolicySnapshotV3, + pub(crate) shared_world: Option, + pub(crate) world_network: transport_api_types::WorldNetworkRoutingV1, pub(crate) cols: u16, pub(crate) rows: u16, } @@ -100,8 +100,8 @@ mod imp { pub(crate) fn for_cwd_and_snapshot( cwd: String, cwd_path: &Path, - policy_snapshot: agent_api_types::PolicySnapshotV3, - world_network: agent_api_types::WorldNetworkRoutingV1, + policy_snapshot: transport_api_types::PolicySnapshotV3, + world_network: transport_api_types::WorldNetworkRoutingV1, ) -> Result<(Self, bool)> { let (env, inherit_from_host) = build_world_env_map_for_cwd(cwd_path)?; let (cols, rows) = terminal_size_or_default(); @@ -376,10 +376,10 @@ mod imp { StartSession { cwd: String, env: HashMap, - policy_snapshot: Box, + policy_snapshot: Box, #[serde(skip_serializing_if = "Option::is_none")] - shared_world: Option, - world_network: agent_api_types::WorldNetworkRoutingV1, + shared_world: Option, + world_network: transport_api_types::WorldNetworkRoutingV1, cols: u16, rows: u16, }, @@ -412,7 +412,7 @@ mod imp { cwd: String, protocol_version: u32, #[serde(default)] - shared_world: Option, + shared_world: Option, }, Stdout { data_b64: String, @@ -673,7 +673,7 @@ mod imp { )); } let seq_note = seq.map(|s| format!(" seq={s}")).unwrap_or_default(); - Err(anyhow!("world-agent error ({code}{seq_note}): {message}")) + Err(anyhow!("world-service error ({code}{seq_note}): {message}")) } } } @@ -682,15 +682,15 @@ mod imp { async fn build_ws_and_start_session_frame( start: ReplSessionStartParams, ) -> Result<(tungs::WebSocketStream, String)> { - super::super::world_ops::ensure_world_agent_ready() - .context("world backend unavailable: ensure world-agent ready")?; + super::super::world_ops::ensure_world_service_ready() + .context("world backend unavailable: ensure world-service ready")?; let socket_path = std::env::var_os("SUBSTRATE_WORLD_SOCKET") .map(std::path::PathBuf::from) .unwrap_or_else(|| std::path::PathBuf::from("/run/substrate.sock")); let stream = UnixStream::connect(&socket_path) .await - .with_context(|| format!("connect world-agent UDS ({})", socket_path.display()))?; + .with_context(|| format!("connect world-service UDS ({})", socket_path.display()))?; let url = url::Url::parse("ws://localhost/v1/stream").expect("static ws URL"); let io: WsIo = Box::new(stream); @@ -742,9 +742,9 @@ mod imp { // Allow explicit socket overrides (used by tests/fixtures and advanced setups). if let Some(socket_path) = std::env::var_os("SUBSTRATE_WORLD_SOCKET") { let socket_path = std::path::PathBuf::from(socket_path); - let stream = UnixStream::connect(&socket_path) - .await - .with_context(|| format!("connect world-agent UDS ({})", socket_path.display()))?; + let stream = UnixStream::connect(&socket_path).await.with_context(|| { + format!("connect world-service UDS ({})", socket_path.display()) + })?; let url = url::Url::parse("ws://localhost/v1/stream").expect("static ws URL"); let io: WsIo = Box::new(stream); let (ws, _resp) = tungs::client_async(url, io) @@ -778,7 +778,7 @@ mod imp { pw::WorldTransport::Unix(path) => { let stream = UnixStream::connect(path) .await - .with_context(|| format!("connect world-agent UDS ({})", path.display()))?; + .with_context(|| format!("connect world-service UDS ({})", path.display()))?; let url = url::Url::parse("ws://localhost/v1/stream").expect("static ws URL"); let io: WsIo = Box::new(stream); tungs::client_async(url, io).await? @@ -788,7 +788,7 @@ mod imp { let url = url::Url::parse(&ws_url).context("invalid ws URL")?; let tcp = tokio::net::TcpStream::connect(format!("{host}:{port}")) .await - .with_context(|| format!("connect world-agent TCP ({host}:{port})"))?; + .with_context(|| format!("connect world-service TCP ({host}:{port})"))?; let io: WsIo = Box::new(tcp); tungs::client_async(url, io).await? } @@ -799,7 +799,7 @@ mod imp { let tcp = tokio::net::TcpStream::connect(format!("{host}:{port}")) .await .with_context(|| { - format!("connect world-agent VSock proxy TCP ({host}:{port})") + format!("connect world-service VSock proxy TCP ({host}:{port})") })?; let io: WsIo = Box::new(tcp); tungs::client_async(url, io).await? @@ -907,36 +907,36 @@ mod imp { let frame = ClientFrame::StartSession { cwd: "/tmp/project".to_string(), env: HashMap::new(), - policy_snapshot: Box::new(agent_api_types::PolicySnapshotV3 { + policy_snapshot: Box::new(transport_api_types::PolicySnapshotV3 { schema_version: 3, net_allowed: vec!["example.com".to_string()], - world_fs: agent_api_types::PolicySnapshotWorldFsV3 { + world_fs: transport_api_types::PolicySnapshotWorldFsV3 { host_visible: true, - fail_closed: agent_api_types::PolicySnapshotWorldFsFailClosedV3 { + fail_closed: transport_api_types::PolicySnapshotWorldFsFailClosedV3 { routing: false, }, deny_enforcement: None, caged_required: false, - discover: Some(agent_api_types::PolicySnapshotWorldFsDimensionV3 { + discover: Some(transport_api_types::PolicySnapshotWorldFsDimensionV3 { allow_list: vec![".".to_string()], deny_list: Vec::new(), }), - read: Some(agent_api_types::PolicySnapshotWorldFsDimensionV3 { + read: Some(transport_api_types::PolicySnapshotWorldFsDimensionV3 { allow_list: vec![".".to_string()], deny_list: Vec::new(), }), - write: agent_api_types::PolicySnapshotWorldFsWriteV3 { + write: transport_api_types::PolicySnapshotWorldFsWriteV3 { enabled: true, allow_list: vec![".".to_string()], deny_list: Vec::new(), }, }, }), - shared_world: Some(agent_api_types::SharedWorldOwnerSpec { + shared_world: Some(transport_api_types::SharedWorldOwnerSpec { orchestration_session_id: "orch-test".to_string(), - action: agent_api_types::SharedWorldOwnerAction::AttachOrCreate, + action: transport_api_types::SharedWorldOwnerAction::AttachOrCreate, }), - world_network: agent_api_types::WorldNetworkRoutingV1 { + world_network: transport_api_types::WorldNetworkRoutingV1 { isolate_network: true, allowed_domains: vec!["example.com".to_string()], }, @@ -1001,19 +1001,19 @@ mod imp { ReplSessionStartParams { cwd: "/tmp/project".to_string(), env: HashMap::new(), - policy_snapshot: agent_api_types::PolicySnapshotV3 { + policy_snapshot: transport_api_types::PolicySnapshotV3 { schema_version: 3, net_allowed: Vec::new(), - world_fs: agent_api_types::PolicySnapshotWorldFsV3 { + world_fs: transport_api_types::PolicySnapshotWorldFsV3 { host_visible: true, - fail_closed: agent_api_types::PolicySnapshotWorldFsFailClosedV3 { + fail_closed: transport_api_types::PolicySnapshotWorldFsFailClosedV3 { routing: false, }, deny_enforcement: None, caged_required: false, discover: None, read: None, - write: agent_api_types::PolicySnapshotWorldFsWriteV3 { + write: transport_api_types::PolicySnapshotWorldFsWriteV3 { enabled: true, allow_list: vec![".".to_string()], deny_list: Vec::new(), @@ -1021,7 +1021,7 @@ mod imp { }, }, shared_world: None, - world_network: agent_api_types::WorldNetworkRoutingV1 { + world_network: transport_api_types::WorldNetworkRoutingV1 { isolate_network: false, allowed_domains: Vec::new(), }, @@ -1182,16 +1182,16 @@ mod imp { pub(crate) world_id: String, pub(crate) cwd: String, pub(crate) protocol_version: u32, - pub(crate) shared_world: Option, + pub(crate) shared_world: Option, } #[derive(Debug, Clone)] pub(crate) struct ReplSessionStartParams { pub(crate) cwd: String, pub(crate) env: HashMap, - pub(crate) policy_snapshot: agent_api_types::PolicySnapshotV3, - pub(crate) shared_world: Option, - pub(crate) world_network: agent_api_types::WorldNetworkRoutingV1, + pub(crate) policy_snapshot: transport_api_types::PolicySnapshotV3, + pub(crate) shared_world: Option, + pub(crate) world_network: transport_api_types::WorldNetworkRoutingV1, pub(crate) cols: u16, pub(crate) rows: u16, } @@ -1200,8 +1200,8 @@ mod imp { pub(crate) fn for_cwd_and_snapshot( cwd: String, _cwd_path: &Path, - policy_snapshot: agent_api_types::PolicySnapshotV3, - world_network: agent_api_types::WorldNetworkRoutingV1, + policy_snapshot: transport_api_types::PolicySnapshotV3, + world_network: transport_api_types::WorldNetworkRoutingV1, ) -> Result<(Self, bool)> { Ok(( Self { diff --git a/crates/shell/src/execution/routing/telemetry.rs b/crates/shell/src/execution/routing/telemetry.rs index 95165b382..331b02adb 100644 --- a/crates/shell/src/execution/routing/telemetry.rs +++ b/crates/shell/src/execution/routing/telemetry.rs @@ -290,7 +290,7 @@ pub(crate) fn append_process_events_to_trace(process_telemetry: &ProcessTelemetr for event in &process_telemetry.process_events { let mut entry = serde_json::to_value(event)?; if let Some(obj) = entry.as_object_mut() { - obj.insert(log_schema::COMPONENT.to_string(), json!("world-agent")); + obj.insert(log_schema::COMPONENT.to_string(), json!("world-service")); } append_to_trace(&entry)?; } diff --git a/crates/shell/src/execution/routing/world.rs b/crates/shell/src/execution/routing/world.rs index 9fad89118..f023d03aa 100644 --- a/crates/shell/src/execution/routing/world.rs +++ b/crates/shell/src/execution/routing/world.rs @@ -6,7 +6,7 @@ use crate::execution::policy_snapshot::bootstrap_world_spec; use crate::execution::world_env_guard; use crate::execution::ShellConfig; #[cfg(any(target_os = "macos", target_os = "windows"))] -use agent_api_types::SharedWorldOwnerSpec; +use transport_api_types::SharedWorldOwnerSpec; #[cfg(target_os = "linux")] use super::dispatch::init_linux_world; @@ -128,8 +128,8 @@ fn init_linux_world_default(config: &ShellConfig) { #[cfg(all(test, any(target_os = "macos", target_os = "windows")))] mod tests { use super::bootstrap_platform_world; - use agent_api_types::{SharedWorldOwnerAction, SharedWorldOwnerSpec}; use std::sync::atomic::{AtomicUsize, Ordering}; + use transport_api_types::{SharedWorldOwnerAction, SharedWorldOwnerSpec}; #[cfg(target_os = "macos")] #[test] diff --git a/crates/shell/src/execution/socket_activation.rs b/crates/shell/src/execution/socket_activation.rs index 5f31b9815..54470f3e7 100644 --- a/crates/shell/src/execution/socket_activation.rs +++ b/crates/shell/src/execution/socket_activation.rs @@ -1,4 +1,4 @@ -//! Socket activation detection helpers for Linux world-agent integration. +//! Socket activation detection helpers for Linux world-service integration. use std::env; use std::io::Read; @@ -10,8 +10,8 @@ use std::sync::{Mutex, OnceLock}; use std::time::{Duration, Instant}; const SOCKET_PATH: &str = "/run/substrate.sock"; -const SOCKET_UNIT: &str = "substrate-world-agent.socket"; -const SERVICE_UNIT: &str = "substrate-world-agent.service"; +const SOCKET_UNIT: &str = "substrate-world-service.socket"; +const SERVICE_UNIT: &str = "substrate-world-service.service"; const DEFAULT_SYSTEMCTL_SHOW_TIMEOUT_MS: u64 = 2_000; static REPORT_CACHE: OnceLock>> = OnceLock::new(); diff --git a/crates/shell/src/execution/workspace_cmd.rs b/crates/shell/src/execution/workspace_cmd.rs index 04db63236..d5cc85a10 100644 --- a/crates/shell/src/execution/workspace_cmd.rs +++ b/crates/shell/src/execution/workspace_cmd.rs @@ -664,7 +664,7 @@ fn run_workspace_sync_impl( macro_rules! build_pending_diff_state { ($record:expr) => {{ (|| -> std::result::Result { - let record: &agent_api_types::PendingDiffRecordV1 = $record; + let record: &transport_api_types::PendingDiffRecordV1 = $record; let mut offending_protected: Vec = Vec::new(); let mut raw_for_decisions: Vec = Vec::new(); @@ -961,7 +961,7 @@ fn run_workspace_sync_impl( } if conflict_policy == SyncConflictPolicy::PreferHost && !from_host_conflicts.is_empty() { - let reconcile_request = agent_api_types::PendingDiffReconcileRequestV1 { + let reconcile_request = transport_api_types::PendingDiffReconcileRequestV1 { profile: request.profile.clone(), cwd: request.cwd.clone(), env: request.env.clone(), @@ -1036,7 +1036,7 @@ fn run_workspace_sync_impl( let meta_paths: Vec = out_writes.iter().chain(out_mods.iter()).cloned().collect(); - let base_request = agent_api_types::WorldFsReadRequestV1 { + let base_request = transport_api_types::WorldFsReadRequestV1 { profile: request.profile.clone(), cwd: request.cwd.clone(), env: request.env.clone(), @@ -1047,11 +1047,12 @@ fn run_workspace_sync_impl( include_contents: false, }; - let world_meta: HashMap = + let world_meta: HashMap = match rt.block_on(async { - let mut out: HashMap = HashMap::new(); + let mut out: HashMap = + HashMap::new(); for path in &meta_paths { - let req = agent_api_types::WorldFsReadRequestV1 { + let req = transport_api_types::WorldFsReadRequestV1 { path: path.clone(), include_contents: false, ..base_request.clone() @@ -1077,14 +1078,14 @@ fn run_workspace_sync_impl( }; match meta.entry_type { - agent_api_types::WorldFsEntryTypeV1::RegularFile => { + transport_api_types::WorldFsEntryTypeV1::RegularFile => { let Some(size) = meta.size else { errln!("substrate: workspace sync failed: missing size for {path}"); return Ok(1); }; observed_bytes_to_copy = observed_bytes_to_copy.saturating_add(size); } - agent_api_types::WorldFsEntryTypeV1::Directory => {} + transport_api_types::WorldFsEntryTypeV1::Directory => {} _ => { errln!("substrate: workspace sync refused: unsupported file type in apply set"); errln!(" path: {path}"); @@ -1221,7 +1222,7 @@ fn run_workspace_sync_impl( } for (_is_write, path) in &writes_mods_sorted { - let meta_req = agent_api_types::WorldFsReadRequestV1 { + let meta_req = transport_api_types::WorldFsReadRequestV1 { path: path.clone(), include_contents: false, ..base_request.clone() @@ -1240,7 +1241,7 @@ fn run_workspace_sync_impl( let host_path = workspace_root.join(path); match meta.entry_type { - agent_api_types::WorldFsEntryTypeV1::Directory => { + transport_api_types::WorldFsEntryTypeV1::Directory => { if let Err(err) = fs::create_dir_all(&host_path) { errln!( "substrate: workspace sync failed: failed to create directory {}", @@ -1257,8 +1258,8 @@ fn run_workspace_sync_impl( } } } - agent_api_types::WorldFsEntryTypeV1::RegularFile => { - let read_req = agent_api_types::WorldFsReadRequestV1 { + transport_api_types::WorldFsEntryTypeV1::RegularFile => { + let read_req = transport_api_types::WorldFsReadRequestV1 { path: path.clone(), include_contents: true, ..base_request.clone() @@ -1309,7 +1310,7 @@ fn run_workspace_sync_impl( } } - let clear_request = agent_api_types::PendingDiffClearRequestV1 { + let clear_request = transport_api_types::PendingDiffClearRequestV1 { profile: request.profile.clone(), cwd: request.cwd.clone(), env: request.env.clone(), diff --git a/crates/shell/src/repl/async_repl.rs b/crates/shell/src/repl/async_repl.rs index 9794a061e..f62b9159f 100644 --- a/crates/shell/src/repl/async_repl.rs +++ b/crates/shell/src/repl/async_repl.rs @@ -8,13 +8,6 @@ use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; -#[cfg(any(target_os = "linux", target_os = "macos"))] -use agent_api_client::AgentClient; -#[cfg(any(target_os = "linux", target_os = "macos"))] -use agent_api_types::{ - ExecuteCancelRequestV1, ExecuteStreamFrame, MemberRuntimeBackendKindV1, - MemberTurnSubmitRequestV1, -}; use anyhow::{anyhow, Context, Result}; use futures::{pin_mut, FutureExt, StreamExt}; use reedline::{ExternalPrinter, Prompt, Reedline, Signal}; @@ -22,6 +15,13 @@ use serde::Deserialize; use tokio::runtime::Builder as TokioRuntimeBuilder; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::task; +#[cfg(any(target_os = "linux", target_os = "macos"))] +use transport_api_client::AgentClient; +#[cfg(any(target_os = "linux", target_os = "macos"))] +use transport_api_types::{ + ExecuteCancelRequestV1, ExecuteStreamFrame, MemberRuntimeBackendKindV1, + MemberTurnSubmitRequestV1, +}; use uuid::Uuid; use crate::execution::agent_events::{ @@ -6526,7 +6526,7 @@ struct WorldDriftRequest<'a> { requested_cwd: String, startup_context: Option<&'a RuntimeOrchestrationContext>, live_runtime_established: bool, - policy_snapshot: agent_api_types::PolicySnapshotV3, + policy_snapshot: transport_api_types::PolicySnapshotV3, snapshot_hash: String, workspace_root: Option, agent_printer: &'a ReplPrinter, @@ -6636,8 +6636,8 @@ async fn handle_detected_world_drift( struct OpenWorldSessionRequest<'a> { requested_cwd: String, requested_path: &'a Path, - resolved_policy_snapshot: agent_api_types::PolicySnapshotV3, - shared_world_request: Option, + resolved_policy_snapshot: transport_api_types::PolicySnapshotV3, + shared_world_request: Option, snapshot_hash: String, workspace_root: Option, on_stdout: StdoutCallback, @@ -6727,9 +6727,9 @@ async fn restart_world_session( let shared_world_request = orchestration_session_id .clone() - .map(|id| agent_api_types::SharedWorldOwnerSpec { + .map(|id| transport_api_types::SharedWorldOwnerSpec { orchestration_session_id: id, - action: agent_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { + action: transport_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { expected_generation: previous_world_generation, reason: reason.message().to_string(), }, @@ -6945,9 +6945,9 @@ async fn start_world_session( let start_hash = resolved_start.snapshot_hash.clone(); let start_workspace_root = find_workspace_root(requested_path); let shared_world_request = - startup_context.map(|context| agent_api_types::SharedWorldOwnerSpec { + startup_context.map(|context| transport_api_types::SharedWorldOwnerSpec { orchestration_session_id: context.orchestration_session_id(), - action: agent_api_types::SharedWorldOwnerAction::AttachOrCreate, + action: transport_api_types::SharedWorldOwnerAction::AttachOrCreate, }); let session = open_world_session(OpenWorldSessionRequest { requested_cwd: requested_cwd.clone(), diff --git a/crates/shell/tests/agent_hub_trace_persistence.rs b/crates/shell/tests/agent_hub_trace_persistence.rs index bd718cded..2fed772a2 100644 --- a/crates/shell/tests/agent_hub_trace_persistence.rs +++ b/crates/shell/tests/agent_hub_trace_persistence.rs @@ -159,7 +159,7 @@ fn write_orchestrator_runtime_config(home_substrate: &Path, fake_codex: &Path) { fs::write( home_substrate.join("agents/codex.yaml"), format!( - "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_codex.display() ), ) diff --git a/crates/shell/tests/agent_public_control_surface_v1.rs b/crates/shell/tests/agent_public_control_surface_v1.rs index 8410d5029..ddee9209b 100644 --- a/crates/shell/tests/agent_public_control_surface_v1.rs +++ b/crates/shell/tests/agent_public_control_surface_v1.rs @@ -23,7 +23,7 @@ use support::{ use support::{MemberDispatchStreamScript, ReplWorldAgentStub, StreamBehavior}; use tempfile::TempDir; -const PURE_AGENT_PROTOCOL: &str = "uaa.agent.session"; +const PURE_AGENT_PROTOCOL: &str = "substrate.agent.session"; #[cfg(unix)] const PRIVATE_STOP_UNIX_PATH_MAX: usize = 100; @@ -660,6 +660,7 @@ fn session_participant_manifests( }; let mut manifests = entries .filter_map(Result::ok) + .filter(|entry| entry.path().extension().and_then(|value| value.to_str()) == Some("json")) .map(|entry| read_json_file(&entry.path())) .collect::>(); manifests.sort_by(|left, right| { @@ -3333,7 +3334,7 @@ fn public_turn_routes_linux_world_member_follow_up_through_typed_submit_path() { Some("active") ); - let guard = records.lock().expect("lock world-agent records"); + let guard = records.lock().expect("lock world-service records"); assert_eq!( guard.member_turn_submit_requests.len(), 1, diff --git a/crates/shell/tests/agent_successor_contract_ahcsitc0.rs b/crates/shell/tests/agent_successor_contract_ahcsitc0.rs index b33f58fc7..f2945292c 100644 --- a/crates/shell/tests/agent_successor_contract_ahcsitc0.rs +++ b/crates/shell/tests/agent_successor_contract_ahcsitc0.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use std::process::Output; use tempfile::{Builder, TempDir}; -const PURE_AGENT_PROTOCOL: &str = "uaa.agent.session"; +const PURE_AGENT_PROTOCOL: &str = "substrate.agent.session"; #[derive(Clone, Copy)] enum CapabilityOverride<'a> { @@ -1346,7 +1346,7 @@ fn agent_list_json_locks_backend_id_derivation_role_and_omission_rules() { ); assert_eq!( orchestrator.pointer("/protocol").and_then(Value::as_str), - Some("uaa.agent.session") + Some("substrate.agent.session") ); let member = agents @@ -1537,7 +1537,7 @@ fn agent_toolbox_env_trace_history_does_not_authorize_active_session() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "orchestrator session is live" } }), @@ -1553,7 +1553,7 @@ fn agent_toolbox_env_trace_history_does_not_authorize_active_session() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "newer orchestrator session is live" } }), @@ -1604,7 +1604,7 @@ fn agent_toolbox_env_prefers_live_manifest_over_trace_fallback() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "trace fallback should lose to live manifest" } })]); @@ -1956,7 +1956,7 @@ fn agent_toolbox_env_invalidated_manifest_and_trace_still_fail_closed() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "historical trace must not resurrect an invalidated session" } })]); @@ -2386,7 +2386,7 @@ fn agent_status_preserves_member_roles_and_filters_them_by_contract_label() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "orchestrator session is live" } }), @@ -2402,7 +2402,7 @@ fn agent_status_preserves_member_roles_and_filters_them_by_contract_label() { "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_active_0002", "world_generation": 7, @@ -2549,7 +2549,7 @@ fn agent_status_prefers_live_manifest_over_trace_fallback_for_selected_orchestra "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "trace fallback should lose to live manifest" } })]); @@ -2775,7 +2775,7 @@ fn agent_status_json_trace_fallback_rows_emit_explicit_null_posture_fields() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "trace fallback row should keep durable posture fields null" } })]); @@ -2851,7 +2851,7 @@ fn agent_status_human_output_includes_durable_session_fields_for_live_rows() { "backend_id=cli:claude_code", "client=claude_code", "router=agent_hub", - "protocol=uaa.agent.session", + "protocol=substrate.agent.session", "execution.scope=host", "role=orchestrator", "posture=active_attached", @@ -2880,7 +2880,7 @@ fn agent_status_human_output_marks_fallback_rows_unknown_for_durable_session_fie "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "trace fallback row should keep text durable posture fields unknown" } })]); @@ -2903,7 +2903,7 @@ fn agent_status_human_output_marks_fallback_rows_unknown_for_durable_session_fie "backend_id=cli:claude_code", "client=claude_code", "router=agent_hub", - "protocol=uaa.agent.session", + "protocol=substrate.agent.session", "execution.scope=host", "role=orchestrator", "posture=", @@ -2942,7 +2942,7 @@ fn agent_status_tombstone_suppression_beats_stale_trace_fallback_for_world_membe "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_old_0001", "world_generation": 6, @@ -3057,7 +3057,7 @@ fn agent_status_keeps_same_agent_concurrent_sessions_visible_across_orchestratio "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "distinct orchestration_session_id values must remain independently visible" } })]); @@ -3227,7 +3227,7 @@ fn agent_status_trace_only_participant_aware_fallback_keeps_same_agent_siblings_ "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_trace_0001", "world_generation": 9, @@ -3246,7 +3246,7 @@ fn agent_status_trace_only_participant_aware_fallback_keeps_same_agent_siblings_ "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_trace_0001", "world_generation": 9, @@ -3317,7 +3317,7 @@ fn agent_status_sibling_specific_suppression_keeps_other_trace_participants_visi "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_trace_0002", "world_generation": 10, @@ -3336,7 +3336,7 @@ fn agent_status_sibling_specific_suppression_keeps_other_trace_participants_visi "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_trace_0002", "world_generation": 10, @@ -3386,7 +3386,7 @@ fn agent_status_nested_parent_correlation_prefers_parent_participant_id_when_sib "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_nested_0001", "world_generation": 11, @@ -3405,7 +3405,7 @@ fn agent_status_nested_parent_correlation_prefers_parent_participant_id_when_sib "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_nested_0001", "world_generation": 11, @@ -3689,7 +3689,7 @@ fn agent_status_json_prefers_live_runtime_member_manifest_with_top_level_world_i "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_trace_member_should_lose", "world_generation": 3, @@ -3807,7 +3807,7 @@ fn agent_status_keeps_stale_terminal_member_trace_rows_auditable_without_revivin "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_terminal_old_0008", "world_generation": 5, @@ -3869,7 +3869,7 @@ fn agent_status_prefers_newest_pure_session_event_when_trace_lines_are_out_of_or "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "data": { "message": "newest member session event intentionally omits world fields" } }), @@ -3885,7 +3885,7 @@ fn agent_status_prefers_newest_pure_session_event_when_trace_lines_are_out_of_or "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_stale_0001", "world_generation": 6, @@ -3918,7 +3918,7 @@ fn agent_status_fails_when_newest_world_scoped_event_omits_top_level_world_id() "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_generation": 8, "data": { "message": "newest member session event omits top-level world_id" } @@ -3935,7 +3935,7 @@ fn agent_status_fails_when_newest_world_scoped_event_omits_top_level_world_id() "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "world_id": "wld_stale_0001", "world_generation": 7, @@ -3965,7 +3965,7 @@ fn agent_status_scope_host_ignores_filtered_out_malformed_world_rows() { "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "member", "data": { "message": "filtered-out world row is malformed" } }), @@ -3981,7 +3981,7 @@ fn agent_status_scope_host_ignores_filtered_out_malformed_world_rows() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "host-scoped orchestrator row remains valid" } }), @@ -4027,7 +4027,7 @@ fn agent_status_ignores_non_selected_trace_orchestrator_roles() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "data": { "message": "selected orchestrator session is live" } }), @@ -4043,7 +4043,7 @@ fn agent_status_ignores_non_selected_trace_orchestrator_roles() { "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -4151,7 +4151,7 @@ fn agent_status_keeps_selected_orchestrator_host_scoped_when_trace_posture_says_ "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "placement_posture": { "execution": "in_world" @@ -4227,7 +4227,7 @@ fn agent_status_unsupported_event_roles_fall_back_to_contract_roles() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "unexpected", "data": { "message": "unsupported role should not leak" } }), @@ -4243,7 +4243,7 @@ fn agent_status_unsupported_event_roles_fall_back_to_contract_roles() { "backend_id": "cli:codex", "client": "codex", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "unexpected", "world_id": "wld_active_0002", "world_generation": 7, @@ -4426,7 +4426,7 @@ fn agent_doctor_fails_at_orchestrator_selection_when_protocol_is_missing() { let output = fixture.run(&["agent", "doctor", "--json"]); assert_doctor_fails_at_orchestrator_selection( &output, - "orchestrator agent 'claude_code' does not advertise protocol 'uaa.agent.session'", + "orchestrator agent 'claude_code' does not advertise protocol 'substrate.agent.session'", ); } @@ -4458,7 +4458,7 @@ fn agent_doctor_fails_at_orchestrator_selection_when_protocol_is_wrong() { let output = fixture.run(&["agent", "doctor", "--json"]); assert_doctor_fails_at_orchestrator_selection( &output, - "orchestrator agent 'claude_code' does not advertise protocol 'uaa.agent.session'", + "orchestrator agent 'claude_code' does not advertise protocol 'substrate.agent.session'", ); } @@ -5190,7 +5190,7 @@ metadata: {} "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "data": { "message": "trace says the orchestrator is healthy" } })]); @@ -5282,7 +5282,7 @@ metadata: {} "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5334,7 +5334,7 @@ metadata: {} ); assert_eq!( session.pointer("/protocol").and_then(Value::as_str), - Some("uaa.agent.session") + Some("substrate.agent.session") ); assert_eq!( session.pointer("/world_id").and_then(Value::as_str), @@ -5450,7 +5450,7 @@ metadata: {} "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5579,7 +5579,7 @@ fn agent_status_ignores_stale_nested_rows_from_historical_parent_runs() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0001", "world_generation": 6, @@ -5615,7 +5615,7 @@ fn agent_status_ignores_stale_nested_rows_from_historical_parent_runs() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5680,7 +5680,7 @@ fn agent_status_fails_closed_when_selected_nested_row_omits_parent_run_id() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5733,7 +5733,7 @@ fn agent_status_fails_closed_when_selected_nested_row_has_empty_parent_run_id() "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5788,7 +5788,7 @@ fn agent_status_fails_closed_when_selected_nested_row_has_unknown_parent_run_id( "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5842,7 +5842,7 @@ fn agent_status_fails_closed_when_selected_nested_row_omits_provider() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5895,7 +5895,7 @@ fn agent_status_fails_closed_when_selected_nested_row_omits_auth_authority() { "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5948,7 +5948,7 @@ fn agent_status_fails_closed_when_selected_nested_row_omits_provider_and_auth_au "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, @@ -5999,7 +5999,7 @@ fn agent_status_ignores_malformed_nested_rows_when_parent_surface_is_filtered_ou "backend_id": "cli:claude_code", "client": "claude_code", "router": "agent_hub", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "role": "orchestrator", "world_id": "wld_active_0002", "world_generation": 7, diff --git a/crates/shell/tests/agents_validate.rs b/crates/shell/tests/agents_validate.rs index 98e8d9698..92d02d32f 100644 --- a/crates/shell/tests/agents_validate.rs +++ b/crates/shell/tests/agents_validate.rs @@ -83,11 +83,11 @@ fn valid_cli_agent_file(agent_id: &str, policy_overlay: Option<&str>) -> String let overlay = policy_overlay.unwrap_or(""); if overlay.is_empty() { format!( - "version: 1\nid: {agent_id}\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: world\n cli:\n binary: codex\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n" + "version: 1\nid: {agent_id}\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: world\n cli:\n binary: codex\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n" ) } else { format!( - "version: 1\nid: {agent_id}\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: world\n cli:\n binary: codex\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\npolicy_overlay:\n{overlay}" + "version: 1\nid: {agent_id}\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: world\n cli:\n binary: codex\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\npolicy_overlay:\n{overlay}" ) } } @@ -187,7 +187,7 @@ id: bad_protocol config: kind: cli enabled: true - protocol: " uaa.agent.session " + protocol: " substrate.agent.session " execution: scope: world cli: diff --git a/crates/shell/tests/cmd_denied_host_only.rs b/crates/shell/tests/cmd_denied_host_only.rs index dadda3a74..09c8af815 100644 --- a/crates/shell/tests/cmd_denied_host_only.rs +++ b/crates/shell/tests/cmd_denied_host_only.rs @@ -139,6 +139,6 @@ fn cmd_denied_enforcement_is_host_only_even_when_world_enabled() { assert_eq!( socket.execute_request_count(), 0, - "expected cmd_denied enforcement to occur before any world-agent execution request" + "expected cmd_denied enforcement to occur before any world-service execution request" ); } diff --git a/crates/shell/tests/command_mode_world_consistency_v1.rs b/crates/shell/tests/command_mode_world_consistency_v1.rs index a35deaab6..6e76efece 100644 --- a/crates/shell/tests/command_mode_world_consistency_v1.rs +++ b/crates/shell/tests/command_mode_world_consistency_v1.rs @@ -234,7 +234,7 @@ fn command_mode_world_consistency_v1_routes_both_c_and_pipe_via_world_socket() { let recorded = records.lock().expect("lock records"); assert!( recorded.len() >= 2, - "expected both -c and pipe mode to issue world-agent execute requests; recorded={}", + "expected both -c and pipe mode to issue world-service execute requests; recorded={}", recorded.len() ); } @@ -377,12 +377,12 @@ fn command_mode_world_consistency_v1_c_mode_does_not_host_canonicalize_cd_pwd() assert!( cmds.iter() .any(|cmd| cmd.contains(&format!("mkdir -p {dir_name}"))), - "expected mkdir command to be sent to world-agent; cmds={cmds:?}" + "expected mkdir command to be sent to world-service; cmds={cmds:?}" ); assert!( cmds.iter() .any(|cmd| cmd.contains(&format!("cd {dir_name}")) && cmd.contains("pwd -P")), - "expected cd/pwd command text to be sent to world-agent (not host builtins); cmds={cmds:?}" + "expected cd/pwd command text to be sent to world-service (not host builtins); cmds={cmds:?}" ); } @@ -460,12 +460,12 @@ fn command_mode_world_consistency_v1_pipe_mode_does_not_host_canonicalize_cd_pwd assert!( cmds.iter() .any(|cmd| cmd.contains(&format!("mkdir -p {dir_name}"))), - "expected mkdir command to be sent to world-agent; cmds={cmds:?}" + "expected mkdir command to be sent to world-service; cmds={cmds:?}" ); assert!( cmds.iter() .any(|cmd| cmd.contains(&format!("cd {dir_name}")) && cmd.contains("pwd -P")), - "expected cd/pwd command text to be sent to world-agent (not host builtins); cmds={cmds:?}" + "expected cd/pwd command text to be sent to world-service (not host builtins); cmds={cmds:?}" ); } diff --git a/crates/shell/tests/doctor_scopes_ds0.rs b/crates/shell/tests/doctor_scopes_ds0.rs index 4f60f007f..21d45ca48 100644 --- a/crates/shell/tests/doctor_scopes_ds0.rs +++ b/crates/shell/tests/doctor_scopes_ds0.rs @@ -232,7 +232,7 @@ fn run_world_doctor_json(report: Value) -> Value { .prefix("substrate-ds0-sock-") .tempdir_in("/tmp") .expect("create ds0 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndDoctorWorld { report }, diff --git a/crates/shell/tests/next_command_visibility_waps_0008.rs b/crates/shell/tests/next_command_visibility_waps_0008.rs index 680898a92..7fae325b1 100644 --- a/crates/shell/tests/next_command_visibility_waps_0008.rs +++ b/crates/shell/tests/next_command_visibility_waps_0008.rs @@ -125,7 +125,7 @@ fn waps_0008_policy_patch_edits_visible_to_next_command() { let fixture = Waps0008Fixture::new(); // Workspace marker file + initial config. Keep policy.mode non-disabled so policy resolution - // always runs before sending a world-agent request. + // always runs before sending a world-service request. fixture.write_workspace_config_patch("{}\n"); fixture.write_global_config_patch( "world:\n enabled: true\n anchor_mode: follow-cwd\n anchor_path: \"\"\n caged: false\n\npolicy:\n mode: observe\n\nsync:\n auto_sync: false\n direction: from_world\n conflict_policy: prefer_host\n exclude: []\n", @@ -174,7 +174,7 @@ fn waps_0008_policy_patch_edits_visible_to_next_command() { assert_eq!( records.len(), 2, - "expected exactly two world-agent execute requests" + "expected exactly two world-service execute requests" ); let write_enabled_1 = records[0] .pointer("/policy_snapshot/world_fs/write/enabled") @@ -245,7 +245,7 @@ fn waps_0008_config_edits_visible_to_next_command() { assert_eq!( records.len(), 2, - "expected exactly two world-agent execute requests" + "expected exactly two world-service execute requests" ); let mode_1 = records[0] diff --git a/crates/shell/tests/persistent_session_client_v1.rs b/crates/shell/tests/persistent_session_client_v1.rs index 53c68d14f..e578c2b00 100644 --- a/crates/shell/tests/persistent_session_client_v1.rs +++ b/crates/shell/tests/persistent_session_client_v1.rs @@ -1,6 +1,6 @@ -use agent_api_types::{SharedWorldOwnerAction, SharedWorldOwnerSpec}; use serde_json::json; use substrate_shell::execution::repl_persistent_session::PersistentSessionClientCore; +use transport_api_types::{SharedWorldOwnerAction, SharedWorldOwnerSpec}; fn ready_frame(protocol_version: u32) -> serde_json::Value { json!({ diff --git a/crates/shell/tests/repl_world_first_routing_v1.rs b/crates/shell/tests/repl_world_first_routing_v1.rs index 768eeb655..3b0a26eb0 100644 --- a/crates/shell/tests/repl_world_first_routing_v1.rs +++ b/crates/shell/tests/repl_world_first_routing_v1.rs @@ -336,7 +336,7 @@ agents: fs::write( home_substrate.join("agents/codex.yaml"), format!( - "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_codex.display() ), ) @@ -379,7 +379,7 @@ agents: fs::write( home_substrate.join("agents/claude_code.yaml"), format!( - "version: 1\nid: claude_code\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: claude_code\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_orchestrator.display() ), ) @@ -387,7 +387,7 @@ agents: fs::write( home_substrate.join("agents/codex.yaml"), format!( - "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: world\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: world\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_member.display() ), ) @@ -430,7 +430,7 @@ agents: fs::write( home_substrate.join("agents/claude_code.yaml"), format!( - "version: 1\nid: claude_code\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: claude_code\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_orchestrator.display() ), ) @@ -438,7 +438,7 @@ agents: fs::write( home_substrate.join("agents/codex.yaml"), format!( - "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_secondary_host.display() ), ) @@ -590,7 +590,7 @@ agents: fs::write( home_substrate.join("agents/codex.yaml"), format!( - "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: uaa.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", + "version: 1\nid: codex\nconfig:\n kind: cli\n enabled: true\n protocol: substrate.agent.session\n execution:\n scope: host\n cli:\n binary: {}\n mode: persistent\n capabilities:\n session_start: true\n session_resume: true\n session_fork: true\n session_stop: true\n status_snapshot: true\n event_stream: true\n llm: true\n mcp_client: false\n", fake_codex.display() ), ) @@ -809,7 +809,7 @@ fn write_live_world_member_manifest( "agent_id": "codex", "backend_id": "cli:codex", "role": "member", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "execution": { "scope": "world" }, "state": "ready", "opened_at": ts, @@ -3957,7 +3957,7 @@ fn c3_drift_fail_closed_suppresses_world_restart_required_agent_event_without_ex assert_eq!( guard.persistent_execs.len(), 1, - "fail-closed drift must stop before the second exec reaches world-agent; records: {guard:#?}" + "fail-closed drift must stop before the second exec reaches world-service; records: {guard:#?}" ); assert_start_sessions_have_no_shared_world_context( &guard, diff --git a/crates/shell/tests/replay_world.rs b/crates/shell/tests/replay_world.rs index a3d8be06e..89ce7a2f4 100644 --- a/crates/shell/tests/replay_world.rs +++ b/crates/shell/tests/replay_world.rs @@ -2099,7 +2099,7 @@ fn replay_logs_copydiff_override_root_and_telemetry() { &path_override, ); // Force agent unavailability so this test doesn't accidentally bind to a host-installed - // world-agent at /run/substrate.sock (developer machines), which would bypass copy-diff. + // world-service at /run/substrate.sock (developer machines), which would bypass copy-diff. cmd.env( "SUBSTRATE_WORLD_SOCKET", fixture.home().join("missing-agent.sock"), diff --git a/crates/shell/tests/shell_env.rs b/crates/shell/tests/shell_env.rs index e716dc19c..26ddf91e1 100644 --- a/crates/shell/tests/shell_env.rs +++ b/crates/shell/tests/shell_env.rs @@ -66,7 +66,7 @@ managers: let output = substrate_command_for_home(&fixture) .env("PATH", &host_path_str) .env("BASH_ENV", &host_bash_env) - // Ensure we don't accidentally route the command through a host-installed world-agent + // Ensure we don't accidentally route the command through a host-installed world-service // (e.g., /run/substrate.sock) which would bypass host-side env injection. .env( "SUBSTRATE_WORLD_SOCKET", diff --git a/crates/shell/tests/socket_activation.rs b/crates/shell/tests/socket_activation.rs index 533cf8975..82502b4ca 100644 --- a/crates/shell/tests/socket_activation.rs +++ b/crates/shell/tests/socket_activation.rs @@ -146,7 +146,7 @@ fn world_doctor_fails_when_socket_activation_unresponsive() { .failure(); let stderr = String::from_utf8_lossy(&assert.get_output().stderr); assert!( - stderr.contains("socket activation") || stderr.contains("world-agent readiness"), + stderr.contains("socket activation") || stderr.contains("world-service readiness"), "expected socket activation error messaging in doctor stderr: {stderr}" ); } @@ -227,7 +227,7 @@ fn world_doctor_text_reports_socket_activation_summary() { .success(); let stdout = String::from_utf8_lossy(&assert.get_output().stdout); assert!( - stdout.contains("agent socket: systemd-managed"), + stdout.contains("world-service socket: systemd-managed"), "doctor text output missing socket activation summary: {stdout}" ); } diff --git a/crates/shell/tests/support/mod.rs b/crates/shell/tests/support/mod.rs index cae0e3baa..b9b3b6c86 100644 --- a/crates/shell/tests/support/mod.rs +++ b/crates/shell/tests/support/mod.rs @@ -14,8 +14,8 @@ pub use common::{binary_path, ensure_substrate_built, substrate_shell_driver, te pub use substrate_common::dedupe_path; mod socket; pub use socket::{AgentSocket, PendingDiffAckError, PendingDiffAckState, SocketResponse}; -mod repl_world_agent; -pub use repl_world_agent::{ +mod repl_world_service; +pub use repl_world_service::{ MemberDispatchStreamScript, PersistentExecRecord, PersistentExecStdoutOverride, PersistentStartSessionRecord, ReplWorldAgentRecords, ReplWorldAgentStub, StreamBehavior, }; diff --git a/crates/shell/tests/support/repl_world_agent.rs b/crates/shell/tests/support/repl_world_service.rs similarity index 95% rename from crates/shell/tests/support/repl_world_agent.rs rename to crates/shell/tests/support/repl_world_service.rs index 9d73bab8b..7b7845ea1 100644 --- a/crates/shell/tests/support/repl_world_agent.rs +++ b/crates/shell/tests/support/repl_world_service.rs @@ -20,7 +20,7 @@ pub struct PersistentStartSessionRecord { pub cwd: String, pub env: HashMap, pub policy_snapshot: JsonValue, - pub shared_world: Option, + pub shared_world: Option, pub world_network: JsonValue, pub cols: u16, pub rows: u16, @@ -52,9 +52,9 @@ pub struct ReplWorldAgentRecords { pub persistent_stdin: Vec>, pub persistent_signals: Vec, pub legacy_pty_starts: Vec, - pub member_dispatch_requests: Vec, - pub member_turn_submit_requests: Vec, - pub execute_cancel_requests: Vec, + pub member_dispatch_requests: Vec, + pub member_turn_submit_requests: Vec, + pub execute_cancel_requests: Vec, } #[derive(Debug, Clone)] @@ -107,7 +107,7 @@ pub struct ReplWorldAgentStub { handle: Option>, } -fn assert_member_dispatch_capture(dispatch: &agent_api_types::MemberDispatchRequestV1) { +fn assert_member_dispatch_capture(dispatch: &transport_api_types::MemberDispatchRequestV1) { assert!( Path::new(&dispatch.resolved_runtime.binary_path).is_absolute(), "captured member dispatch binary_path must remain absolute" @@ -325,7 +325,7 @@ impl ReplWorldAgentStub { async fn write_chunked_frame( stream: &mut tokio::net::UnixStream, - frame: &agent_api_types::ExecuteStreamFrame, + frame: &transport_api_types::ExecuteStreamFrame, ) { let mut payload = serde_json::to_vec(frame).expect("serialize stream frame"); payload.push(b'\n'); @@ -343,12 +343,12 @@ impl ReplWorldAgentStub { } fn build_member_dispatch_ready_event( - request: &agent_api_types::ExecuteRequest, - dispatch: &agent_api_types::MemberDispatchRequestV1, + request: &transport_api_types::ExecuteRequest, + dispatch: &transport_api_types::MemberDispatchRequestV1, span_id: &str, session_handle_id: &str, - ) -> agent_api_types::ExecuteStreamFrame { - agent_api_types::ExecuteStreamFrame::Event { + ) -> transport_api_types::ExecuteStreamFrame { + transport_api_types::ExecuteStreamFrame::Event { event: substrate_common::agent_events::AgentEvent { ts: chrono::Utc::now(), agent_id: request.agent_id.clone(), @@ -411,7 +411,7 @@ impl ReplWorldAgentStub { } if first_line.starts_with("POST /v1/execute/stream ") { - let parsed: agent_api_types::ExecuteRequest = + let parsed: transport_api_types::ExecuteRequest = match serde_json::from_slice(&body) { Ok(p) => p, Err(_) => { @@ -443,7 +443,7 @@ impl ReplWorldAgentStub { write_http_stream_start(&mut stream).await; write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Start { + &transport_api_types::ExecuteStreamFrame::Start { span_id: span_id.clone(), }, ) @@ -466,13 +466,13 @@ impl ReplWorldAgentStub { .await; write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Exit { + &transport_api_types::ExecuteStreamFrame::Exit { exit: exit_code, span_id: span_id.clone(), scopes_used: Vec::new(), fs_diff: None, process_telemetry: - agent_api_types::ProcessTelemetry::default(), + transport_api_types::ProcessTelemetry::default(), }, ) .await; @@ -514,13 +514,13 @@ impl ReplWorldAgentStub { } write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Exit { + &transport_api_types::ExecuteStreamFrame::Exit { exit: exit_code_on_cancel, span_id: span_id.clone(), scopes_used: Vec::new(), fs_diff: None, process_telemetry: - agent_api_types::ProcessTelemetry::default(), + transport_api_types::ProcessTelemetry::default(), }, ) .await; @@ -529,13 +529,13 @@ impl ReplWorldAgentStub { MemberDispatchStreamScript::ExitWithoutReady { exit_code } => { write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Exit { + &transport_api_types::ExecuteStreamFrame::Exit { exit: exit_code, span_id: span_id.clone(), scopes_used: Vec::new(), fs_diff: None, process_telemetry: - agent_api_types::ProcessTelemetry::default(), + transport_api_types::ProcessTelemetry::default(), }, ) .await; @@ -544,7 +544,7 @@ impl ReplWorldAgentStub { MemberDispatchStreamScript::ErrorBeforeReady { message } => { write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Error { + &transport_api_types::ExecuteStreamFrame::Error { message, }, ) @@ -570,7 +570,7 @@ impl ReplWorldAgentStub { let mut frames = String::new(); frames.push_str( - &serde_json::to_string(&agent_api_types::ExecuteStreamFrame::Start { + &serde_json::to_string(&transport_api_types::ExecuteStreamFrame::Start { span_id: "agent-span".to_string(), }) .expect("serialize start"), @@ -579,7 +579,7 @@ impl ReplWorldAgentStub { if !output.stdout.is_empty() { frames.push_str( &serde_json::to_string( - &agent_api_types::ExecuteStreamFrame::Stdout { + &transport_api_types::ExecuteStreamFrame::Stdout { chunk_b64: stdout_b64, }, ) @@ -590,7 +590,7 @@ impl ReplWorldAgentStub { if !output.stderr.is_empty() { frames.push_str( &serde_json::to_string( - &agent_api_types::ExecuteStreamFrame::Stderr { + &transport_api_types::ExecuteStreamFrame::Stderr { chunk_b64: stderr_b64, }, ) @@ -599,12 +599,12 @@ impl ReplWorldAgentStub { frames.push('\n'); } frames.push_str( - &serde_json::to_string(&agent_api_types::ExecuteStreamFrame::Exit { + &serde_json::to_string(&transport_api_types::ExecuteStreamFrame::Exit { exit: output.status.code().unwrap_or(-1), span_id: "agent-span".to_string(), scopes_used: Vec::new(), fs_diff: None, - process_telemetry: agent_api_types::ProcessTelemetry::default(), + process_telemetry: transport_api_types::ProcessTelemetry::default(), }) .expect("serialize exit"), ); @@ -615,7 +615,7 @@ impl ReplWorldAgentStub { } if first_line.starts_with("POST /v1/execute/cancel ") { - let parsed: agent_api_types::ExecuteCancelRequestV1 = + let parsed: transport_api_types::ExecuteCancelRequestV1 = match serde_json::from_slice(&body) { Ok(p) => p, Err(_) => { @@ -645,7 +645,7 @@ impl ReplWorldAgentStub { } if first_line.starts_with("POST /v1/member_turn/stream ") { - let parsed: agent_api_types::MemberTurnSubmitRequestV1 = + let parsed: transport_api_types::MemberTurnSubmitRequestV1 = match serde_json::from_slice(&body) { Ok(p) => p, Err(_) => { @@ -662,26 +662,26 @@ impl ReplWorldAgentStub { write_http_stream_start(&mut stream).await; write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Start { + &transport_api_types::ExecuteStreamFrame::Start { span_id: span_id.clone(), }, ) .await; write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Stdout { + &transport_api_types::ExecuteStreamFrame::Stdout { chunk_b64: BASE64.encode(stdout.as_bytes()), }, ) .await; write_chunked_frame( &mut stream, - &agent_api_types::ExecuteStreamFrame::Exit { + &transport_api_types::ExecuteStreamFrame::Exit { exit: 0, span_id, scopes_used: Vec::new(), fs_diff: None, - process_telemetry: agent_api_types::ProcessTelemetry::default(), + process_telemetry: transport_api_types::ProcessTelemetry::default(), }, ) .await; @@ -690,7 +690,7 @@ impl ReplWorldAgentStub { } if first_line.starts_with("POST /v1/execute ") { - let parsed: agent_api_types::ExecuteRequest = + let parsed: transport_api_types::ExecuteRequest = match serde_json::from_slice(&body) { Ok(p) => p, Err(_) => { @@ -710,8 +710,8 @@ impl ReplWorldAgentStub { let output = cmd.output().expect("run host command"); let shared_world = parsed.shared_world.as_ref().map(|request| { let generation = match request.action { - agent_api_types::SharedWorldOwnerAction::AttachOrCreate => 0, - agent_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { + transport_api_types::SharedWorldOwnerAction::AttachOrCreate => 0, + transport_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { expected_generation, .. } => expected_generation.saturating_add(1), @@ -982,8 +982,8 @@ impl ReplWorldAgentStub { "protocol_version": 1, "shared_world": shared_world.as_ref().map(|request| { let world_generation = match request.action { - agent_api_types::SharedWorldOwnerAction::AttachOrCreate => 0, - agent_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { + transport_api_types::SharedWorldOwnerAction::AttachOrCreate => 0, + transport_api_types::SharedWorldOwnerAction::ReplaceExpectedGeneration { expected_generation, .. } => expected_generation.saturating_add(1), @@ -1212,7 +1212,7 @@ impl ReplWorldAgentStub { }); // Avoid races with callers that immediately probe `GET /v1/capabilities` (e.g. - // `ensure_world_agent_ready()`): wait until the socket responds with 200. + // `ensure_world_service_ready()`): wait until the socket responds with 200. let deadline = Instant::now() + Duration::from_secs(2); while Instant::now() < deadline { if let Ok(mut stream) = std::os::unix::net::UnixStream::connect(&ready_path) { diff --git a/crates/shell/tests/support/socket.rs b/crates/shell/tests/support/socket.rs index 7c0ef4e8f..d238bf3a6 100644 --- a/crates/shell/tests/support/socket.rs +++ b/crates/shell/tests/support/socket.rs @@ -87,7 +87,7 @@ pub enum SocketResponse { process_events_dropped: Option, }, /// Executes `/v1/execute` and `/v1/execute/stream` requests on the host, using - /// the request's `cwd` and `env` for a lightweight world-agent simulation. + /// the request's `cwd` and `env` for a lightweight world-service simulation. CapabilitiesAndHostExecute { scopes: Vec }, /// Like `CapabilitiesAndHostExecute`, but also records each `/v1/execute` and /// `/v1/execute/stream` request JSON payload. @@ -123,7 +123,7 @@ pub enum SocketResponse { }, } -/// Minimal Unix socket server used to simulate socket-activated world-agent +/// Minimal Unix socket server used to simulate socket-activated world-service /// listeners. pub struct AgentSocket { path: PathBuf, @@ -900,7 +900,7 @@ fn write_capabilities_with_features(stream: &mut UnixStream, features: &[String] let body = json!({ "version": "v1", "features": features, - "backend": "world-agent", + "backend": "world-service", "platform": "linux", "listener_mode": "socket_activation" }) @@ -998,7 +998,7 @@ fn record_execute_request(records: &Arc>>, request: &HttpRe pub fn decode_recorded_execute_requests( records: &Arc>>, -) -> anyhow::Result> { +) -> anyhow::Result> { let guard = records .lock() .map_err(|_| anyhow::anyhow!("recorded execute request mutex poisoned"))?; @@ -1118,11 +1118,11 @@ struct ExecuteRequestStub { cwd: Option, env: Option>, #[serde(default)] - member_dispatch: Option, + member_dispatch: Option, } fn assert_member_dispatch_capture( - dispatch: &agent_api_types::MemberDispatchRequestV1, + dispatch: &transport_api_types::MemberDispatchRequestV1, ) -> anyhow::Result<()> { anyhow::ensure!( Path::new(&dispatch.resolved_runtime.binary_path).is_absolute(), diff --git a/crates/shell/tests/wfgadax1_routing_fail_closed.rs b/crates/shell/tests/wfgadax1_routing_fail_closed.rs index 28a2875a3..119be62a1 100644 --- a/crates/shell/tests/wfgadax1_routing_fail_closed.rs +++ b/crates/shell/tests/wfgadax1_routing_fail_closed.rs @@ -138,7 +138,7 @@ fn wfgadax1_runtime_routing_fail_closed_strategy_unavailable_maps_to_exit_4() { .prefix("substrate-wfgadax1-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path: PathBuf = socket_dir.path().join("world-agent.sock"); + let socket_path: PathBuf = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecuteStreamError { @@ -180,7 +180,7 @@ fn wfgadax1_exports_fail_closed_routing_state_env_var_and_deletes_require_world( .prefix("substrate-wfgadax1-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path: PathBuf = socket_dir.path().join("world-agent.sock"); + let socket_path: PathBuf = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndHostExecute { scopes: vec![] }, @@ -225,7 +225,7 @@ fn wfgadax1_exports_fail_closed_routing_state_false_is_0_and_output_only() { .prefix("substrate-wfgadax1-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path: PathBuf = socket_dir.path().join("world-agent.sock"); + let socket_path: PathBuf = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndHostExecute { scopes: vec![] }, diff --git a/crates/shell/tests/wfgadax2_caging_required.rs b/crates/shell/tests/wfgadax2_caging_required.rs index fd9bdd768..f3fa7e481 100644 --- a/crates/shell/tests/wfgadax2_caging_required.rs +++ b/crates/shell/tests/wfgadax2_caging_required.rs @@ -84,7 +84,7 @@ fn wfgadax2_control_caged_required_allows_caged_workspace_execution() { .prefix("substrate-wfgadax2-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndHostExecute { scopes: vec![] }, @@ -104,7 +104,7 @@ fn wfgadax2_control_caged_required_allows_caged_workspace_execution() { ); assert!( socket.execute_request_count() > 0, - "expected control run to reach world-agent execution" + "expected control run to reach world-service execution" ); } @@ -126,7 +126,7 @@ fn wfgadax2_caged_required_rejects_world_caged_false_exit_2() { .prefix("substrate-wfgadax2-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndHostExecute { scopes: vec![] }, @@ -147,7 +147,7 @@ fn wfgadax2_caged_required_rejects_world_caged_false_exit_2() { assert_eq!( socket.execute_request_count(), 0, - "expected incompatibility to fail before any world-agent execution request" + "expected incompatibility to fail before any world-service execution request" ); let stderr = String::from_utf8_lossy(&assert.get_output().stderr); @@ -177,7 +177,7 @@ fn wfgadax2_caged_required_rejects_anchor_mode_follow_cwd_exit_2() { .prefix("substrate-wfgadax2-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndHostExecute { scopes: vec![] }, @@ -198,7 +198,7 @@ fn wfgadax2_caged_required_rejects_anchor_mode_follow_cwd_exit_2() { assert_eq!( socket.execute_request_count(), 0, - "expected incompatibility to fail before any world-agent execution request" + "expected incompatibility to fail before any world-service execution request" ); let stderr = String::from_utf8_lossy(&assert.get_output().stderr); diff --git a/crates/shell/tests/workspace_auto_sync_ws3.rs b/crates/shell/tests/workspace_auto_sync_ws3.rs index 7917f6695..9efab819b 100644 --- a/crates/shell/tests/workspace_auto_sync_ws3.rs +++ b/crates/shell/tests/workspace_auto_sync_ws3.rs @@ -130,7 +130,7 @@ fn auto_sync_runs_after_successful_non_pty_world_command_and_applies_pending_dif .prefix("substrate-ws3-sock-") .tempdir_in("/tmp") .expect("create ws3 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = ws3_socket_state(pending_diff_record_deletes( "2100-01-01T00:00:00Z", "diff_ws3_01", @@ -210,7 +210,7 @@ fn auto_sync_does_not_run_when_non_pty_world_command_exits_nonzero() { .prefix("substrate-ws3-sock-") .tempdir_in("/tmp") .expect("create ws3 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = ws3_socket_state(pending_diff_record_deletes( "2100-01-01T00:00:00Z", "diff_ws3_nonzero", @@ -286,7 +286,7 @@ fn auto_sync_failure_propagates_exit_code_and_prefixes_error() { .prefix("substrate-ws3-sock-") .tempdir_in("/tmp") .expect("create ws3 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = ws3_socket_state(json!({ "session_started_at": "2100-01-01T00:00:00Z", "diff_id": "diff_ws3_protected", @@ -349,7 +349,7 @@ fn auto_sync_noops_when_effective_direction_is_from_host() { .prefix("substrate-ws3-sock-") .tempdir_in("/tmp") .expect("create ws3 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = ws3_socket_state(pending_diff_record_deletes( "2100-01-01T00:00:00Z", "diff_ws3_from_host", diff --git a/crates/shell/tests/workspace_sync_ws0.rs b/crates/shell/tests/workspace_sync_ws0.rs index f4537a1ca..9700db363 100644 --- a/crates/shell/tests/workspace_sync_ws0.rs +++ b/crates/shell/tests/workspace_sync_ws0.rs @@ -221,7 +221,7 @@ fn workspace_sync_dry_run_from_world_prints_pending_diff_summary_and_preview() { .prefix("substrate-ws1-sock-") .tempdir_in("/tmp") .expect("create ws1 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let pending = json!({ "session_started_at": "2026-02-10T18:38:23Z", "diff_id": "diff_test_01", @@ -337,7 +337,7 @@ fn workspace_sync_dry_run_supports_from_host_and_both_directions_in_ws5() { .prefix("substrate-ws0-sock-") .tempdir_in("/tmp") .expect("create ws0 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let pending = json!({ "schema_version": 1, "session_started_at": "2100-01-01T00:00:00Z", @@ -510,7 +510,7 @@ fn workspace_sync_dry_run_exits_4_when_backend_lacks_pending_diff_discovery() { .prefix("substrate-ws1-sock-") .tempdir_in("/tmp") .expect("create ws1 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start(&socket_path, SocketResponse::Capabilities); let mut cmd = fixture.command(); @@ -563,7 +563,7 @@ fn workspace_sync_dry_run_exits_3_when_pending_diff_fails_after_capabilities_suc .prefix("substrate-ws1-sock-") .tempdir_in("/tmp") .expect("create ws1 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndPendingDiffHttpError { diff --git a/crates/shell/tests/workspace_sync_ws2.rs b/crates/shell/tests/workspace_sync_ws2.rs index 0cb8b6698..3294a1fbe 100644 --- a/crates/shell/tests/workspace_sync_ws2.rs +++ b/crates/shell/tests/workspace_sync_ws2.rs @@ -113,7 +113,7 @@ fn workspace_sync_apply_from_world_applies_deletes_respects_excludes_and_clears_ .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ @@ -211,7 +211,7 @@ fn workspace_sync_apply_refuses_when_pending_diff_contains_protected_paths_and_d .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec!["execute".to_string(), "pending_diff_v1".to_string()], @@ -271,7 +271,7 @@ fn workspace_sync_apply_refuses_when_max_paths_guard_exceeded_and_includes_thres .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let deletes = (0..10001) .map(|idx| format!("too_many_{idx:05}.txt")) @@ -328,7 +328,7 @@ fn workspace_sync_apply_conflict_policy_abort_refuses_and_does_not_clear_pending .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ @@ -422,7 +422,7 @@ fn workspace_sync_apply_conflict_policy_prefer_host_skips_conflicts_exits_0_and_ .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ @@ -526,7 +526,7 @@ fn workspace_sync_apply_exits_1_when_applied_but_pending_diffs_not_cleared_and_d .prefix("substrate-ws2-sock-") .tempdir_in("/tmp") .expect("create ws2 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let initial = pending_diff_record( "2100-01-01T00:00:00Z", diff --git a/crates/shell/tests/workspace_sync_ws4.rs b/crates/shell/tests/workspace_sync_ws4.rs index b7578ea37..8eecaafdb 100644 --- a/crates/shell/tests/workspace_sync_ws4.rs +++ b/crates/shell/tests/workspace_sync_ws4.rs @@ -137,7 +137,7 @@ fn workspace_sync_dry_run_reports_pty_and_non_pty_pending_diff_summaries_when_su .prefix("substrate-ws4-sock-") .tempdir_in("/tmp") .expect("create ws4 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let pending = json!({ "schema_version": 1, "session_started_at": "2026-02-10T18:38:23Z", @@ -236,7 +236,7 @@ fn workspace_sync_dry_run_reports_pty_pending_diffs_unsupported_explicitly() { .prefix("substrate-ws4-sock-") .tempdir_in("/tmp") .expect("create ws4 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let pending = json!({ "schema_version": 1, "session_started_at": "2026-02-10T18:38:23Z", diff --git a/crates/shell/tests/workspace_sync_ws5.rs b/crates/shell/tests/workspace_sync_ws5.rs index 1abbdb7cd..b3f3c8e1d 100644 --- a/crates/shell/tests/workspace_sync_ws5.rs +++ b/crates/shell/tests/workspace_sync_ws5.rs @@ -108,7 +108,7 @@ fn workspace_sync_apply_from_world_includes_pty_deletes() { .prefix("substrate-ws5-sock-") .tempdir_in("/tmp") .expect("create ws5 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ @@ -194,7 +194,7 @@ fn workspace_sync_dry_run_from_host_reports_conflicts_and_per_path_decisions() { .prefix("substrate-ws5-sock-") .tempdir_in("/tmp") .expect("create ws5 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let pending = json!({ "schema_version": 1, @@ -280,7 +280,7 @@ fn workspace_sync_from_host_mutates_world_but_not_host() { .prefix("substrate-ws5-sock-") .tempdir_in("/tmp") .expect("create ws5 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ @@ -389,7 +389,7 @@ fn workspace_sync_direction_both_applies_non_pty_and_pty_deletes() { .prefix("substrate-ws5-sock-") .tempdir_in("/tmp") .expect("create ws5 socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let state = PendingDiffAckState { features: vec![ diff --git a/crates/shell/tests/world_deps_apt_fail_early_wdap1.rs b/crates/shell/tests/world_deps_apt_fail_early_wdap1.rs index 9a35c0714..bfcccc431 100644 --- a/crates/shell/tests/world_deps_apt_fail_early_wdap1.rs +++ b/crates/shell/tests/world_deps_apt_fail_early_wdap1.rs @@ -836,7 +836,7 @@ fn current_install_conflicting_apt_pins_exits_2_before_world_execution() { let cmds = recorded_cmds(&records); assert!( cmds.is_empty(), - "expected conflicts to fail before any world-agent execution; cmds={cmds:?}" + "expected conflicts to fail before any world-service execution; cmds={cmds:?}" ); } diff --git a/crates/shell/tests/world_deps_apt_install_wdp5.rs b/crates/shell/tests/world_deps_apt_install_wdp5.rs index c3da48937..acb49c0d8 100644 --- a/crates/shell/tests/world_deps_apt_install_wdp5.rs +++ b/crates/shell/tests/world_deps_apt_install_wdp5.rs @@ -192,7 +192,7 @@ fn first_index_containing(cmds: &[String], needle: &str) -> Option { } #[test] -fn test_current_install_manual_exits_4_and_does_not_call_world_agent() { +fn test_current_install_manual_exits_4_and_does_not_call_world_service() { let fixture = ShellEnvFixture::new(); let ws_root = workspace_root(&fixture); fs::create_dir_all(ws_root.join(".substrate")).expect("create workspace .substrate"); @@ -332,7 +332,7 @@ fn test_current_install_probes_apt_before_scripts_and_does_not_mutate_enabled_pa assert!( probe_idx.is_some(), - "expected a dpkg-query probe to be sent to world-agent; cmds={cmds:?}" + "expected a dpkg-query probe to be sent to world-service; cmds={cmds:?}" ); assert!( script_idx.is_some(), diff --git a/crates/shell/tests/world_deps_enabled_wdp1.rs b/crates/shell/tests/world_deps_enabled_wdp1.rs index a91e1ef50..3411009fd 100644 --- a/crates/shell/tests/world_deps_enabled_wdp1.rs +++ b/crates/shell/tests/world_deps_enabled_wdp1.rs @@ -300,7 +300,7 @@ fn test_scoped_list_enabled_is_patch_view_not_effective_merged() { } #[test] -fn test_current_list_enabled_merges_scopes_and_does_not_call_world_agent() { +fn test_current_list_enabled_merges_scopes_and_does_not_call_world_service() { let fixture = ShellEnvFixture::new(); let ws_root = workspace_root(&fixture); fs::create_dir_all(&ws_root).expect("create ws root"); @@ -347,7 +347,7 @@ fn test_current_list_enabled_merges_scopes_and_does_not_call_world_agent() { ); assert!( !stderr.contains("world backend unavailable"), - "current list enabled must not call world-agent: stderr={stderr}" + "current list enabled must not call world-service: stderr={stderr}" ); let a_pos = find_name_pos(&stdout, "a"); let b_pos = find_name_pos(&stdout, "b"); diff --git a/crates/shell/tests/world_enable.rs b/crates/shell/tests/world_enable.rs index 8680d30bc..9ee9290bd 100644 --- a/crates/shell/tests/world_enable.rs +++ b/crates/shell/tests/world_enable.rs @@ -278,16 +278,16 @@ impl WorldEnableFixture { version_dir } - fn install_accepted_staged_world_agent(&self, version_dir: &Path, relative_path: &str) { + fn install_accepted_staged_world_service(&self, version_dir: &Path, relative_path: &str) { let path = version_dir.join(relative_path); - fs::create_dir_all(path.parent().expect("world-agent parent")) - .expect("create staged world-agent dir"); - fs::write(&path, "#!/usr/bin/env bash\nexit 0\n").expect("write staged world-agent"); + fs::create_dir_all(path.parent().expect("world-service parent")) + .expect("create staged world-service dir"); + fs::write(&path, "#!/usr/bin/env bash\nexit 0\n").expect("write staged world-service"); let mut perms = fs::metadata(&path) - .expect("staged world-agent metadata") + .expect("staged world-service metadata") .permissions(); perms.set_mode(0o755); - fs::set_permissions(&path, perms).expect("chmod staged world-agent"); + fs::set_permissions(&path, perms).expect("chmod staged world-service"); } } @@ -505,7 +505,7 @@ fn world_enable_uses_prefix_runtime_bundle_when_version_binary_is_missing() { } #[test] -fn world_enable_exits_3_when_accepted_staged_world_agent_missing() { +fn world_enable_exits_3_when_accepted_staged_world_service_missing() { let fixture = WorldEnableFixture::new(); fixture.install_prefix_runtime_scripts(); let _version_dir = fixture.install_version_dir_binary(); @@ -513,16 +513,16 @@ fn world_enable_exits_3_when_accepted_staged_world_agent_missing() { let output = fixture .command_without_override() .output() - .expect("failed to run substrate world enable with missing staged world-agent"); + .expect("failed to run substrate world enable with missing staged world-service"); assert!( !output.status.success(), - "missing accepted staged world-agent should fail closed: {output:?}" + "missing accepted staged world-service should fail closed: {output:?}" ); assert_eq!( output.status.code(), Some(3), - "missing accepted staged world-agent should exit 3: {output:?}" + "missing accepted staged world-service should exit 3: {output:?}" ); let stderr = String::from_utf8_lossy(&output.stderr); assert!( @@ -538,15 +538,15 @@ fn world_enable_exits_3_when_accepted_staged_world_agent_missing() { "preflight failure should not create env.sh" ); assert!( - stderr.contains("accepted staged world-agent artifact missing"), + stderr.contains("accepted staged world-service artifact missing"), "stderr should identify the missing staged artifact: {stderr}" ); assert!( - stderr.contains("bin/world-agent"), + stderr.contains("bin/world-service"), "stderr should list the root staged path suffix: {stderr}" ); assert!( - stderr.contains("bin/linux/world-agent"), + stderr.contains("bin/linux/world-service"), "stderr should list the linux fallback staged path suffix: {stderr}" ); assert!( @@ -554,13 +554,13 @@ fn world_enable_exits_3_when_accepted_staged_world_agent_missing() { "stderr should point operators at the staging remediation: {stderr}" ); assert!( - stderr.contains("cargo build -p world-agent"), - "stderr should point operators at rebuilding world-agent: {stderr}" + stderr.contains("cargo build -p world-service"), + "stderr should point operators at rebuilding world-service: {stderr}" ); } #[test] -fn world_enable_dry_run_exits_3_when_accepted_staged_world_agent_missing() { +fn world_enable_dry_run_exits_3_when_accepted_staged_world_service_missing() { let fixture = WorldEnableFixture::new(); fixture.install_prefix_runtime_scripts(); let _version_dir = fixture.install_version_dir_binary(); @@ -569,28 +569,28 @@ fn world_enable_dry_run_exits_3_when_accepted_staged_world_agent_missing() { .command_without_override() .arg("--dry-run") .output() - .expect("failed to run substrate world enable dry-run with missing staged world-agent"); + .expect("failed to run substrate world enable dry-run with missing staged world-service"); assert!( !output.status.success(), - "missing accepted staged world-agent should fail closed in dry-run: {output:?}" + "missing accepted staged world-service should fail closed in dry-run: {output:?}" ); assert_eq!( output.status.code(), Some(3), - "missing accepted staged world-agent should exit 3 in dry-run: {output:?}" + "missing accepted staged world-service should exit 3 in dry-run: {output:?}" ); let stderr = String::from_utf8_lossy(&output.stderr); assert!( - stderr.contains("accepted staged world-agent artifact missing"), + stderr.contains("accepted staged world-service artifact missing"), "stderr should identify the missing staged artifact: {stderr}" ); assert!( - stderr.contains("bin/world-agent"), + stderr.contains("bin/world-service"), "stderr should list the root staged path suffix: {stderr}" ); assert!( - stderr.contains("bin/linux/world-agent"), + stderr.contains("bin/linux/world-service"), "stderr should list the linux fallback staged path suffix: {stderr}" ); assert!( @@ -598,8 +598,8 @@ fn world_enable_dry_run_exits_3_when_accepted_staged_world_agent_missing() { "stderr should point operators at the staging remediation: {stderr}" ); assert!( - stderr.contains("cargo build -p world-agent"), - "stderr should point operators at rebuilding world-agent: {stderr}" + stderr.contains("cargo build -p world-service"), + "stderr should point operators at rebuilding world-service: {stderr}" ); assert!( fixture.log_contents().is_none(), @@ -616,21 +616,21 @@ fn world_enable_dry_run_exits_3_when_accepted_staged_world_agent_missing() { } #[test] -fn world_enable_dry_run_accepts_root_staged_world_agent() { +fn world_enable_dry_run_accepts_root_staged_world_service() { let fixture = WorldEnableFixture::new(); fixture.install_prefix_runtime_scripts(); let version_dir = fixture.install_version_dir_binary(); - fixture.install_accepted_staged_world_agent(&version_dir, "bin/world-agent"); + fixture.install_accepted_staged_world_service(&version_dir, "bin/world-service"); let output = fixture .command_without_override() .arg("--dry-run") .output() - .expect("failed to run substrate world enable dry-run with root staged world-agent"); + .expect("failed to run substrate world enable dry-run with root staged world-service"); assert!( output.status.success(), - "root staged world-agent should satisfy dry-run preflight: {output:?}" + "root staged world-service should satisfy dry-run preflight: {output:?}" ); assert!( fixture.log_contents().is_none(), @@ -641,21 +641,21 @@ fn world_enable_dry_run_accepts_root_staged_world_agent() { } #[test] -fn world_enable_dry_run_accepts_linux_fallback_staged_world_agent() { +fn world_enable_dry_run_accepts_linux_fallback_staged_world_service() { let fixture = WorldEnableFixture::new(); fixture.install_prefix_runtime_scripts(); let version_dir = fixture.install_version_dir_binary(); - fixture.install_accepted_staged_world_agent(&version_dir, "bin/linux/world-agent"); + fixture.install_accepted_staged_world_service(&version_dir, "bin/linux/world-service"); let output = fixture .command_without_override() .arg("--dry-run") .output() - .expect("failed to run substrate world enable dry-run with linux staged world-agent"); + .expect("failed to run substrate world enable dry-run with linux staged world-service"); assert!( output.status.success(), - "linux fallback staged world-agent should satisfy dry-run preflight: {output:?}" + "linux fallback staged world-service should satisfy dry-run preflight: {output:?}" ); assert!( fixture.log_contents().is_none(), diff --git a/crates/shell/tests/world_enable_provision_deps_wdap0.rs b/crates/shell/tests/world_enable_provision_deps_wdap0.rs index 0b64317a2..7476ef3ea 100644 --- a/crates/shell/tests/world_enable_provision_deps_wdap0.rs +++ b/crates/shell/tests/world_enable_provision_deps_wdap0.rs @@ -376,7 +376,7 @@ fn world_enable_provision_deps_dry_run_runs_probe_and_skips_apt_side_effects() { } #[test] -fn world_enable_provision_deps_conflicts_fail_before_helper_or_world_agent() { +fn world_enable_provision_deps_conflicts_fail_before_helper_or_world_service() { let fixture = WorldEnableProvisionFixture::new(); fixture.write_global_config(&["nodejs-20", "nodejs-18"]); fixture.write_workspace_config(&[]); @@ -403,7 +403,7 @@ fn world_enable_provision_deps_conflicts_fail_before_helper_or_world_agent() { ); assert!( recorded_requests(&records).is_empty(), - "conflict path must not execute world-agent requests" + "conflict path must not execute world-service requests" ); } @@ -434,7 +434,7 @@ fn world_enable_provision_deps_forces_helper_no_sync_and_provision_profile() { assert_eq!( first_apt_like_profile(&records).as_deref(), Some("world-deps-provision"), - "first apt/dpkg world-agent request must force profile=world-deps-provision; records={:?}", + "first apt/dpkg world-service request must force profile=world-deps-provision; records={:?}", recorded_requests(&records) ); } @@ -630,7 +630,7 @@ fn world_enable_provision_deps_runs_pacman_with_normalized_order_and_internal_pr } #[test] -fn world_enable_provision_deps_rejects_mixed_manager_sets_before_helper_or_world_agent() { +fn world_enable_provision_deps_rejects_mixed_manager_sets_before_helper_or_world_service() { let fixture = WorldEnableProvisionFixture::new(); fixture.write_global_config(&["apt-only", "pacman-only"]); fixture.write_workspace_config(&[]); @@ -657,7 +657,7 @@ fn world_enable_provision_deps_rejects_mixed_manager_sets_before_helper_or_world ); assert!( recorded_requests(&records).is_empty(), - "mixed-manager rejection must not execute world-agent requests" + "mixed-manager rejection must not execute world-service requests" ); } diff --git a/crates/shell/tests/world_overlayfs_enumeration_wo0.rs b/crates/shell/tests/world_overlayfs_enumeration_wo0.rs index a11f54fcb..7b00c772c 100644 --- a/crates/shell/tests/world_overlayfs_enumeration_wo0.rs +++ b/crates/shell/tests/world_overlayfs_enumeration_wo0.rs @@ -45,7 +45,7 @@ fn world_doctor_json_includes_strategy_keys_per_adr_0004() { .prefix("substrate-wo0-sock-") .tempdir_in("/tmp") .expect("create wo0 socket tempdir"); - let socket_path: PathBuf = socket_dir.path().join("world-agent.sock"); + let socket_path: PathBuf = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start(&socket_path, SocketResponse::Capabilities); let output = get_substrate_binary() diff --git a/crates/shell/tests/world_process_exec_tracing_parity_wpep1.rs b/crates/shell/tests/world_process_exec_tracing_parity_wpep1.rs index 508f962f2..eeb3b81ae 100644 --- a/crates/shell/tests/world_process_exec_tracing_parity_wpep1.rs +++ b/crates/shell/tests/world_process_exec_tracing_parity_wpep1.rs @@ -51,7 +51,7 @@ fn world_command_complete_defaults_process_event_diagnostics_when_capture_unavai .prefix("substrate-wpep1-defaults-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecute { @@ -107,7 +107,7 @@ fn world_process_events_append_before_command_complete_summary() { .prefix("substrate-wpep1-persistence-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecuteWithProcessEvents { @@ -120,7 +120,7 @@ fn world_process_events_append_before_command_complete_summary() { "ts": "2026-04-01T00:00:00Z", "ts_unix_ns": 1_743_465_600_000_000_000u64, "event_type": "world_process_start", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep1", "world_id": "wld_demo", "pid": 101, @@ -134,7 +134,7 @@ fn world_process_events_append_before_command_complete_summary() { "ts": "2026-04-01T00:00:01Z", "ts_unix_ns": 1_743_465_601_000_000_000u64, "event_type": "world_process_exit", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep1", "world_id": "wld_demo", "pid": 101, diff --git a/crates/shell/tests/world_process_exec_tracing_parity_wpep2.rs b/crates/shell/tests/world_process_exec_tracing_parity_wpep2.rs index 8a3c285e6..5d35d92c4 100644 --- a/crates/shell/tests/world_process_exec_tracing_parity_wpep2.rs +++ b/crates/shell/tests/world_process_exec_tracing_parity_wpep2.rs @@ -51,7 +51,7 @@ fn world_command_complete_preserves_ptrace_not_permitted_diagnostics() { .prefix("substrate-wpep2-ptrace-denied-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecuteWithProcessEvents { @@ -111,7 +111,7 @@ fn world_process_events_preserve_linux_capture_fields_and_explicit_argv_omission .prefix("substrate-wpep2-linux-events-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecuteWithProcessEvents { @@ -124,7 +124,7 @@ fn world_process_events_preserve_linux_capture_fields_and_explicit_argv_omission "ts": "2026-04-01T00:00:00Z", "ts_unix_ns": 1_743_465_600_000_000_000u64, "event_type": "world_process_start", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep2", "world_id": "wld_demo", "pid": 101, @@ -138,7 +138,7 @@ fn world_process_events_preserve_linux_capture_fields_and_explicit_argv_omission "ts": "2026-04-01T00:00:01Z", "ts_unix_ns": 1_743_465_601_000_000_000u64, "event_type": "world_process_exit", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep2", "world_id": "wld_demo", "pid": 101, @@ -190,7 +190,7 @@ fn world_process_events_preserve_linux_capture_fields_and_explicit_argv_omission for event in &process_events { assert_eq!( event.get("component").and_then(Value::as_str), - Some("world-agent") + Some("world-service") ); assert_eq!( event.get("argv_omitted").and_then(Value::as_bool), diff --git a/crates/shell/tests/world_process_exec_tracing_parity_wpep3.rs b/crates/shell/tests/world_process_exec_tracing_parity_wpep3.rs index aca73b74d..6ca7c01ae 100644 --- a/crates/shell/tests/world_process_exec_tracing_parity_wpep3.rs +++ b/crates/shell/tests/world_process_exec_tracing_parity_wpep3.rs @@ -51,7 +51,7 @@ fn world_process_events_preserve_linux_capture_fields_and_redacted_argv() { .prefix("substrate-wpep3-linux-events-sock-") .tempdir_in("/tmp") .expect("create socket tempdir"); - let socket_path = socket_dir.path().join("world-agent.sock"); + let socket_path = socket_dir.path().join("world-service.sock"); let _socket = AgentSocket::start( &socket_path, SocketResponse::CapabilitiesAndExecuteWithProcessEvents { @@ -64,7 +64,7 @@ fn world_process_events_preserve_linux_capture_fields_and_redacted_argv() { "ts": "2026-04-01T00:00:00Z", "ts_unix_ns": 1_743_465_600_000_000_000u64, "event_type": "world_process_start", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep3", "world_id": "wld_demo", "pid": 101, @@ -78,7 +78,7 @@ fn world_process_events_preserve_linux_capture_fields_and_redacted_argv() { "ts": "2026-04-01T00:00:01Z", "ts_unix_ns": 1_743_465_601_000_000_000u64, "event_type": "world_process_exit", - "component": "world-agent", + "component": "world-service", "session_id": "ses_wpep3", "world_id": "wld_demo", "pid": 101, @@ -130,7 +130,7 @@ fn world_process_events_preserve_linux_capture_fields_and_redacted_argv() { for event in &process_events { assert_eq!( event.get("component").and_then(Value::as_str), - Some("world-agent") + Some("world-service") ); assert!( event.get("argv_omitted").is_none(), diff --git a/crates/agent-api-client/Cargo.toml b/crates/transport-api-client/Cargo.toml similarity index 84% rename from crates/agent-api-client/Cargo.toml rename to crates/transport-api-client/Cargo.toml index 0ba37b428..e2c1d254f 100644 --- a/crates/agent-api-client/Cargo.toml +++ b/crates/transport-api-client/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "agent-api-client" +name = "transport-api-client" version = "0.2.8" edition = "2021" authors = ["Atomize"] -description = "Client for forwarding Agent API requests to world-agent" +description = "Client for forwarding Agent API requests to world-service" license = "MIT" [dependencies] @@ -17,7 +17,7 @@ http-body-util = "0.1" tokio = { version = "1", features = ["full"] } tracing = "0.1" dirs = "5" -agent-api-types = { version = "0.2.8", path = "../agent-api-types" } +transport-api-types = { version = "0.2.8", path = "../transport-api-types" } async-trait = "0.1" tower-service = "0.3" diff --git a/crates/agent-api-client/src/lib.rs b/crates/transport-api-client/src/lib.rs similarity index 98% rename from crates/agent-api-client/src/lib.rs rename to crates/transport-api-client/src/lib.rs index a0777d681..4f17a3fcb 100644 --- a/crates/agent-api-client/src/lib.rs +++ b/crates/transport-api-client/src/lib.rs @@ -1,29 +1,29 @@ -//! Agent API client for forwarding requests to world-agent. +//! Agent API client for forwarding requests to world-service. //! //! This crate provides the client implementation used by host-proxy to forward -//! Agent API requests to world-agent running inside worlds/VMs. +//! Agent API requests to world-service running inside worlds/VMs. use std::path::Path; use std::sync::Arc; -use agent_api_types::{ +use anyhow::{anyhow, Context, Result}; +use http_body_util::{BodyExt, Full}; +use hyper::{body::Bytes, Method, Request, Response, StatusCode}; +use serde::{Deserialize, Serialize}; +use transport_api_types::{ ApiError, ExecuteCancelRequestV1, ExecuteCancelResponseV1, ExecuteRequest, ExecuteResponse, GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, MemberTurnSubmitRequestV1, PendingDiffClearRequestV1, PendingDiffClearResponseV1, PendingDiffReconcileRequestV1, PendingDiffReconcileResponseV1, PendingDiffRecordV1, PendingDiffRequestV1, WorldDoctorReportV1, WorldFsReadRequestV1, WorldFsReadResponseV1, }; -use anyhow::{anyhow, Context, Result}; -use http_body_util::{BodyExt, Full}; -use hyper::{body::Bytes, Method, Request, Response, StatusCode}; -use serde::{Deserialize, Serialize}; pub mod retry; pub mod transport; pub use transport::{build_connector, Connector, Transport, TransportMode}; -/// Client for communicating with world-agent. +/// Client for communicating with world-service. pub struct AgentClient { transport: Transport, connector: Arc, diff --git a/crates/agent-api-client/src/retry.rs b/crates/transport-api-client/src/retry.rs similarity index 100% rename from crates/agent-api-client/src/retry.rs rename to crates/transport-api-client/src/retry.rs diff --git a/crates/agent-api-client/src/transport/mod.rs b/crates/transport-api-client/src/transport/mod.rs similarity index 99% rename from crates/agent-api-client/src/transport/mod.rs rename to crates/transport-api-client/src/transport/mod.rs index 4cda5550e..2744b2361 100644 --- a/crates/agent-api-client/src/transport/mod.rs +++ b/crates/transport-api-client/src/transport/mod.rs @@ -32,7 +32,7 @@ use tokio::net::UnixStream; #[cfg(unix)] use tower_service::Service; -/// Transport options for communicating with world-agent. +/// Transport options for communicating with world-service. #[derive(Debug, Clone)] pub enum Transport { /// Unix domain socket connection. diff --git a/crates/agent-api-core/Cargo.toml b/crates/transport-api-core/Cargo.toml similarity index 65% rename from crates/agent-api-core/Cargo.toml rename to crates/transport-api-core/Cargo.toml index 1e955915b..1c0d717a7 100644 --- a/crates/agent-api-core/Cargo.toml +++ b/crates/transport-api-core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "agent-api-core" +name = "transport-api-core" version = "0.2.8" edition = "2021" @@ -9,6 +9,6 @@ http = "1.1" serde = { version = "1", features = ["derive"] } serde_json = "1" tower = "0.5" -agent-api-types = { version = "0.2.8", path = "../agent-api-types" } +transport-api-types = { version = "0.2.8", path = "../transport-api-types" } thiserror = "1" async-trait = "0.1" diff --git a/crates/agent-api-core/src/lib.rs b/crates/transport-api-core/src/lib.rs similarity index 95% rename from crates/agent-api-core/src/lib.rs rename to crates/transport-api-core/src/lib.rs index 63fc9f193..2c3557c28 100644 --- a/crates/agent-api-core/src/lib.rs +++ b/crates/transport-api-core/src/lib.rs @@ -1,8 +1,7 @@ -//! Agent API core: service trait and router builder shared by host-proxy and world-agent. +//! Agent API core: service trait and router builder shared by host-proxy and world-service. use std::sync::Arc; -use agent_api_types::{ApiError, ErrorResponse, ExecuteRequest, ExecuteResponse}; use axum::{ extract::{Path, State}, http::StatusCode, @@ -10,8 +9,9 @@ use axum::{ routing::{get, post}, Json, Router, }; +use transport_api_types::{ApiError, ErrorResponse, ExecuteRequest, ExecuteResponse}; -// Service trait implemented by both host-proxy (delegating) and world-agent (executing) +// Service trait implemented by both host-proxy (delegating) and world-service (executing) #[async_trait::async_trait] pub trait AgentService: Send + Sync + 'static { async fn execute(&self, req: ExecuteRequest) -> Result; diff --git a/crates/agent-api-types/Cargo.toml b/crates/transport-api-types/Cargo.toml similarity index 90% rename from crates/agent-api-types/Cargo.toml rename to crates/transport-api-types/Cargo.toml index 710c7e154..e44564f76 100644 --- a/crates/agent-api-types/Cargo.toml +++ b/crates/transport-api-types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "agent-api-types" +name = "transport-api-types" version = "0.2.8" edition = "2021" diff --git a/crates/agent-api-types/src/lib.rs b/crates/transport-api-types/src/lib.rs similarity index 99% rename from crates/agent-api-types/src/lib.rs rename to crates/transport-api-types/src/lib.rs index 174b056e7..04037bd53 100644 --- a/crates/agent-api-types/src/lib.rs +++ b/crates/transport-api-types/src/lib.rs @@ -1563,10 +1563,10 @@ pub struct WorldDoctorReportV1 { pub schema_version: u32, pub ok: bool, pub collected_at_utc: String, - /// Whether the connected world-agent supports ingesting `PolicySnapshotV1` on execution requests. + /// Whether the connected world-service supports ingesting `PolicySnapshotV1` on execution requests. #[serde(default)] pub policy_snapshot_v1_supported: bool, - /// The policy resolution mode most recently used by the world-agent (when known). + /// The policy resolution mode most recently used by the world-service (when known). #[serde(default)] pub policy_resolution_mode: Option, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -2549,7 +2549,7 @@ mod tests { parent_participant_id: Some("ash_parent_123".into()), resumed_from_participant_id: Some("ash_old_123".into()), backend_id: "cli:codex".into(), - protocol: "uaa.agent.session".into(), + protocol: "substrate.agent.session".into(), run_id: "run_123".into(), world_id: "world_123".into(), world_generation: 7, @@ -2576,7 +2576,7 @@ mod tests { parent_participant_id: Some("ash_parent_123".into()), resumed_from_participant_id: Some("ash_old_123".into()), backend_id: "cli:codex".into(), - protocol: "uaa.agent.session".into(), + protocol: "substrate.agent.session".into(), run_id: "run_123".into(), world_id: "world_123".into(), world_generation: 7, @@ -2712,7 +2712,7 @@ mod tests { "participant_id": "ash_member_123", "orchestrator_participant_id": "ash_orch_123", "backend_id": "cli:codex", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "run_id": "run_123", "world_id": "world_123", "world_generation": 7, @@ -2759,7 +2759,7 @@ mod tests { "participant_id": "ash_member_123", "orchestrator_participant_id": "ash_orch_123", "backend_id": "cli:codex", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "run_id": "run_123", "world_id": "world_123", "world_generation": 7, @@ -2787,7 +2787,7 @@ mod tests { "participant_id": "ash_member_123", "orchestrator_participant_id": "ash_member_123", "backend_id": "cli:codex", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "run_id": "run_123", "world_id": "world_123", "world_generation": 7, @@ -2832,7 +2832,7 @@ mod tests { "participant_id": "ash_member_123", "orchestrator_participant_id": "ash_orch_123", "backend_id": "cli:codex", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "run_id": "run_123", "world_id": "world_123", "world_generation": 7 @@ -2854,7 +2854,7 @@ mod tests { "participant_id": "ash_member_123", "orchestrator_participant_id": "ash_orch_123", "backend_id": "cli:codex", - "protocol": "uaa.agent.session", + "protocol": "substrate.agent.session", "run_id": "run_123", "world_id": "world_123", "world_generation": 7, @@ -3254,7 +3254,7 @@ mod tests { #[test] fn world_doctor_report_v1_defaults_snapshot_fields_when_missing() { - // Legacy world-agents may omit snapshot fields; the client schema must default safely. + // Legacy world-services may omit snapshot fields; the client schema must default safely. let json = r#"{ "schema_version": 1, "ok": true, diff --git a/crates/world-agent/tests/entrypoint.rs b/crates/world-agent/tests/entrypoint.rs deleted file mode 100644 index 4cda82777..000000000 --- a/crates/world-agent/tests/entrypoint.rs +++ /dev/null @@ -1,18 +0,0 @@ -#[test] -fn world_agent_main_delegates_to_library_entrypoint() { - let source = include_str!("../src/main.rs"); - - assert!( - source.contains("run_world_agent().await"), - "main should delegate to run_world_agent" - ); - - let line_count = source - .lines() - .filter(|line| !line.trim().is_empty()) - .count(); - assert!( - line_count <= 12, - "world-agent main should stay a thin wrapper around the library (got {line_count} lines)" - ); -} diff --git a/crates/world-mac-lima/Cargo.toml b/crates/world-mac-lima/Cargo.toml index e61842139..9e4a3bd0a 100644 --- a/crates/world-mac-lima/Cargo.toml +++ b/crates/world-mac-lima/Cargo.toml @@ -19,8 +19,8 @@ uuid = { workspace = true } thiserror = "1" which = "4" world-api = { path = "../world-api" } -agent-api-client = { path = "../agent-api-client" } -agent-api-types = { path = "../agent-api-types" } +transport-api-client = { path = "../transport-api-client" } +transport-api-types = { path = "../transport-api-types" } dirs = "6.0.0" [dev-dependencies] diff --git a/crates/world-mac-lima/src/lib.rs b/crates/world-mac-lima/src/lib.rs index 3d1c18e71..be32bd305 100644 --- a/crates/world-mac-lima/src/lib.rs +++ b/crates/world-mac-lima/src/lib.rs @@ -2,20 +2,20 @@ //! macOS world backend using Lima VM with Linux isolation inside. //! //! This backend provides identical policy enforcement semantics to LinuxLocal -//! by running a Linux VM via Lima and delegating to the world-agent inside. +//! by running a Linux VM via Lima and delegating to the world-service inside. -use agent_api_client::AgentClient; -use agent_api_types::{ - ExecuteRequest, ExecuteResponse, MemberDispatchRequestV1, - MemberRuntimeBackendKindV1 as AgentMemberRuntimeBackendKindV1, PolicySnapshotV3, - PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, - PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, WorldFsMode, -}; use anyhow::{Context, Result}; use serde::Deserialize; use std::path::PathBuf; use substrate_common::FsDiff; use tokio::runtime::Runtime; +use transport_api_client::AgentClient; +use transport_api_types::{ + ExecuteRequest, ExecuteResponse, MemberDispatchRequestV1, + MemberRuntimeBackendKindV1 as AgentMemberRuntimeBackendKindV1, PolicySnapshotV3, + PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, + PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, WorldFsMode, +}; use world_api::{ ExecRequest, ExecResult, SharedWorldBindingSnapshot, SharedWorldBindingState, SharedWorldOwnerAction, WorldBackend, WorldHandle, WorldSpec, @@ -30,7 +30,7 @@ pub use forwarding::{ForwardingHandle, ForwardingKind}; pub use transport::Transport; pub use vm::LimaVM; -/// macOS backend that delegates to Linux world-agent inside Lima VM. +/// macOS backend that delegates to Linux world-service inside Lima VM. pub struct MacLimaBackend { vm_name: String, agent_socket: PathBuf, @@ -178,7 +178,7 @@ impl MacLimaBackend { let max_attempts = 30; let mut attempts = 0; - tracing::info!("Waiting for world-agent to be ready..."); + tracing::info!("Waiting for world-service to be ready..."); while attempts < max_attempts { // Try to connect to the agent socket @@ -190,7 +190,7 @@ impl MacLimaBackend { attempts += 1; if attempts % 5 == 0 { tracing::debug!( - "Still waiting for world-agent... ({}/{})", + "Still waiting for world-service... ({}/{})", attempts, max_attempts ); @@ -391,7 +391,7 @@ impl MacLimaBackend { } } - fn get_agent_endpoint(&self) -> Result { + fn get_agent_endpoint(&self) -> Result { let forwarding = self .forwarding .lock() @@ -399,10 +399,10 @@ impl MacLimaBackend { match forwarding.as_ref() { Some(handle) => match handle.kind() { ForwardingKind::SshUds { path } => { - Ok(agent_api_client::Transport::UnixSocket { path: path.clone() }) + Ok(transport_api_client::Transport::UnixSocket { path: path.clone() }) } ForwardingKind::SshTcp { port } | ForwardingKind::Vsock { port } => { - Ok(agent_api_client::Transport::Tcp { + Ok(transport_api_client::Transport::Tcp { host: "127.0.0.1".to_string(), port: *port, }) @@ -412,7 +412,7 @@ impl MacLimaBackend { } } - /// Convert world_api::ExecRequest to agent_api_types::ExecuteRequest. + /// Convert world_api::ExecRequest to transport_api_types::ExecuteRequest. fn convert_exec_request(&self, req: &ExecRequest, fs_mode: WorldFsMode) -> ExecuteRequest { let write_enabled = matches!(fs_mode, WorldFsMode::Writable); let policy_snapshot = PolicySnapshotV3 { @@ -455,7 +455,7 @@ impl MacLimaBackend { } } - /// Convert agent_api_types::ExecuteResponse to world_api::ExecResult. + /// Convert transport_api_types::ExecuteResponse to world_api::ExecResult. fn convert_exec_response(&self, resp: ExecuteResponse) -> ExecResult { use base64::Engine; let engine = base64::engine::general_purpose::STANDARD; diff --git a/crates/world-agent/Cargo.toml b/crates/world-service/Cargo.toml similarity index 90% rename from crates/world-agent/Cargo.toml rename to crates/world-service/Cargo.toml index 689af5ea4..ea8ab4ba5 100644 --- a/crates/world-agent/Cargo.toml +++ b/crates/world-service/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "world-agent" +name = "world-service" version = "0.2.8" edition = "2021" authors = ["Atomize"] @@ -8,7 +8,7 @@ license = "MIT" repository = "https://github.com/atomize-hq/substrate" [[bin]] -name = "world-agent" +name = "world-service" path = "src/main.rs" [package.metadata.dist] @@ -40,8 +40,8 @@ once_cell = "1" libc = "0.2" sha2 = { workspace = true } substrate-common = { path = "../common" } -agent-api-core = { path = "../agent-api-core" } -agent-api-types = { path = "../agent-api-types" } +transport-api-core = { path = "../transport-api-core" } +transport-api-types = { path = "../transport-api-types" } world-api = { path = "../world-api" } substrate-broker = { path = "../broker" } diff --git a/crates/world-agent/src/enforcement_plan.rs b/crates/world-service/src/enforcement_plan.rs similarity index 99% rename from crates/world-agent/src/enforcement_plan.rs rename to crates/world-service/src/enforcement_plan.rs index a72b355fe..ce33a21ca 100644 --- a/crates/world-agent/src/enforcement_plan.rs +++ b/crates/world-service/src/enforcement_plan.rs @@ -1,8 +1,8 @@ -use agent_api_types::{PolicySnapshotV3, WorldFsDenyEnforcementV3}; use anyhow::{anyhow, Context, Result}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use serde::{Deserialize, Serialize}; +use transport_api_types::{PolicySnapshotV3, WorldFsDenyEnforcementV3}; pub(crate) const WORLD_FS_ENFORCEMENT_PLAN_B64_ENV: &str = "SUBSTRATE_WORLD_FS_ENFORCEMENT_PLAN_B64"; @@ -180,11 +180,11 @@ fn validate_deny_wildcards(pattern: &str) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use agent_api_types::{ + use std::sync::{Mutex, OnceLock}; + use transport_api_types::{ PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, WorldFsDenyEnforcementV3, }; - use std::sync::{Mutex, OnceLock}; static ENV_LOCK: OnceLock> = OnceLock::new(); diff --git a/crates/world-agent/src/gateway_runtime.rs b/crates/world-service/src/gateway_runtime.rs similarity index 99% rename from crates/world-agent/src/gateway_runtime.rs rename to crates/world-service/src/gateway_runtime.rs index 6c95ce848..31299abdc 100644 --- a/crates/world-agent/src/gateway_runtime.rs +++ b/crates/world-service/src/gateway_runtime.rs @@ -1,7 +1,3 @@ -use agent_api_types::{ - validate_gateway_backend_id_selector, GatewayCliCodexIntegratedAuthV1, GatewayClientWiringV1, - GatewayIntegratedAuthPayloadV1, GatewayLifecycleResponseV1, GatewayStatusV1, -}; use anyhow::{anyhow, Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -22,6 +18,10 @@ use substrate_common::{ SUBSTRATE_LLM_BACKEND_AUTH_CLI_CODEX_ACCOUNT_ID, }; use tokio::sync::Mutex as AsyncMutex; +use transport_api_types::{ + validate_gateway_backend_id_selector, GatewayCliCodexIntegratedAuthV1, GatewayClientWiringV1, + GatewayIntegratedAuthPayloadV1, GatewayLifecycleResponseV1, GatewayStatusV1, +}; pub(crate) const GATEWAY_REQUEST_ENABLED_ENV: &str = "SUBSTRATE_LLM_GATEWAY_ENABLED"; pub(crate) const GATEWAY_REQUEST_MODE_ENV: &str = "SUBSTRATE_LLM_GATEWAY_MODE"; @@ -1521,14 +1521,14 @@ fn lifecycle_status_transient_failure( #[cfg(all(test, target_os = "linux"))] mod tests { use super::*; - use agent_api_types::{ - GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, - GatewayIntegratedAuthPayloadV1, - }; use once_cell::sync::Lazy; use std::io::{Read, Write}; use std::os::unix::fs::PermissionsExt; use tempfile::TempDir; + use transport_api_types::{ + GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, + GatewayIntegratedAuthPayloadV1, + }; static ENV_LOCK: Lazy> = Lazy::new(|| AsyncMutex::new(())); const MISSING_CAPABILITY_BINDING: GatewayBackendBinding = GatewayBackendBinding { diff --git a/crates/world-agent/src/gc.rs b/crates/world-service/src/gc.rs similarity index 100% rename from crates/world-agent/src/gc.rs rename to crates/world-service/src/gc.rs diff --git a/crates/world-agent/src/handlers.rs b/crates/world-service/src/handlers.rs similarity index 94% rename from crates/world-agent/src/handlers.rs rename to crates/world-service/src/handlers.rs index bdea1e35c..3219a136b 100644 --- a/crates/world-agent/src/handlers.rs +++ b/crates/world-service/src/handlers.rs @@ -1,16 +1,6 @@ //! HTTP handlers for the world agent API. -use crate::service::WorldAgentService; -use agent_api_types::{ - ApiError, ExecuteCancelRequestV1, ExecuteCancelResponseV1, ExecuteRequest, ExecuteResponse, - GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, MemberTurnSubmitRequestV1, - PendingDiffClearRequestV1, PendingDiffClearResponseV1, PendingDiffReconcileRequestV1, - PendingDiffReconcileResponseV1, PendingDiffRecordV1, PendingDiffRequestV1, - WorldDoctorLandlockV1, WorldDoctorNetfilterStatusV1, WorldDoctorReportV1, - WorldDoctorWorldFsStrategyKindV1, WorldDoctorWorldFsStrategyProbeResultV1, - WorldDoctorWorldFsStrategyProbeV1, WorldDoctorWorldFsStrategyV1, WorldFsReadRequestV1, - WorldFsReadResponseV1, -}; +use crate::service::WorldService; use axum::{ body::Bytes, extract::{Json, Path, State}, @@ -21,6 +11,16 @@ use chrono::SecondsFormat; use serde_json::{json, Value}; #[cfg(target_os = "linux")] use substrate_common::{WorldFsMode, WorldFsStrategyProbeResult}; +use transport_api_types::{ + ApiError, ExecuteCancelRequestV1, ExecuteCancelResponseV1, ExecuteRequest, ExecuteResponse, + GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, MemberTurnSubmitRequestV1, + PendingDiffClearRequestV1, PendingDiffClearResponseV1, PendingDiffReconcileRequestV1, + PendingDiffReconcileResponseV1, PendingDiffRecordV1, PendingDiffRequestV1, + WorldDoctorLandlockV1, WorldDoctorNetfilterStatusV1, WorldDoctorReportV1, + WorldDoctorWorldFsStrategyKindV1, WorldDoctorWorldFsStrategyProbeResultV1, + WorldDoctorWorldFsStrategyProbeV1, WorldDoctorWorldFsStrategyV1, WorldFsReadRequestV1, + WorldFsReadResponseV1, +}; #[cfg(target_os = "linux")] fn doctor_world_netfilter_enable_present() -> bool { @@ -81,14 +81,14 @@ pub async fn capabilities() -> Result, ApiErrorResponse> { "trace_retrieval", "scope_requests" ], - "backend": "world-agent", + "backend": "world-service", "platform": std::env::consts::OS }))) } /// Get agent-reported world enforcement readiness. pub async fn doctor_world( - State(service): State, + State(service): State, ) -> Result, ApiErrorResponse> { let collected_at_utc = chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true); @@ -183,7 +183,7 @@ pub async fn doctor_world( /// Execute a command in the world. pub async fn execute( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -203,7 +203,7 @@ pub async fn execute( /// Retrieve the current session's pending diff record. pub async fn pending_diff( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -223,7 +223,7 @@ pub async fn pending_diff( /// Conditionally clear the current session's pending diff snapshot. pub async fn pending_diff_clear( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -243,7 +243,7 @@ pub async fn pending_diff_clear( /// Conditionally reconcile pending diff paths by discarding upper/work entries. pub async fn pending_diff_reconcile( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -263,7 +263,7 @@ pub async fn pending_diff_reconcile( /// Read metadata and optionally contents from the current session's overlay filesystem. pub async fn world_fs_read( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -283,7 +283,7 @@ pub async fn world_fs_read( /// Return the typed gateway lifecycle/status surface. pub async fn gateway_status( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -300,7 +300,7 @@ pub async fn gateway_status( /// Return the typed gateway lifecycle sync surface. pub async fn gateway_sync( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -317,7 +317,7 @@ pub async fn gateway_sync( /// Return the typed gateway lifecycle restart surface. pub async fn gateway_restart( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -334,7 +334,7 @@ pub async fn gateway_restart( /// Execute a command and stream incremental output. pub async fn execute_stream( - State(service): State, + State(service): State, body: Bytes, ) -> Result { let payload: Value = serde_json::from_slice(&body) @@ -352,7 +352,7 @@ pub async fn execute_stream( /// Submit a follow-up turn to a retained world member and stream the response. pub async fn member_turn_stream( - State(service): State, + State(service): State, body: Bytes, ) -> Result { let payload: Value = serde_json::from_slice(&body) @@ -370,7 +370,7 @@ pub async fn member_turn_stream( /// Send a signal to an active streamed execution. pub async fn execute_cancel( - State(service): State, + State(service): State, body: Bytes, ) -> Result, ApiErrorResponse> { let payload: Value = serde_json::from_slice(&body) @@ -390,7 +390,7 @@ pub async fn execute_cancel( /// Handle WebSocket upgrade for PTY streaming. pub async fn stream( - State(service): State, + State(service): State, ws: axum::extract::ws::WebSocketUpgrade, ) -> axum::response::Response { ws.on_upgrade(move |socket| async move { @@ -401,7 +401,7 @@ pub async fn stream( /// Get trace information for a span. pub async fn get_trace( Path(span_id): Path, - State(service): State, + State(service): State, ) -> Result, ApiErrorResponse> { let trace = service .get_trace(&span_id) @@ -413,7 +413,7 @@ pub async fn get_trace( /// Request additional scopes. pub async fn request_scopes( - State(service): State, + State(service): State, Json(scopes): Json>, ) -> Result, ApiErrorResponse> { let response = service @@ -426,7 +426,7 @@ pub async fn request_scopes( /// Garbage collect orphaned network namespaces. pub async fn gc( - State(_service): State, + State(_service): State, ) -> Result, ApiErrorResponse> { // Read TTL from environment let ttl = std::env::var("SUBSTRATE_NETNS_GC_TTL_SECS") @@ -448,7 +448,7 @@ pub async fn gc( mod tests { use super::*; #[cfg(target_os = "linux")] - use crate::service::WorldAgentService; + use crate::service::WorldService; #[cfg(target_os = "linux")] use tokio::sync::Mutex; @@ -498,7 +498,7 @@ mod tests { assert_eq!(value["version"], "v1"); assert!(value["features"].is_array()); - assert_eq!(value["backend"], "world-agent"); + assert_eq!(value["backend"], "world-service"); } #[cfg(target_os = "linux")] @@ -506,7 +506,7 @@ mod tests { async fn doctor_world_defaults_netfilter_status_when_no_request_seen() { let _lock = ENV_LOCK.lock().await; let _guard = EnvGuard::set(&[("WORLD_NETFILTER_ENABLE", None)]); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let result = doctor_world(State(service)).await.expect("doctor"); let report = result.0; @@ -523,7 +523,7 @@ mod tests { async fn doctor_world_reports_requested_and_guard_presence() { let _lock = ENV_LOCK.lock().await; let _guard = EnvGuard::set(&[("WORLD_NETFILTER_ENABLE", Some("true"))]); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.set_last_netfilter_requested(true); let result = doctor_world(State(service)).await.expect("doctor"); @@ -541,7 +541,7 @@ mod tests { async fn doctor_world_reports_requested_without_guard_as_disabled() { let _lock = ENV_LOCK.lock().await; let _guard = EnvGuard::set(&[("WORLD_NETFILTER_ENABLE", None)]); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.set_last_netfilter_requested(true); let result = doctor_world(State(service)).await.expect("doctor"); @@ -559,7 +559,7 @@ mod tests { async fn doctor_world_surfaces_last_netfilter_failure_reason() { let _lock = ENV_LOCK.lock().await; let _guard = EnvGuard::set(&[("WORLD_NETFILTER_ENABLE", None)]); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.set_last_netfilter_requested(true); service.set_last_netfilter_failure_reason(Some( "WORLD_NETFILTER_ENABLE must be set to 1/true/yes before requested network isolation can install nftables rules" diff --git a/crates/world-agent/src/internal_exec.rs b/crates/world-service/src/internal_exec.rs similarity index 100% rename from crates/world-agent/src/internal_exec.rs rename to crates/world-service/src/internal_exec.rs diff --git a/crates/world-agent/src/lib.rs b/crates/world-service/src/lib.rs similarity index 97% rename from crates/world-agent/src/lib.rs rename to crates/world-service/src/lib.rs index 19e5bf73c..6d6629cc0 100644 --- a/crates/world-agent/src/lib.rs +++ b/crates/world-service/src/lib.rs @@ -17,7 +17,7 @@ mod world_exec_guard; #[cfg(unix)] pub use crate::socket_activation::test_support as socket_activation_test_support; -pub use service::WorldAgentService; +pub use service::WorldService; #[cfg(unix)] use crate::socket_activation::{ @@ -50,12 +50,12 @@ const SOCKET_PATH: &str = "/run/substrate.sock"; const TCP_ENV_VAR: &str = "SUBSTRATE_AGENT_TCP_PORT"; const SOCKET_ENV_VAR: &str = "SUBSTRATE_WORLD_SOCKET"; -pub async fn run_world_agent() -> Result<()> { +pub async fn run_world_service() -> Result<()> { fmt().with_env_filter(EnvFilter::from_default_env()).init(); let _ = set_global_broker(BrokerHandle::new()); - info!("Starting Substrate World Agent"); + info!("Starting Substrate World Service"); let socket_path = std::env::var_os(SOCKET_ENV_VAR) .map(PathBuf::from) @@ -66,7 +66,7 @@ pub async fn run_world_agent() -> Result<()> { log_listener_mode(socket_activation.as_ref()); #[cfg(not(unix))] info!( - component = "world-agent", + component = "world-service", event = "listener_state", mode = "direct_bind", listen_fds = 0, @@ -120,7 +120,7 @@ pub async fn run_world_agent() -> Result<()> { } } - let service = WorldAgentService::new()?; + let service = WorldService::new()?; let router = build_router(service.clone()); info!("Routes registered, starting listeners"); @@ -158,7 +158,7 @@ pub async fn run_world_agent() -> Result<()> { .await .with_context(|| format!("Failed to bind TCP listener on {addr}"))?; info!( - component = "world-agent", + component = "world-service", event = "listener_state", listener_kind = "tcp", listener_mode = "direct_bind", @@ -171,7 +171,7 @@ pub async fn run_world_agent() -> Result<()> { }); } else { info!( - component = "world-agent", + component = "world-service", event = "listener_state", listener_kind = "tcp", listener_mode = "socket_activation", @@ -182,7 +182,7 @@ pub async fn run_world_agent() -> Result<()> { } } else if tcp_handles.is_empty() { info!( - component = "world-agent", + component = "world-service", event = "listener_state", listener_kind = "tcp", listener_mode = "disabled", @@ -190,7 +190,7 @@ pub async fn run_world_agent() -> Result<()> { ); } else { info!( - component = "world-agent", + component = "world-service", event = "listener_state", listener_kind = "tcp", listener_mode = "socket_activation", @@ -251,7 +251,7 @@ fn prepare_socket_path(path: &Path) -> Result<()> { Ok(()) } -fn build_router(service: WorldAgentService) -> Router { +fn build_router(service: WorldService) -> Router { Router::new() .route("/v1/capabilities", get(handlers::capabilities)) .route("/v1/doctor/world", get(handlers::doctor_world)) @@ -280,7 +280,7 @@ fn build_router(service: WorldAgentService) -> Router { fn log_listener_mode(activation: Option<&SocketActivation>) { if let Some(activation) = activation { info!( - component = "world-agent", + component = "world-service", event = "listener_state", mode = "socket_activation", listen_fds = activation.total_fds, @@ -290,7 +290,7 @@ fn log_listener_mode(activation: Option<&SocketActivation>) { ); } else { info!( - component = "world-agent", + component = "world-service", event = "listener_state", mode = "direct_bind", listen_fds = 0, @@ -330,7 +330,7 @@ async fn run_uds_server( let stream = UnixListenerStream::new(listener); let inherited_name = name.as_deref().unwrap_or(""); info!( - component = "world-agent", + component = "world-service", event = "listener_ready", listener_kind = "uds", listener_mode = mode, @@ -423,7 +423,7 @@ async fn run_tcp_server( let inherited_fd = source.fd(); let inherited_name = source.name().unwrap_or(""); info!( - component = "world-agent", + component = "world-service", event = "listener_ready", listener_kind = "tcp", listener_mode = source.mode(), diff --git a/crates/world-agent/src/main.rs b/crates/world-service/src/main.rs similarity index 75% rename from crates/world-agent/src/main.rs rename to crates/world-service/src/main.rs index ac965af1c..d67b5c590 100644 --- a/crates/world-agent/src/main.rs +++ b/crates/world-service/src/main.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use world_agent::{internal_exec, run_world_agent}; +use world_service::{internal_exec, run_world_service}; #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { @@ -10,5 +10,5 @@ async fn main() -> Result<()> { return internal_exec::run_landlock_exec(); } - run_world_agent().await + run_world_service().await } diff --git a/crates/world-agent/src/member_runtime.rs b/crates/world-service/src/member_runtime.rs similarity index 99% rename from crates/world-agent/src/member_runtime.rs rename to crates/world-service/src/member_runtime.rs index 453266f94..01bf976ce 100644 --- a/crates/world-agent/src/member_runtime.rs +++ b/crates/world-service/src/member_runtime.rs @@ -7,10 +7,6 @@ use agent_api::{ AgentWrapperEventKind, AgentWrapperGateway, AgentWrapperKind, AgentWrapperRunControl, AgentWrapperRunRequest, }; -use agent_api_types::{ - ExecuteStreamFrame, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, - MemberTurnSubmitRequestV1, ProcessTelemetry, -}; use anyhow::{anyhow, Result}; use axum::{ body::{boxed, Bytes, StreamBody}, @@ -28,6 +24,10 @@ use std::{ }; use substrate_common::agent_events::{AgentEvent, AgentEventKind, MessageEventKind}; use tokio_stream::wrappers::UnboundedReceiverStream; +use transport_api_types::{ + ExecuteStreamFrame, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, + MemberTurnSubmitRequestV1, ProcessTelemetry, +}; use world_api::SharedWorldBindingSnapshot; use crate::gateway_runtime::{prepare_linux_world_entry_launcher, LinuxWorldPlacementContext}; diff --git a/crates/world-agent/src/pty.rs b/crates/world-service/src/pty.rs similarity index 99% rename from crates/world-agent/src/pty.rs rename to crates/world-service/src/pty.rs index 6c1529b1a..007dc4c90 100644 --- a/crates/world-agent/src/pty.rs +++ b/crates/world-service/src/pty.rs @@ -1,10 +1,10 @@ -//! PTY WebSocket handler for world-agent implementing JSON frame protocol +//! PTY WebSocket handler for world-service implementing JSON frame protocol #[cfg(target_os = "linux")] use crate::enforcement_plan; #[cfg(target_os = "linux")] use crate::request_routing::resolve_snapshot_routing; -use crate::service::WorldAgentService; +use crate::service::WorldService; #[cfg(target_os = "linux")] use crate::service::{ apply_full_isolation_helper_env, build_world_spec, is_full_isolation, @@ -12,9 +12,6 @@ use crate::service::{ resolve_project_write_allowlist_prefixes, resolve_shared_world_binding, WORLD_FS_MODE_ENV, WORLD_FS_WRITE_ALLOWLIST_ENV, }; -#[cfg(target_os = "linux")] -use agent_api_types::PolicyResolutionModeV1; -use agent_api_types::{PolicySnapshotV3, SharedWorldOwnerSpec, WorldNetworkRoutingV1}; use axum::extract::ws::{Message, WebSocket}; #[cfg(target_os = "linux")] use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; @@ -44,6 +41,9 @@ use tokio::sync::Mutex; use tracing::warn; use tracing::{error, info}; #[cfg(target_os = "linux")] +use transport_api_types::PolicyResolutionModeV1; +use transport_api_types::{PolicySnapshotV3, SharedWorldOwnerSpec, WorldNetworkRoutingV1}; +#[cfg(target_os = "linux")] use world::exec::PROJECT_BIND_MOUNT_ENFORCEMENT_SCRIPT; #[cfg(target_os = "linux")] use world::guard::{should_guard_anchor, wrap_with_anchor_guard}; @@ -52,7 +52,7 @@ use world_api::WorldFsMode; #[cfg(target_os = "linux")] fn record_doctor_request_context_for_pty( - service: &WorldAgentService, + service: &WorldService, policy_resolution_mode: PolicyResolutionModeV1, isolate_network: bool, ) { @@ -690,7 +690,7 @@ printf '%s' ok"#, } pub async fn handle_ws_pty( - #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] service: WorldAgentService, + #[cfg_attr(not(target_os = "linux"), allow(unused_variables))] service: WorldService, ws: WebSocket, ) { info!("ws_pty: client connected"); @@ -776,7 +776,7 @@ pub async fn handle_ws_pty( #[cfg(not(target_os = "linux"))] async fn handle_persistent_session( - _service: WorldAgentService, + _service: WorldService, tx: Arc>, _rx: WsReceiver, _first_text: String, @@ -785,7 +785,7 @@ async fn handle_persistent_session( &tx, &PersistentServerMessage::Error { code: "internal_error".to_string(), - message: "Persistent sessions are only supported on Linux world-agent".to_string(), + message: "Persistent sessions are only supported on Linux world-service".to_string(), fatal: true, seq: None, }, @@ -795,7 +795,7 @@ async fn handle_persistent_session( #[cfg(target_os = "linux")] async fn handle_persistent_session( - service: WorldAgentService, + service: WorldService, tx: Arc>, mut rx: WsReceiver, first_text: String, @@ -1516,7 +1516,7 @@ fn apply_full_isolation_env_from_snapshot( #[cfg(all(test, target_os = "linux"))] mod wfgad2_tests { use super::*; - use agent_api_types::{ + use transport_api_types::{ PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, WorldFsDenyEnforcementV3, }; @@ -1570,7 +1570,7 @@ mod wfgad2_tests { #[cfg(target_os = "linux")] fn prepare_persistent_world_context( - service: &WorldAgentService, + service: &WorldService, session_env: &HashMap, ready_cwd: &std::path::Path, policy_snapshot: &PolicySnapshotV3, @@ -2259,7 +2259,7 @@ fn traced_wait_loop( #[cfg(target_os = "linux")] async fn handle_legacy_start( - service: WorldAgentService, + service: WorldService, tx: Arc>, mut rx: WsReceiver, first_text: String, @@ -2430,7 +2430,7 @@ async fn handle_legacy_start( warn!( error = %e, cwd = %cwd.display(), - "world-agent: failed to detect policy profile for PTY request" + "world-service: failed to detect policy profile for PTY request" ); } ( @@ -2816,7 +2816,7 @@ async fn handle_legacy_start( #[cfg(not(target_os = "linux"))] async fn handle_legacy_start( - _service: WorldAgentService, + _service: WorldService, tx: Arc>, mut rx: WsReceiver, _first_text: String, @@ -2825,7 +2825,7 @@ async fn handle_legacy_start( &tx, &PersistentServerMessage::Error { code: "internal_error".to_string(), - message: "Legacy PTY streaming is only supported on Linux world-agent".to_string(), + message: "Legacy PTY streaming is only supported on Linux world-service".to_string(), fatal: true, seq: None, }, @@ -2919,7 +2919,7 @@ mod tests { #[cfg(target_os = "linux")] #[test] fn record_doctor_request_context_for_pty_updates_requested_state() { - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); record_doctor_request_context_for_pty(&service, PolicyResolutionModeV1::SnapshotV3, true); diff --git a/crates/world-agent/src/request_routing.rs b/crates/world-service/src/request_routing.rs similarity index 99% rename from crates/world-agent/src/request_routing.rs rename to crates/world-service/src/request_routing.rs index 3184d1aa0..6d4c28821 100644 --- a/crates/world-agent/src/request_routing.rs +++ b/crates/world-service/src/request_routing.rs @@ -1,8 +1,8 @@ -use agent_api_types::{ +use substrate_common::WorldFsMode; +use transport_api_types::{ canonicalize_net_allowed, validate_net_allowed_for_enforcement, PolicySnapshotV3, WorldNetworkRoutingV1, }; -use substrate_common::WorldFsMode; #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct ResolvedWorldNetworkRouting { @@ -88,7 +88,7 @@ fn resolve_world_network( #[cfg(test)] mod tests { use super::*; - use agent_api_types::{ + use transport_api_types::{ PolicySnapshotWorldFsDimensionV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, }; diff --git a/crates/world-agent/src/service.rs b/crates/world-service/src/service.rs similarity index 96% rename from crates/world-agent/src/service.rs rename to crates/world-service/src/service.rs index 953d2d3a5..6cf0162af 100644 --- a/crates/world-agent/src/service.rs +++ b/crates/world-service/src/service.rs @@ -1,20 +1,5 @@ //! Core service implementation for world agent. -#[cfg(target_os = "linux")] -use agent_api_types::ExecuteStreamFrame; -#[cfg(not(target_os = "linux"))] -use agent_api_types::GatewayStatusV1; -#[cfg(any(target_os = "linux", test))] -use agent_api_types::PendingDiffBucketV1; -#[cfg(target_os = "linux")] -use agent_api_types::WorldFsEntryTypeV1; -use agent_api_types::{ - Budget, ExecuteCancelRequestV1, ExecuteCancelResponseV1, ExecuteRequest, ExecuteResponse, - GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, MemberTurnSubmitRequestV1, - PendingDiffClearRequestV1, PendingDiffClearResponseV1, PendingDiffReconcileRequestV1, - PendingDiffReconcileResponseV1, PendingDiffRecordV1, PendingDiffRequestV1, ProcessTelemetry, - WorldFsReadRequestV1, WorldFsReadResponseV1, WorldNetworkRoutingV1, -}; #[cfg(target_os = "linux")] use anyhow::Context; use anyhow::{anyhow, Result}; @@ -50,6 +35,21 @@ use tokio::task; #[cfg(target_os = "linux")] use tokio_stream::wrappers::UnboundedReceiverStream; #[cfg(target_os = "linux")] +use transport_api_types::ExecuteStreamFrame; +#[cfg(not(target_os = "linux"))] +use transport_api_types::GatewayStatusV1; +#[cfg(any(target_os = "linux", test))] +use transport_api_types::PendingDiffBucketV1; +#[cfg(target_os = "linux")] +use transport_api_types::WorldFsEntryTypeV1; +use transport_api_types::{ + Budget, ExecuteCancelRequestV1, ExecuteCancelResponseV1, ExecuteRequest, ExecuteResponse, + GatewayLifecycleRequestV1, GatewayLifecycleResponseV1, MemberTurnSubmitRequestV1, + PendingDiffClearRequestV1, PendingDiffClearResponseV1, PendingDiffReconcileRequestV1, + PendingDiffReconcileResponseV1, PendingDiffRecordV1, PendingDiffRequestV1, ProcessTelemetry, + WorldFsReadRequestV1, WorldFsReadResponseV1, WorldNetworkRoutingV1, +}; +#[cfg(target_os = "linux")] use world::stream::{install_stream_sink, StreamKind, StreamSink}; use world_api::{ MemberDispatchRequestV1 as BackendMemberDispatchRequestV1, @@ -85,8 +85,8 @@ pub(crate) const WORLD_FS_LANDLOCK_DISCOVER_ALLOWLIST_ENV: &str = "SUBSTRATE_WORLD_FS_LANDLOCK_DISCOVER_ALLOWLIST"; pub(crate) const LANDLOCK_HELPER_SRC_ENV: &str = "SUBSTRATE_LANDLOCK_HELPER_SRC"; -const CARGO_BIN_EXE_WORLD_AGENT_ENV: &str = "CARGO_BIN_EXE_world-agent"; -const CARGO_BIN_EXE_WORLD_AGENT_ALT_ENV: &str = "CARGO_BIN_EXE_world_agent"; +const CARGO_BIN_EXE_WORLD_AGENT_ENV: &str = "CARGO_BIN_EXE_world-service"; +const CARGO_BIN_EXE_WORLD_AGENT_ALT_ENV: &str = "CARGO_BIN_EXE_world_service"; const NETFILTER_ENABLE_REQUIRED_TEXT: &str = "WORLD_NETFILTER_ENABLE must be set to 1/true/yes before requested network isolation can install nftables rules"; @@ -100,20 +100,20 @@ const NETFILTER_CGROUP_FORCE_DIRECT_TEXT: &str = "SUBSTRATE_WORLD_EXEC_FORCE_DIRECT is unsupported when isolate_network=true because cgroup attach is not guaranteed"; fn resolve_landlock_helper_src_from_exe(exe: &std::path::Path) -> Option { - let is_world_agent_exe = exe.file_name().is_some_and(|name| { - name == std::ffi::OsStr::new("world-agent") - || name == std::ffi::OsStr::new("substrate-world-agent") + let is_world_service_exe = exe.file_name().is_some_and(|name| { + name == std::ffi::OsStr::new("world-service") + || name == std::ffi::OsStr::new("substrate-world-service") }); - if is_world_agent_exe && exe.is_file() { + if is_world_service_exe && exe.is_file() { return Some(exe.to_owned()); } // Common case in `cargo test`: current_exe is the test harness under `target/*/deps/...`. - // Walk up a few parents and look for the workspace-built `world-agent` binary. + // Walk up a few parents and look for the workspace-built `world-service` binary. let mut dir = exe.parent(); for _ in 0..8 { let Some(d) = dir else { break }; - let candidate = d.join("world-agent"); + let candidate = d.join("world-service"); if candidate.is_file() { return Some(candidate); } @@ -153,7 +153,7 @@ fn resolve_landlock_helper_src() -> Option { if let Ok(candidate) = std::env::var(LANDLOCK_HELPER_SRC_ENV) { if let Some(path) = accept_candidate(&candidate) { let is_test_harness_self = exe.as_ref().is_some_and(|exe| { - exe.file_name().is_none_or(|n| n != "world-agent") && same_file(&path, exe) + exe.file_name().is_none_or(|n| n != "world-service") && same_file(&path, exe) }); if !is_test_harness_self { return Some(path.display().to_string()); @@ -186,7 +186,7 @@ fn resolve_landlock_helper_src() -> Option { /// Main service running inside the world. #[derive(Clone)] -pub struct WorldAgentService { +pub struct WorldService { backend: Arc, #[cfg(target_os = "linux")] linux_backend: Arc, @@ -253,7 +253,7 @@ impl AgentBudgetTracker { } } -impl WorldAgentService { +impl WorldService { pub fn new() -> Result { #[cfg(target_os = "linux")] { @@ -1387,7 +1387,7 @@ impl WorldAgentService { service.clear_last_netfilter_failure_on_success(isolate_network); #[cfg(target_os = "linux")] if let Some(ref diff) = exec_result.fs_diff { - let snapshot = WorldAgentService::normalize_pending_diff_bucket(diff); + let snapshot = WorldService::normalize_pending_diff_bucket(diff); service.note_pending_diff_origin_for_world( &world.id, PendingDiffOrigin::NonPty, @@ -1617,7 +1617,7 @@ impl WorldAgentService { #[cfg(target_os = "linux")] fn resolve_authoritative_member_placement_context( &self, - dispatch: &agent_api_types::MemberDispatchRequestV1, + dispatch: &transport_api_types::MemberDispatchRequestV1, world: WorldHandle, authoritative_binding: Option<&SharedWorldBindingSnapshot>, project_dir: &Path, @@ -1779,7 +1779,7 @@ struct PreparedGatewayRuntimeRequest { project_dir: PathBuf, world_spec: WorldSpec, selected_backend: String, - integrated_auth: Option, + integrated_auth: Option, } #[cfg(target_os = "linux")] @@ -1994,10 +1994,10 @@ fn resolve_member_runtime_effective_cwd( #[cfg(test)] mod pending_diff_id_tests { - use super::WorldAgentService; - use agent_api_types::PendingDiffBucketV1; + use super::WorldService; use std::path::PathBuf; use substrate_common::FsDiff; + use transport_api_types::PendingDiffBucketV1; #[test] fn diff_id_is_stable_across_writes_vs_mods_reclassification() { @@ -2012,8 +2012,8 @@ mod pending_diff_id_tests { deletes: vec![], }; assert_eq!( - WorldAgentService::pending_diff_id_for_snapshot(&s1), - WorldAgentService::pending_diff_id_for_snapshot(&s2), + WorldService::pending_diff_id_for_snapshot(&s1), + WorldService::pending_diff_id_for_snapshot(&s2), "diff_id must not change when a path moves between writes and mods" ); } @@ -2036,13 +2036,13 @@ mod pending_diff_id_tests { deletes: vec!["gone.txt".to_string()], }; assert_ne!( - WorldAgentService::pending_diff_id_for_snapshot(&base), - WorldAgentService::pending_diff_id_for_snapshot(&with_more_updates), + WorldService::pending_diff_id_for_snapshot(&base), + WorldService::pending_diff_id_for_snapshot(&with_more_updates), "diff_id must change when the update set changes" ); assert_ne!( - WorldAgentService::pending_diff_id_for_snapshot(&base), - WorldAgentService::pending_diff_id_for_snapshot(&with_delete), + WorldService::pending_diff_id_for_snapshot(&base), + WorldService::pending_diff_id_for_snapshot(&with_delete), "diff_id must change when the delete set changes" ); } @@ -2058,8 +2058,8 @@ mod pending_diff_id_tests { ..Default::default() }; assert_eq!( - WorldAgentService::pending_diff_id_for_diff(&d1), - WorldAgentService::pending_diff_id_for_diff(&d2), + WorldService::pending_diff_id_for_diff(&d1), + WorldService::pending_diff_id_for_diff(&d2), "diff_id must not change when FsDiff reclassifies a path between writes and mods" ); } @@ -2068,13 +2068,13 @@ mod pending_diff_id_tests { #[cfg(all(target_os = "linux", test))] mod gateway_runtime_binding_tests { use super::*; - use agent_api_types::{ + use serde_json::json; + use tempfile::TempDir; + use transport_api_types::{ GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, GatewayIntegratedAuthPayloadV1, PolicySnapshotV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, WorldNetworkRoutingV1, }; - use serde_json::json; - use tempfile::TempDir; fn minimal_policy_snapshot() -> PolicySnapshotV3 { PolicySnapshotV3 { @@ -2130,7 +2130,7 @@ mod gateway_runtime_binding_tests { #[test] fn request_preparation_rejects_integrated_auth_for_different_backend() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let mut request = gateway_request_with_backend(temp_dir.path(), "cli:codex"); let mut env = HashMap::new(); env.insert("OPENAI_API_KEY".to_string(), "sk-test".to_string()); @@ -2214,7 +2214,7 @@ mod gateway_runtime_binding_tests { #[test] fn request_preparation_accepts_valid_cli_codex() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service .prepare_gateway_runtime_request(&gateway_request(temp_dir.path())) @@ -2224,7 +2224,7 @@ mod gateway_runtime_binding_tests { #[test] fn non_isolated_binding_uses_synthetic_runtime_key() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let prepared = service .prepare_gateway_runtime_request(&gateway_request(temp_dir.path())) .expect("prepared request"); @@ -2248,7 +2248,7 @@ mod gateway_runtime_binding_tests { #[test] fn unbound_api_backend_returns_none_without_invalid_integration() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let mut request = gateway_request_with_backend(temp_dir.path(), "api:anthropic"); let mut env = HashMap::new(); env.insert("OPENAI_API_KEY".to_string(), "sk-test".to_string()); @@ -2276,7 +2276,7 @@ mod gateway_runtime_binding_tests { #[test] fn isolated_binding_does_not_synthesize_runtime_without_session() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let mut request = gateway_request(temp_dir.path()); request.world_network = Some(WorldNetworkRoutingV1 { isolate_network: true, @@ -2332,7 +2332,7 @@ mod gateway_runtime_binding_tests { #[test] fn request_preparation_rejects_multi_facet_payloads() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let mut request = gateway_request(temp_dir.path()); let mut env = HashMap::new(); env.insert("OPENAI_API_KEY".to_string(), "sk-test".to_string()); @@ -2354,7 +2354,7 @@ mod gateway_runtime_binding_tests { #[test] fn request_preparation_rejects_blank_required_values() { let temp_dir = TempDir::new().unwrap(); - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let mut request = gateway_request(temp_dir.path()); request.integrated_auth = Some(GatewayIntegratedAuthPayloadV1 { backend_id: "cli:codex".to_string(), @@ -2475,12 +2475,12 @@ struct PolicyInputs { } fn resolve_policy_inputs( - policy_snapshot: &agent_api_types::PolicySnapshotV3, + policy_snapshot: &transport_api_types::PolicySnapshotV3, world_network: Option<&WorldNetworkRoutingV1>, _cwd: &Path, project_dir: &Path, -) -> Result<(agent_api_types::PolicyResolutionModeV1, PolicyInputs)> { - use agent_api_types::PolicyResolutionModeV1; +) -> Result<(transport_api_types::PolicyResolutionModeV1, PolicyInputs)> { + use transport_api_types::PolicyResolutionModeV1; let resolved = resolve_snapshot_routing(policy_snapshot, world_network).map_err(BadRequestError::new)?; @@ -2575,7 +2575,7 @@ fn requested_shared_world_owner_spec(req: &ExecuteRequest) -> Option BackendMemberDispatchRequestV1 { BackendMemberDispatchRequestV1 { schema_version: dispatch.schema_version, @@ -2600,13 +2600,13 @@ fn convert_member_dispatch_request( } fn convert_member_runtime_backend_kind( - backend_kind: agent_api_types::MemberRuntimeBackendKindV1, + backend_kind: transport_api_types::MemberRuntimeBackendKindV1, ) -> BackendMemberRuntimeBackendKindV1 { match backend_kind { - agent_api_types::MemberRuntimeBackendKindV1::Codex => { + transport_api_types::MemberRuntimeBackendKindV1::Codex => { BackendMemberRuntimeBackendKindV1::Codex } - agent_api_types::MemberRuntimeBackendKindV1::ClaudeCode => { + transport_api_types::MemberRuntimeBackendKindV1::ClaudeCode => { BackendMemberRuntimeBackendKindV1::ClaudeCode } } @@ -2614,7 +2614,7 @@ fn convert_member_runtime_backend_kind( #[cfg(target_os = "linux")] fn validate_member_dispatch_binding( - dispatch: &agent_api_types::MemberDispatchRequestV1, + dispatch: &transport_api_types::MemberDispatchRequestV1, world: &WorldHandle, authoritative_binding: Option<&SharedWorldBindingSnapshot>, ) -> Result { @@ -2762,7 +2762,7 @@ pub(crate) fn apply_full_isolation_helper_env( } } -impl WorldAgentService { +impl WorldService { pub(crate) fn policy_snapshot_v1_supported(&self) -> bool { false } @@ -2780,21 +2780,21 @@ impl WorldAgentService { pub(crate) fn last_policy_resolution_mode( &self, - ) -> Option { + ) -> Option { match self.last_policy_resolution_mode.load(Ordering::Relaxed) { - 1 => Some(agent_api_types::PolicyResolutionModeV1::SnapshotV3), - 2 => Some(agent_api_types::PolicyResolutionModeV1::LegacyLocal), + 1 => Some(transport_api_types::PolicyResolutionModeV1::SnapshotV3), + 2 => Some(transport_api_types::PolicyResolutionModeV1::LegacyLocal), _ => None, } } pub(crate) fn set_last_policy_resolution_mode( &self, - mode: agent_api_types::PolicyResolutionModeV1, + mode: transport_api_types::PolicyResolutionModeV1, ) { let encoded = match mode { - agent_api_types::PolicyResolutionModeV1::SnapshotV3 => 1, - agent_api_types::PolicyResolutionModeV1::LegacyLocal => 2, + transport_api_types::PolicyResolutionModeV1::SnapshotV3 => 1, + transport_api_types::PolicyResolutionModeV1::LegacyLocal => 2, }; self.last_policy_resolution_mode .store(encoded, Ordering::Relaxed); @@ -2814,7 +2814,7 @@ impl WorldAgentService { pub(crate) fn record_doctor_request_context( &self, - mode: agent_api_types::PolicyResolutionModeV1, + mode: transport_api_types::PolicyResolutionModeV1, requested: bool, ) { self.set_last_policy_resolution_mode(mode); @@ -3032,9 +3032,9 @@ mod tests { use world_api::{SharedWorldOwnerAction, WorldHandle}; #[test] - fn landlock_helper_src_accepts_substrate_world_agent_name() { + fn landlock_helper_src_accepts_substrate_world_service_name() { let tmp = tempfile::tempdir().expect("tempdir"); - let candidate = tmp.path().join("substrate-world-agent"); + let candidate = tmp.path().join("substrate-world-service"); std::fs::write(&candidate, b"").expect("write"); let resolved = resolve_landlock_helper_src_from_exe(&candidate); @@ -3063,7 +3063,7 @@ mod tests { #[test] fn test_execute_response_serde_fs_diff_roundtrip() { - let resp = agent_api_types::ExecuteResponse { + let resp = transport_api_types::ExecuteResponse { exit: 0, span_id: "spn_test".to_string(), stdout_b64: BASE64.encode(b"ok"), @@ -3109,7 +3109,7 @@ mod tests { }; let json = serde_json::to_string(&resp).expect("serialize ExecuteResponse"); - let back: agent_api_types::ExecuteResponse = + let back: transport_api_types::ExecuteResponse = serde_json::from_str(&json).expect("deserialize ExecuteResponse"); assert_eq!(back.exit, 0); @@ -3235,7 +3235,7 @@ mod tests { #[cfg(target_os = "linux")] #[test] - fn test_resolve_helper_src_prefers_sibling_world_agent_over_test_harness() { + fn test_resolve_helper_src_prefers_sibling_world_service_over_test_harness() { let tmp = tempfile::tempdir().expect("tempdir"); let deps = tmp.path().join("target").join("debug").join("deps"); std::fs::create_dir_all(&deps).expect("create deps"); @@ -3243,18 +3243,18 @@ mod tests { let harness = deps.join("wfgad3_wildcard_deny_symlink_handling-abcdef"); std::fs::write(&harness, b"").expect("write harness"); - let world_agent = deps.parent().unwrap().join("world-agent"); - std::fs::write(&world_agent, b"").expect("write world-agent"); + let world_service = deps.parent().unwrap().join("world-service"); + std::fs::write(&world_service, b"").expect("write world-service"); let resolved = resolve_landlock_helper_src_from_exe(&harness).expect("resolve from harness exe"); - assert_eq!(resolved, world_agent); + assert_eq!(resolved, world_service); } #[cfg(target_os = "linux")] #[tokio::test] async fn test_service_creation() { - let service = WorldAgentService::new().unwrap(); + let service = WorldService::new().unwrap(); assert_eq!(service.worlds.read().unwrap().len(), 0); assert!(!service.last_netfilter_requested()); assert!(service.last_netfilter_failure_reason().is_none()); @@ -3263,27 +3263,27 @@ mod tests { #[cfg(target_os = "linux")] #[test] fn record_doctor_request_context_updates_requested_state_for_non_pty_requests() { - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.record_doctor_request_context( - agent_api_types::PolicyResolutionModeV1::SnapshotV3, + transport_api_types::PolicyResolutionModeV1::SnapshotV3, true, ); assert_eq!( service.last_policy_resolution_mode(), - Some(agent_api_types::PolicyResolutionModeV1::SnapshotV3) + Some(transport_api_types::PolicyResolutionModeV1::SnapshotV3) ); assert!(service.last_netfilter_requested()); service.record_doctor_request_context( - agent_api_types::PolicyResolutionModeV1::LegacyLocal, + transport_api_types::PolicyResolutionModeV1::LegacyLocal, false, ); assert_eq!( service.last_policy_resolution_mode(), - Some(agent_api_types::PolicyResolutionModeV1::LegacyLocal) + Some(transport_api_types::PolicyResolutionModeV1::LegacyLocal) ); assert!(!service.last_netfilter_requested()); } @@ -3291,7 +3291,7 @@ mod tests { #[cfg(target_os = "linux")] #[test] fn record_last_netfilter_failure_for_error_tracks_only_published_failure_classes() { - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); let known_errors = [ anyhow::anyhow!(NETFILTER_ENABLE_REQUIRED_TEXT), anyhow::anyhow!("nft command failed: permission denied"), @@ -3315,7 +3315,7 @@ mod tests { #[cfg(target_os = "linux")] #[test] fn record_last_netfilter_failure_for_error_ignores_unrelated_or_unrequested_errors() { - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.record_last_netfilter_failure_for_error( true, &anyhow::anyhow!("plain command failed"), @@ -3332,7 +3332,7 @@ mod tests { #[cfg(target_os = "linux")] #[test] fn clear_last_netfilter_failure_on_success_only_resets_isolated_runs() { - let service = WorldAgentService::new().expect("service"); + let service = WorldService::new().expect("service"); service.set_last_netfilter_failure_reason(Some("nft command failed: denied".to_string())); service.clear_last_netfilter_failure_on_success(false); @@ -3367,7 +3367,7 @@ mod tests { #[cfg(not(target_os = "linux"))] #[test] fn test_service_creation_fails_on_non_linux() { - let result = WorldAgentService::new(); + let result = WorldService::new(); assert!(result.is_err()); } } diff --git a/crates/world-agent/src/socket_activation.rs b/crates/world-service/src/socket_activation.rs similarity index 100% rename from crates/world-agent/src/socket_activation.rs rename to crates/world-service/src/socket_activation.rs diff --git a/crates/world-agent/src/world_exec_guard.rs b/crates/world-service/src/world_exec_guard.rs similarity index 100% rename from crates/world-agent/src/world_exec_guard.rs rename to crates/world-service/src/world_exec_guard.rs diff --git a/crates/world-service/tests/entrypoint.rs b/crates/world-service/tests/entrypoint.rs new file mode 100644 index 000000000..12004f12d --- /dev/null +++ b/crates/world-service/tests/entrypoint.rs @@ -0,0 +1,18 @@ +#[test] +fn world_service_main_delegates_to_library_entrypoint() { + let source = include_str!("../src/main.rs"); + + assert!( + source.contains("run_world_service().await"), + "main should delegate to run_world_service" + ); + + let line_count = source + .lines() + .filter(|line| !line.trim().is_empty()) + .count(); + assert!( + line_count <= 12, + "world-service main should stay a thin wrapper around the library (got {line_count} lines)" + ); +} diff --git a/crates/world-agent/tests/fs_mode.rs b/crates/world-service/tests/fs_mode.rs similarity index 97% rename from crates/world-agent/tests/fs_mode.rs rename to crates/world-service/tests/fs_mode.rs index 135a5150d..886fcd056 100644 --- a/crates/world-agent/tests/fs_mode.rs +++ b/crates/world-service/tests/fs_mode.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsMode, -}; use axum::extract::ws::WebSocketUpgrade; use axum::routing::get; use axum::{Router, Server}; @@ -16,8 +11,13 @@ use tempfile::tempdir; use tokio::runtime::Runtime; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; -use world_agent::pty::{handle_ws_pty, ServerMessage}; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsMode, +}; +use world_service::pty::{handle_ws_pty, ServerMessage}; +use world_service::WorldService; fn decode(b64: &str) -> String { String::from_utf8_lossy( @@ -71,7 +71,7 @@ fn non_pty_read_only_mode_blocks_writes() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping read-only fs_mode test: service init failed: {err}"); @@ -130,7 +130,7 @@ fn non_pty_read_only_mode_blocks_absolute_project_writes() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping read-only absolute-path test: service init failed: {err}"); @@ -193,7 +193,7 @@ fn non_pty_writable_mode_records_diffs_for_writes() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping writable fs_mode test: service init failed: {err}"); @@ -256,7 +256,7 @@ async fn pty_read_only_mode_returns_clear_error() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping pty read-only test: service init failed: {err}"); @@ -366,7 +366,7 @@ async fn pty_writable_mode_keeps_writes_in_overlay() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping pty writable test: service init failed: {err}"); @@ -499,7 +499,7 @@ async fn pty_and_non_pty_share_overlay_state_across_mode_switch() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping mode-switch overlay test: service init failed: {err}"); diff --git a/crates/world-agent/tests/full_isolation_nonpty.rs b/crates/world-service/tests/full_isolation_nonpty.rs similarity index 97% rename from crates/world-agent/tests/full_isolation_nonpty.rs rename to crates/world-service/tests/full_isolation_nonpty.rs index cb2959b7b..10ff9121b 100644 --- a/crates/world-agent/tests/full_isolation_nonpty.rs +++ b/crates/world-service/tests/full_isolation_nonpty.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsMode, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use std::collections::HashMap; @@ -15,7 +10,12 @@ use std::process::Command; use substrate_broker::{set_global_broker, BrokerHandle}; use tempfile::tempdir; use tokio::runtime::Runtime; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsMode, +}; +use world_service::WorldService; fn decode(b64: &str) -> String { String::from_utf8_lossy( @@ -47,7 +47,7 @@ fn landlock_exec_subprocess_entry() { return; } - match world_agent::internal_exec::run_landlock_exec() { + match world_service::internal_exec::run_landlock_exec() { Ok(()) => { eprintln!("unexpected success: landlock exec wrapper returned Ok(())"); std::process::exit(1); @@ -59,9 +59,9 @@ fn landlock_exec_subprocess_entry() { } } -fn make_service() -> Option { +fn make_service() -> Option { let _ = set_global_broker(BrokerHandle::new()); - match WorldAgentService::new() { + match WorldService::new() { Ok(svc) => Some(svc), Err(err) => { eprintln!("skipping full-isolation test: service init failed: {err}"); @@ -71,13 +71,13 @@ fn make_service() -> Option { } fn execute_non_pty( - service: &WorldAgentService, + service: &WorldService, cwd: &Path, cmd: &str, env: HashMap, world_fs_mode: WorldFsMode, write_allow_list: Vec, -) -> Option { +) -> Option { let write_enabled = matches!(world_fs_mode, WorldFsMode::Writable); let policy_snapshot = PolicySnapshotV3 { schema_version: 3, @@ -418,7 +418,7 @@ fn landlock_exec_fails_closed_with_actionable_error_when_overlay_backing_dirs_mi .env("SUBSTRATE_INNER_CMD", "echo should-not-run") .env("SUBSTRATE_INNER_LOGIN_SHELL", "0") .output() - .expect("run world-agent landlock exec wrapper"); + .expect("run world-service landlock exec wrapper"); assert_eq!( output.status.code(), diff --git a/crates/world-agent/tests/full_isolation_pty.rs b/crates/world-service/tests/full_isolation_pty.rs similarity index 95% rename from crates/world-agent/tests/full_isolation_pty.rs rename to crates/world-service/tests/full_isolation_pty.rs index 9ab5ad2fc..97816e1fa 100644 --- a/crates/world-agent/tests/full_isolation_pty.rs +++ b/crates/world-service/tests/full_isolation_pty.rs @@ -13,8 +13,8 @@ use tempfile::tempdir; use tokio::time::{timeout, Duration}; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; -use world_agent::pty::{handle_ws_pty, ServerMessage}; -use world_agent::WorldAgentService; +use world_service::pty::{handle_ws_pty, ServerMessage}; +use world_service::WorldService; fn decode(b64: &str) -> String { String::from_utf8_lossy( @@ -42,7 +42,7 @@ fn base_cage_env() -> HashMap { env } -async fn spawn_pty_server(service: WorldAgentService) -> (SocketAddr, tokio::task::JoinHandle<()>) { +async fn spawn_pty_server(service: WorldService) -> (SocketAddr, tokio::task::JoinHandle<()>) { let router = Router::new().route( "/pty", get(move |ws: WebSocketUpgrade| { @@ -65,7 +65,7 @@ async fn spawn_pty_server(service: WorldAgentService) -> (SocketAddr, tokio::tas } async fn run_pty( - service: WorldAgentService, + service: WorldService, cwd: &Path, cmd: &str, env: HashMap, @@ -147,7 +147,7 @@ async fn pty_full_isolation_prevents_host_tmp_writes() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping full-isolation PTY test: service init failed: {err}"); @@ -209,7 +209,7 @@ async fn pty_full_isolation_prevents_host_tmp_reads() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping full-isolation PTY test: service init failed: {err}"); @@ -268,7 +268,7 @@ async fn pty_full_isolation_read_only_blocks_project_writes() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping full-isolation PTY read-only test: service init failed: {err}"); @@ -322,7 +322,7 @@ async fn pty_full_isolation_runs_from_tmp_rooted_project() { return; } - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping full-isolation PTY test: service init failed: {err}"); diff --git a/crates/world-agent/tests/gateway_runtime_parity.rs b/crates/world-service/tests/gateway_runtime_parity.rs similarity index 99% rename from crates/world-agent/tests/gateway_runtime_parity.rs rename to crates/world-service/tests/gateway_runtime_parity.rs index 710ba9945..ae3fd68c3 100644 --- a/crates/world-agent/tests/gateway_runtime_parity.rs +++ b/crates/world-service/tests/gateway_runtime_parity.rs @@ -1,11 +1,5 @@ #![cfg(target_os = "linux")] -use agent_api_types::{ - GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, GatewayIntegratedAuthPayloadV1, - GatewayLifecycleRequestV1, GatewayStatusV1, PolicySnapshotV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldNetworkRoutingV1, -}; use once_cell::sync::Lazy; use std::fs; use std::os::unix::fs::PermissionsExt; @@ -19,7 +13,13 @@ use substrate_common::{ }; use tempfile::TempDir; use tokio::sync::Mutex; -use world_agent::WorldAgentService; +use transport_api_types::{ + GatewayApiEnvIntegratedAuthV1, GatewayCliCodexIntegratedAuthV1, GatewayIntegratedAuthPayloadV1, + GatewayLifecycleRequestV1, GatewayStatusV1, PolicySnapshotV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldNetworkRoutingV1, +}; +use world_service::WorldService; const REGRESSION_FLOOR_BACKEND_ID: &str = "cli:codex"; const FIRST_ADDITIONAL_BACKEND_ID: &str = "api:openai"; @@ -116,8 +116,8 @@ fn gateway_request_for_backend(cwd: &Path, backend_id: &str) -> GatewayLifecycle } } -fn service_or_skip() -> Option { - match WorldAgentService::new() { +fn service_or_skip() -> Option { + match WorldService::new() { Ok(service) => Some(service), Err(err) => { eprintln!("skipping gateway runtime parity test: service init failed: {err}"); diff --git a/crates/world-agent/tests/member_runtime_world_placement_v1.rs b/crates/world-service/tests/member_runtime_world_placement_v1.rs similarity index 98% rename from crates/world-agent/tests/member_runtime_world_placement_v1.rs rename to crates/world-service/tests/member_runtime_world_placement_v1.rs index f37ca5741..2fb65f7cd 100644 --- a/crates/world-agent/tests/member_runtime_world_placement_v1.rs +++ b/crates/world-service/tests/member_runtime_world_placement_v1.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteCancelRequestV1, ExecuteRequest, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, - PolicySnapshotV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, - PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, -}; use hyper::body::HttpBody; use serde_json::Value; use std::collections::HashMap; @@ -13,8 +8,13 @@ use std::path::{Path, PathBuf}; use std::time::Duration; use tempfile::tempdir; use tokio::time::timeout; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteCancelRequestV1, ExecuteRequest, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, + PolicySnapshotV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, + PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, +}; use world_api::{SharedWorldOwnerAction, SharedWorldOwnerSpec, WorldReuseMode, WorldSpec}; +use world_service::WorldService; const WORLD_PROJECT_DIR_OVERRIDE_ENV: &str = "SUBSTRATE_WORLD_PROJECT_DIR"; @@ -65,7 +65,7 @@ fn make_member_dispatch_request( parent_participant_id: None, resumed_from_participant_id: None, backend_id: "cli:codex".to_string(), - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), run_id: "run-member-world-placement-test".to_string(), world_id: world_id.to_string(), world_generation, @@ -195,7 +195,7 @@ fn parse_proof_value<'a>(proof: &'a str, key: &str) -> Option<&'a str> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn member_runtime_launches_inside_authoritative_overlay_and_cgroup() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping member placement proof test: service init failed: {err}"); diff --git a/crates/world-agent/tests/overlayfs_enumeration.rs b/crates/world-service/tests/overlayfs_enumeration.rs similarity index 95% rename from crates/world-agent/tests/overlayfs_enumeration.rs rename to crates/world-service/tests/overlayfs_enumeration.rs index dfbdbdbe7..4d9ed3265 100644 --- a/crates/world-agent/tests/overlayfs_enumeration.rs +++ b/crates/world-service/tests/overlayfs_enumeration.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsMode, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use std::collections::HashMap; @@ -13,7 +8,12 @@ use std::path::Path; use substrate_broker::{set_global_broker, BrokerHandle}; use tempfile::tempdir; use tokio::runtime::Runtime; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsMode, +}; +use world_service::WorldService; fn decode(b64: &str) -> String { String::from_utf8_lossy( @@ -32,12 +32,12 @@ fn overlay_available() -> bool { } fn execute_non_pty( - service: &WorldAgentService, + service: &WorldService, cwd: &Path, cmd: &str, env: HashMap, world_fs_mode: WorldFsMode, -) -> Option { +) -> Option { let write_enabled = matches!(world_fs_mode, WorldFsMode::Writable); let policy_snapshot = PolicySnapshotV3 { schema_version: 3, @@ -98,7 +98,7 @@ fn world_overlay_directory_enumeration_includes_created_file() { } let _ = set_global_broker(BrokerHandle::new()); - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping overlayfs enumeration test: service init failed: {err}"); diff --git a/crates/world-agent/tests/repl_persistent_session_bootstrap_v1.rs b/crates/world-service/tests/repl_persistent_session_bootstrap_v1.rs similarity index 93% rename from crates/world-agent/tests/repl_persistent_session_bootstrap_v1.rs rename to crates/world-service/tests/repl_persistent_session_bootstrap_v1.rs index 692473244..8b995e7df 100644 --- a/crates/world-agent/tests/repl_persistent_session_bootstrap_v1.rs +++ b/crates/world-service/tests/repl_persistent_session_bootstrap_v1.rs @@ -15,7 +15,7 @@ use tokio::net::TcpListener; use tokio::time::timeout; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; -use world_agent::WorldAgentService; +use world_service::WorldService; type Ws = tokio_tungstenite::WebSocketStream>; @@ -31,15 +31,15 @@ fn minimal_policy_snapshot() -> Value { }) } -async fn spawn_world_agent_ws( - service: WorldAgentService, +async fn spawn_world_service_ws( + service: WorldService, ) -> ( SocketAddr, tokio::sync::oneshot::Sender<()>, tokio::task::JoinHandle<()>, ) { let router = Router::new() - .route("/v1/stream", get(world_agent::handlers::stream)) + .route("/v1/stream", get(world_service::handlers::stream)) .with_state(service); let listener = TcpListener::bind("127.0.0.1:0") @@ -179,7 +179,7 @@ fn install_seccomp_deny_ioctl_fionread() -> std::io::Result<()> { Ok(()) } -async fn spawn_world_agent_ws_with_fionread_blocked() -> ( +async fn spawn_world_service_ws_with_fionread_blocked() -> ( SocketAddr, tokio::sync::oneshot::Sender<()>, std::thread::JoinHandle<()>, @@ -193,9 +193,9 @@ async fn spawn_world_agent_ws_with_fionread_blocked() -> ( .expect("tokio runtime"); rt.block_on(async move { - let service = WorldAgentService::new().expect("WorldAgentService::new"); + let service = WorldService::new().expect("WorldService::new"); let router = Router::new() - .route("/v1/stream", get(world_agent::handlers::stream)) + .route("/v1/stream", get(world_service::handlers::stream)) .with_state(service); let listener = TcpListener::bind("127.0.0.1:0") @@ -319,7 +319,7 @@ async fn connect_and_start_session_or_skip( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn legacy_one_shot_start_remains_accepted() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping legacy /v1/stream test: service init failed: {err}"); @@ -327,7 +327,7 @@ async fn legacy_one_shot_start_remains_accepted() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -401,7 +401,7 @@ async fn legacy_one_shot_start_remains_accepted() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn persistent_session_enforces_first_frame_start_session() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping persistent first-frame test: service init failed: {err}"); @@ -409,7 +409,7 @@ async fn persistent_session_enforces_first_frame_start_session() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -453,7 +453,7 @@ async fn persistent_session_enforces_first_frame_start_session() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn start_session_rejects_policy_snapshot_with_unknown_fields_fail_closed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping policy_snapshot unknown-fields test: service init failed: {err}"); @@ -461,7 +461,7 @@ async fn start_session_rejects_policy_snapshot_with_unknown_fields_fail_closed() } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -492,7 +492,7 @@ async fn start_session_rejects_policy_snapshot_with_unknown_fields_fail_closed() #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn start_session_requires_policy_snapshot_fail_closed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping policy_snapshot required test: service init failed: {err}"); @@ -500,7 +500,7 @@ async fn start_session_requires_policy_snapshot_fail_closed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -532,7 +532,7 @@ async fn start_session_requires_policy_snapshot_fail_closed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn start_session_yields_ready_with_fresh_hex32_session_nonce() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ready.session_nonce test: service init failed: {err}"); @@ -540,7 +540,7 @@ async fn start_session_yields_ready_with_fresh_hex32_session_nonce() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); let (ws, frame) = match connect_and_start_session_or_skip(addr, cwd.as_path()).await { @@ -593,7 +593,7 @@ async fn start_session_yields_ready_with_fresh_hex32_session_nonce() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn session_nonce_is_unique_per_session_restart() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping session_nonce uniqueness test: service init failed: {err}"); @@ -604,7 +604,7 @@ async fn session_nonce_is_unique_per_session_restart() { let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); - let (addr1, shutdown1, server1) = spawn_world_agent_ws(service.clone()).await; + let (addr1, shutdown1, server1) = spawn_world_service_ws(service.clone()).await; let (ws1, ready1) = match connect_and_start_session_or_skip(addr1, cwd.as_path()).await { Some(ok) => ok, None => { @@ -627,7 +627,7 @@ async fn session_nonce_is_unique_per_session_restart() { drop(ws1); stop_server(shutdown1, server1).await; - let (addr2, shutdown2, server2) = spawn_world_agent_ws(service).await; + let (addr2, shutdown2, server2) = spawn_world_service_ws(service).await; let (ws2, ready2) = match connect_and_start_session_or_skip(addr2, cwd.as_path()).await { Some(ok) => ok, None => { @@ -658,8 +658,8 @@ async fn session_nonce_is_unique_per_session_restart() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn start_session_fails_closed_when_fionread_watermark_is_unavailable() { // DR-23 preflight is fail-closed: if the required PTY watermark query is unavailable for - // protocol v1 (Linux ioctl(FIONREAD)), world-agent MUST NOT emit `ready`. - let (addr, shutdown, server_thread) = spawn_world_agent_ws_with_fionread_blocked().await; + // protocol v1 (Linux ioctl(FIONREAD)), world-service MUST NOT emit `ready`. + let (addr, shutdown, server_thread) = spawn_world_service_ws_with_fionread_blocked().await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -691,10 +691,10 @@ async fn start_session_fails_closed_when_fionread_watermark_is_unavailable() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn start_session_is_robust_to_inheritable_non_stdio_fds() { // DR-22 preflight: the evaluator MUST NOT inherit non-stdio fds. - // This test opens a non-CLOEXEC fd in the world-agent process and requires that the + // This test opens a non-CLOEXEC fd in the world-service process and requires that the // session bootstrap still succeeds, implying the session's spawn strategy doesn't rely on // CLOEXEC alone. - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping DR-22 inheritable-fd test: service init failed: {err}"); @@ -710,7 +710,7 @@ async fn start_session_is_robust_to_inheritable_non_stdio_fds() { let rc = unsafe { libc::fcntl(fd, libc::F_SETFD, 0) }; assert_eq!(rc, 0, "clear CLOEXEC"); - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let (ws, frame) = match connect_and_start_session_or_skip(addr, cwd.as_path()).await { Some(ok) => ok, None => { diff --git a/crates/world-agent/tests/repl_persistent_session_exec_v1.rs b/crates/world-service/tests/repl_persistent_session_exec_v1.rs similarity index 95% rename from crates/world-agent/tests/repl_persistent_session_exec_v1.rs rename to crates/world-service/tests/repl_persistent_session_exec_v1.rs index 9fe45c2ce..ad717f5af 100644 --- a/crates/world-agent/tests/repl_persistent_session_exec_v1.rs +++ b/crates/world-service/tests/repl_persistent_session_exec_v1.rs @@ -13,7 +13,7 @@ use tokio::net::TcpListener; use tokio::time::{sleep, timeout}; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; -use world_agent::WorldAgentService; +use world_service::WorldService; type Ws = tokio_tungstenite::WebSocketStream>; @@ -29,15 +29,15 @@ fn minimal_policy_snapshot() -> Value { }) } -async fn spawn_world_agent_ws( - service: WorldAgentService, +async fn spawn_world_service_ws( + service: WorldService, ) -> ( SocketAddr, tokio::sync::oneshot::Sender<()>, tokio::task::JoinHandle<()>, ) { let router = Router::new() - .route("/v1/stream", get(world_agent::handlers::stream)) + .route("/v1/stream", get(world_service::handlers::stream)) .with_state(service); let listener = TcpListener::bind("127.0.0.1:0") @@ -273,7 +273,7 @@ async fn recv_for_duration(ws: &mut Ws, duration: Duration) -> Vec { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_rejects_invalid_base64_fail_closed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping exec invalid-base64 test: service init failed: {err}"); @@ -281,7 +281,7 @@ async fn exec_rejects_invalid_base64_fail_closed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -314,7 +314,7 @@ async fn exec_rejects_invalid_base64_fail_closed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_rejects_invalid_utf8_fail_closed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping exec invalid-utf8 test: service init failed: {err}"); @@ -322,7 +322,7 @@ async fn exec_rejects_invalid_utf8_fail_closed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -354,7 +354,7 @@ async fn exec_rejects_invalid_utf8_fail_closed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_rejects_nul_fail_closed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping exec NUL test: service init failed: {err}"); @@ -362,7 +362,7 @@ async fn exec_rejects_nul_fail_closed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -394,7 +394,7 @@ async fn exec_rejects_nul_fail_closed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_while_busy_is_fatal_protocol_error() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping exec-while-busy test: service init failed: {err}"); @@ -402,7 +402,7 @@ async fn exec_while_busy_is_fatal_protocol_error() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -472,7 +472,7 @@ async fn exec_while_busy_is_fatal_protocol_error() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn stdout_is_drained_before_command_complete() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping output ordering test: service init failed: {err}"); @@ -480,7 +480,7 @@ async fn stdout_is_drained_before_command_complete() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -556,7 +556,7 @@ async fn stdout_is_drained_before_command_complete() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn stdin_is_dropped_unless_passthrough_and_never_leaks_across_commands() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping stdin gating test: service init failed: {err}"); @@ -564,7 +564,7 @@ async fn stdin_is_dropped_unless_passthrough_and_never_leaks_across_commands() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -691,7 +691,7 @@ async fn stdin_is_dropped_unless_passthrough_and_never_leaks_across_commands() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn signal_targets_foreground_process_group_and_session_survives() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping signal targeting test: service init failed: {err}"); @@ -699,7 +699,7 @@ async fn signal_targets_foreground_process_group_and_session_survives() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -766,7 +766,7 @@ async fn signal_targets_foreground_process_group_and_session_survives() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn persists_physical_cwd_and_exported_env_across_execs() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping persistence test: service init failed: {err}"); @@ -774,7 +774,7 @@ async fn persists_physical_cwd_and_exported_env_across_execs() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -867,7 +867,7 @@ echo SET_OK"# #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn caged_session_prevents_escape_from_anchor() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping caged-escape test: service init failed: {err}"); @@ -875,7 +875,7 @@ async fn caged_session_prevents_escape_from_anchor() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -926,7 +926,7 @@ async fn caged_session_prevents_escape_from_anchor() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn evaluator_is_bash_noprofile_norc_and_prompts_suppressed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping evaluator shell test: service init failed: {err}"); @@ -934,7 +934,7 @@ async fn evaluator_is_bash_noprofile_norc_and_prompts_suppressed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -989,7 +989,7 @@ test -z "${PROMPT_COMMAND-}" && echo PROMPT_COMMAND_EMPTY"#; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn incomplete_construct_does_not_hang_and_session_returns_to_idle() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping PS2 continuation test: service init failed: {err}"); @@ -997,7 +997,7 @@ async fn incomplete_construct_does_not_hang_and_session_returns_to_idle() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -1051,7 +1051,7 @@ async fn incomplete_construct_does_not_hang_and_session_returns_to_idle() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn program_text_sent_over_stdin_is_not_executed() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping command/control separation test: service init failed: {err}"); @@ -1059,7 +1059,7 @@ async fn program_text_sent_over_stdin_is_not_executed() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -1118,7 +1118,7 @@ async fn program_text_sent_over_stdin_is_not_executed() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn evaluator_cannot_see_inherited_socket_fds_dr22_smoke() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping DR-22 adversarial fd test: service init failed: {err}"); @@ -1126,7 +1126,7 @@ async fn evaluator_cannot_see_inherited_socket_fds_dr22_smoke() { } }; - let (addr, shutdown, server) = spawn_world_agent_ws(service).await; + let (addr, shutdown, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); diff --git a/crates/world-agent/tests/socket_activation.rs b/crates/world-service/tests/socket_activation.rs similarity index 99% rename from crates/world-agent/tests/socket_activation.rs rename to crates/world-service/tests/socket_activation.rs index b7607a533..3004d1f1a 100644 --- a/crates/world-agent/tests/socket_activation.rs +++ b/crates/world-service/tests/socket_activation.rs @@ -8,7 +8,7 @@ use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixStream; use std::sync::Mutex; use tokio::runtime::Runtime; -use world_agent::socket_activation_test_support; +use world_service::socket_activation_test_support; const ACTIVATION_ENV_VARS: &[&str] = &[ "LISTEN_FDS", diff --git a/crates/world-agent/tests/streamed_execute_cancel_v1.rs b/crates/world-service/tests/streamed_execute_cancel_v1.rs similarity index 98% rename from crates/world-agent/tests/streamed_execute_cancel_v1.rs rename to crates/world-service/tests/streamed_execute_cancel_v1.rs index cd06391cf..43887b9c0 100644 --- a/crates/world-agent/tests/streamed_execute_cancel_v1.rs +++ b/crates/world-service/tests/streamed_execute_cancel_v1.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteCancelRequestV1, ExecuteRequest, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, - PolicySnapshotV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, - PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, -}; use hyper::body::HttpBody; use serde_json::Value; use std::collections::HashMap; @@ -13,8 +8,13 @@ use std::path::Path; use std::time::Duration; use tempfile::tempdir; use tokio::time::timeout; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteCancelRequestV1, ExecuteRequest, MemberDispatchRequestV1, MemberRuntimeBackendKindV1, + PolicySnapshotV3, PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, + PolicySnapshotWorldFsWriteV3, ResolvedMemberRuntimeDescriptorV1, +}; use world_api::{SharedWorldOwnerAction, SharedWorldOwnerSpec, WorldReuseMode, WorldSpec}; +use world_service::WorldService; fn minimal_policy_snapshot() -> PolicySnapshotV3 { PolicySnapshotV3 { @@ -98,7 +98,7 @@ fn make_member_dispatch_request_with_backend( parent_participant_id: None, resumed_from_participant_id: None, backend_id: backend_id.to_string(), - protocol: "uaa.agent.session".to_string(), + protocol: "substrate.agent.session".to_string(), run_id: run_id.to_string(), world_id: world_id.to_string(), world_generation, @@ -295,7 +295,7 @@ where #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn execute_stream_cancel_interrupts_live_non_pty_command() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping streamed execute cancel test: service init failed: {err}"); @@ -354,7 +354,7 @@ async fn execute_stream_cancel_interrupts_live_non_pty_command() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn execute_stream_cancel_interrupts_live_member_runtime() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping member streamed cancel test: service init failed: {err}"); @@ -436,7 +436,7 @@ async fn execute_stream_cancel_interrupts_live_member_runtime() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn member_runtime_backend_slots_allow_distinct_backends_and_reject_duplicates() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping member backend slot test: service init failed: {err}"); diff --git a/crates/world-agent/tests/wfgad1_policy_snapshot_v2_rejection.rs b/crates/world-service/tests/wfgad1_policy_snapshot_v2_rejection.rs similarity index 93% rename from crates/world-agent/tests/wfgad1_policy_snapshot_v2_rejection.rs rename to crates/world-service/tests/wfgad1_policy_snapshot_v2_rejection.rs index 56823c999..69c91de77 100644 --- a/crates/world-agent/tests/wfgad1_policy_snapshot_v2_rejection.rs +++ b/crates/world-service/tests/wfgad1_policy_snapshot_v2_rejection.rs @@ -12,7 +12,7 @@ use tokio::time::timeout; use tokio_tungstenite::connect_async; use tokio_tungstenite::tungstenite::Message; use tower::ServiceExt; -use world_agent::WorldAgentService; +use world_service::WorldService; type Ws = tokio_tungstenite::WebSocketStream>; @@ -43,9 +43,9 @@ fn minimal_execute_request_with_snapshot(policy_snapshot: Value) -> Value { }) } -async fn post_execute_json(service: WorldAgentService, payload: Value) -> (StatusCode, Value) { +async fn post_execute_json(service: WorldService, payload: Value) -> (StatusCode, Value) { let app = Router::new() - .route("/v1/execute", post(world_agent::handlers::execute)) + .route("/v1/execute", post(world_service::handlers::execute)) .with_state(service); let req = Request::builder() @@ -95,7 +95,7 @@ fn assert_http_400_error_shape(body: &Value) -> &str { #[tokio::test(flavor = "current_thread")] async fn http_execute_rejects_policy_snapshot_schema_version_1_and_2() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping /v1/execute rejection test: service init failed: {err}"); @@ -123,7 +123,7 @@ async fn http_execute_rejects_policy_snapshot_schema_version_1_and_2() { #[tokio::test(flavor = "current_thread")] async fn http_execute_rejects_policy_snapshot_unknown_fields() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping /v1/execute unknown-fields test: service init failed: {err}"); @@ -149,7 +149,7 @@ async fn http_execute_rejects_policy_snapshot_unknown_fields() { #[tokio::test(flavor = "current_thread")] async fn http_execute_rejects_missing_policy_snapshot() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!( @@ -178,7 +178,7 @@ async fn http_execute_rejects_missing_policy_snapshot() { #[tokio::test(flavor = "current_thread")] async fn http_execute_rejects_policy_snapshot_invalid_allow_list_patterns() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping /v1/execute invalid-pattern test: service init failed: {err}"); @@ -204,7 +204,7 @@ async fn http_execute_rejects_policy_snapshot_invalid_allow_list_patterns() { #[tokio::test(flavor = "current_thread")] async fn http_execute_rejects_write_disabled_without_fail_closed_routing() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping /v1/execute routing-invariant test: service init failed: {err}"); @@ -229,11 +229,11 @@ async fn http_execute_rejects_write_disabled_without_fail_closed_routing() { ); } -async fn spawn_world_agent_ws( - service: WorldAgentService, +async fn spawn_world_service_ws( + service: WorldService, ) -> (SocketAddr, tokio::task::JoinHandle<()>) { let router = Router::new() - .route("/v1/stream", get(world_agent::handlers::stream)) + .route("/v1/stream", get(world_service::handlers::stream)) .with_state(service); let listener = TcpListener::bind("127.0.0.1:0") @@ -311,7 +311,7 @@ async fn assert_ws_closes_after_fatal(ws: &mut Ws) { #[tokio::test(flavor = "current_thread")] async fn ws_start_session_rejects_policy_snapshot_schema_version_1_and_2_and_closes() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ws start_session rejection test: service init failed: {err}"); @@ -319,7 +319,7 @@ async fn ws_start_session_rejects_policy_snapshot_schema_version_1_and_2_and_clo } }; - let (addr, server) = spawn_world_agent_ws(service).await; + let (addr, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -356,7 +356,7 @@ async fn ws_start_session_rejects_policy_snapshot_schema_version_1_and_2_and_clo #[tokio::test(flavor = "current_thread")] async fn ws_start_session_rejects_policy_snapshot_unknown_fields_and_closes() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ws unknown-fields test: service init failed: {err}"); @@ -364,7 +364,7 @@ async fn ws_start_session_rejects_policy_snapshot_unknown_fields_and_closes() { } }; - let (addr, server) = spawn_world_agent_ws(service).await; + let (addr, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -402,7 +402,7 @@ async fn ws_start_session_rejects_policy_snapshot_unknown_fields_and_closes() { #[tokio::test(flavor = "current_thread")] async fn ws_start_session_rejects_missing_policy_snapshot_and_closes() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ws missing-policy_snapshot test: service init failed: {err}"); @@ -410,7 +410,7 @@ async fn ws_start_session_rejects_missing_policy_snapshot_and_closes() { } }; - let (addr, server) = spawn_world_agent_ws(service).await; + let (addr, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -441,7 +441,7 @@ async fn ws_start_session_rejects_missing_policy_snapshot_and_closes() { #[tokio::test(flavor = "current_thread")] async fn ws_start_session_rejects_policy_snapshot_invalid_allow_list_patterns_and_closes() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ws invalid-pattern test: service init failed: {err}"); @@ -449,7 +449,7 @@ async fn ws_start_session_rejects_policy_snapshot_invalid_allow_list_patterns_an } }; - let (addr, server) = spawn_world_agent_ws(service).await; + let (addr, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); @@ -487,7 +487,7 @@ async fn ws_start_session_rejects_policy_snapshot_invalid_allow_list_patterns_an #[tokio::test(flavor = "current_thread")] async fn ws_start_session_rejects_write_disabled_without_fail_closed_routing_and_closes() { - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping ws routing-invariant test: service init failed: {err}"); @@ -495,7 +495,7 @@ async fn ws_start_session_rejects_write_disabled_without_fail_closed_routing_and } }; - let (addr, server) = spawn_world_agent_ws(service).await; + let (addr, server) = spawn_world_service_ws(service).await; let tmp = tempfile::tempdir().expect("tempdir"); let cwd = tmp.path().to_path_buf(); diff --git a/crates/world-agent/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs b/crates/world-service/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs similarity index 74% rename from crates/world-agent/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs rename to crates/world-service/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs index 5cf184edd..44eac5a9f 100644 --- a/crates/world-agent/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs +++ b/crates/world-service/tests/wfgad2_enforcement_plan_fail_closed_exit4.rs @@ -2,13 +2,13 @@ use std::process::Command; #[test] fn helper_fails_closed_with_exit_4_on_invalid_enforcement_plan() { - let bin = env!("CARGO_BIN_EXE_world-agent"); + let bin = env!("CARGO_BIN_EXE_world-service"); let output = Command::new(bin) - .arg(world_agent::internal_exec::LANDLOCK_EXEC_ARG) + .arg(world_service::internal_exec::LANDLOCK_EXEC_ARG) .env("SUBSTRATE_WORLD_FS_ENFORCEMENT_PLAN_B64", "not-base64") .output() - .expect("run world-agent helper"); + .expect("run world-service helper"); assert_eq!(output.status.code(), Some(4)); let stderr = String::from_utf8_lossy(&output.stderr); diff --git a/crates/world-agent/tests/wfgad3_wildcard_deny_symlink_handling.rs b/crates/world-service/tests/wfgad3_wildcard_deny_symlink_handling.rs similarity index 96% rename from crates/world-agent/tests/wfgad3_wildcard_deny_symlink_handling.rs rename to crates/world-service/tests/wfgad3_wildcard_deny_symlink_handling.rs index 480bddf99..bbe00c6b0 100644 --- a/crates/world-agent/tests/wfgad3_wildcard_deny_symlink_handling.rs +++ b/crates/world-service/tests/wfgad3_wildcard_deny_symlink_handling.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsDenyEnforcementV3, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use std::collections::HashMap; @@ -13,7 +8,12 @@ use std::path::Path; use substrate_broker::{set_global_broker, BrokerHandle}; use tempfile::tempdir; use tokio::runtime::Runtime; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsDenyEnforcementV3, +}; +use world_service::WorldService; #[cfg(unix)] use std::os::unix::fs::symlink; @@ -27,7 +27,7 @@ fn decode(b64: &str) -> String { .into_owned() } -fn combined_output(resp: &agent_api_types::ExecuteResponse) -> String { +fn combined_output(resp: &transport_api_types::ExecuteResponse) -> String { format!("{}{}", decode(&resp.stdout_b64), decode(&resp.stderr_b64)) } @@ -85,12 +85,12 @@ fn snapshot_writable_with_write_deny(deny_list: Vec) -> PolicySnapshotV3 } fn execute_non_pty( - service: &WorldAgentService, + service: &WorldService, cwd: &Path, cmd: &str, env: HashMap, policy_snapshot: PolicySnapshotV3, -) -> Option { +) -> Option { let req = ExecuteRequest { profile: None, cmd: cmd.to_string(), @@ -124,7 +124,7 @@ fn wildcard_deny_snapshot_scan_does_not_follow_symlinked_directories() { } let _ = set_global_broker(BrokerHandle::new()); - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping WFGAD3 test: service init failed: {err}"); @@ -267,7 +267,7 @@ fn write_deny_returns_read_only_file_system() { } let _ = set_global_broker(BrokerHandle::new()); - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping WFGAD3 test: service init failed: {err}"); diff --git a/crates/world-agent/tests/wfgad4_discover_allowlist.rs b/crates/world-service/tests/wfgad4_discover_allowlist.rs similarity index 96% rename from crates/world-agent/tests/wfgad4_discover_allowlist.rs rename to crates/world-service/tests/wfgad4_discover_allowlist.rs index fd58bc55b..665f72312 100644 --- a/crates/world-agent/tests/wfgad4_discover_allowlist.rs +++ b/crates/world-service/tests/wfgad4_discover_allowlist.rs @@ -21,7 +21,7 @@ fn landlock_exec_subprocess_entry() { return; } - match world_agent::internal_exec::run_landlock_exec() { + match world_service::internal_exec::run_landlock_exec() { Ok(()) => { eprintln!("unexpected success: landlock exec wrapper returned Ok(())"); std::process::exit(1); @@ -105,7 +105,7 @@ echo READ_BLOCKED tmp.path().display().to_string(), ) .output() - .expect("run world-agent landlock exec wrapper"); + .expect("run world-service landlock exec wrapper"); assert_eq!( output.status.code(), diff --git a/crates/world-agent/tests/wfgad5_strict_deny_lockdown.rs b/crates/world-service/tests/wfgad5_strict_deny_lockdown.rs similarity index 96% rename from crates/world-agent/tests/wfgad5_strict_deny_lockdown.rs rename to crates/world-service/tests/wfgad5_strict_deny_lockdown.rs index 5edbe839f..123e5abb1 100644 --- a/crates/world-agent/tests/wfgad5_strict_deny_lockdown.rs +++ b/crates/world-service/tests/wfgad5_strict_deny_lockdown.rs @@ -1,10 +1,5 @@ #![cfg(all(unix, target_os = "linux"))] -use agent_api_types::{ - ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - WorldFsDenyEnforcementV3, -}; use base64::engine::general_purpose::STANDARD as BASE64; use base64::Engine; use std::collections::HashMap; @@ -13,7 +8,12 @@ use std::path::Path; use substrate_broker::{set_global_broker, BrokerHandle}; use tempfile::tempdir; use tokio::runtime::Runtime; -use world_agent::WorldAgentService; +use transport_api_types::{ + ExecuteRequest, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + WorldFsDenyEnforcementV3, +}; +use world_service::WorldService; fn decode(b64: &str) -> String { String::from_utf8_lossy( @@ -24,7 +24,7 @@ fn decode(b64: &str) -> String { .into_owned() } -fn combined_output(resp: &agent_api_types::ExecuteResponse) -> String { +fn combined_output(resp: &transport_api_types::ExecuteResponse) -> String { format!("{}{}", decode(&resp.stdout_b64), decode(&resp.stderr_b64)) } @@ -59,12 +59,12 @@ fn snapshot_strict_read_only_with_read_deny(deny_list: Vec) -> PolicySna } fn execute_non_pty( - service: &WorldAgentService, + service: &WorldService, cwd: &Path, cmd: &str, env: HashMap, policy_snapshot: PolicySnapshotV3, -) -> Option { +) -> Option { let req = ExecuteRequest { profile: None, cmd: cmd.to_string(), @@ -98,7 +98,7 @@ fn strict_mode_blocks_mount_syscalls_with_eperm_and_denies_remain_enforced() { } let _ = set_global_broker(BrokerHandle::new()); - let service = match WorldAgentService::new() { + let service = match WorldService::new() { Ok(svc) => svc, Err(err) => { eprintln!("skipping WFGAD5 test: service init failed: {err}"); diff --git a/crates/world-windows-wsl/Cargo.toml b/crates/world-windows-wsl/Cargo.toml index c32a5ed75..a550d60c9 100644 --- a/crates/world-windows-wsl/Cargo.toml +++ b/crates/world-windows-wsl/Cargo.toml @@ -2,13 +2,13 @@ name = "world-windows-wsl" version = "0.1.0" edition = "2021" -description = "Windows world backend delegating to WSL world-agent" +description = "Windows world backend delegating to WSL world-service" license = "MIT" [dependencies] anyhow = { workspace = true } -agent-api-client = { path = "../agent-api-client" } -agent-api-types = { path = "../agent-api-types" } +transport-api-client = { path = "../transport-api-client" } +transport-api-types = { path = "../transport-api-types" } base64 = "0.22" serde_json = { workspace = true } substrate-common = { path = "../common" } diff --git a/crates/world-windows-wsl/src/backend.rs b/crates/world-windows-wsl/src/backend.rs index 20db89971..164951613 100644 --- a/crates/world-windows-wsl/src/backend.rs +++ b/crates/world-windows-wsl/src/backend.rs @@ -1,12 +1,6 @@ use crate::paths::{normalize_diff, to_wsl_path}; use crate::transport::{detect_tcp_forwarder, DEFAULT_AGENT_PIPE, DEFAULT_DISTRO}; use crate::warm::WarmCmd; -use agent_api_client::{AgentClient, Transport}; -use agent_api_types::{ - ExecuteRequest, ExecuteResponse, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, - PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, - ProcessTelemetry, WorldFsMode, -}; use anyhow::{anyhow, Context, Result}; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use base64::Engine as _; @@ -17,6 +11,12 @@ use std::time::Duration; use substrate_common::FsDiff; use tokio::runtime::{self, Runtime}; use tracing::{debug, warn}; +use transport_api_client::{AgentClient, Transport}; +use transport_api_types::{ + ExecuteRequest, ExecuteResponse, PolicySnapshotV3, PolicySnapshotWorldFsDimensionV3, + PolicySnapshotWorldFsFailClosedV3, PolicySnapshotWorldFsV3, PolicySnapshotWorldFsWriteV3, + ProcessTelemetry, WorldFsMode, +}; use uuid::Uuid; use world_api::{ExecRequest, ExecResult, WorldBackend, WorldHandle, WorldSpec}; @@ -27,7 +27,7 @@ pub(crate) trait AgentApiMock: Send + Sync { fn get_trace(&self, span_id: &str) -> Result; } -/// Windows backend delegating to world-agent inside WSL. +/// Windows backend delegating to world-service inside WSL. pub struct WindowsWslBackend { pub(crate) distro: String, pub(crate) project_path: PathBuf, diff --git a/crates/world-windows-wsl/src/tests.rs b/crates/world-windows-wsl/src/tests.rs index 3fb00dc12..9da2f2a8f 100644 --- a/crates/world-windows-wsl/src/tests.rs +++ b/crates/world-windows-wsl/src/tests.rs @@ -1,7 +1,6 @@ use super::backend::AgentApiMock; use super::warm::WarmCmd; use super::WindowsWslBackend; -use agent_api_types::{ExecuteRequest, ExecuteResponse, FsDiff as AgentFsDiff, ProcessTelemetry}; use anyhow::anyhow; use base64::engine::general_purpose::STANDARD as BASE64_STANDARD; use base64::Engine as _; @@ -10,6 +9,9 @@ use std::collections::{HashMap, VecDeque}; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use transport_api_types::{ + ExecuteRequest, ExecuteResponse, FsDiff as AgentFsDiff, ProcessTelemetry, +}; use world_api::{ExecRequest, WorldBackend, WorldSpec}; struct MockAgent { diff --git a/crates/world/src/exec.rs b/crates/world/src/exec.rs index 8e9e3029b..dfe6b8460 100644 --- a/crates/world/src/exec.rs +++ b/crates/world/src/exec.rs @@ -1171,7 +1171,7 @@ if [ "${SUBSTRATE_WORLD_FS_ISOLATION:-workspace}" = "full" ]; then fi fi - # Optional: bind-mount the host world-agent binary into the isolated rootfs so it can apply Landlock + # Optional: bind-mount the host world-service binary into the isolated rootfs so it can apply Landlock # restrictions before executing the command. if [ "$landlock_requested" = "1" ]; then if [ -z "${SUBSTRATE_LANDLOCK_HELPER_SRC:-}" ] || [ ! -e "${SUBSTRATE_LANDLOCK_HELPER_SRC:-}" ]; then @@ -1410,7 +1410,7 @@ pub(crate) fn execute_shell_command_with_project_bind_mount_capture( command.arg("-c"); command.arg(script); command.current_dir("/"); - // Ensure the unshare wrapper and its child workload do not inherit the host/world-agent + // Ensure the unshare wrapper and its child workload do not inherit the host/world-service // service environment. The caller must fully specify the desired environment. command.env_clear(); command.envs(env_map); diff --git a/dist-workspace.toml b/dist-workspace.toml index f22da8ff4..001b5cc28 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -12,7 +12,7 @@ installers = [] # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Limit releases to the binaries the installers require. -packages = ["host-proxy", "substrate", "substrate-forwarder", "substrate-gateway", "world-agent"] +packages = ["host-proxy", "substrate", "substrate-forwarder", "substrate-gateway", "world-service"] # Release CI should only run on tag pushes; use promotion workflow + release tags for verification. pr-run-mode = "skip" allow-dirty = ["ci"] diff --git a/dist/release-template.md b/dist/release-template.md index 13d35e44e..22eb036d8 100644 --- a/dist/release-template.md +++ b/dist/release-template.md @@ -30,7 +30,7 @@ Refer to `docs/INSTALLATION.md` and `docs/WORLD.md` for advanced flags, prereqs, | macOS | arm64 | [substrate-v${VERSION}-macos_arm64.tar.gz](https://github.com/atomize-hq/substrate/releases/download/${RELEASE_TAG}/substrate-v${VERSION}-macos_arm64.tar.gz) | | Windows | x86_64 | [substrate-v${VERSION}-windows_x86_64.zip](https://github.com/atomize-hq/substrate/releases/download/${RELEASE_TAG}/substrate-v${VERSION}-windows_x86_64.zip) | -Each bundle includes `substrate`, `substrate-shim`, `host-proxy`, and `substrate-gateway`. Linux bundles ship host `world-agent` and `substrate-gateway` binaries in `bin/`; macOS/Windows bundles ship the Linux guest `world-agent` and `substrate-gateway` binaries under `bin/linux`, alongside platform helpers like `substrate-forwarder.exe`. +Each bundle includes `substrate`, `substrate-shim`, `host-proxy`, and `substrate-gateway`. Linux bundles ship host `world-service` and `substrate-gateway` binaries in `bin/`; macOS/Windows bundles ship the Linux guest `world-service` and `substrate-gateway` binaries under `bin/linux`, alongside platform helpers like `substrate-forwarder.exe`. ## Support + Checksums diff --git a/dist/scripts/assemble-release-bundles.sh b/dist/scripts/assemble-release-bundles.sh index d4ac21bbf..7b55b8e68 100755 --- a/dist/scripts/assemble-release-bundles.sh +++ b/dist/scripts/assemble-release-bundles.sh @@ -20,6 +20,19 @@ fatal() { exit 1 } +resolve_path() { + local target="$1" + local strict="${2:-0}" + python3 - "$target" "$strict" <<'PY' +import pathlib +import sys + +target = pathlib.Path(sys.argv[1]).expanduser() +strict = sys.argv[2] == "1" +print(target.resolve(strict=strict)) +PY +} + TAG="" ARTIFACTS_DIR="" OUTPUT_DIR="" @@ -56,8 +69,8 @@ done VERSION="${TAG#v}" [[ -n "$VERSION" ]] || fatal "Tag $TAG must start with 'v' followed by the version number" -ARTIFACTS_DIR="$(realpath "$ARTIFACTS_DIR")" -OUTPUT_DIR="$(realpath -m "$OUTPUT_DIR")" +ARTIFACTS_DIR="$(resolve_path "$ARTIFACTS_DIR" 1)" +OUTPUT_DIR="$(resolve_path "$OUTPUT_DIR" 0)" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT @@ -69,39 +82,6 @@ SUPPORT_DIR="$TMP_DIR/support" STAGING_DIR="$TMP_DIR/staging" mkdir -p "$WORK_DIR" "$SUPPORT_DIR" "$STAGING_DIR" -# Mapping between bundle labels and Rust targets we build. -declare -A HOST_TARGETS=( - [linux_x86_64]="x86_64-unknown-linux-gnu" - [linux_aarch64]="aarch64-unknown-linux-gnu" - [macos_x86_64]="x86_64-apple-darwin" - [macos_arm64]="aarch64-apple-darwin" - [windows_x86_64]="x86_64-pc-windows-msvc" -) - -declare -A HOST_KIND=( - [linux_x86_64]="linux" - [linux_aarch64]="linux" - [macos_x86_64]="mac" - [macos_arm64]="mac" - [windows_x86_64]="windows" -) - -declare -A GUEST_AGENT_TARGET=( - [linux_x86_64]="x86_64-unknown-linux-gnu" - [linux_aarch64]="aarch64-unknown-linux-gnu" - [macos_x86_64]="x86_64-unknown-linux-gnu" - [macos_arm64]="aarch64-unknown-linux-gnu" - [windows_x86_64]="x86_64-unknown-linux-gnu" -) - -declare -A ARCHIVE_EXT=( - [windows_x86_64]="zip" - [linux_x86_64]="tar.gz" - [linux_aarch64]="tar.gz" - [macos_x86_64]="tar.gz" - [macos_arm64]="tar.gz" -) - BUNDLE_LABELS=( linux_x86_64 linux_aarch64 @@ -110,6 +90,45 @@ BUNDLE_LABELS=( windows_x86_64 ) +host_target_for() { + case "$1" in + linux_x86_64) printf '%s\n' "x86_64-unknown-linux-gnu" ;; + linux_aarch64) printf '%s\n' "aarch64-unknown-linux-gnu" ;; + macos_x86_64) printf '%s\n' "x86_64-apple-darwin" ;; + macos_arm64) printf '%s\n' "aarch64-apple-darwin" ;; + windows_x86_64) printf '%s\n' "x86_64-pc-windows-msvc" ;; + *) fatal "Unknown bundle label: $1" ;; + esac +} + +host_kind_for() { + case "$1" in + linux_x86_64|linux_aarch64) printf '%s\n' "linux" ;; + macos_x86_64|macos_arm64) printf '%s\n' "mac" ;; + windows_x86_64) printf '%s\n' "windows" ;; + *) fatal "Unknown bundle label: $1" ;; + esac +} + +guest_agent_target_for() { + case "$1" in + linux_x86_64|linux_aarch64|macos_x86_64|macos_arm64|windows_x86_64) + printf '%s\n' "x86_64-unknown-linux-gnu" + ;; + *) + fatal "Unknown bundle label: $1" + ;; + esac +} + +archive_ext_for() { + case "$1" in + windows_x86_64) printf '%s\n' "zip" ;; + linux_x86_64|linux_aarch64|macos_x86_64|macos_arm64) printf '%s\n' "tar.gz" ;; + *) fatal "Unknown bundle label: $1" ;; + esac +} + find_artifact() { local crate="$1" local target="$2" @@ -259,17 +278,17 @@ package_bundle() { fi if [[ "$kind" == "linux" ]]; then - copy_bin_from_artifact "world-agent" "$target" "$bundle_dir/bin" + copy_bin_from_artifact "world-service" "$target" "$bundle_dir/bin" copy_bin_from_artifact "substrate-gateway" "$target" "$bundle_dir/bin" else local guest_dir="$bundle_dir/bin/linux" mkdir -p "$guest_dir" - copy_bin_from_artifact "world-agent" "$guest_agent" "$guest_dir" + copy_bin_from_artifact "world-service" "$guest_agent" "$guest_dir" copy_bin_from_artifact "substrate-gateway" "$guest_agent" "$guest_dir" fi local archive_name="substrate-v${VERSION}-${label}." - archive_name+="${ARCHIVE_EXT[$label]}" + archive_name+="$(archive_ext_for "$label")" local archive_path="$OUTPUT_DIR/$archive_name" if [[ "$kind" == "windows" ]]; then @@ -286,7 +305,11 @@ GENERATED_FILES=() GENERATED_FILES+=("$SUPPORT_TAR_OUT" "$SUPPORT_ZIP_OUT") for label in "${BUNDLE_LABELS[@]}"; do - package_bundle "$label" "${HOST_TARGETS[$label]}" "${HOST_KIND[$label]}" "${GUEST_AGENT_TARGET[$label]}" + package_bundle \ + "$label" \ + "$(host_target_for "$label")" \ + "$(host_kind_for "$label")" \ + "$(guest_agent_target_for "$label")" done sha256sum "${GENERATED_FILES[@]}" > "$OUTPUT_DIR/SHA256SUMS" diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 3bf954d37..06a90d654 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -9,8 +9,8 @@ Keep concise, actionable, and security-focused. - Problem: host execution is richly observable via the shim (per-process exec logging), but world execution is primarily observable at the “one command per world execute” level. This creates blind spots for world-deps installs and wrapper-based tools (e.g. `nvm` wrappers invoking `bash -lc ...`), where internal subprocesses are not recorded as structured events. - Goal: in-world activity is traceable at the same granularity as the host: every spawned process (argv/env redaction/exit code/timing) is captured and attached to spans, so debugging and policy auditing do not depend on stdout/stderr inference. - Work: - - Define an in-world execution event schema (align with `crates/common/src/lib.rs` `log_schema`) for per-process spawn/exit telemetry emitted by world-agent. - - Extend `world-agent` execution paths (`/v1/execute`, `/v1/stream`) to capture process tree events for the executed command (including wrapper-invoked shells like `bash -lc`) and return them to the host. + - Define an in-world execution event schema (align with `crates/common/src/lib.rs` `log_schema`) for per-process spawn/exit telemetry emitted by world-service. + - Extend `world-service` execution paths (`/v1/execute`, `/v1/stream`) to capture process tree events for the executed command (including wrapper-invoked shells like `bash -lc`) and return them to the host. - Follow-on optimization: stream per-process events incrementally over `/v1/stream` (v1 batches on Exit). - Plumb the returned process events into `crates/trace` spans so they are persisted alongside existing world fs diffs and policy decisions. - Ensure redaction rules apply consistently (no secrets in argv/env); reuse shared redaction helpers. @@ -75,7 +75,7 @@ Keep concise, actionable, and security-focused. - **P1 – Structured anchor-guard events for caged `cd` (dedupe warnings; avoid heuristics)** - Problem: In persistent world sessions, the in-world anchor guard prints user-facing “caged root guard” warnings, but the host async REPL also performs host-side `cd` prediction/caging to keep `world_cwd` consistent when the world reports an unchanged cwd. This can produce duplicate warnings (guard + prediction) and forces brittle host-side suppression heuristics. - - Goal: Make “anchor guard bounced `cd` back into the cage” a first-class structured signal from world-agent so the host prints exactly once, consistently formatted, without stderr parsing or cwd-heuristic dedupe. + - Goal: Make “anchor guard bounced `cd` back into the cage” a first-class structured signal from world-service so the host prints exactly once, consistently formatted, without stderr parsing or cwd-heuristic dedupe. - Proposed design (backwards-compatible; no protocol_version bump): - Add a per-command sentinel env var set by the in-world guard *only when it bounces* (example): - `__SUBSTRATE_GUARD_EVENT_B64=` @@ -85,13 +85,13 @@ Keep concise, actionable, and security-focused. - `anchor_root: ` - `returned_to: ` - `display_scope: "host"|"world"` (optional; controls `([Substrate Host])` vs `([Substrate World])`) - - On persistent session completion, world-agent already captures the child shell’s env; extract + decode this sentinel and: + - On persistent session completion, world-service already captures the child shell’s env; extract + decode this sentinel and: - include it as an optional field on `command_complete` (e.g., `guard_event` / `guard_events`), - strip the sentinel key(s) from the persisted env before storing (so it never leaks into later commands). - Host shell prints from the structured field (once) and stops printing prediction-layer warnings for world sessions (prediction remains state-only). - Work (concrete): - `crates/world/src/guard.rs`: set `__SUBSTRATE_GUARD_EVENT_B64` when bouncing a `cd` (and optionally stop printing the user-facing warning directly once the structured path is plumbed end-to-end). - - `crates/world-agent/src/pty.rs`: + - `crates/world-service/src/pty.rs`: - plumb sentinel extraction from `PersistentChildEvent::Finished { env, .. }`, - extend `PersistentServerMessage::CommandComplete { .. }` with an optional `guard_event(s)` field (serde should ignore unknown fields for older clients), - strip sentinel keys from the session env before persisting. @@ -99,7 +99,7 @@ Keep concise, actionable, and security-focused. - `crates/shell/src/repl/async_repl.rs`: delete any remaining prediction-layer warning emission for world sessions (keep only the cwd correction). - Optional: record `guard_event(s)` into `trace.jsonl` (span extra) so guard bounces are auditable without stdout/stderr inference. - Tests: - - `world-agent` persistent session test: run `cd ..` outside anchor and assert `command_complete.guard_event(s)` present; assert sentinel key stripped from persisted env. + - `world-service` persistent session test: run `cd ..` outside anchor and assert `command_complete.guard_event(s)` present; assert sentinel key stripped from persisted env. - `shell` client tests: unknown/missing `guard_event(s)` remains compatible; when present, message prints once. - End-to-end (Linux): `substrate` async REPL `cd ../` outside anchor prints exactly one caged warning line (no duplicates). - Acceptance: no duplicate caged warnings; no stderr parsing; persisted env does not retain sentinel; old/new clients interoperate via optional fields. @@ -114,7 +114,7 @@ Keep concise, actionable, and security-focused. - **P1 – Policy-driven world fs mode** - Problem: write permissions inside worlds currently depend on systemd hardening + overlay success, not on broker policy. Sensitive repos need a policy bit to force read-only worlds while other projects remain writable, without editing unit files manually. - - Work: extend broker schema to accept `world.fs_mode = read_only|writable` (global + per-project), plumb into shell/world-agent so PTY + non-PTY sessions honor it, and update docs/doctor to surface the active mode. Systemd units must allow `/home` writes so policy can enforce RO vs writable deterministically. + - Work: extend broker schema to accept `world.fs_mode = read_only|writable` (global + per-project), plumb into shell/world-service so PTY + non-PTY sessions honor it, and update docs/doctor to surface the active mode. Systemd units must allow `/home` writes so policy can enforce RO vs writable deterministically. - Acceptance: policy defaults to writable; flipping to read-only blocks writes (clear errors, trace telemetry); installers/docs explain the knob and tests cover both modes (with skip notes for hosts lacking overlay/cgroup permissions). - **P1 – Add world OS/distro fields to doctor JSON** @@ -133,7 +133,7 @@ Keep concise, actionable, and security-focused. - Acceptance: operators can pin the world image per workspace and see the effective image in doctor/health; behavior is reproducible across machines; unsafe/unknown images fail closed with clear guidance. - **P1 fs_diff parity (agent HTTP + PTY)** - - *Agent HTTP path:* Today only replay/local backends attach `fs_diff`; agent-routed non-PTY commands drop the diff. Extend `agent-api-types::ExecuteResponse` / `world-agent` so `/v1/execute` returns `fs_diff: Option` and update the shell to record it in completion spans. Acceptance: `fs_diff` shows up in `trace.jsonl` for agent HTTP runs. + - *Agent HTTP path:* Today only replay/local backends attach `fs_diff`; agent-routed non-PTY commands drop the diff. Extend `transport-api-types::ExecuteResponse` / `world-service` so `/v1/execute` returns `fs_diff: Option` and update the shell to record it in completion spans. Acceptance: `fs_diff` shows up in `trace.jsonl` for agent HTTP runs. - *PTY sessions:* Interactive runs still lack filesystem diffs. Explore capturing post-exit diffs via overlayfs/copydiff and plumb the result through the PTY telemetry path so REPL + `substrate -i` sessions produce the same audit artifacts as non-PTY commands. Document caveats (long-running PTYs, partial diffs) and add tests to prove PTY diffs land in spans. - **P1 – Record `world_id` for REPL/local exec spans** @@ -238,7 +238,7 @@ Keep concise, actionable, and security-focused. - Considerations: support non-interactive modes (`--defaults`, `--profile`), validate paths, respect existing configs (idempotent), and clearly document the choices made. - **P4 – macOS bootstrap automation** - - Build a signed installer script (or helper binary) that validates macOS version/virtualization support, installs required Homebrew packages (`lima`, `jq`, `openssh`, `coreutils`, `gnused`, `gnu-tar`, `gettext`), provisions Lima, deploys `substrate-world-agent`, and runs doctor checks automatically. + - Build a signed installer script (or helper binary) that validates macOS version/virtualization support, installs required Homebrew packages (`lima`, `jq`, `openssh`, `coreutils`, `gnused`, `gnu-tar`, `gettext`), provisions Lima, deploys `substrate-world-service`, and runs doctor checks automatically. - Acceptance: Fresh macOS host can run a single installer command and end up with the Lima VM, agent, and smoke test ready without manual steps. - **P4 – macOS installer dependency automation** @@ -325,11 +325,11 @@ Keep concise, actionable, and security-focused. - Future readers can answer, from docs and tests, whether any shipped entrypoint still uses a hidden bootstrap prompt. - Either the hidden fallback is removed, or it remains with explicit documentation and tests proving its exact reachability boundary. - Document backend divergence - - Clarify why shell uses world-agent and replay uses LinuxLocalBackend; outline future convergence plan. + - Clarify why shell uses world-service and replay uses LinuxLocalBackend; outline future convergence plan. - Redaction hardening for “command body” tracing (preexec + future trace surfaces) - Problem: some debug-grade event families (e.g., bash preexec `builtin_command`) can include raw command bodies that may contain tokens/headers/secrets. Today we omit bodies from canonical trace for safety; to safely include bodies we need hardened, shared redaction across the codebase. - Work: - - Define a shared redaction module usable by shell/shim/world-agent/preexec (flag consumes next arg, `--flag=value`, URL credentials, headers, exports, common token env patterns). + - Define a shared redaction module usable by shell/shim/world-service/preexec (flag consumes next arg, `--flag=value`, URL credentials, headers, exports, common token env patterns). - Add tests that prove representative leaks are redacted (tokens, Authorization headers, proxy URLs, `export FOO_TOKEN=...`, etc.). - Add an explicit opt-in mode to include redacted command bodies in canonical trace for debug profiles (and ensure it is non-triggerable by default for ADR-0029 routing). - Acceptance: command-body logging is safe-by-default, can be enabled explicitly for debug with strong redaction + tests, and is clearly labeled/filtered. @@ -387,7 +387,7 @@ Code changes required (touch points) - `crates/world-windows-wsl/src/lib.rs` - Ensure `build_agent_client()` resolves the same default and exposes the chosen endpoint for telemetry. -- `crates/agent-api-client` (tests) +- `crates/transport-api-client` (tests) - Update unit tests that assert the hard‑coded legacy pipe to accept the dynamic, user‑scoped default. - `crates/shell` (tests/telemetry) @@ -446,11 +446,11 @@ Risks / considerations ## DONE -- IMPLEMENTED -- ~~**P0 –Socket-activated world-agent service~~ **(Done)** - - Current provisioning only installs `substrate-world-agent.service`; the agent binds `/run/substrate.sock` itself and must stay running. Introduce a matching `.socket` unit so systemd listens on the socket, launches the agent on demand, and restarts it transparently. +- ~~**P0 –Socket-activated world-service service~~ **(Done)** + - Current provisioning only installs `substrate-world-service.service`; the agent binds `/run/substrate.sock` itself and must stay running. Introduce a matching `.socket` unit so systemd listens on the socket, launches the agent on demand, and restarts it transparently. - Work items: - - Update `world-agent` to accept an inherited listener (LISTEN_FDS) in addition to binding directly; keep the existing code path for non-systemd environments. - - Teach `ensure_world_agent_ready()` to tolerate socket-activated setups (socket already present, service only starts when probed). + - Update `world-service` to accept an inherited listener (LISTEN_FDS) in addition to binding directly; keep the existing code path for non-systemd environments. + - Teach `ensure_world_service_ready()` to tolerate socket-activated setups (socket already present, service only starts when probed). - Extend Linux/Lima/WSL installers, world-enable helper, and uninstall scripts to deploy/remove both `.service` and `.socket` units. - Refresh docs/tests to describe the new flow; ensure guidance around sudo/user installs and permissions highlights the benefit. - Can run in parallel with other features—touches agent + provisioning scripts but does not block shell, tracing, or UX work. @@ -458,7 +458,7 @@ Risks / considerations - ~~**Replay polish – isolation + verbose scopes~~ **(Done)** - *Isolation follow-up:* Most of the Phase 4.5 isolation plan shipped (per-replay netns + nft scoping), but optional enhancements remain: nft cgroup matching fallback, documentation updates (`COMPLETE_FIXES_PHASE4_PRE45.md`), and diagnostic tooling for leftover netns/rules. - *Verbose scopes:* When running `substrate --replay --replay-verbose`, show a concise `scopes: [...]` line next to the “world strategy” output so operators can see which policy scopes were exercised per replay. - - *Clear warnings:* Differentiate shell vs. replay world warnings—shell path messages should explicitly say “shell world-agent path”, while replay warnings keep the `[replay] …` prefix. + - *Clear warnings:* Differentiate shell vs. replay world warnings—shell path messages should explicitly say “shell world-service path”, while replay warnings keep the `[replay] …` prefix. - *Default-to-world replay tests:* Add integration coverage for the default world-on path, `--no-world`, and env opt-out so replay regressions are caught automatically. - Treat all of the above as a single replay-focused bucket so backend polish and CLI visibility ship together (shared tests/docs). @@ -471,16 +471,16 @@ Risks / considerations - Implementation: `substrate config init` scaffolds `~/.substrate/config.yaml`, `config show` renders YAML/JSON with redaction hooks, and `config set` applies multi-key updates atomically with schema validation. - Docs/installer output highlight the new subcommands across Linux/macOS/Windows, and the precedence stack (flags → directory config → global config → env) remains unchanged. -- ~~Auto-start world-agent on shell startup (Linux)~~ **(Done)** +- ~~Auto-start world-service on shell startup (Linux)~~ **(Done)** - Implementation: `run_shell()` now initializes the Linux backend, flips `SUBSTRATE_WORLD=enabled`, sets `SUBSTRATE_WORLD_ID`, and uses `ensure_session()` before handling commands (`crates/shell/src/lib.rs:1576-1620`). - Notes: macOS and Windows paths share the same default-on behavior; the Linux - helper still attempts to spawn `world-agent` if `/run/substrate.sock` is + helper still attempts to spawn `world-service` if `/run/substrate.sock` is stale (`crates/shell/src/lib.rs:3680-3687`). - ~~Non-PTY agent auto-start parity (shell)~~ **(Done)** - - Implementation: Non-PTY routing now calls `ensure_world_agent_ready()` when + - Implementation: Non-PTY routing now calls `ensure_world_service_ready()` when worlds are enabled (Linux HTTP path) and records transport metadata for macOS/Windows agent calls (`crates/shell/src/lib.rs:3680-3703`, `3560-3663`). - Result: The shell only falls back to host execution after a single warning @@ -493,10 +493,10 @@ Risks / considerations - Implement the async agent output path so events can stream without prompt corruption. - ~~macOS binary distribution~~ **(Done)** - - Publish prebuilt `substrate` and `substrate-world-agent` artifacts (e.g., GitHub releases or Homebrew formula) so operators do not need a Rust toolchain to onboard. + - Publish prebuilt `substrate` and `substrate-world-service` artifacts (e.g., GitHub releases or Homebrew formula) so operators do not need a Rust toolchain to onboard. - Acceptance: Installer/bootstrap script can download versioned binaries; manual instructions reference the published artifacts instead of local builds. - ~~macOS backend enablement (Lima)~~ — **DONE (2025-09)** - - Implemented `world-mac-lima` agent calls via `agent-api-client` with VSock/SSH fallbacks. + - Implemented `world-mac-lima` agent calls via `transport-api-client` with VSock/SSH fallbacks. - Shell now routes macOS commands through Lima, ensures VM/forwarding, and mirrors Linux telemetry. - Acceptance met: `scripts/mac/smoke.sh` validates non-PTY, PTY, and replay; docs refreshed (`docs/WORLD.md`, `docs/dev/mac_world_setup.md`, `docs/INSTALLATION.md`). diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 4830aa361..1d8a8d4d6 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -13,7 +13,7 @@ This document mirrors the mental model of the CLI parser in `crates/shell/src/ex | Version metadata | `--version-json` | `--world`, `--no-world`, `--shim-skip` | `-c`, `-f`, shim mgmt, trace/replay | Information-only request (`cli.rs:62`). | | Trace inspect | `--trace ` | `--world`, `--no-world`, anchor/caging flags | `-c`, `-f`, shim mgmt, `--replay` | Pull span metadata (`cli.rs:94`). | | Replay | `--replay ` | `--replay-verbose`, world + anchor toggles | `-c`, `-f`, `--trace`, shim mgmt | Agent-first replay on Linux with a single `[replay] warn: agent replay unavailable (); falling back to local backend. Run \`substrate world doctor --json\` (or \`substrate host doctor --json\` for transport-only checks) or set SUBSTRATE_WORLD_SOCKET to point at a healthy agent socket` warning before switching to the local backend/copy-diff. Replay-local opt-outs keep their fixed host fragments (`--no-world flag`, `SUBSTRATE_REPLAY_USE_WORLD=disabled`), and config-based host fallbacks use the tokenized reason fragments from `docs/REPLAY.md`. | -| Replay verbose | `--replay --replay-verbose` | Same as replay | Requires `--replay` | Adds command/cwd/mode diagnostics, the active world toggle summary, exact origin/warning text, strategy/scopes lines, and replay warnings (agent fallback, copy-diff retries). Shell transport probes still use the `shell world-agent path ...` prefix so you can distinguish them from `[replay] warn:` entries. | +| Replay verbose | `--replay --replay-verbose` | Same as replay | Requires `--replay` | Adds command/cwd/mode diagnostics, the active world toggle summary, exact origin/warning text, strategy/scopes lines, and replay warnings (agent fallback, copy-diff retries). Shell transport probes still use the `shell world-service path ...` prefix so you can distinguish them from `[replay] warn:` entries. | ## Root Command: Order-Independent Flags @@ -78,7 +78,7 @@ Once you type `graph`, `host`, `world`, `config`, `policy`, `workspace`, `shim`, | Invocation | Positional Arguments | Subcommand Flags | Notes | | --- | --- | --- | --- | -| `substrate world doctor` | — | `--json` | World-scoped readiness report (host + world-agent facts). | +| `substrate world doctor` | — | `--json` | World-scoped readiness report (host + world-service facts). | | `substrate world enable` | — | `--prefix`, `--profile`, `--provision-deps`, `--dry-run`, `--verbose`, `--force`, `--timeout` | Provisioning control per `cli.rs:197`; `--provision-deps` is the operator-facing APT workflow. See `docs/reference/world/deps/README.md` and `docs/project_management/packs/draft/world-deps-apt-provisioning/contract.md`. | | `substrate world deps current list [VIEW]` | `view` (`available`, `enabled`, `applied`) | `--all`, `--json` | `available/enabled` are host-only; `applied` queries the world backend. | | `substrate world deps current show ` | `item` name | `--json`, `--explain` | `--explain` queries the world backend and prints remediation/manual instructions when blocked. | diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 037c91d0b..5d21e7d42 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -39,7 +39,7 @@ Agent Hub successor routing is configured through the normal Substrate config an Config keys: - `agents.hub.orchestrator_agent_id` selects the canonical host-scoped orchestrator agent for `substrate agent status` and `substrate agent doctor`. - Agent inventory entries continue to define each agent's adapter kind and execution posture; the derived `backend_id` remains `:`. -- The shell-owned v1 runtime only realizes selected orchestrators with `config.kind=cli`, `protocol=uaa.agent.session`, and `cli.mode=persistent`. +- The shell-owned v1 runtime only realizes selected orchestrators with `config.kind=cli`, `protocol=substrate.agent.session`, and `cli.mode=persistent`. - The first realized shell-owned UAA backends are `cli:codex` and `cli:claude_code`. Other inventory items may still validate and list successfully, but they are not runtime-realizable on the selected orchestrator path in v1. - `config.cli.binary` for the selected orchestrator must resolve on the host during `substrate agent doctor` and async REPL bootstrap. @@ -60,7 +60,7 @@ version: 1 id: claude_code config: kind: cli - protocol: uaa.agent.session + protocol: substrate.agent.session execution: scope: host cli: @@ -174,7 +174,7 @@ Other world-adjacent variables: | Variable | Purpose | Default | Example | |----------|---------|---------|---------| -| `SUBSTRATE_WORLD_REQUEST_PROFILE` | Sets the Agent API request `profile` for world-agent executions (advanced/internal only). Built-in world-deps profiles such as `world-deps-provision` and `world-deps-probe` are reserved for Substrate’s own world-deps flows and are ignored when supplied through this env var; the operator-facing APT provisioning workflow remains `substrate world enable --provision-deps`. See `docs/reference/world/deps/README.md`, `docs/project_management/packs/implemented/world-deps-apt-provisioning/contract.md`, and the historical draft-pack path `docs/project_management/packs/draft/world-deps-apt-provisioning/contract.md`. | *unset* | `wdap-smoke-profile` | +| `SUBSTRATE_WORLD_REQUEST_PROFILE` | Sets the Agent API request `profile` for world-service executions (advanced/internal only). Built-in world-deps profiles such as `world-deps-provision` and `world-deps-probe` are reserved for Substrate’s own world-deps flows and are ignored when supplied through this env var; the operator-facing APT provisioning workflow remains `substrate world enable --provision-deps`. See `docs/reference/world/deps/README.md`, `docs/project_management/packs/implemented/world-deps-apt-provisioning/contract.md`, and the historical draft-pack path `docs/project_management/packs/draft/world-deps-apt-provisioning/contract.md`. | *unset* | `wdap-smoke-profile` | | `SUBSTRATE_SOCKET_ACTIVATION_OVERRIDE` | Force socket activation mode reporting (`socket_activation`, `manual`, or `unknown`) for diagnostics/tests | auto-detect via systemd | `socket_activation` | | `SUBSTRATE_SYSTEMCTL_TIMEOUT_MS` | Timeout (ms) for `systemctl show …` probes used by Linux socket-activation detection; prevents hangs when systemd/dbus is unhealthy | `2000` | `250` | @@ -246,7 +246,7 @@ Unknown keys and extra tables are preserved for future expansion. - Legacy installs that still have `config.json` are read automatically, but new writes use `config.yaml`. - The generated `~/.substrate/manager_env.sh` exports derived `SUBSTRATE_*` state so shims and subprocesses observe a consistent view of the effective config. - Supported Linux/macOS provisioning helpers also wire `SUBSTRATE_HOME` into the - `substrate-world-agent` systemd unit and keep that exact path writable via + `substrate-world-service` systemd unit and keep that exact path writable via `ReadWritePaths`. WSL helper scripts are intentionally fail-closed in this slice and do not claim that placement contract yet. @@ -267,7 +267,7 @@ enforcement is visible without extra flags. Substrate policy files on disk are always *patch-only*: a sparse YAML mapping where omitted keys mean “inherit”. The broker resolves a single effective policy for the current directory and all execution -paths (shell/shim/world-agent) consume that same resolved policy. +paths (shell/shim/world-service) consume that same resolved policy. Effective policy resolution order: diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index e7e9f1d4c..218bd02a9 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -10,8 +10,8 @@ until it matches the Linux-first placement contract. - **Linux**: systemd-based distributions with `sudo`, `curl`, `tar`, and `jq` available. When world provisioning is enabled, the installer also provisions the host `libseccomp` runtime package needed for seccomp-backed hardening. The - world backend runs via the `substrate-world-agent.service` + - `substrate-world-agent.socket` units. + world backend runs via the `substrate-world-service.service` + + `substrate-world-service.socket` units. - **macOS 14+ (arm64)**: requires Apple Virtualization Framework and Lima (the installer verifies both). - **Windows / WSL**: world provisioning is intentionally fail-closed in this @@ -80,8 +80,8 @@ The installer will: (`install.world_enabled: true` unless `--no-world` is provided). The metadata is consumed by `substrate world enable` and shims/CLI commands that need to detect pass-through mode. -7. Install `substrate-world-agent` and `substrate-gateway` under `/usr/local/bin` and manage the - systemd `.service` + `.socket` units (`/etc/systemd/system/substrate-world-agent.{service,socket}`) +7. Install `substrate-world-service` and `substrate-gateway` under `/usr/local/bin` and manage the + systemd `.service` + `.socket` units (`/etc/systemd/system/substrate-world-service.{service,socket}`) 8. Run `substrate world doctor --json` for a final readiness report 9. Ensure the `substrate` group exists on Linux hosts, add the invoking user, and reload the socket/service units so `/run/substrate` is recreated as @@ -139,7 +139,7 @@ During installation the script: - Records the host PATH (for use as `SHIM_ORIGINAL_PATH`) without mutating it. - Deploys fresh shims and appends a small PATH snippet (between `# >>> substrate >>>` markers) so `substrate` is callable from your terminal. -- Installs `substrate-world-agent` as a systemd service plus socket, places +- Installs `substrate-world-service` as a systemd service plus socket, places `substrate-gateway` beside it under `/usr/local/bin`, and runs `substrate world doctor --json` (inspect the `host.world_socket` block) without adding the shim directory to PATH to avoid self-referential lookups. @@ -182,7 +182,7 @@ The macOS flow mirrors the Linux installer but additionally: - Requires the Lima CLI (`limactl`) to be installed beforehand - Requires `envsubst` (install via `brew install gettext` to provide it) - Provisions the Lima VM (`scripts/mac/lima-warm.sh`) and copies the Linux - `world-agent` and `substrate-gateway` binaries into the guest + `world-service` and `substrate-gateway` binaries into the guest - Writes the guest systemd unit with `SUBSTRATE_HOME=/.substrate` and keeps that path writable in `ReadWritePaths`, matching the Linux placement contract for runtime state and manager/config files @@ -217,7 +217,7 @@ substrate world doctor --json | jq '{ok, world_enabled, host_ok: .host.ok, world (The installer already ran the doctor without the shim directory in PATH, so your output should match unless the host environment changed.) -Snapshot-specific behavior (including `policy_resolution_mode` and snapshot-related trace fields) is specified in `docs/project_management/_archived/world-agent-policy-snapshot/policy-snapshot-spec.md`. +Snapshot-specific behavior (including `policy_resolution_mode` and snapshot-related trace fields) is specified in `docs/project_management/_archived/world-service-policy-snapshot/policy-snapshot-spec.md`. ### Windows @@ -301,7 +301,7 @@ scripts/substrate/dev-uninstall-substrate.sh ``` These scripts build `substrate`, `substrate-shim`, `substrate-forwarder`, -`host-proxy`, `substrate-gateway`, and (on supported platforms) `world-agent`, +`host-proxy`, `substrate-gateway`, and (on supported platforms) `world-service`, symlink them into `~/.substrate/bin`, and deploy shim wrappers that point back to `target/`. The generated `~/.substrate/dev-shim-env.sh` prepends both the bin and shim directories to `PATH` while preserving `SHIM_ORIGINAL_PATH`, so @@ -330,8 +330,8 @@ It also reports whether `loginctl enable-linger ` still needs to be run. `substrate world doctor --json`; attach both to bug reports so we can spot PATH vs kernel/virtualization gaps quickly. - **World agent inactive (Linux)**: confirm `systemctl status - substrate-world-agent.socket` reports `listening`, `systemctl status - substrate-world-agent.service` reports `active` (or restarts cleanly), and that `/run/substrate.sock` + substrate-world-service.socket` reports `listening`, `systemctl status + substrate-world-service.service` reports `active` (or restarts cleanly), and that `/run/substrate.sock` exists as `root substrate 0660` (`sudo ls -l /run/substrate.sock`). If the socket shows another group, rerun the installer to refresh the units. Permission issues usually mean your user is missing from the `substrate` group—check with diff --git a/docs/ISOLATION_SUPPORT_MATRIX.md b/docs/ISOLATION_SUPPORT_MATRIX.md index 7b4d9dd5c..29c4cc11e 100644 --- a/docs/ISOLATION_SUPPORT_MATRIX.md +++ b/docs/ISOLATION_SUPPORT_MATRIX.md @@ -6,13 +6,13 @@ It is intentionally grounded in the current implementation (see “Code Pointers Scope: - `world_fs.mode`: `writable | read_only` - `world_fs.isolation`: `workspace | full` -- The “world backend” path (world-agent + overlay + mount namespace + optional Landlock) +- The “world backend” path (world-service + overlay + mount namespace + optional Landlock) ## Comparison Matrix | Topic | Linux (native backend) | macOS (Lima-backed backend) | |---|---|---| -| Where enforcement runs | On the Linux host via the world backend (world-agent) and its Linux kernel primitives. | Inside the Linux guest VM (world-agent runs in the guest); the macOS host only provides transport/forwarding. | +| Where enforcement runs | On the Linux host via the world backend (world-service) and its Linux kernel primitives. | Inside the Linux guest VM (world-service runs in the guest); the macOS host only provides transport/forwarding. | | `world_fs.isolation=workspace` (what it tries to prevent) | Prevents absolute-path escapes back into the host project by placing the overlay at the project path inside a private mount namespace. | Same mechanics, but “host paths” mean *guest-visible mounts* (the project and any other host-shared directories that are mounted into the guest). | | `workspace`: are non-project paths nameable? | Yes. The process can still name other host paths (it’s not a `pivot_root` cage). | Yes, for any paths that exist inside the guest (including shared host mounts, if present). | | `workspace`: write protection outside project | Best-effort write blocking via Landlock “write-only allowlist”: allows writes to `/tmp`, `/var/tmp`, `/dev`, `/var/lib/substrate/world-deps`, plus the project dir (`SUBSTRATE_MOUNT_PROJECT_DIR`); other writes are denied by Landlock. | Same Landlock write-only allowlist behavior, but applied inside the guest kernel. Writes to other guest-visible mounts outside the project should be denied by Landlock. | @@ -20,10 +20,10 @@ Scope: | `world_fs.isolation=full` (what it tries to prevent) | Makes host paths “not nameable” by constructing a minimal rootfs and `pivot_root`ing into it. | Same `pivot_root`-based cage, but executed inside the guest (so it hides guest-visible host mounts as well). | | `full`: which mounts exist | Script bind-mounts a minimal set (`/usr`, `/bin`, `/lib*`, `/etc` as read-only; `/dev` read-only; `/proc`; `/tmp` as tmpfs; `/var/lib/substrate/world-deps` read-write) plus the project mounted at both `/project` and the host-absolute project path. | Same mount set inside the guest. | | `full` + `mode=writable`: project writability | The project is remounted read-only by default; only prefixes derived from `world_fs.write_allowlist` are remounted `rw` (via `SUBSTRATE_WORLD_FS_WRITE_ALLOWLIST`). If the allowlist is empty, project writes should fail. | Same semantics inside the guest. | -| `full` + `mode=writable`: overlayfs copy-up prereq | The world-agent service needs `cap_chown` (systemd `CapabilityBoundingSet`/`AmbientCapabilities`) so overlayfs can copy-up directories while preserving ownership/metadata; missing capability can manifest as `EPERM` even for allowlisted paths. | Same, but the capability must be present on the guest systemd unit installed by `scripts/mac/lima-warm.sh`. | +| `full` + `mode=writable`: overlayfs copy-up prereq | The world-service service needs `cap_chown` (systemd `CapabilityBoundingSet`/`AmbientCapabilities`) so overlayfs can copy-up directories while preserving ownership/metadata; missing capability can manifest as `EPERM` even for allowlisted paths. | Same, but the capability must be present on the guest systemd unit installed by `scripts/mac/lima-warm.sh`. | | `full` + `mode=read_only` | The project mount is remounted read-only; allowlisted prefixes are not remounted writable because `SUBSTRATE_MOUNT_FS_MODE=read_only`. | Same semantics inside the guest. | -| `full`: read_allowlist / write_allowlist usage | In addition to mount semantics, the world-agent can apply Landlock allowlists in full mode: `read_allowlist` feeds `SUBSTRATE_WORLD_FS_LANDLOCK_READ_ALLOWLIST`, `write_allowlist` feeds `SUBSTRATE_WORLD_FS_LANDLOCK_WRITE_ALLOWLIST`, and `write_allowlist` also drives the mount-time writable prefix remounting. | Same behavior inside the guest (subject to Landlock support in the guest kernel). | -| Diagnostics surface area | `substrate host doctor` reports host prerequisites + transport readiness; `substrate world doctor` includes a `host` block plus an agent-reported `world` block sourced from `GET /v1/doctor/world` (Landlock support/ABI + world fs strategy probe, with legacy fallbacks when the endpoint is missing). | `substrate host doctor (macOS)` focuses on Lima + transport + guest service status; `substrate world doctor (macOS)` includes the same `host` block plus guest-kernel facts from the world-agent via `GET /v1/doctor/world` (Landlock support/ABI + world fs strategy probe, with legacy fallbacks when the endpoint is missing). | +| `full`: read_allowlist / write_allowlist usage | In addition to mount semantics, the world-service can apply Landlock allowlists in full mode: `read_allowlist` feeds `SUBSTRATE_WORLD_FS_LANDLOCK_READ_ALLOWLIST`, `write_allowlist` feeds `SUBSTRATE_WORLD_FS_LANDLOCK_WRITE_ALLOWLIST`, and `write_allowlist` also drives the mount-time writable prefix remounting. | Same behavior inside the guest (subject to Landlock support in the guest kernel). | +| Diagnostics surface area | `substrate host doctor` reports host prerequisites + transport readiness; `substrate world doctor` includes a `host` block plus an agent-reported `world` block sourced from `GET /v1/doctor/world` (Landlock support/ABI + world fs strategy probe, with legacy fallbacks when the endpoint is missing). | `substrate host doctor (macOS)` focuses on Lima + transport + guest service status; `substrate world doctor (macOS)` includes the same `host` block plus guest-kernel facts from the world-service via `GET /v1/doctor/world` (Landlock support/ABI + world fs strategy probe, with legacy fallbacks when the endpoint is missing). | | Verification harness caveat | `substrate world verify` uses the OS temp dir by default for its scratch projects/logs; this is usually fine on Linux hosts. | `substrate world verify` default root may land under macOS `/var/folders/...`. If that path is not mirrored into the guest, full-isolation verification can fail for environmental reasons; overriding `--root` to a host path that is visible in the guest (e.g. under `$HOME`) avoids this. | ## Code Pointers (Implementation Ground Truth) @@ -33,11 +33,11 @@ Filesystem/mount enforcement (Linux shell script used by the world backend): Landlock policy application: - `crates/world/src/landlock.rs` (`apply_filesystem_policy`, `apply_write_only_allowlist`) -- `crates/world-agent/src/internal_exec.rs` (`__substrate_world_landlock_exec` wrapper; applies full-mode allowlists, and applies the workspace-mode write-only allowlist when isolation is not full) +- `crates/world-service/src/internal_exec.rs` (`__substrate_world_landlock_exec` wrapper; applies full-mode allowlists, and applies the workspace-mode write-only allowlist when isolation is not full) How allowlists are resolved and injected (full isolation): -- `crates/world-agent/src/service.rs` (non-PTY `/v1/execute`) -- `crates/world-agent/src/pty.rs` (PTY `/v1/stream`) +- `crates/world-service/src/service.rs` (non-PTY `/v1/execute`) +- `crates/world-service/src/pty.rs` (PTY `/v1/stream`) macOS transport / “world backend available” vs fallback: - `crates/shell/src/execution/platform/macos.rs` (doctor checks Lima + guest service) diff --git a/docs/REPLAY.md b/docs/REPLAY.md index 339ed484c..969f0707a 100644 --- a/docs/REPLAY.md +++ b/docs/REPLAY.md @@ -79,12 +79,12 @@ substrate --replay --replay-verbose --no-world # [replay] scopes: [] ``` -Shell-side fallbacks emit `substrate: warn: shell world-agent path () ...` so you can +Shell-side fallbacks emit `substrate: warn: shell world-service path () ...` so you can distinguish them from `[replay] warn: ...` messages that come from the replay runtime. By default on Linux, replay will: -- Prefer the world-agent path when `/run/substrate.sock` responds; fall back once to the local world backend/copy-diff with a single warning when the agent is unavailable +- Prefer the world-service path when `/run/substrate.sock` responds; fall back once to the local world backend/copy-diff with a single warning when the agent is unavailable - Configure the world with `always_isolate: true`, forcing isolation for ALL commands (even simple ones like `echo` that normally wouldn't be isolated) - Carry world-root/caging env into the replay so cwd/path alignment matches the original span when isolation is active - Return `fs_diff` (writes/mods/deletes) and `scopes_used` from the isolated execution diff --git a/docs/TRACE.md b/docs/TRACE.md index f35fb56b5..8070c3eee 100644 --- a/docs/TRACE.md +++ b/docs/TRACE.md @@ -169,7 +169,7 @@ Agent-hub successor telemetry keeps adapter identity separate from semantic iden - `backend_id`: derived adapter identifier in `:` form. This remains the allowlist and adapter-selection token. - `client`: pure-agent client identity for successor agent-hub records. - `router`: routing surface for the record. Pure-agent records use `agent_hub`; nested gateway-backed records use `substrate_gateway`. -- `protocol`: protocol family for the record, such as `uaa.agent.session`. +- `protocol`: protocol family for the record, such as `substrate.agent.session`. - `provider`: nested gateway-backed provider identity only; pure-agent records omit it. - `auth_authority`: nested gateway-backed auth authority only; pure-agent records omit it. - `parent_run_id`: nested gateway-backed trace correlation only; points at the parent pure-agent `run_id`. @@ -181,11 +181,11 @@ Boundary note: - Projection of the active shared-world binding into shell-owned runtime state remains PLAN-04. - Replacement/invalidation semantics for prior generations remain PLAN-05; consumers must not infer global invalidation from trace fields alone. -`uaa.agent.session` is currently a Substrate-local normalized protocol-family id, not an automatic claim of upstream Unified Agent API wire or API compatibility. In the current repo, pure-agent records are stamped with that label by [agent_events.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs:17), and `substrate agent status` / orchestrator-selection logic consumes the same label in [agents_cmd.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agents_cmd.rs:25). +`substrate.agent.session` is currently a Substrate-local normalized protocol-family id, not an automatic claim of upstream Unified Agent API wire or API compatibility. In the current repo, pure-agent records are stamped with that label by [agent_events.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/common/src/agent_events.rs:17), and `substrate agent status` / orchestrator-selection logic consumes the same label in [agents_cmd.rs](/Users/spensermcconnell/__Active_Code/atomize-hq/substrate/crates/shell/src/execution/agents_cmd.rs:25). The shell-owned UAA runtime translates external `agent_api` wrapper events into canonical `agent_event` trace rows with: - `router=agent_hub` -- `protocol=uaa.agent.session` +- `protocol=substrate.agent.session` - `provider` omitted - `auth_authority` omitted @@ -292,12 +292,12 @@ jq 'select(.kind == "alert" and .data.code? and (.data.code == "world_restarted" ### Policy Snapshot Metadata (No Raw Policy Content) The current field semantics for `policy_resolution_mode` and related snapshot metadata are defined in this document. The archived -`docs/project_management/_archived/world-agent-policy-snapshot/policy-snapshot-spec.md` is historical background only. +`docs/project_management/_archived/world-service-policy-snapshot/policy-snapshot-spec.md` is historical background only. See the field notes below for the current consumer-facing meaning. Command completion spans record snapshot metadata without logging raw policy contents: -- `policy_resolution_mode` (string): always present on `command_complete` spans; `snapshot_v1` when the host attached a `PolicySnapshotV1` to a world-agent request, otherwise `legacy_local`. +- `policy_resolution_mode` (string): always present on `command_complete` spans; `snapshot_v1` when the host attached a `PolicySnapshotV1` to a world-service request, otherwise `legacy_local`. - `policy_snapshot_schema` (number, optional): snapshot schema version when `policy_resolution_mode == "snapshot_v1"`. - `policy_snapshot_hash` (string, optional): stable SHA-256 hex digest of the serialized snapshot when `policy_resolution_mode == "snapshot_v1"`. @@ -314,9 +314,9 @@ Command completion spans record snapshot metadata without logging raw policy con Verbose replay outputs (`--replay-verbose` or JSON mode) print `[replay] scopes: [...]` adjacent to the world strategy line so the CLI summary mirrors the `scopes_used` array above. When the -shell falls back to host execution it now prefixes warnings with `shell world-agent path (...)` +shell falls back to host execution it now prefixes warnings with `shell world-service path (...)` to keep them distinct from `[replay] warn: ...` diagnostics emitted by the replay runtime -(agent fallback, copy-diff retries, isolation opt-outs). Replay prefers the world-agent path +(agent fallback, copy-diff retries, isolation opt-outs). Replay prefers the world-service path (`/run/substrate.sock`) when it responds; verbose output shows `[replay] world strategy: agent (...)` and the runtime emits a single `[replay] warn: agent replay unavailable (); falling back to local backend. Run `substrate world doctor --json` or set SUBSTRATE_WORLD_SOCKET to point at a healthy agent socket. The warning appears before falling back to local isolation/copy-diff when the socket is unhealthy. Replay appends a `replay_strategy` telemetry entry for each run so traces capture the chosen backend and any @@ -357,7 +357,7 @@ fields appear only when that fallback is used. - Windows adds an optional `fs_diff.display_path` map that pairs canonical paths (e.g., `/mnt/c/...`) with native Windows representations; Linux and macOS omit this field. The map is populated by the `world-windows-wsl` backend and available whenever a diff is returned. - `transport.mode` reflects the active connector: `unix` for native Linux and Lima guests, `named_pipe` when routed through the Windows forwarder, and `tcp` only when an explicit fallback is selected. -- `transport.socket_activation` is emitted on Linux when the world-agent socket originated from systemd socket activation (`true`) versus direct/manual binds (`false`). Non-Linux transports omit this field. +- `transport.socket_activation` is emitted on Linux when the world-service socket originated from systemd socket activation (`true`) versus direct/manual binds (`false`). Non-Linux transports omit this field. - `execution_origin` on command_complete spans identifies where the command actually ran (`host` vs `world`). `replay_context` also includes the recorded origin/transport plus host/user/shell/term and anchor/world-root/caging hints (`SUBSTRATE_ANCHOR_*`, `SUBSTRATE_WORLD_ROOT_*`, `SUBSTRATE_CAGED`) so replays can honor the original environment. ## Architecture diff --git a/docs/UNINSTALL.md b/docs/UNINSTALL.md index 822d8cf04..a505d269d 100644 --- a/docs/UNINSTALL.md +++ b/docs/UNINSTALL.md @@ -40,19 +40,19 @@ sudo env HOME="$HOME" ./scripts/substrate/uninstall.sh Additional automated steps on systemd hosts: -- Stops and disables the `substrate-world-agent` systemd service. -- Stops/disables `substrate-world-agent.socket` as well. -- Removes `/etc/systemd/system/substrate-world-agent.{service,socket}` and reloads +- Stops and disables the `substrate-world-service` systemd service. +- Stops/disables `substrate-world-service.socket` as well. +- Removes `/etc/systemd/system/substrate-world-service.{service,socket}` and reloads systemd. -- Deletes `/usr/local/bin/substrate-world-agent`, `/var/lib/substrate`, and the +- Deletes `/usr/local/bin/substrate-world-service`, `/var/lib/substrate`, and the `/run/substrate` runtime directory. ### Manual Verification ```bash -systemctl status substrate-world-agent.socket # Should be "Unit not found" -systemctl status substrate-world-agent.service # Should be "Unit not found" -ls -l /usr/local/bin | grep substrate # No world-agent binary +systemctl status substrate-world-service.socket # Should be "Unit not found" +systemctl status substrate-world-service.service # Should be "Unit not found" +ls -l /usr/local/bin | grep substrate # No world-service binary ls ~/.substrate # Should report "No such file" ``` diff --git a/docs/USAGE.md b/docs/USAGE.md index 442616af4..df52c5592 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -166,8 +166,8 @@ substrate --shim-remove # delete ~/.substrate/shims SUBSTRATE_NO_SHIMS=1 substrate # skip deployment for this invocation ``` -On Linux, `substrate --shim-status[ --json]` also reports whether the world-agent -socket is managed by systemd socket activation (`substrate-world-agent.socket`) +On Linux, `substrate --shim-status[ --json]` also reports whether the world-service +socket is managed by systemd socket activation (`substrate-world-service.socket`) or a manual listener, making it easier to distinguish provisioned services from legacy bindings. @@ -297,7 +297,7 @@ manager and point `BASH_ENV` at `~/.substrate_bashenv` explicitly. ### World Commands - `substrate host doctor [--json]` – host readiness for world routing (host prerequisites + transport readiness) -- `substrate world doctor [--json]` – world readiness report (includes host doctor + world-agent facts) +- `substrate world doctor [--json]` – world readiness report (includes host doctor + world-service facts) - `substrate world gateway sync|status|restart` – operator gateway lifecycle/status entrypoints. - `substrate world gateway status` is the stable operator entrypoint for gateway availability and discovery. - `substrate world gateway status --json` is the authoritative machine-readable status surface for that entrypoint. diff --git a/docs/WORLD.md b/docs/WORLD.md index 9495bf9de..6538f7437 100644 --- a/docs/WORLD.md +++ b/docs/WORLD.md @@ -16,7 +16,7 @@ A “world” is a reusable Linux execution context providing: Two cooperating components: - `substrate` (shell): orchestrates execution, tracing, and routing (non‑PTY via REST, PTY via WS) -- `world-agent`: runs inside the target Linux environment and exposes a small API over a Unix domain socket (`/run/substrate.sock`) +- `world-service`: runs inside the target Linux environment and exposes a small API over a Unix domain socket (`/run/substrate.sock`) On Linux the agent runs directly on the host. On macOS the agent runs inside a Lima VM; the shell ensures transport forwarding (VSock → SSH UDS → SSH TCP) back to the guest socket. @@ -28,7 +28,7 @@ Helper scripts (`scripts/mac/lima-*.sh`, `scripts/mac/smoke.sh`) keep the Lima e These socket ACL details are runtime transport facts, not a second operator contract. -- The authorization boundary for world-agent requests is the OS-level transport ACL (Linux: Unix socket ownership/mode for `/run/substrate.sock`). +- The authorization boundary for world-service requests is the OS-level transport ACL (Linux: Unix socket ownership/mode for `/run/substrate.sock`). - On Linux, `/run/substrate.sock` MUST be owned by `root:substrate` with mode `0660` (`srw-rw----`). - Managed gateway runtime diagnostics under `/run/substrate/substrate-gateway-runtime/` use that same `substrate` group boundary on Linux and in the macOS Lima guest path: directories remain `0750`, runtime files remain `0640`, and they are not expected to be world-readable. - Access to the socket is granted by membership in the `substrate` group; verify with `id -nG "$USER"`, grant with `sudo usermod -aG substrate `, and re-login so the new group membership takes effect. @@ -42,11 +42,11 @@ These socket ACL details are runtime transport facts, not a second operator cont Always-on by default (unless disabled via `SUBSTRATE_WORLD=disabled`): - Non-PTY commands - - Shell POSTs to `world-agent /v1/execute` over a Unix socket (Linux) or forwarded socket (macOS). The shell auto-starts or ensures availability before sending the request. + - Shell POSTs to `world-service /v1/execute` over a Unix socket (Linux) or forwarded socket (macOS). The shell auto-starts or ensures availability before sending the request. - Response returns `exit`, `stdout_b64`, `stderr_b64`, `scopes_used`, and `fs_diff` (when available). The shell attaches `fs_diff` to the span on both platforms. - PTY commands (interactive/TUIs) - - Shell connects a WebSocket to `world-agent /v1/stream` over the active transport. + - Shell connects a WebSocket to `world-service /v1/stream` over the active transport. - The agent allocates a PTY and spawns the child inside the session world, forwarding I/O, window resize, and signals. - REPL integration @@ -113,7 +113,7 @@ Deliberate boundary for later lanes: ### Native Linux provisioning helper - Run `scripts/linux/world-provision.sh` from the repository root (without `sudo`) to install the - world-agent under `/usr/local/bin`, write the `.service` **and** `.socket` units, + world-service under `/usr/local/bin`, write the `.service` **and** `.socket` units, and enable socket activation. The script uses `sudo` for filesystem and systemd operations and will prompt if elevated credentials are required. - The generated service unit exports `SUBSTRATE_HOME` (default: `/.substrate`, @@ -130,9 +130,9 @@ Deliberate boundary for later lanes: status) so socket activation survives logout or reboot. - After provisioning, verify the listener, units, and capabilities: ```bash - systemctl status substrate-world-agent.socket --no-pager - systemctl status substrate-world-agent.service --no-pager - systemctl show substrate-world-agent.service -p CapabilityBoundingSet -p AmbientCapabilities + systemctl status substrate-world-service.socket --no-pager + systemctl status substrate-world-service.service --no-pager + systemctl show substrate-world-service.service -p CapabilityBoundingSet -p AmbientCapabilities sudo ls -l /run/substrate.sock sudo curl --unix-socket /run/substrate.sock http://localhost/v1/capabilities | jq . substrate host doctor --json | jq '.host.world_socket' @@ -152,12 +152,12 @@ Deliberate boundary for later lanes: "probe_error": null, "systemd_error": null, "systemd_socket": { - "name": "substrate-world-agent.socket", + "name": "substrate-world-service.socket", "active_state": "listening", "unit_file_state": "enabled" }, "systemd_service": { - "name": "substrate-world-agent.service", + "name": "substrate-world-service.service", "active_state": "active", "unit_file_state": "enabled" } @@ -182,15 +182,15 @@ Deliberate boundary for later lanes: ## 3) macOS Architecture (Lima) -Substrate on macOS uses a Lima VM (“substrate”) to host the world-agent. The shell guarantees the VM, agent, and forwarding layer are ready before routing commands. -Hosted installer behavior coverage on macOS flows through this Lima-backed Linux guest/world-agent path; package-manager selection itself remains Linux-only and does not define native macOS package-manager selection. +Substrate on macOS uses a Lima VM (“substrate”) to host the world-service. The shell guarantees the VM, agent, and forwarding layer are ready before routing commands. +Hosted installer behavior coverage on macOS flows through this Lima-backed Linux guest/world-service path; package-manager selection itself remains Linux-only and does not define native macOS package-manager selection. - Provisioning & lifecycle - `scripts/mac/lima-warm.sh` starts or creates the VM from `scripts/mac/lima/substrate.yaml`, installs required packages, and ensures the systemd unit writes to `/run/substrate.sock` and managed gateway runtime artifacts under `/run/substrate/substrate-gateway-runtime/` with the same `substrate`-group boundary inside the guest, exports `SUBSTRATE_HOME=/.substrate`, and keeps that path plus `/tmp` in `ReadWritePaths`. - `scripts/mac/lima-stop.sh` shuts the VM down cleanly; `scripts/mac/lima-doctor.sh` reports health (virtualization, agent socket, service status, forwarding tools). - The helper scripts substitute the active project path so `/src` inside the VM mirrors the host repo checkout. - If full isolation writable allowlists fail with `EPERM` in the guest, confirm the guest service has `cap_chown`: - `limactl shell substrate systemctl show substrate-world-agent.service -p CapabilityBoundingSet -p AmbientCapabilities` + `limactl shell substrate systemctl show substrate-world-service.service -p CapabilityBoundingSet -p AmbientCapabilities` - Transport selection (host ⇄ guest) 1. VSock via `vsock-proxy` (preferred when Virtualization.framework exposes VSock) @@ -202,7 +202,7 @@ Hosted installer behavior coverage on macOS flows through this Lima-backed Linux overriding the transport. - Logs & diagnostics - - Agent logs live in the guest: `substrate sudo journalctl -u substrate-world-agent -n 200` (the CLI shells into Lima automatically) or manually via `limactl shell substrate sudo journalctl -u substrate-world-agent -n 200`. + - Agent logs live in the guest: `substrate sudo journalctl -u substrate-world-service -n 200` (the CLI shells into Lima automatically) or manually via `limactl shell substrate sudo journalctl -u substrate-world-service -n 200`. - Forwarding issues surface in shell `DEBUG` logs with the selected transport. `scripts/mac/lima-doctor.sh` mirrors doctor CLI checks. - Validation @@ -234,7 +234,7 @@ Per session world (identified by `WORLD_ID`, e.g., `wld_01994…`): ## 5) Agent API (over UDS) -This section describes Substrate's local host/world transport API over the `world-agent` socket. It is not the external Unified Agent API (`unified-agent-api` on crates.io; imported in Rust code as `agent_api`) used for CLI-agent runtime abstraction. +This section describes Substrate's local host/world transport API over the `world-service` socket. It is not the external Unified Agent API (`unified-agent-api` on crates.io; imported in Rust code as `agent_api`) used for CLI-agent runtime abstraction. Socket: `/run/substrate.sock` @@ -275,8 +275,8 @@ Notes - PTY shared-world requests must use `start_session`; the agent rejects `shared_world` on legacy `start`. - `profile` is an advanced request field used for backend-specific execution posture. For example, `substrate world enable --provision-deps` sets `profile=world-deps-provision` so provisioning can - run with the expected world-agent behavior. On guest Linux agents, that profile is executed via a - transient `systemd-run` unit so the normal `substrate-world-agent.service` sandbox can stay + run with the expected world-service behavior. On guest Linux agents, that profile is executed via a + transient `systemd-run` unit so the normal `substrate-world-service.service` sandbox can stay hardened. Internal world-deps profiles are reserved for Substrate’s built-in flows and are not selected through `SUBSTRATE_WORLD_REQUEST_PROFILE`. Runtime `substrate world deps current ...` commands do not use the provisioning profile and never perform runtime APT mutation. @@ -302,7 +302,7 @@ Notes - The REPL wraps PTY runs in `reedline::suspend_guard()` to avoid prompt corruption during external output. - Readiness & auto‑spawn - The shell probes `/v1/capabilities`; if stale socket is found, it removes it. - - If the agent isn’t running, the shell attempts to spawn it (Linux dev flow: `target/debug/world-agent`). + - If the agent isn’t running, the shell attempts to spawn it (Linux dev flow: `target/debug/world-service`). - macOS invokes the Lima backend ensure path to boot the VM and wire up its tunnel. - Windows/WSL helper flows are intentionally fail-closed in this slice; see `docs/cross-platform/wsl_world_setup.md`. - Fallback @@ -336,10 +336,10 @@ Legacy `world-deps.yaml` overlay plumbing and `SUBSTRATE_WORLD_DEPS_MANIFEST` ar ## 7) Logging & Telemetry - Policy snapshot behavior and the trace fields `policy_resolution_mode`, `policy_snapshot_schema`, and `policy_snapshot_hash` are documented in `docs/TRACE.md`. -- The archived `docs/project_management/_archived/world-agent-policy-snapshot/policy-snapshot-spec.md` remains historical background only. +- The archived `docs/project_management/_archived/world-service-policy-snapshot/policy-snapshot-spec.md` remains historical background only. - Windows spans include an optional `fs_diff.display_path` map pairing canonical WSL paths with native Windows paths for telemetry consumers. -- world-agent (PTY) +- world-service (PTY) - Logs: client connected; `start cmd=… cwd=… span_id=… cols=… rows=…`; - In‑world details: `world_id=… ns=… cgroup=… in_world=true` on PTY start - Forwarded signals: `ws_pty: forwarded signal to pid ` @@ -356,7 +356,7 @@ Legacy `world-deps.yaml` overlay plumbing and `SUBSTRATE_WORLD_DEPS_MANIFEST` ar - `SUBSTRATE_WORLD_ID` (set by the shell on ensure) - `SUBSTRATE_AGENT_ID` (tracing/attribution; defaults to "human") - PTY/WS - - `SUBSTRATE_WS_DEBUG=1` (prints “using world-agent PTY WS” on connect) + - `SUBSTRATE_WS_DEBUG=1` (prints “using world-service PTY WS” on connect) - `SUBSTRATE_FORCE_PTY` / `SUBSTRATE_DISABLE_PTY` (developer toggles) - GC Configuration - `SUBSTRATE_NETNS_GC_INTERVAL_SECS` (default 600; 0 disables periodic) @@ -367,8 +367,8 @@ Legacy `world-deps.yaml` overlay plumbing and `SUBSTRATE_WORLD_DEPS_MANIFEST` ar ## 9) Validation (Quick Start) - Build & run agent - - `cargo build && cargo build -p world-agent` - - `RUST_LOG=info target/debug/world-agent &` + - `cargo build && cargo build -p world-service` + - `RUST_LOG=info target/debug/world-service &` - `ls -l /run/substrate.sock` - Non‑interactive PTY over WS @@ -393,7 +393,7 @@ Legacy `world-deps.yaml` overlay plumbing and `SUBSTRATE_WORLD_DEPS_MANIFEST` ar - `scripts/mac/lima-doctor.sh` - `PATH="$(pwd)/target/debug:$PATH" scripts/mac/smoke.sh` (non‑PTY, PTY, replay + fs_diff assertion) - `PATH="$(pwd)/target/debug:$PATH" scripts/mac/orchestration-smoke.sh` (Lima warm + live backend reachability + shared-owner/member-runtime orchestration contract regressions) - - `substrate sudo journalctl -u substrate-world-agent -n 200` (or `limactl shell substrate sudo journalctl -u substrate-world-agent -n 200`) to review guest logs + - `substrate sudo journalctl -u substrate-world-service -n 200` (or `limactl shell substrate sudo journalctl -u substrate-world-service -n 200`) to review guest logs --- @@ -401,7 +401,7 @@ Legacy `world-deps.yaml` overlay plumbing and `SUBSTRATE_WORLD_DEPS_MANIFEST` ar - No WS debug line / no agent logs - The shell fell back to host PTY. Ensure `SUBSTRATE_WORLD=enabled` or start the agent (`/run/substrate.sock` must exist) and re‑run. -- “Exec format error” running `world-agent` +- “Exec format error” running `world-service` - You’re trying to run a binary built for a different arch/OS. Build within the container/host you’re running on. - `stty -a` fails in REPL without PTY - Use the PTY path (`:pty …` in REPL or `--pty -c` on CLI). `stty` requires a real TTY. @@ -447,7 +447,7 @@ Implemented features: - Primary path installs nft rules inside the dedicated netns. - If the host refuses `ip netns add`, the code falls back to socket cgroup matching and scopes nft rules to `/sys/fs/cgroup/substrate/` so host processes are not impacted. - When neither netns nor writable cgroups are available, nft scoping is disabled and warnings point to `substrate world cleanup --purge` to remove leftovers before retrying. -- These logs surface in `--replay-verbose` output (`[replay] warn: using socket cgroup fallback...`) and in world-agent traces (`[agent] netns ... unavailable`). +- These logs surface in `--replay-verbose` output (`[replay] warn: using socket cgroup fallback...`) and in world-service traces (`[agent] netns ... unavailable`). - Replay stays agent-first on Linux; when `/run/substrate.sock` is unhealthy it emits a single `[replay] warn: agent replay unavailable (); falling back to local backend. Run `substrate world doctor --json` (or `substrate host doctor --json` for transport-only checks) or set SUBSTRATE_WORLD_SOCKET to point at a healthy agent socket before switching to the local backend/copy-diff. Copy-diff retries log `[replay] warn: copy-diff ...` with the attempted scratch root so you can adjust disk space or set `SUBSTRATE_COPYDIFF_ROOT`. --- diff --git a/docs/WORLD_FS_POLICY_V2_QUICK_REFERENCE.md b/docs/WORLD_FS_POLICY_V2_QUICK_REFERENCE.md index bc76446aa..b4360bdf5 100644 --- a/docs/WORLD_FS_POLICY_V2_QUICK_REFERENCE.md +++ b/docs/WORLD_FS_POLICY_V2_QUICK_REFERENCE.md @@ -5,8 +5,8 @@ This is a practical “what to set + what to try” guide for the newer `world_f ## 0) Preconditions (so tests are meaningful) - The deny/strict behavior is a **Linux** implementation detail: - - Linux host: world-agent runs on the host. - - macOS host: world-agent runs inside a **Lima VM** (Linux in VM). + - Linux host: world-service runs on the host. + - macOS host: world-service runs inside a **Lima VM** (Linux in VM). - Verify world backend health before testing: - `substrate world doctor` - If that fails, run `substrate world enable` (or on macOS: `scripts/mac/lima-warm.sh`). diff --git a/docs/contracts/substrate-gateway-runtime-parity.md b/docs/contracts/substrate-gateway-runtime-parity.md index beea73b67..b5370c50a 100644 --- a/docs/contracts/substrate-gateway-runtime-parity.md +++ b/docs/contracts/substrate-gateway-runtime-parity.md @@ -18,7 +18,7 @@ Concrete rules: - Shell/operator entrypoints consume a typed runtime surface; they must not rebuild gateway state via raw exec probing, backend-private config files, or log scraping. - `crates/shell/src/builtins/world_gateway.rs` and later shell execution wiring are consumers of that typed surface. They may render operator output and exit codes, but they do not own runtime truth. -- The typed runtime surface is owned by the world/backend boundary: `crates/world-agent`, shared request/response models in `crates/agent-api-types`, and transport helpers in `crates/agent-api-client`. +- The typed runtime surface is owned by the world/backend boundary: `crates/world-service`, shared request/response models in `crates/transport-api-types`, and transport helpers in `crates/transport-api-client`. - Non-isolated gateway lifecycle/status flows must not depend on reusable session-world creation or recovery when no world-backed isolation is required for the gateway runtime. In that posture, the typed runtime surface may use a stable synthetic runtime identity derived from the effective lifecycle binding inputs. - Isolated gateway lifecycle/status flows must continue to require a real compatible world/session identity and any required world-backed attachment primitives. - `substrate world gateway status --json` remains governed by `docs/contracts/substrate-gateway-status-schema.md`; this contract may not widen the JSON field list or redefine `client_wiring.*`. @@ -35,7 +35,7 @@ Concrete rules: - On Linux and the macOS Lima guest path, the managed runtime artifact boundary is the `substrate` group: runtime directories remain `0750`, runtime files remain `0640`, and these artifacts must not rely on world-readable permissions. - Platform transport and bootstrap mechanics may differ only in the hidden backend layer: - Linux uses direct Unix socket transport to `/run/substrate.sock`. - - macOS uses Lima-backed forwarding to the guest `world-agent`. + - macOS uses Lima-backed forwarding to the guest `world-service`. - Windows uses the WSL-backed forwarder path, with named-pipe or TCP bridge transport as needed. - Platform-specific provisioning helpers, doctor flows, and socket/forwarder mechanics are verification evidence, not separate operator contracts. - Base URLs discovered through `status --json` or the stable env exports remain intended for in-world reachability, not as a guarantee of direct host reachability. @@ -57,10 +57,10 @@ The later execution slices must keep the runtime/parity contract aligned across - `crates/shell/src/builtins/world_gateway.rs` - `crates/shell/tests/world_gateway.rs` -- `crates/world-agent/src/lib.rs` -- `crates/world-agent/src/gateway_runtime.rs` -- `crates/world-agent/tests/socket_activation.rs` -- `crates/agent-api-types/src/lib.rs` -- `crates/agent-api-client/src/lib.rs` +- `crates/world-service/src/lib.rs` +- `crates/world-service/src/gateway_runtime.rs` +- `crates/world-service/tests/socket_activation.rs` +- `crates/transport-api-types/src/lib.rs` +- `crates/transport-api-client/src/lib.rs` - `docs/project_management/packs/draft/substrate-gateway-boundary-and-runtime-ownership/platform-parity-spec.md` - `docs/WORLD.md` diff --git a/docs/cross-platform/mac_world_setup.md b/docs/cross-platform/mac_world_setup.md index 76f81bb86..83f18b09b 100644 --- a/docs/cross-platform/mac_world_setup.md +++ b/docs/cross-platform/mac_world_setup.md @@ -32,7 +32,7 @@ This guide walks through setting up the Lima-based Linux world backend for Subst command -v envsubst ``` -4. **Ensure Rust toolchain is installed** (for building world-agent): +4. **Ensure Rust toolchain is installed** (for building world-service): ```sh # If not installed: curl https://sh.rustup.rs -sSf | sh @@ -65,7 +65,7 @@ limactl start --tty=false --name substrate /tmp/substrate-dev.yaml This script will: - Create a new Lima VM named "substrate" with Ubuntu 24.04 - Install required packages (nftables, iproute2, dnsmasq, etc.) - - Configure the systemd service for substrate-world-agent + - Configure the systemd service for substrate-world-service - Mount your home directory (read-only) and project directory (read-write) - Configure `/var/lib/substrate`, `/run/substrate`, and `/tmp` as guest writeable paths via `ReadWritePaths` (handled automatically by the provisioning script; no manual edits required) @@ -77,43 +77,43 @@ limactl start --tty=false --name substrate /tmp/substrate-dev.yaml # Should show status: Running ``` -### Step 2: Build and Deploy World Agent +### Step 2: Build and Deploy World Service 1. **Compile inside the Lima guest** (recommended): ```sh # Build from the mounted project directory - limactl shell substrate bash -lc 'cd /src && cargo build -p world-agent --release' + limactl shell substrate bash -lc 'cd /src && cargo build -p world-service --release' ``` 2. **Install the binary inside the VM**: ```sh - limactl shell substrate sudo install -m755 /src/target/release/world-agent /usr/local/bin/substrate-world-agent + limactl shell substrate sudo install -m755 /src/target/release/world-service /usr/local/bin/substrate-world-service ``` > **Alternative:** Cross-compile on the host using a Linux target (e.g. - > `cargo build -p world-agent --release --target aarch64-unknown-linux-gnu`) and + > `cargo build -p world-service --release --target aarch64-unknown-linux-gnu`) and > copy the resulting binary into the guest. Avoid copying the default macOS > build—Mach-O binaries cannot run inside the Linux VM. 3. **Verify the binary works**: ```sh - limactl shell substrate substrate-world-agent --version + limactl shell substrate substrate-world-service --version ``` -### Step 3: Start World Agent Service +### Step 3: Start World Service 1. **Enable and start the systemd service**: ```sh limactl shell substrate sudo systemctl daemon-reload - limactl shell substrate sudo systemctl enable substrate-world-agent.service - limactl shell substrate sudo systemctl enable --now substrate-world-agent.socket - limactl shell substrate sudo systemctl restart substrate-world-agent.service + limactl shell substrate sudo systemctl enable substrate-world-service.service + limactl shell substrate sudo systemctl enable --now substrate-world-service.socket + limactl shell substrate sudo systemctl restart substrate-world-service.service ``` 2. **Verify service is running**: ```sh - limactl shell substrate systemctl status substrate-world-agent.socket - limactl shell substrate systemctl status substrate-world-agent.service + limactl shell substrate systemctl status substrate-world-service.socket + limactl shell substrate systemctl status substrate-world-service.service ``` 3. **Test the agent API**: @@ -145,7 +145,7 @@ All critical checks should show `[PASS]`. If any show `[FAIL]`, see the troubles 2. **Check agent logs** for any issues: ```sh - limactl shell substrate journalctl -u substrate-world-agent -n 50 + limactl shell substrate journalctl -u substrate-world-service -n 50 ``` ### Substrate CLI Smoke Script @@ -194,7 +194,7 @@ target/debug/substrate world doctor target/debug/substrate world doctor --json | jq . ``` -`substrate host doctor` is host-scoped (limactl + virtualization + VM/service reachability). `substrate world doctor` includes the host report plus world-agent-reported “in-world” facts (guest-kernel Landlock support/ABI + world fs strategy probe) via `/v1/doctor/world`. The legacy `scripts/mac/lima-doctor.sh` script remains available for deeper troubleshooting but the CLI commands are the canonical entry points. +`substrate host doctor` is host-scoped (limactl + virtualization + VM/service reachability). `substrate world doctor` includes the host report plus world-service-reported “in-world” facts (guest-kernel Landlock support/ABI + world fs strategy probe) via `/v1/doctor/world`. The legacy `scripts/mac/lima-doctor.sh` script remains available for deeper troubleshooting but the CLI commands are the canonical entry points. ## Helper Scripts @@ -211,19 +211,19 @@ target/debug/substrate world doctor --json | jq . | Virtualization not available | `sysctl kern.hv_support` returns 0 | Enable virtualization in System Settings → Privacy & Security → Developer Tools | | Lima VM fails to start | Check `limactl start substrate` output | Ensure sufficient disk space; check `~/Library/Logs/lima/` for detailed logs | | SSH connection fails | `limactl shell substrate` fails | Run `limactl shell substrate` once to accept host key | -| Agent not responding | `substrate host doctor` shows agent unreachable | Check systemd: `limactl shell substrate systemctl status substrate-world-agent.socket` and `.service`, then probe directly in-guest: `limactl shell substrate sudo -n curl --fail --unix-socket /run/substrate.sock http://localhost/v1/doctor/world | jq .` | +| Agent not responding | `substrate host doctor` shows agent unreachable | Check systemd: `limactl shell substrate systemctl status substrate-world-service.socket` and `.service`, then probe directly in-guest: `limactl shell substrate sudo -n curl --fail --unix-socket /run/substrate.sock http://localhost/v1/doctor/world | jq .` | | Agent binary missing | Service fails to start | Rebuild and copy binary as shown in Step 2 | | Permission errors | Socket operations fail | Ensure directories exist with correct permissions: `/run/substrate` (0750) | | DNS resolution issues | Network operations fail in VM | Check dnsmasq: `limactl shell substrate systemctl status dnsmasq` | | `sudo: unable to resolve host lima-substrate` | Sudo emits warning due to missing host mapping | `limactl shell substrate sudo bash -lc "grep -q 'lima-substrate' /etc/hosts || echo '127.0.1.1 lima-substrate' >> /etc/hosts"` | -| `Exec format error` starting agent | Copied host-compiled binary into guest | Build inside VM: `limactl shell substrate` → `cargo build -p world-agent --release` → copy to `/usr/local/bin/substrate-world-agent` | +| `Exec format error` starting agent | Copied host-compiled binary into guest | Build inside VM: `limactl shell substrate` → `cargo build -p world-service --release` → copy to `/usr/local/bin/substrate-world-service` | | SSH UDS not creating local socket | SSH ControlMaster multiplexing interferes | Disable ControlMaster: add `-o ControlMaster=no -o ControlPath=none` | | TCP forwarding resets | SSH cannot forward TCP→UDS directly | Use SSH UDS; TCP fallback requires a guest TCP↔UDS bridge (e.g., `socat`) | ### Viewing Logs - **VM provisioning logs**: `limactl start substrate --debug` -- **Agent service logs**: `limactl shell substrate journalctl -u substrate-world-agent -f` +- **Agent service logs**: `limactl shell substrate journalctl -u substrate-world-service -f` - **System logs**: `limactl shell substrate journalctl -n 100` ### Resetting the Environment @@ -249,7 +249,7 @@ The shell manages transport detection automatically. The only knobs you should n - `SUBSTRATE_WORLD=disabled`: Temporarily bypass the world (defaults to `enabled`). - `SUBSTRATE_WORLD_ID`: Set by the shell; useful for correlating spans while debugging. - `SUBSTRATE_WORLD_NETFILTER_ENABLE=1`: Host-side input for `scripts/mac/lima-warm.sh`; writes - `WORLD_NETFILTER_ENABLE=1` into the guest `substrate-world-agent.service` unit so requested + `WORLD_NETFILTER_ENABLE=1` into the guest `substrate-world-service.service` unit so requested netfilter enforcement can be honored. For a quick guest-env check without reprovisioning, run: @@ -263,7 +263,7 @@ The output should say whether the guest systemd service currently includes ## Next Steps -Once the Lima VM and world-agent are running: +Once the Lima VM and world-service are running: 1. The Substrate shell will automatically detect and use the Lima backend on macOS 2. All commands will execute inside the isolated Linux world diff --git a/docs/cross-platform/release_notes_macos_always_world.md b/docs/cross-platform/release_notes_macos_always_world.md index a04d13e5a..abed0c6c8 100644 --- a/docs/cross-platform/release_notes_macos_always_world.md +++ b/docs/cross-platform/release_notes_macos_always_world.md @@ -10,19 +10,19 @@ - macOS 13.0+ with Virtualization.framework enabled (`sysctl kern.hv_support` → `1`). - Homebrew packages: `lima jq openssh coreutils gnused gnu-tar gettext` (ensure `envsubst` and `vsock-proxy` are on `PATH`). -- Rust toolchain installed for building `substrate-world-agent`. +- Rust toolchain installed for building `substrate-world-service`. ## Setup Flow 1. From the repo root run `scripts/mac/lima-warm.sh` to create or start the `substrate` Lima VM. The helper script applies the default profile with writable mounts (`/src`, `/tmp`), installs required packages, and deploys the systemd unit. -2. Build the agent on the host (`cargo build -p world-agent --release`) and copy it into the guest (`limactl copy ...`, `sudo mv /usr/local/bin/substrate-world-agent`). -3. Enable the service: `limactl shell substrate sudo systemctl enable --now substrate-world-agent`. +2. Build the agent on the host (`cargo build -p world-service --release`) and copy it into the guest (`limactl copy ...`, `sudo mv /usr/local/bin/substrate-world-service`). +3. Enable the service: `limactl shell substrate sudo systemctl enable --now substrate-world-service`. 4. Validate with `scripts/mac/lima-doctor.sh`; all critical checks must return `[PASS]`. ## Smoke & Diagnostics - End-to-end verification: `PATH="$(pwd)/target/debug:$PATH" scripts/mac/smoke.sh` (expects replay `fs_diff` to include `world-mac-smoke/file.txt`). -- Guest logs: `substrate sudo journalctl -u substrate-world-agent -n 200` (the CLI shells into Lima automatically); manual fallback `limactl shell substrate sudo journalctl -u substrate-world-agent -n 200`. +- Guest logs: `substrate sudo journalctl -u substrate-world-service -n 200` (the CLI shells into Lima automatically); manual fallback `limactl shell substrate sudo journalctl -u substrate-world-service -n 200`. - Transport order and fallback: VSock → SSH UDS (`~/.substrate/sock/agent.sock`) → SSH TCP. The shell logs the chosen transport; on failure it emits a single warning and runs the command on the host. ## Environment & Compatibility diff --git a/docs/cross-platform/transport_parity_design.md b/docs/cross-platform/transport_parity_design.md index 4a44f2813..88a9d7267 100644 --- a/docs/cross-platform/transport_parity_design.md +++ b/docs/cross-platform/transport_parity_design.md @@ -10,10 +10,10 @@ Related Spike: docs/SPIKE_TRANSPORT_PARITY_PLAN.md This document captures the transport architecture needed for cross-platform parity. - Host binaries (`substrate-shell`, `host-proxy`, tooling) - communicate only through the `agent-api-client` transport abstraction. + communicate only through the `transport-api-client` transport abstraction. - The Windows forwarder mediates between host Named Pipes and the world agent running in WSL. -- `world-agent` exposes both its canonical Unix domain socket and a +- `world-service` exposes both its canonical Unix domain socket and a gated loopback TCP listener. - Telemetry records the active transport in every span to aid troubleshooting and parity verification. @@ -25,7 +25,7 @@ This document captures the transport architecture needed for cross-platform pari ```text +-------------------+ +-----------------------+ -| Host CLI (all OS) |--HTTP----->| agent-api-client | +| Host CLI (all OS) |--HTTP----->| transport-api-client | | substrate, proxy | | - Connector trait | +-------------------+ | - Endpoint enum | +-----------+-----------+ @@ -44,7 +44,7 @@ This document captures the transport architecture needed for cross-platform pari | | v v +-------------+ +-------------------+ -| world-agent | | world-agent | +| world-service | | world-service | | Unix socket | | Loopback TCP | | /run/... | | 127.0.0.1: | +-------------+ +-------------------+ @@ -93,7 +93,7 @@ This document captures the transport architecture needed for cross-platform pari - Forwarder → agent (inside WSL) defaults to loopback TCP `127.0.0.1:61337` (enabled by systemd unit via `SUBSTRATE_AGENT_TCP_PORT`). -## World Agent Dual Listener +## World Service Dual Listener - Unix socket remains `/run/substrate.sock` with permissions 0666 inside the world. diff --git a/docs/cross-platform/wsl_world_troubleshooting.md b/docs/cross-platform/wsl_world_troubleshooting.md index 270e201aa..54d5ae227 100644 --- a/docs/cross-platform/wsl_world_troubleshooting.md +++ b/docs/cross-platform/wsl_world_troubleshooting.md @@ -160,27 +160,27 @@ Get-Content "$env:LOCALAPPDATA\Substrate\logs\forwarder.log" -Tail 20 ### T-006 Agent service not running - **Symptom**: Doctor shows FAIL for `Agent Socket` or `Agent Capabilities`. -- **Likely cause**: `substrate-world-agent` service crashed or the binary is +- **Likely cause**: `substrate-world-service` service crashed or the binary is outdated. - **Remediation**: 1. Inspect logs inside WSL: ```powershell - wsl -d substrate-wsl -- bash -lc 'journalctl -u substrate-world-agent -n 200' + wsl -d substrate-wsl -- bash -lc 'journalctl -u substrate-world-service -n 200' ``` - 1. Rebuild `world-agent` on Windows and rerun warm if the binary changed. + 1. Rebuild `world-service` on Windows and rerun warm if the binary changed. 1. Restart the service inside WSL: ```powershell - wsl -d substrate-wsl -- bash -lc 'sudo systemctl restart substrate-world-agent' + wsl -d substrate-wsl -- bash -lc 'sudo systemctl restart substrate-world-service' ``` - **Verify**: ```powershell -wsl -d substrate-wsl -- bash -lc 'systemctl is-active substrate-world-agent' +wsl -d substrate-wsl -- bash -lc 'systemctl is-active substrate-world-service' ``` ### T-007 Doctor nftables failure @@ -217,7 +217,7 @@ wsl -d substrate-wsl -- bash -lc 'nft list tables' 1. Capture PTY logs inside WSL: ```powershell - wsl -d substrate-wsl -- bash -lc 'journalctl -u substrate-world-agent -n 200' | + wsl -d substrate-wsl -- bash -lc 'journalctl -u substrate-world-service -n 200' | Select-String -Pattern pty ``` diff --git a/docs/internals/config/README.md b/docs/internals/config/README.md index 8147a09cb..ade54ad28 100644 --- a/docs/internals/config/README.md +++ b/docs/internals/config/README.md @@ -7,5 +7,5 @@ This section complements the operator-facing config contract in `docs/reference/ ## Documents - `docs/internals/config/world_root_and_caging.md`: how `world.anchor_mode`, `world.anchor_path`, and `world.caged` flow - through the shell → world-agent → world backend (including persistent-session behavior). + through the shell → world-service → world backend (including persistent-session behavior). diff --git a/docs/internals/config/world_root_and_caging.md b/docs/internals/config/world_root_and_caging.md index b16dd3eb7..55870c5dc 100644 --- a/docs/internals/config/world_root_and_caging.md +++ b/docs/internals/config/world_root_and_caging.md @@ -75,9 +75,9 @@ In the async REPL, the shell also updates these env vars when starting/restartin session env. - The REPL restarts a persistent world session when the policy snapshot or workspace root changes (the “drift restart”). -The world-agent uses `SUBSTRATE_ANCHOR_MODE/PATH` + the request cwd to compute `project_dir`: +The world-service uses `SUBSTRATE_ANCHOR_MODE/PATH` + the request cwd to compute `project_dir`: -- `crates/world-agent/src/service.rs` (`resolve_project_dir`) +- `crates/world-service/src/service.rs` (`resolve_project_dir`) - `workspace`: `project_dir = SUBSTRATE_ANCHOR_PATH || cwd` - `follow-cwd`: `project_dir = cwd` - `custom`: `project_dir = SUBSTRATE_ANCHOR_PATH` (required) @@ -139,7 +139,7 @@ If the workspace root is `/work` and you are currently in `/work/testdir`: - `touch ./file` in `/work/testdir` is denied - `touch ./testdir/file` is allowed -This is observable in the computed Landlock env vars that world-agent passes into the world backend (Linux): +This is observable in the computed Landlock env vars that world-service passes into the world backend (Linux): - `SUBSTRATE_WORLD_FS_WRITE_ALLOWLIST`: write prefixes (root-relative) - `SUBSTRATE_WORLD_FS_LANDLOCK_WRITE_ALLOWLIST`: absolute paths that Landlock should allow writing to diff --git a/docs/internals/env/generation.md b/docs/internals/env/generation.md index 8b74efef1..3ae55fec2 100644 --- a/docs/internals/env/generation.md +++ b/docs/internals/env/generation.md @@ -72,6 +72,6 @@ When a variable is platform-specific, keep it in the catalog with its OS-specifi - Windows: `scripts/windows/*.ps1`, `crates/world-windows-wsl/**` - macOS: `scripts/mac/*.sh`, `crates/world-mac-lima/**` -- Linux: `scripts/linux/*.sh`, `crates/world/**`, `crates/world-agent/**` +- Linux: `scripts/linux/*.sh`, `crates/world/**`, `crates/world-service/**` The catalog’s evidence columns must include at least one real platform-specific usage site for these variables. diff --git a/docs/internals/env/inventory.md b/docs/internals/env/inventory.md index 6e8435906..d298e2795 100644 --- a/docs/internals/env/inventory.md +++ b/docs/internals/env/inventory.md @@ -109,10 +109,10 @@ Column notes: | `SHIM_TRACE_LOG_MAX_MB` | `SHIM` | internal/shim | read | trace | — | crates/trace/src/output.rs:31 | string | internal | no | yes (trace component) | | `SHIM_VERSION` | `SHIM` | internal/shim | both | shim | crates/shim/build.rs:29 | crates/shim/src/lib.rs:202 | string | internal | no | no direct site | | `SUBSTRATE_AGENT_ID` | `SUBSTRATE` | override-only / internal | read | replay, shell, telemetry-lib, trace, world-backend | — | crates/world-windows-wsl/src/backend.rs:71; crates/trace/src/span.rs:143 | string | internal | no | yes (trace component) | -| `SUBSTRATE_AGENT_TCP_PORT` | `SUBSTRATE` | state (exported) | both | scripts, world-agent | crates/world-agent/src/lib.rs:547; crates/world-agent/src/lib.rs:561 | crates/world-agent/src/lib.rs:424 | string | internal | no | no direct site | +| `SUBSTRATE_AGENT_TCP_PORT` | `SUBSTRATE` | state (exported) | both | scripts, world-service | crates/world-service/src/lib.rs:547; crates/world-service/src/lib.rs:561 | crates/world-service/src/lib.rs:424 | string | internal | no | no direct site | | `SUBSTRATE_AGENT_TRANSPORT` | `SUBSTRATE` | override-only / internal | read | host-proxy | — | crates/host-proxy/src/runtime.rs:333 | string | internal | no | no direct site | -| `SUBSTRATE_ANCHOR_MODE` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, trace, world-agent, world-backend | crates/shell/src/execution/env_scripts.rs:58; crates/replay/src/state.rs:138 | crates/trace/src/context.rs:169; crates/world-agent/src/service.rs:511 | string | internal | no | yes (trace component) | -| `SUBSTRATE_ANCHOR_PATH` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, trace, world-agent | crates/shell/src/execution/env_scripts.rs:62; crates/replay/src/state.rs:140 | crates/trace/src/context.rs:170; crates/world-agent/src/service.rs:516 | string | internal | no | yes (trace component) | +| `SUBSTRATE_ANCHOR_MODE` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, trace, world-service, world-backend | crates/shell/src/execution/env_scripts.rs:58; crates/replay/src/state.rs:138 | crates/trace/src/context.rs:169; crates/world-service/src/service.rs:511 | string | internal | no | yes (trace component) | +| `SUBSTRATE_ANCHOR_PATH` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, trace, world-service | crates/shell/src/execution/env_scripts.rs:62; crates/replay/src/state.rs:140 | crates/trace/src/context.rs:170; crates/world-service/src/service.rs:516 | string | internal | no | yes (trace component) | | `SUBSTRATE_BASHENV_ACTIVE` | `SUBSTRATE` | state (exported) | both | shell | crates/shell/src/builtins/shim_doctor/repair.rs:21 | crates/shell/src/builtins/shim_doctor/repair.rs:18 | string | internal | no | no direct site | | `SUBSTRATE_BIN` | `SUBSTRATE` | override-only / internal | both | scripts | scripts/validate_phase_d.sh:20 | scripts/mac/smoke.sh:11 | string | script-only | no | no direct site | | `SUBSTRATE_CAGED` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, trace, world-backend | crates/shell/src/execution/settings/runtime.rs:15; crates/shell/src/execution/env_scripts.rs:56 | crates/trace/src/context.rs:174; crates/world/src/guard.rs:13 | string | internal | no | yes (trace component) | @@ -138,8 +138,8 @@ Column notes: | `SUBSTRATE_FORWARDER_TCP_HOST` | `SUBSTRATE` | override-only / internal | read | world-backend | — | crates/world-windows-wsl/src/transport.rs:25 | string | internal | no | no direct site | | `SUBSTRATE_FORWARDER_TCP_PORT` | `SUBSTRATE` | override-only / internal | read | scripts, world-backend | — | crates/world-windows-wsl/src/transport.rs:27; scripts/windows/wsl-warm.ps1:217 | int | internal | no | no direct site | | `SUBSTRATE_HOME` | `SUBSTRATE` | state (exported) | both | broker, common, scripts, shell | crates/shell/src/builtins/world_enable/runner.rs:30; crates/shell/src/builtins/world_enable/runner/manager_env.rs:35 | crates/common/src/paths.rs:9; crates/common/src/paths.rs:25 | string | internal | no | no direct site | -| `SUBSTRATE_INNER_CMD` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world/src/exec.rs:269; crates/world-agent/src/internal_exec.rs:97 | string | internal | no | no direct site | -| `SUBSTRATE_INNER_LOGIN_SHELL` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world/src/exec.rs:212; crates/world-agent/src/internal_exec.rs:98 | string | internal | no | no direct site | +| `SUBSTRATE_INNER_CMD` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world/src/exec.rs:269; crates/world-service/src/internal_exec.rs:97 | string | internal | no | no direct site | +| `SUBSTRATE_INNER_LOGIN_SHELL` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world/src/exec.rs:212; crates/world-service/src/internal_exec.rs:98 | string | internal | no | no direct site | | `SUBSTRATE_INSTALLER_EXPECT_SOCKET` | `SUBSTRATE` | override-only / internal | read | tests | — | tests/installers/install_smoke.sh:117 | string | test-only | no | no direct site | | `SUBSTRATE_INSTALL_ARCHIVE` | `SUBSTRATE` | override-only / internal | read | scripts | — | scripts/substrate/install-substrate.sh:23 | string | script-only | no | no direct site | | `SUBSTRATE_INSTALL_ARTIFACT_DIR` | `SUBSTRATE` | override-only / internal | read | scripts | — | scripts/substrate/install-substrate.sh:23 | string | script-only | no | no direct site | @@ -151,7 +151,7 @@ Column notes: | `SUBSTRATE_INSTALL_REF` | `SUBSTRATE` | override-only / internal | read | scripts | — | scripts/substrate/install.sh:19; scripts/substrate/uninstall.sh:89 | string | script-only | no | no direct site | | `SUBSTRATE_INSTALL_WRAPPER_BASE_URL` | `SUBSTRATE` | override-only / internal | read | scripts | — | scripts/substrate/install.sh:55 | string | script-only | no | no direct site | | `SUBSTRATE_LANDLOCK_HELPER_PATH` | `SUBSTRATE` | state (exported) | both | world-backend | crates/world/src/exec.rs:184; crates/world/src/exec.rs:200 | crates/world/src/exec.rs:209 | string | internal | no | no direct site | -| `SUBSTRATE_LANDLOCK_HELPER_SRC` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world/src/exec.rs:180 | string | internal | no | no direct site | +| `SUBSTRATE_LANDLOCK_HELPER_SRC` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world/src/exec.rs:180 | string | internal | no | no direct site | | `SUBSTRATE_LIMA_SKIP_GUEST_BUILD` | `SUBSTRATE` | override-only / internal | read | scripts | — | scripts/mac/lima-warm.sh:12 | string | script-only | no | no direct site | | `SUBSTRATE_M5B_MANAGER_INIT_MARKER` | `SUBSTRATE` | override-only / internal | both | shell | crates/shell/tests/world_deps.rs:584 | crates/shell/tests/world_deps.rs:67; crates/shell/tests/world_deps.rs:68 | string | test-only | no | no direct site | | `SUBSTRATE_MANAGER_ENV` | `SUBSTRATE` | state (exported) | both | shell | crates/shell/src/execution/routing.rs:277; crates/shell/src/execution/routing.rs:279 | crates/shell/src/execution/routing/builtin/tests.rs:130 | string | internal | no | no direct site | @@ -160,11 +160,11 @@ Column notes: | `SUBSTRATE_MANAGER_INIT_DEBUG` | `SUBSTRATE` | state (exported) | both | shell | crates/shell/src/execution/manager_init/tests.rs:115; crates/shell/src/execution/manager_init/tests.rs:132 | crates/shell/src/execution/manager_init/config.rs:32; crates/shell/src/execution/manager_init/tests.rs:111 | string | internal | no | no direct site | | `SUBSTRATE_MANAGER_INIT_SHELL` | `SUBSTRATE` | override-only / internal | read | shell | — | crates/shell/src/execution/manager_init/runtime.rs:255 | string | internal | no | no direct site | | `SUBSTRATE_MANAGER_MANIFEST` | `SUBSTRATE` | override-only / internal | both | shell, shim | crates/shell/tests/shell_env.rs:80; crates/shell/tests/fail_closed_semantics.rs:72 | crates/shell/src/execution/manager.rs:79; crates/shim/src/exec/logging.rs:194 | string | internal | no | no direct site | -| `SUBSTRATE_MOUNT_CWD` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world-agent/src/internal_exec.rs:94 | string | internal | no | no direct site | -| `SUBSTRATE_MOUNT_FS_MODE` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world/src/exec.rs:130; crates/world/src/exec.rs:153 | enum (string) | internal | no | no direct site | -| `SUBSTRATE_MOUNT_PROJECT_DIR` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world-agent/src/internal_exec.rs:49; crates/world-agent/src/internal_exec.rs:75 | string | internal | no | no direct site | -| `SUBSTRATE_NETNS_GC_INTERVAL_SECS` | `SUBSTRATE` | override-only / internal | read | world-agent | — | crates/world-agent/src/lib.rs:118 | u64 (seconds) | internal | no | no direct site | -| `SUBSTRATE_NETNS_GC_TTL_SECS` | `SUBSTRATE` | override-only / internal | read | world-agent | — | crates/world-agent/src/handlers.rs:119; crates/world-agent/src/lib.rs:95 | u64 (seconds) | internal | no | no direct site | +| `SUBSTRATE_MOUNT_CWD` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world-service/src/internal_exec.rs:94 | string | internal | no | no direct site | +| `SUBSTRATE_MOUNT_FS_MODE` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world/src/exec.rs:130; crates/world/src/exec.rs:153 | enum (string) | internal | no | no direct site | +| `SUBSTRATE_MOUNT_PROJECT_DIR` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world-service/src/internal_exec.rs:49; crates/world-service/src/internal_exec.rs:75 | string | internal | no | no direct site | +| `SUBSTRATE_NETNS_GC_INTERVAL_SECS` | `SUBSTRATE` | override-only / internal | read | world-service | — | crates/world-service/src/lib.rs:118 | u64 (seconds) | internal | no | no direct site | +| `SUBSTRATE_NETNS_GC_TTL_SECS` | `SUBSTRATE` | override-only / internal | read | world-service | — | crates/world-service/src/handlers.rs:119; crates/world-service/src/lib.rs:95 | u64 (seconds) | internal | no | no direct site | | `SUBSTRATE_NO_SHIMS` | `SUBSTRATE` | state (exported) | both | shell | crates/shell/src/execution/invocation/tests.rs:54; crates/shell/tests/shim_deployment.rs:120 | crates/shell/src/execution/shim_deploy.rs:51; crates/shell/src/execution/invocation/tests.rs:50 | string | internal | no | no direct site | | `SUBSTRATE_ORIGINAL_BASH_ENV` | `SUBSTRATE` | override-only / internal | both | scripts, shell | crates/shell/src/builtins/world_deps/guest.rs:270; crates/shell/src/builtins/world_deps/guest.rs:272 | crates/shell/src/execution/manager.rs:181; scripts/substrate/install-substrate.sh:882 | string | internal | no | no direct site | | `SUBSTRATE_OVERRIDE_ANCHOR_MODE` | `SUBSTRATE_OVERRIDE` | override input | both | replay, scripts, shell, tests | scripts/substrate/install-substrate.sh:925; crates/shell/tests/config_show.rs:192 | crates/shell/src/execution/config_model.rs:299; crates/replay/src/state.rs:167 | enum (string) | internal | no | no direct site | @@ -241,13 +241,13 @@ Column notes: | `SUBSTRATE_WORLD_ENABLED` | `SUBSTRATE` | state (exported) | both | replay, scripts, shell, shim | crates/shell/src/builtins/world_deps/state.rs:194; crates/shell/src/execution/invocation/tests.rs:53 | crates/shell/src/builtins/world_deps/state.rs:18; crates/shell/src/builtins/world_deps/state.rs:192 | string | internal | no | no direct site | | `SUBSTRATE_WORLD_ENABLE_SCRIPT` | `SUBSTRATE` | override-only / internal | both | shell | crates/shell/tests/world_enable.rs:131 | crates/shell/src/builtins/world_enable/runner.rs:58 | string | internal | no | no direct site | | `SUBSTRATE_WORLD_ENABLE_SKIP_DOCTOR` | `SUBSTRATE` | override-only / internal | both | shell | crates/shell/tests/world_enable.rs:141 | crates/shell/src/builtins/world_enable/runner/verify.rs:61 | string | internal | no | no direct site | -| `SUBSTRATE_WORLD_FS_ISOLATION` | `SUBSTRATE` | state (exported) | both | shell, world-agent, world-backend | crates/shell/src/execution/routing/dispatch/exec.rs:127; crates/shell/src/execution/platform/mod.rs:80 | crates/world/src/exec.rs:76; crates/world-agent/src/service.rs:538 | enum (string) | internal | no | no direct site | -| `SUBSTRATE_WORLD_FS_LANDLOCK_READ_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-agent | — | crates/world-agent/src/internal_exec.rs:17 | string | internal | no | no direct site | -| `SUBSTRATE_WORLD_FS_LANDLOCK_WRITE_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-agent | — | crates/world-agent/src/internal_exec.rs:18 | string | internal | no | no direct site | -| `SUBSTRATE_WORLD_FS_MODE` | `SUBSTRATE` | state (exported) | both | replay, shell, trace, world-agent, world-backend | crates/shell/src/execution/routing/dispatch/exec.rs:126; crates/shell/src/execution/platform/mod.rs:79 | crates/world-windows-wsl/src/backend.rs:228; crates/trace/src/context.rs:173 | string | internal | no | yes (trace component) | -| `SUBSTRATE_WORLD_FS_WRITE_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-agent, world-backend | — | crates/world/src/exec.rs:130; crates/world/src/exec.rs:153 | string | internal | no | no direct site | +| `SUBSTRATE_WORLD_FS_ISOLATION` | `SUBSTRATE` | state (exported) | both | shell, world-service, world-backend | crates/shell/src/execution/routing/dispatch/exec.rs:127; crates/shell/src/execution/platform/mod.rs:80 | crates/world/src/exec.rs:76; crates/world-service/src/service.rs:538 | enum (string) | internal | no | no direct site | +| `SUBSTRATE_WORLD_FS_LANDLOCK_READ_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-service | — | crates/world-service/src/internal_exec.rs:17 | string | internal | no | no direct site | +| `SUBSTRATE_WORLD_FS_LANDLOCK_WRITE_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-service | — | crates/world-service/src/internal_exec.rs:18 | string | internal | no | no direct site | +| `SUBSTRATE_WORLD_FS_MODE` | `SUBSTRATE` | state (exported) | both | replay, shell, trace, world-service, world-backend | crates/shell/src/execution/routing/dispatch/exec.rs:126; crates/shell/src/execution/platform/mod.rs:79 | crates/world-windows-wsl/src/backend.rs:228; crates/trace/src/context.rs:173 | string | internal | no | yes (trace component) | +| `SUBSTRATE_WORLD_FS_WRITE_ALLOWLIST` | `SUBSTRATE` | override-only / internal | read | world-service, world-backend | — | crates/world/src/exec.rs:130; crates/world/src/exec.rs:153 | string | internal | no | no direct site | | `SUBSTRATE_WORLD_ID` | `SUBSTRATE` | state (exported) | both | shell, shim, telemetry-lib, trace | crates/shell/src/execution/platform_world/windows.rs:80; crates/shell/src/execution/platform_world/windows.rs:211 | crates/trace/src/span.rs:162; crates/trace/src/span.rs:280 | string | internal | no | yes (trace component) | -| `SUBSTRATE_WORLD_REQUIRE_WORLD` | `SUBSTRATE` | override-only / internal | read | shell, world-agent | — | crates/world-agent/tests/full_isolation_pty.rs:37; crates/world-agent/tests/full_isolation_nonpty.rs:72 | string | internal | no | no direct site | +| `SUBSTRATE_WORLD_REQUIRE_WORLD` | `SUBSTRATE` | override-only / internal | read | shell, world-service | — | crates/world-service/tests/full_isolation_pty.rs:37; crates/world-service/tests/full_isolation_nonpty.rs:72 | string | internal | no | no direct site | | `SUBSTRATE_WORLD_ROOT_MODE` | `SUBSTRATE` | override-only / internal | both | replay, shell, trace | crates/shell/tests/replay_world.rs:1057; crates/shell/tests/replay_world.rs:1188 | crates/trace/src/context.rs:171 | string | internal | no | yes (trace component) | | `SUBSTRATE_WORLD_ROOT_PATH` | `SUBSTRATE` | override-only / internal | both | replay, shell, trace | crates/shell/tests/replay_world.rs:1058; crates/shell/tests/replay_world.rs:1189 | crates/trace/src/context.rs:172 | string | internal | no | yes (trace component) | | `SUBSTRATE_WORLD_SOCKET` | `SUBSTRATE` | override-only / internal | both | replay, scripts, shell | crates/shell/src/builtins/world_enable/runner/helper_script.rs:44; crates/shell/tests/world_enable.rs:132 | crates/shell/src/builtins/world_enable/runner/paths.rs:74; crates/shell/src/execution/socket_activation.rs:149 | string | internal | no | no direct site | @@ -317,7 +317,7 @@ Column notes: | `GOENV_MARKER` | `standard` | standard | write | shell | crates/shell/tests/manager_init.rs:66 | — | string | test-only | no | no direct site | | `GOENV_ROOT` | `standard` | standard | both | common, other | crates/common/src/manager_manifest/tests.rs:454; config/manager_hooks.yaml:386 | crates/common/src/manager_manifest/tests.rs:581; config/manager_hooks.yaml:386 | string | test-only | no | no direct site | | `HEALTHY_MARKER` | `standard` | standard | write | shell | crates/shell/tests/shim_health.rs:21; crates/shell/tests/shim_health.rs:23 | — | string | test-only | no | no direct site | -| `HOME` | `standard` | standard | both | CI, host-proxy, other, scripts, shell, telemetry-lib, tests, world-agent, world-backend | crates/shell/tests/world_enable.rs:129; crates/shell/tests/world_enable.rs:290 | crates/world/src/exec.rs:192; crates/world/src/exec.rs:202 | string | external | no | yes (telemetry correlation) | +| `HOME` | `standard` | standard | both | CI, host-proxy, other, scripts, shell, telemetry-lib, tests, world-service, world-backend | crates/shell/tests/world_enable.rs:129; crates/shell/tests/world_enable.rs:290 | crates/world/src/exec.rs:192; crates/world/src/exec.rs:202 | string | external | no | yes (telemetry correlation) | | `HOSTNAME` | `standard` | standard | read | replay, trace | — | crates/trace/src/context.rs:162; crates/replay/src/state.rs:132 | string | external | no | yes (trace component) | | `HOST_PROXY_SOCKET` | `standard` | standard | read | host-proxy | — | crates/host-proxy/src/runtime.rs:296 | string | external | no | no direct site | | `HOST_STATE_PATH` | `standard` | standard | read | scripts | — | scripts/substrate/install-substrate.sh:226 | string | script-only | no | no direct site | @@ -334,10 +334,10 @@ Column notes: | `LIMA_PROFILE_PATH` | `standard` | standard | read | scripts | — | scripts/mac/lima-warm.sh:5 | string | script-only | no | no direct site | | `LIMA_VM_NAME` | `standard` | standard | read | scripts, tests | — | scripts/mac/lima-warm.sh:4; tests/mac/installer_parity_fixture.sh:336 | string | script-only | no | no direct site | | `LINES` | `standard` | standard | both | shell | crates/shell/src/execution/pty/io/runner.rs:119; crates/shell/src/execution/pty/io/types.rs:295 | crates/shell/src/execution/pty/io/types.rs:236; crates/shell/src/execution/pty/io/types.rs:286 | string | external | no | no direct site | -| `LISTEN_FDNAMES` | `standard` | standard | both | world-agent | crates/world-agent/src/socket_activation.rs:155 | crates/world-agent/src/socket_activation.rs:117 | string | external | no | no direct site | -| `LISTEN_FDS` | `standard` | standard | both | world-agent | crates/world-agent/src/socket_activation.rs:153 | crates/world-agent/src/socket_activation.rs:49 | int | external | no | no direct site | -| `LISTEN_FD_START` | `standard` | standard | both | world-agent | crates/world-agent/src/socket_activation.rs:156 | crates/world-agent/src/socket_activation.rs:89 | string | external | no | no direct site | -| `LISTEN_PID` | `standard` | standard | both | world-agent | crates/world-agent/src/socket_activation.rs:154 | crates/world-agent/src/socket_activation.rs:65 | int | external | no | no direct site | +| `LISTEN_FDNAMES` | `standard` | standard | both | world-service | crates/world-service/src/socket_activation.rs:155 | crates/world-service/src/socket_activation.rs:117 | string | external | no | no direct site | +| `LISTEN_FDS` | `standard` | standard | both | world-service | crates/world-service/src/socket_activation.rs:153 | crates/world-service/src/socket_activation.rs:49 | int | external | no | no direct site | +| `LISTEN_FD_START` | `standard` | standard | both | world-service | crates/world-service/src/socket_activation.rs:156 | crates/world-service/src/socket_activation.rs:89 | string | external | no | no direct site | +| `LISTEN_PID` | `standard` | standard | both | world-service | crates/world-service/src/socket_activation.rs:154 | crates/world-service/src/socket_activation.rs:65 | int | external | no | no direct site | | `LOCALAPPDATA` | `standard` | standard | read | forwarder, scripts | — | crates/forwarder/src/config.rs:191; crates/forwarder/src/windows.rs:47 | string | external | no | no direct site | | `LP1_PROVISION_PROFILE` | `standard` | standard | read | tests | — | tests/installers/world_provision_smoke.sh:7 | string | test-only | no | no direct site | | `MAC_DOCTOR_SCENARIO` | `standard` | standard | read | tests | — | tests/mac/lima_doctor_fixture.sh:35 | string | test-only | no | no direct site | @@ -433,5 +433,5 @@ Column notes: | `WSL_SEPARATE` | `standard` | standard | read | scripts | — | `make planning-new-feature` | string | script-only | no | no direct site | | `XDG_CACHE_HOME` | `standard` | standard | read | world-backend | — | crates/world/src/exec.rs:203 | string | external | no | no direct site | | `XDG_CONFIG_HOME` | `standard` | standard | read | world-backend | — | crates/world/src/exec.rs:204 | string | external | no | no direct site | -| `XDG_DATA_HOME` | `standard` | standard | read | world-agent, world-backend | — | crates/world/src/exec.rs:205; crates/world-agent/src/pty.rs:641 | string | external | no | no direct site | +| `XDG_DATA_HOME` | `standard` | standard | read | world-service, world-backend | — | crates/world/src/exec.rs:205; crates/world-service/src/pty.rs:641 | string | external | no | no direct site | | `XDG_RUNTIME_DIR` | `standard` | standard | both | shell, world-backend | crates/shell/tests/replay_world.rs:244; crates/shell/tests/replay_world.rs:438 | crates/world/src/copydiff.rs:73; crates/world/src/copydiff.rs:139 | string | external | no | no direct site | diff --git a/docs/internals/repl/persistent_session.md b/docs/internals/repl/persistent_session.md index d66f69008..53bd4cb81 100644 --- a/docs/internals/repl/persistent_session.md +++ b/docs/internals/repl/persistent_session.md @@ -1,6 +1,6 @@ # Persistent Session REPL: Execution Notes -Substrate’s world-first REPL uses the world-agent WebSocket `/v1/stream` “persistent session” +Substrate’s world-first REPL uses the world-service WebSocket `/v1/stream` “persistent session” protocol (v1) to execute interactive commands while persisting a small amount of state across commands (not a long-lived login shell). @@ -11,7 +11,7 @@ This doc is intentionally implementation-focused. For the authoritative project - A REPL “session” is long-lived (WebSocket stays up), but each command is executed as a separate `bash -c ""` process inside the world. -- After each command completes, world-agent captures: +- After each command completes, world-service captures: - physical cwd (`pwd -P` equivalent), and - exported environment variables and persists them for the next command. @@ -33,7 +33,7 @@ The guard is implemented as shell code that: The guard must be evaluated in the *same shell process* that interprets the user’s program text. -Gotcha (fixed in `world-agent` persistent-session exec): +Gotcha (fixed in `world-service` persistent-session exec): - If you wrap the *outer* command (e.g. the “inner command” that does `exec /bin/bash -c "$PROGRAM"`), the `exec` replaces the shell process, and the guard’s `cd()` override is lost. - Correct approach: wrap the *program string* itself (the thing passed to `bash -c ...`) so the guard diff --git a/docs/internals/world/deps.md b/docs/internals/world/deps.md index 1ad9e9d7c..2b3237753 100644 --- a/docs/internals/world/deps.md +++ b/docs/internals/world/deps.md @@ -26,12 +26,12 @@ Code pointers: ## Why `$HOME` installs are ephemeral in hardened worlds -Substrate’s hardened world-agent service runs with a restrictive systemd sandbox on guest Linux (Lima/WSL), including: +Substrate’s hardened world-service service runs with a restrictive systemd sandbox on guest Linux (Lima/WSL), including: - `ProtectSystem=strict` (rootfs is effectively read-only) - `ReadWritePaths=... /var/lib/substrate ... /tmp` (Substrate-owned writable surfaces) On macOS (Lima), see: -- `scripts/mac/substrate-world-agent.service` +- `scripts/mac/substrate-world-service.service` Additionally, Substrate’s world env contract sets: - `HOME=/root` @@ -63,7 +63,7 @@ In the macOS Lima guest, you will typically observe: - `/` is read-only - `/var/lib/substrate` is writable -This difference is expected: it comes from the world-agent service sandbox. +This difference is expected: it comes from the world-service service sandbox. ## Wrapper fields are literal strings (no ${VAR} expansion) @@ -151,7 +151,7 @@ System-package provisioning is owned by: - `substrate world enable --provision-deps` On guest Linux agents, the `world-deps-provision` request profile is executed through a transient -`systemd-run` unit. This keeps the long-lived `substrate-world-agent.service` sandbox hardened for +`systemd-run` unit. This keeps the long-lived `substrate-world-service.service` sandbox hardened for normal runtime execution while still allowing explicit provisioning-time OS mutation (`apt`, package postinst scripts, privilege drops to `_apt`) to touch the guest system paths it needs. Those internal world-deps profiles are reserved for Substrate’s built-in world-deps flows; generic diff --git a/docs/internals/world/workspace_sync_filesystem_model.md b/docs/internals/world/workspace_sync_filesystem_model.md index 9b5f08482..dfed56573 100644 --- a/docs/internals/world/workspace_sync_filesystem_model.md +++ b/docs/internals/world/workspace_sync_filesystem_model.md @@ -15,7 +15,7 @@ world-only layer is applied to the host and/or discarded. ## Glossary - **Host workspace**: the real directory on the developer machine (e.g. `/home//repo`). -- **World session**: a reusable isolated context created by `world-agent` (one per compatible +- **World session**: a reusable isolated context created by `world-service` (one per compatible `WorldSpec` in the daemon process). - **Overlay lower**: the host workspace directory (bind-mounted for overlay mounting). - **Overlay upper/work**: where world-only writes and deletes are materialized. @@ -31,7 +31,7 @@ Shell (“workspace sync” CLI and apply logic): - Implements directions `from_world | from_host | both` and conflict policy. World agent (pending diff record, conditional clear, reconciliation): -- `crates/world-agent/src/service.rs` +- `crates/world-service/src/service.rs` - `pending_diff()` builds `PendingDiffRecordV1` (includes `session_started_at` + `diff_id`). - `pending_diff_clear()` clears only if the `diff_id` still matches. - `pending_diff_reconcile()` discards selected overlay paths (host-preferred reconciliation). @@ -72,7 +72,7 @@ Pending diff computation walks overlay upper and interprets whiteouts: ### 3) `workspace sync` (from_world) applies pending world diffs to host, then clears the snapshot The host apply path: -1) Fetch pending diff record from world-agent (includes `diff_id`). +1) Fetch pending diff record from world-service (includes `diff_id`). 2) Apply deletes then writes/mods (after preflight validation). 3) Attempt to clear the pending diff snapshot using `pending_diff_clear(diff_id=...)`. @@ -97,7 +97,7 @@ Because host is already the baseline, “host→world” sync is implemented as: This is implemented by `pending_diff_reconcile_v1`: - shell: `crates/shell/src/execution/workspace_cmd.rs` -- agent: `crates/world-agent/src/service.rs` (`pending_diff_reconcile()`) +- agent: `crates/world-service/src/service.rs` (`pending_diff_reconcile()`) - backend: `crates/world/src/lib.rs` (`discard_pending_paths()`) - overlay: `crates/world/src/overlayfs/mod.rs` (`discard_paths()`) diff --git a/docs/manual_verification/linux_world_socket.md b/docs/manual_verification/linux_world_socket.md index b74038dce..8b1a6d2c3 100644 --- a/docs/manual_verification/linux_world_socket.md +++ b/docs/manual_verification/linux_world_socket.md @@ -9,10 +9,10 @@ block in `substrate host doctor --json` (and in the `host` block of `substrate w ## Requirements - Linux host with sudo access and systemd (the script installs/starts - `substrate-world-agent.service` + `.socket`). + `substrate-world-service.service` + `.socket`). - Rust toolchain installed so `cargo build -p substrate --bin substrate` succeeds. - `jq` available on PATH to slice the doctor JSON. -- Ability to tolerate `/usr/local/bin/substrate-world-agent` and systemd unit changes during the test. +- Ability to tolerate `/usr/local/bin/substrate-world-service` and systemd unit changes during the test. ## Running the harness @@ -28,7 +28,7 @@ The script performs the following: 1. Builds the local `substrate` CLI (debug profile) if needed. 2. Calls `scripts/linux/world-provision.sh --profile ` to install the - world-agent, write both systemd units, and enable the socket. + world-service, write both systemd units, and enable the socket. 3. Records `systemctl status` output for the `.socket` and `.service` into `artifacts/linux/world-socket-verify-/`. 4. Captures `/run/substrate.sock` ownership/perms (`root:substrate 0660`), diff --git a/docs/manual_verification/netfilter_enforcement.md b/docs/manual_verification/netfilter_enforcement.md index e20fcd6da..1e83486b5 100644 --- a/docs/manual_verification/netfilter_enforcement.md +++ b/docs/manual_verification/netfilter_enforcement.md @@ -17,7 +17,7 @@ When all three gates line up for a restrictive posture, the runtime must enforce - `requested`: whether the last run asked the backend to enforce outbound filtering. - `enabled`: whether the backend was both requested and allowed to enforce. -- `world_netfilter_enable_present`: whether the world-agent service currently has `WORLD_NETFILTER_ENABLE=1`. +- `world_netfilter_enable_present`: whether the world-service service currently has `WORLD_NETFILTER_ENABLE=1`. - `last_failure_reason`: the last actionable enforcement failure for a requested run, if any. ## Linux privileged verification diff --git a/docs/project_management/adrs/draft/ADR-0003-policy-and-config-mental-model-simplification_OG.md b/docs/project_management/adrs/draft/ADR-0003-policy-and-config-mental-model-simplification_OG.md index cfaa6c2f3..4e8b6e2dd 100644 --- a/docs/project_management/adrs/draft/ADR-0003-policy-and-config-mental-model-simplification_OG.md +++ b/docs/project_management/adrs/draft/ADR-0003-policy-and-config-mental-model-simplification_OG.md @@ -824,7 +824,7 @@ Primary hot spots (current code reality that must be rewritten to satisfy this A - Policy schema: - `crates/broker/src/policy.rs` (`world_fs.cage` -> `world_fs.isolation`) - World env plumbing and parsing: - - `crates/world-agent/src/service.rs` + - `crates/world-service/src/service.rs` - `crates/world/src/exec.rs` - `crates/shell/src/execution/routing/dispatch/exec.rs` - CLI flags: diff --git a/docs/project_management/adrs/draft/ADR-0009-linux-guest-rootfs-backend-and-linux-system-packages-provisioning.md b/docs/project_management/adrs/draft/ADR-0009-linux-guest-rootfs-backend-and-linux-system-packages-provisioning.md index 15bfb4312..8ab6586a0 100644 --- a/docs/project_management/adrs/draft/ADR-0009-linux-guest-rootfs-backend-and-linux-system-packages-provisioning.md +++ b/docs/project_management/adrs/draft/ADR-0009-linux-guest-rootfs-backend-and-linux-system-packages-provisioning.md @@ -31,7 +31,7 @@ ADR_BODY_SHA256: ` fails because the host cannot resolve the path on the host filesystem. - The workaround (`:pty bash`) works because it enters a real shell inside the world, but it is: @@ -96,7 +96,7 @@ ADR_BODY_SHA256: 339caac9c2e14856e07f9f2d2af15038207bc67717c1b81d736717267a279c4 - Auto-PTY is preserved: interactive/TUI commands run in PTY passthrough mode automatically, using the existing “needs PTY” heuristic; `:pty` forces PTY passthrough for a line when heuristics are wrong (see `docs/project_management/_archived/world-first-repl-persistent-pty/STATE_MACHINE.md`). - If a command is misclassified into line mode but reads from the controlling TTY (e.g., via `/dev/tty`), it may block; `Ctrl+C` is the supported escape, then rerun with `:pty `. - Operator recovery contract (no fallback): if a line-mode command appears “hung” due to needing a TTY, abort with `Ctrl+C` and rerun with `:pty `. Implementations MAY print a non-fatal hint after a short delay (guidance only; not a completion fallback). - - The prompt is line-editor driven (no interactive PS2 continuation prompts), but a single submission MAY contain embedded newlines (e.g., pastes/multiline input). Submissions are sent to world-agent via explicit `exec` messages (not as raw PTY stdin bytes), and completion is reported via `command_complete`. Job control/backgrounding remains out of scope (see `docs/project_management/_archived/world-first-repl-persistent-pty/STATE_MACHINE.md` and decision register DR-13). + - The prompt is line-editor driven (no interactive PS2 continuation prompts), but a single submission MAY contain embedded newlines (e.g., pastes/multiline input). Submissions are sent to world-service via explicit `exec` messages (not as raw PTY stdin bytes), and completion is reported via `command_complete`. Job control/backgrounding remains out of scope (see `docs/project_management/_archived/world-first-repl-persistent-pty/STATE_MACHINE.md` and decision register DR-13). - Output note: PTY `stdout` bytes MAY still arrive while the REPL is idle (e.g., from background writers); Substrate forwards/renders them, but they are **unattributed** to a specific command by default. - Output ordering note (MUST): for a given `exec`, `command_complete` MUST NOT be emitted until all foreground Session PTY output bytes for that command have been forwarded to the host. v1 uses a watermark-based post-exit drain barrier (Linux: `ioctl(FIONREAD)`); if the platform cannot support the watermark query, v1 persistent sessions MUST fail closed (see `docs/project_management/_archived/world-first-repl-persistent-pty/PROTOCOL.md`). - Concurrent output note (MUST): Session PTY `stdout` frames are raw PTY bytes only (stdout+stderr combined). Substrate-managed concurrent output (e.g., `:demo-agent`, future AgentHub events) MUST NOT be injected into PTY bytes; during PTY passthrough, such structured events SHOULD be buffered and rendered only after the foreground PTY command completes to avoid corrupting TUIs (see `docs/project_management/_archived/world-first-repl-persistent-pty/PROTOCOL.md`). @@ -157,10 +157,10 @@ ADR_BODY_SHA256: 339caac9c2e14856e07f9f2d2af15038207bc67717c1b81d736717267a279c4 ### Platform guarantees - Linux: - - Interactive REPL uses the Linux world backend (world-agent over UDS) when enabled and available. + - Interactive REPL uses the Linux world backend (world-service over UDS) when enabled and available. - When world execution is enabled (e.g., `--world` or effective config enables world), the REPL must fail closed on startup if the world backend is not available (no implicit host fallback). `world_fs.require_world=true` strengthens this by ensuring world execution cannot be disabled by policy/config. - macOS: - - Interactive REPL uses Lima-backed world-agent streaming when enabled and available. + - Interactive REPL uses Lima-backed world-service streaming when enabled and available. - When world execution is enabled (e.g., `--world` or effective config enables world), the REPL must fail closed on startup if the world backend is not available (no implicit host fallback). `world_fs.require_world=true` strengthens this by ensuring world execution cannot be disabled by policy/config. - Windows: - No changes required by this ADR; world PTY parity is explicitly out of scope. @@ -173,8 +173,8 @@ ADR_BODY_SHA256: 339caac9c2e14856e07f9f2d2af15038207bc67717c1b81d736717267a279c4 - World session abstraction (new): - A long-lived PTY-backed session exists inside the world for the duration of the REPL session (Session PTY + trusted driver component). - - Substrate submits each REPL “submission” to world-agent as an explicit `exec` request (not as raw PTY stdin bytes) and receives streamed PTY output. - - Substrate derives per-submission exit status and updates its in-world cwd tracking from world-agent `command_complete` messages. + - Substrate submits each REPL “submission” to world-service as an explicit `exec` request (not as raw PTY stdin bytes) and receives streamed PTY output. + - Substrate derives per-submission exit status and updates its in-world cwd tracking from world-service `command_complete` messages. - World backend requirements (Linux/macOS): - The world backend must support a long-lived interactive session (PTY stream) with: @@ -187,7 +187,7 @@ ADR_BODY_SHA256: 339caac9c2e14856e07f9f2d2af15038207bc67717c1b81d736717267a279c4 - A command that mutates state (cwd/env) in the world session must have effects visible to subsequent unprefixed commands. ## Sequencing / Dependencies -- This ADR depends on the existing world-agent streaming (`/v1/stream`) and REPL routing layers but introduces a new “persistent session” requirement. +- This ADR depends on the existing world-service streaming (`/v1/stream`) and REPL routing layers but introduces a new “persistent session” requirement. - No explicit triad dependencies are declared in this Draft; once scheduled, this work must be integrated into: - `docs/project_management/packs/sequencing.json` - `docs/project_management/_archived/world-first-repl-persistent-pty/tasks.json` diff --git a/docs/project_management/adrs/draft/ADR-0017-agent-hub-concurrent-execution-and-output-routing.md b/docs/project_management/adrs/draft/ADR-0017-agent-hub-concurrent-execution-and-output-routing.md index 16a710c5b..f10ed196b 100644 --- a/docs/project_management/adrs/draft/ADR-0017-agent-hub-concurrent-execution-and-output-routing.md +++ b/docs/project_management/adrs/draft/ADR-0017-agent-hub-concurrent-execution-and-output-routing.md @@ -51,7 +51,7 @@ - Grounding code references: - Concurrent structured output in REPL: `crates/shell/src/execution/agent_events.rs`, `crates/shell/src/repl/async_repl.rs` - Example command that emits concurrent events: search for `:demo-agent` - - World PTY byte streaming plumbing: `crates/world-agent/src/pty.rs` + - World PTY byte streaming plumbing: `crates/world-service/src/pty.rs` ## Executive Summary (Operator) @@ -101,7 +101,7 @@ ADR_BODY_SHA256: f0d3b640100a78346549730cb1bb9a2051e2875038315f4f7e1b0ada4cd52ff - Designing the full agent hub configuration model (providers, credentials, tool installation). - Providing a full block-based terminal UI as part of this ADR. - Solving job control/backgrounding in the REPL (out of scope for ADR-0016 and remains out of scope here). -- Adding new world-agent wire protocols beyond what ADR-0016 requires. +- Adding new world-service wire protocols beyond what ADR-0016 requires. ## User Contract (Authoritative) @@ -258,7 +258,7 @@ Fail-closed drift posture (`agents.hub.world_restart.on_drift=fail_closed`) - All platforms: - The output routing and rendering rules in this ADR are required anywhere PTY passthrough and structured agent events can occur concurrently. - Linux/macOS: - - PTY passthrough is expected to use world-agent streaming (`/v1/stream`) per ADR-0016; structured event printing must remain out-of-band (no PTY injection). + - PTY passthrough is expected to use world-service streaming (`/v1/stream`) per ADR-0016; structured event printing must remain out-of-band (no PTY injection). - Windows: - This ADR introduces no platform-specific exceptions; if PTY passthrough is supported on Windows, the same non-injection rule applies. @@ -271,7 +271,7 @@ Fail-closed drift posture (`agents.hub.world_restart.on_drift=fail_closed`) - PTY-bytes rendering path (binary-safe). - Structured-event rendering path (typed messages; existing external-printer style). - Ensures structured-event output is not injected into PTY passthrough. -- `crates/world-agent`: +- `crates/world-service`: - Continues to treat `stdout` as a raw PTY byte stream. - `crates/common` / `crates/trace`: - Structured agent events MUST be traceable with stable correlation identifiers (persisted to canonical `trace.jsonl`). @@ -282,7 +282,7 @@ Fail-closed drift posture (`agents.hub.world_restart.on_drift=fail_closed`) ### End-to-end flow - Inputs: - - PTY byte stream from world-agent streaming (`/v1/stream`) or host PTY passthrough. + - PTY byte stream from world-service streaming (`/v1/stream`) or host PTY passthrough. - Structured agent events emitted by agent hub wrappers/orchestration and/or internal REPL tasks. - Effective config (`repl.max_pty_buffered_lines`) from config layering. - Derived state: diff --git a/docs/project_management/adrs/draft/ADR-0023-in-world-llm-gateway-front-door.md b/docs/project_management/adrs/draft/ADR-0023-in-world-llm-gateway-front-door.md index dc1f019a8..fc7fba304 100644 --- a/docs/project_management/adrs/draft/ADR-0023-in-world-llm-gateway-front-door.md +++ b/docs/project_management/adrs/draft/ADR-0023-in-world-llm-gateway-front-door.md @@ -69,7 +69,7 @@ Supersession note: ### CLI - Commands: - Gateway lifecycle is owned by the world subsystem (session world management + deps provisioning). - - v1 requirement (legacy): the “ensure gateway running” path MUST pass any required secret values to the in-world gateway/engine spawn request over the existing world-agent transport (see `docs/project_management/_archived/next/llm_gateway_in_world/specs/env_injection.md`). + - v1 requirement (legacy): the “ensure gateway running” path MUST pass any required secret values to the in-world gateway/engine spawn request over the existing world-service transport (see `docs/project_management/_archived/next/llm_gateway_in_world/specs/env_injection.md`). - Phase 8 additive upgrade (preferred): secret values MUST NOT live in the in-world gateway/manager process environment by default. Host→world secret delivery SHOULD use a secret-channel payload and an inherited one-time FD/pipe bundle into the gateway/manager (see `docs/project_management/_archived/next/llm_gateway_in_world/decision_register.md` (DR-0018)). - `substrate world gateway status`: - Behavior: prints per-world-session gateway state (running/not), bind endpoints inside the world, active backend kind, and policy mode. @@ -135,17 +135,17 @@ Non-negotiable rules (Phase 8): - Gateway runs inside the world network namespace; outbound egress is subject to world-level enforcement. - Fail-closed: when effective policy has `llm.fail_closed.routing=true`, do not fall back to a host-level gateway when `SUBSTRATE_WORLD=enabled`. - macOS: - - Gateway runs inside the Lima guest world. Host talks to it via the existing Substrate transport to world-agent. + - Gateway runs inside the Lima guest world. Host talks to it via the existing Substrate transport to world-service. - Fail-closed: if the guest is not available, gateway use fails with exit code `5` when `llm.fail_closed.routing=true`. - Windows: - - Gateway runs inside the WSL world distribution. Host talks to it via the existing Substrate transport to world-agent. + - Gateway runs inside the WSL world distribution. Host talks to it via the existing Substrate transport to world-service. - Fail-closed: same as macOS (when `llm.fail_closed.routing=true`). ## Architecture Shape - Components: - `crates/llm-gateway` (new): binary “front door” HTTP server for OpenAI+Anthropic dialects; minimal parsing; emits structured events/spans. - `crates/llm-manager` (new): backend router + policy gate + logging abstraction; selects engine by config. - - `crates/world-agent` (existing): supervises starting/stopping gateway inside the world session; exposes health/status over existing agent transport. + - `crates/world-service` (existing): supervises starting/stopping gateway inside the world session; exposes health/status over existing agent transport. - `crates/trace` (existing): stores canonical spans/events for LLM requests in JSONL; optional fs diff integration not required here. - `crates/broker` (existing): optional policy decision integration point (allow/deny/require-approval) for LLM operations. diff --git a/docs/project_management/adrs/draft/ADR-0025-agent-hub-core-role-swappable.md b/docs/project_management/adrs/draft/ADR-0025-agent-hub-core-role-swappable.md index b89a94217..e2e94b05e 100644 --- a/docs/project_management/adrs/draft/ADR-0025-agent-hub-core-role-swappable.md +++ b/docs/project_management/adrs/draft/ADR-0025-agent-hub-core-role-swappable.md @@ -23,7 +23,7 @@ ADR_BODY_SHA256: a1beafeadd0d0cb480745724dfb520a52ef5ae886dcd2174c2d66b4567db63dc ### Changes (operator-facing) - Agent Hub provides a stable registry + session router for CLI and API agents - - Existing: Substrate can run worlds, trace commands, and call a world-agent API, but “agents” are not uniformly registered/routed as role-swappable backends. + - Existing: Substrate can run worlds, trace commands, and call a world-service API, but “agents” are not uniformly registered/routed as role-swappable backends. - New: Substrate maintains an Agent Hub registry where any backend (CLI or API) can assume the `orchestrator` role (privileged) or a non-orchestrator role (taxonomy label), and emits stable event attribution for concurrent routing. - Why: Enable consistent orchestration across Codex/Claude/Gemini CLIs and API agents without hardcoding roles into types. - Links: diff --git a/docs/project_management/adrs/draft/ADR-0027-llm-and-agent-config-policy-surface.md b/docs/project_management/adrs/draft/ADR-0027-llm-and-agent-config-policy-surface.md index 3afc42332..84af18a5a 100644 --- a/docs/project_management/adrs/draft/ADR-0027-llm-and-agent-config-policy-surface.md +++ b/docs/project_management/adrs/draft/ADR-0027-llm-and-agent-config-policy-surface.md @@ -86,7 +86,7 @@ ADR_BODY_SHA256: 36c3baa794d0878fae02ffa4dfa7bb9dbeed355c4aa7e4a0a554df7dd116443 - and compatibility with the existing layered resolution system (workspace/global, CLI/env overrides, profiles). - If we create new config/policy files ad hoc per feature, we will: - multiply precedence rules, - - create drift between shell/shim/world-agent, and + - create drift between shell/shim/world-service, and - increase the probability of boundary errors (e.g., LLM egress happening outside the world boundary without an explicit operator decision). ## Goals @@ -306,7 +306,7 @@ This `workflow.router` namespace is unrelated to the LLM identity-tuple field `r - `crates/broker`: - extend policy schema + patch schema to include `llm.*` and `agents.*` - ensure effective policy resolution and `--explain` provenance include the new keys - - `crates/world-agent` / world backends: + - `crates/world-service` / world backends: - continue to enforce network allowlists via `net_allowed` (used by LLM egress paths) - LLM gateway/manager + agent hub components (defined in other ADRs): - consume effective config/policy and enforce fail-closed rules described above diff --git a/docs/project_management/adrs/draft/ADR-0028-in-world-process-execution-tracing-parity.md b/docs/project_management/adrs/draft/ADR-0028-in-world-process-execution-tracing-parity.md index 656266898..affbaef12 100644 --- a/docs/project_management/adrs/draft/ADR-0028-in-world-process-execution-tracing-parity.md +++ b/docs/project_management/adrs/draft/ADR-0028-in-world-process-execution-tracing-parity.md @@ -34,7 +34,7 @@ ADR_BODY_SHA256: 799e22a5981fbf644d83626d36c3bbd7b788b80a22ec85629e59c499c25815a ### Changes (operator-facing) - World executions gain subprocess-level visibility (exec/exit telemetry) comparable to host shim tracing - Existing: host execution is richly observable via shims, but world execution is observable primarily at “one command per world execute/stream” granularity (no structured visibility into spawned subprocess trees). - - New: world-agent returns a redacted process tree (spawn/exec/exit) for each world execution, and the shell persists these as structured trace events alongside existing spans, policy decisions, and fs diffs. + - New: world-service returns a redacted process tree (spawn/exec/exit) for each world execution, and the shell persists these as structured trace events alongside existing spans, policy decisions, and fs diffs. - Why: eliminate audit/debug blind spots for dependency installers and wrapper-based tools, and make world-first execution diagnosable without stdout/stderr inference. - Links: - `docs/project_management/adrs/draft/ADR-0028-in-world-process-execution-tracing-parity.md#L1` @@ -48,7 +48,7 @@ ADR_BODY_SHA256: 799e22a5981fbf644d83626d36c3bbd7b788b80a22ec85629e59c499c25815a - policy debugging and audit scenarios where stdout/stderr inference is insufficient. ### Repo reality checks (what exists today) -- `crates/world-agent/src/service.rs`: +- `crates/world-service/src/service.rs`: - `/v1/execute`: spans are generated after execution, and `ExecRequest.span_id` is not currently populated. - `/v1/stream`: span id is generated up-front and `ExecRequest.span_id` is populated. - `crates/world/src/session.rs` + `crates/world/src/exec.rs`: @@ -65,14 +65,14 @@ ADR_BODY_SHA256: 799e22a5981fbf644d83626d36c3bbd7b788b80a22ec85629e59c499c25815a - shim argv redaction is robust (`crates/shim/src/logger.rs`), including “flag consumes next arg” semantics. - `substrate_common::redact_sensitive()` is not sufficient for safe argv/env capture at process granularity (it does not redact values following flags). - Provisioning (Linux-backed backends): - - The `substrate-world-agent` systemd unit currently does not grant `CAP_SYS_PTRACE` by default (`scripts/linux/world-provision.sh`, `scripts/mac/lima-warm.sh`), so ptrace-based capture may be blocked unless the unit is updated. + - The `substrate-world-service` systemd unit currently does not grant `CAP_SYS_PTRACE` by default (`scripts/linux/world-provision.sh`, `scripts/mac/lima-warm.sh`), so ptrace-based capture may be blocked unless the unit is updated. ## Goals - Achieve in-world per-process execution tracing parity with the host shim model: - capture a process tree for each world execution (exec/exit at minimum; fork/clone for parent relationships), - capture argv/env/cwd with safe redaction and data minimization, - capture pid/ppid and exit status, plus timing (start timestamp + duration). -- Return process events from world-agent for both: +- Return process events from world-service for both: - `/v1/execute` (batched in the response), - `/v1/stream` (batched on the Exit frame initially). - Persist process events into `~/.substrate/trace.jsonl` via the existing shell trace append pathway so they are co-located with: @@ -98,10 +98,10 @@ ADR_BODY_SHA256: 799e22a5981fbf644d83626d36c3bbd7b788b80a22ec85629e59c499c25815a - No new CLI commands or flags are introduced by this ADR. - Operator-visible behavior change: - When world execution succeeds and process tracing is supported, `~/.substrate/trace.jsonl` gains additional event records with `event_type` `world_process_start` and `world_process_exit`. - - When process tracing is unavailable or fails, execution MUST still succeed (see “Failure behavior”), but the world-agent response MUST carry an explicit structured diagnostic so operators can distinguish “no subprocesses spawned” from “subprocess tracing unavailable”. + - When process tracing is unavailable or fails, execution MUST still succeed (see “Failure behavior”), but the world-service response MUST carry an explicit structured diagnostic so operators can distinguish “no subprocesses spawned” from “subprocess tracing unavailable”. ### World-Agent API (Authoritative) -This ADR extends the world-agent responses to optionally include process events. +This ADR extends the world-service responses to optionally include process events. - `/v1/execute` response: - MUST include `span_id` generated before execution. @@ -149,7 +149,7 @@ World process events are structured trace events aligned with `crates/common/src - Minimum required fields (always present): - `ts` (timestamp) - `event_type` (`world_process_start` or `world_process_exit`) - - `component` (`world-agent`) + - `component` (`world-service`) - `session_id` (from `SHIM_SESSION_ID` env propagated into the world execution) - `world_id` - `pid`, `ppid` @@ -183,7 +183,7 @@ Default env capture policy for process events: To bound volume: - Cap maximum events per execution (default: 10,000). - Cap argv length per event and env value lengths (default: 4KB/value). -- When truncation occurs, the world-agent response MUST include summary fields: +- When truncation occurs, the world-service response MUST include summary fields: - `process_events_status: "truncated"` - `process_events_dropped: ` @@ -354,12 +354,12 @@ Any record intended to trigger routing or cross-component attribution MUST carry - fix span parent linkage bug in `crates/trace/src/span.rs` by capturing parent span at span start and restoring env stack discipline on finish - `crates/world` (Linux only; behind `cfg(target_os=\"linux\")`): - implement ptrace-based process tree capture in world exec paths (`crates/world/src/exec.rs`) - - note: this Linux runtime powers both native Linux deployments and macOS Lima deployments (world-agent runs in a Linux guest; `docs/WORLD.md`) + - note: this Linux runtime powers both native Linux deployments and macOS Lima deployments (world-service runs in a Linux guest; `docs/WORLD.md`) - store captured events in session state keyed by `span_id` and provide take semantics to avoid unbounded growth - - `crates/world-agent`: + - `crates/world-service`: - generate and plumb `span_id` consistently for `/v1/execute` and `/v1/stream` - retrieve captured events from the backend and return them in responses/frames - - `crates/agent-api-types`: + - `crates/transport-api-types`: - extend response/frame types to transport `process_events` (greenfield; lockstep updates; breaking allowed) - `crates/shell`: - parse `process_events` from responses/Exit frames @@ -375,7 +375,7 @@ Any record intended to trigger routing or cross-component attribution MUST carry - Actions: - execute command inside world - capture exec/exit events for the process tree - - return events to host via world-agent API + - return events to host via world-service API - append events to `trace.jsonl` - Outputs: - process event records co-located with spans for deterministic reconstruction @@ -404,7 +404,7 @@ Any record intended to trigger routing or cross-component attribution MUST carry - shared argv redaction covers “flag consumes next arg” patterns and `--flag=value` patterns - env redaction strips credentials from URL-like values and redacts obvious secret keys - span parent linkage: no self-parent spans; stack discipline restores prior parent span -- Integration tests (world-agent): +- Integration tests (world-service): - child spawn parity: - run a deterministic child-spawning command and assert at least two processes are observed with correct parent relationships and exit metadata - wrapper-based execution: @@ -422,7 +422,7 @@ Any record intended to trigger routing or cross-component attribution MUST carry ## Rollout / Backwards Compatibility - Policy: greenfield breaking is allowed -- Backwards compatibility: not a goal for this ADR. Host + world-agent + API types are updated in lockstep; breaking changes are allowed. +- Backwards compatibility: not a goal for this ADR. Host + world-service + API types are updated in lockstep; breaking changes are allowed. - Trace volume increase is expected; bounded by caps and truncation summaries. ## Decision Summary diff --git a/docs/project_management/adrs/draft/ADR-0029-host-event-bus-and-router-daemon.md b/docs/project_management/adrs/draft/ADR-0029-host-event-bus-and-router-daemon.md index 43dae7163..4450005ef 100644 --- a/docs/project_management/adrs/draft/ADR-0029-host-event-bus-and-router-daemon.md +++ b/docs/project_management/adrs/draft/ADR-0029-host-event-bus-and-router-daemon.md @@ -59,7 +59,7 @@ ADR_BODY_SHA256: af9cb004268a0a6cbf00a7981662d531b51f11c2afca77201becae5583feb9d - tails `trace.jsonl` using durable cursors, - matches events against global + workspace-scoped routing rules, - produces durable, policy-gated requests/actions. -- Ensure the router daemon is a host service (not world-agent) so it remains available when worlds are disabled or when a VM/WSL backend is down. +- Ensure the router daemon is a host service (not world-service) so it remains available when worlds are disabled or when a VM/WSL backend is down. - Support cross-workspace routing by introducing an explicit workspace registry under `SUBSTRATE_HOME` updated by `substrate workspace init|enable|disable`. - Support selective file-operation triggers based on Substrate-collected fs diffs, scoped to specific file paths/directories (workspace-relative matching). @@ -149,7 +149,7 @@ Only an explicit allowlist of event families is triggerable. v1 supports: - The workflow router MUST re-resolve effective config/policy for workspace B at execution time (not reuse workspace A’s). ### Daemon behavior (Authoritative) -- The workflow router service MUST be host-level and MUST run independently of world-agent availability. +- The workflow router service MUST be host-level and MUST run independently of world-service availability. - It runs as a `substrate` subcommand: - `substrate workflow serve [--foreground]` - It MUST degrade gracefully: @@ -275,7 +275,7 @@ Only an explicit allowlist of event families is triggerable. v1 supports: - If no enabled workspace is resolvable for an event `cwd`, the router MUST treat it as non-routable (emit a derived deny/failure event with a stable reason code). - Service boundary: - the workflow router is a host service (not in-world) and must remain available in host-only mode. - - it MAY reuse transport patterns and code organization from world-agent, but must not depend on world-agent being available. + - it MAY reuse transport patterns and code organization from world-service, but must not depend on world-service being available. - Event recursion guard: - workflow-router-emitted events MUST be identifiable (e.g., `component=workflow_router`) and MUST be excluded from re-trigger evaluation by default to avoid infinite loops. - FS trigger derivation: diff --git a/docs/project_management/adrs/draft/ADR-0030-provisioning-otter.md b/docs/project_management/adrs/draft/ADR-0030-provisioning-otter.md index 894088892..99b1ae633 100644 --- a/docs/project_management/adrs/draft/ADR-0030-provisioning-otter.md +++ b/docs/project_management/adrs/draft/ADR-0030-provisioning-otter.md @@ -98,7 +98,7 @@ Add a `substrate world deps provision` command that provisions APT/system packag - Exit codes: - Exit code taxonomy: `docs/project_management/system/standards/shared/EXIT_CODE_TAXONOMY.md` (authoritative unless explicitly overridden here) - `0`: success - - `3`: world backend unavailable / cannot connect to world-agent + - `3`: world backend unavailable / cannot connect to world-service - `4`: unmet prerequisites or unsupported operation (includes: APT provisioning not supported on this backend; runtime sync/install encountered APT items and requires provisioning) - `5`: hardening conflict / fail-closed safety violation (reserved; runtime flow should avoid triggering this for APT by failing early) @@ -122,7 +122,7 @@ Add a `substrate world deps provision` command that provisions APT/system packag - Components: - `crates/shell/src/builtins/world_enable/…`: extend `world enable` to derive APT requirements from the effective enabled world-deps set and run a provisioning-time install step (guest worlds only). - `crates/shell/src/builtins/world_deps/surfaces.rs`: change runtime `current sync|install` path to preflight-detect APT items and fail early with remediation instead of building/running APT commands. - - `crates/world-agent/src/service.rs`: ensure provisioning execution is possible without weakening hardened runtime execution (e.g., distinct request profile or explicit guard rails). + - `crates/world-service/src/service.rs`: ensure provisioning execution is possible without weakening hardened runtime execution (e.g., distinct request profile or explicit guard rails). - Docs: add operator-facing explanation that APT-backed world-deps are provisioning-time under hardening (`docs/reference/world/deps/…`). - End-to-end flow: - Inputs: diff --git a/docs/project_management/adrs/draft/ADR-0033-routing-weasel.md b/docs/project_management/adrs/draft/ADR-0033-routing-weasel.md index 7b8f1fcd7..3a14355ff 100644 --- a/docs/project_management/adrs/draft/ADR-0033-routing-weasel.md +++ b/docs/project_management/adrs/draft/ADR-0033-routing-weasel.md @@ -122,7 +122,7 @@ Introduce an abstract system-packages method and have Substrate translate packag - Exit code taxonomy: `docs/project_management/system/standards/shared/EXIT_CODE_TAXONOMY.md` (authoritative unless explicitly overridden here) - `0`: success - `2`: invalid inventory/config schema (including unsupported `install.method` / invalid `install.pacman` shape) - - `3`: world backend unavailable / cannot connect to world-agent + - `3`: world backend unavailable / cannot connect to world-service - `4`: unmet prerequisites or unsupported operation (includes: provisioning unsupported on this backend; enabled set contains system-package items requiring provisioning; detected world OS manager does not match required install methods) - `5`: safety / policy violation (reserved; runtime flow should avoid triggering this by failing early) @@ -153,7 +153,7 @@ Introduce an abstract system-packages method and have Substrate translate packag - `crates/shell/src/builtins/world_enable/…`: extend provisioning-time flow to: - probe the world OS family/manager, and - execute the correct system-package provisioning path (APT per `ADR-0030`; pacman per this ADR) when supported. - - `crates/world-agent/src/service.rs` (and/or execution plumbing): ensure provisioning execution is possible without weakening hardened runtime execution (distinct request profile or explicit guard rails). + - `crates/world-service/src/service.rs` (and/or execution plumbing): ensure provisioning execution is possible without weakening hardened runtime execution (distinct request profile or explicit guard rails). - Docs: update operator reference and error text (`docs/reference/world/deps/…`) to reflect manager-aware provisioning guidance without overriding the pack-root contract. - End-to-end flow: - Inputs: diff --git a/docs/project_management/adrs/draft/ADR-0034-staging-beaver.md b/docs/project_management/adrs/draft/ADR-0034-staging-beaver.md index 7cdbf6bd3..9bb26a6f5 100644 --- a/docs/project_management/adrs/draft/ADR-0034-staging-beaver.md +++ b/docs/project_management/adrs/draft/ADR-0034-staging-beaver.md @@ -29,7 +29,7 @@ ADR_BODY_SHA256: eeac9675069d6bb9816517edb5a7b66846dd48204e4a1002a6341010ee562c8 ### Changes (operator-facing) - Dev installs stage a stable runtime bundle for `substrate world enable` under `$SUBSTRATE_HOME`. - Existing: `dev-install-substrate.sh` links `~/.substrate/bin/substrate` directly to `/target//substrate`, and helper/runtime assets are inferred from `/target/...`; that can break after `cargo clean` and can leave macOS `world enable` unable to find `scripts/mac/lima-warm.sh`. - - New: `dev-install-substrate.sh` stages `world-enable.sh`, `install-substrate.sh`, `world-deps.yaml`, `scripts/mac/lima-warm.sh`, the `scripts/mac/lima/{substrate.yaml,substrate-dev.yaml}` profile subtree, and Linux guest binaries under `$SUBSTRATE_HOME/{scripts,bin/linux}/…`. On macOS, the normal world-enabled dev-install path now persists copied Linux `substrate` and `world-agent` ELFs from Lima into `$SUBSTRATE_HOME/bin/linux/`, so later `substrate world enable` can succeed from the prefix bundle without depending on repo sources. + - New: `dev-install-substrate.sh` stages `world-enable.sh`, `install-substrate.sh`, `world-deps.yaml`, `scripts/mac/lima-warm.sh`, the `scripts/mac/lima/{substrate.yaml,substrate-dev.yaml}` profile subtree, and Linux guest binaries under `$SUBSTRATE_HOME/{scripts,bin/linux}/…`. On macOS, the normal world-enabled dev-install path now persists copied Linux `substrate` and `world-service` ELFs from Lima into `$SUBSTRATE_HOME/bin/linux/`, so later `substrate world enable` can succeed from the prefix bundle without depending on repo sources. - Why: Decouple `world enable` runtime assets from `/target/*` build artifacts while keeping `$SUBSTRATE_HOME/bin/substrate` pointed at the live host build output. - Links: - `crates/shell/src/builtins/world_enable/runner/paths.rs#L33` (helper search order includes `$SUBSTRATE_HOME/scripts/…`) @@ -50,7 +50,7 @@ ADR_BODY_SHA256: eeac9675069d6bb9816517edb5a7b66846dd48204e4a1002a6341010ee562c8 - Changing how the Substrate binaries are built (still `cargo build` from the repo). - Changing production install layout under `$SUBSTRATE_HOME/versions/…`. - Changing `substrate world enable` version-directory inference logic (it may continue to infer `/target/` for dev installs). -- Solving other `/target/*` coupling beyond helper discovery (example: staging or discovering world-agent artifacts). +- Solving other `/target/*` coupling beyond helper discovery (example: staging or discovering world-service artifacts). ## Out of Scope - Full “bundle parity” dev installs under `$SUBSTRATE_HOME/versions/