Skip to content

fix(desktop): sandbox boot-retry, status, per-dashboard workspace#266

Merged
robdmac merged 7 commits into
mainfrom
fix/desktop-sandbox-boot-status-isolation
Jul 14, 2026
Merged

fix(desktop): sandbox boot-retry, status, per-dashboard workspace#266
robdmac merged 7 commits into
mainfrom
fix/desktop-sandbox-boot-status-isolation

Conversation

@robdmac

@robdmac robdmac commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Fix the local app sandbox boot status

robdmac and others added 7 commits July 14, 2026 12:36
…ngle-user UI

- Auto-retry the local session-create for ~30s (12×3s) while the desktop VM boots,
  instead of failing the first terminal on a cold launch with "Network connection
  lost" (ensureDashboardSandboxCreate legacy/shared-SANDBOX_URL path).
- Fix the sandbox status showing red "Sandbox asleep" while the local VM is healthy:
  the desktop sandbox has no Fly machine, so the status guard keyed on
  sandbox_machine_id short-circuited before the desktop "ready" branch. Now keys
  "no sandbox" on sandbox_session_id and returns ready for non-Fly deployments.
- Per-dashboard workspace isolation on desktop: default each dashboard's terminals to
  /workspace/<dashboardId> (shared-sandbox deployments only; downloaded dashboards
  already set their own workingDir). The sandbox auto-creates the subdir on the
  desktop VM (ORCABOT_DEBUG_EXEC gate) so the first terminal lands there instead of
  falling back to the shared root; cloud is unchanged (each dashboard has its own VM).
- Hide the multiplayer "N online" presence indicator on desktop (always one local
  user).

Note: the sandbox auto-create (session.go) needs a VM-image rebuild to take effect
for newly-created dashboards; downloaded dashboards isolate immediately (their subdir
already exists). Control-plane + frontend changes take effect on a resources rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
Move per-dashboard workspace isolation from a per-PTY workingDir default to the
SANDBOX session root, and add a real health check to the desktop status.

- [P2] Isolate every dashboard-scoped execution path: manager.Create now roots each
  desktop session at /workspace/<dashboardID> (ORCABOT_DEBUG_EXEC gate, safe-component
  checked). Since the whole Session/Workspace is built from that root, UI terminals,
  chat run_command, schedules, and messaging all inherit it — no per-call plumbing.
  Reverted the control-plane per-PTY workingDir default (option a). Cloud is unchanged
  (one VM per dashboard; gate off).
- [P2] Fixes the stale-session-retry losing isolation too — isolation no longer
  depends on the (retry-dropped) workingDir.
- [P2] resolveWorkingDir no longer auto-creates arbitrary missing dirs — a typo errors
  (E79708) again; only the dashboard root is auto-created, in manager.Create.
- [P2] Desktop status now probes the local sandbox /health instead of trusting the
  persisted dashboard_sandboxes row, so a stale row after restart/crash shows
  "starting" (not green) until the VM actually serves.
- cloud-sync: download copies terminal content as-is (no subdir prepend) — the session
  root provides isolation; the download still writes files to <workspace>/<id> which
  equals that root.

The session-root change is in the guest sandbox binary, so it needs a VM-image rebuild
to take effect on desktop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
… root

The session-root isolation (/workspace/<dashboardId>) broke two hardcoded-/workspace
assumptions:

- [P1] Dashboard chat run_command timed out: the wrapper wrote completion markers to
  absolute /workspace/.orc-run-* but read them via the session-scoped file API, which
  now resolves under /workspace/<dashboardId> — marker never found. Now the shell
  redirect uses the SAME relative names as the read (the run PTY cwds into the session
  root, and the file API resolves relative to it), so it works whether the root is
  /workspace (cloud) or /workspace/<dashboardId> (desktop).
- [P2] Codex folder-trust pre-trusted the fixed /workspace, so desktop terminals
  starting in /workspace/<dashboardId> would re-show the "trust this directory?" prompt
  and block unattended startup. generateCodexHooks now trusts the actual session
  workspaceRoot it's already given (= s.workspace.Root()).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
… round 11)

- [P2] Codex completion events broke across dashboards: each dashboard's per-session
  hook path was appended to the SHARED /etc/codex/config.toml notify array as a second
  element, so Codex ran the first script with the second path as $1 instead of the
  event JSON. Now the notify script is a single GLOBAL, workspace-independent file
  (/etc/codex/orcabot-codex-stop.sh) — it already derives session/pty/secret from
  per-PTY env vars at runtime — and notify is REPLACED (not appended) with exactly that
  one path, idempotent across dashboards.
- [P2] The "~30s" boot retry could block 3+ minutes (12 × up-to-15s client timeout +
  11 × 3s) and retried deterministic 4xx failures. Now bounded by an overall 30s
  deadline and retries ONLY connection/transient failures ("fetch error:"), throwing
  immediately on HTTP status errors (e.g. auth mismatch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…d 12)

The incremental config assembly appended notify/check_for_updates AFTER the
[projects."<root>"] trust tables, so TOML scoped `notify` under a project table
instead of top-level — Codex (which reads notify as a top-level array<string>) never
saw the global hook. Now rebuild the OrcaBot-managed system config deterministically:
top-level keys (check_for_updates, notify) FIRST, then one [projects."<root>"] trust
table per accumulated workspace root. Extracted the pure builder
(buildCodexSystemConfig) and added a regression test asserting notify precedes any
[table], trust accumulates across dashboards, and there's exactly one notify line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…view round 13)

The config rebuild wrote only check_for_updates/notify/trust, wiping the
[mcp_servers.*] sections that mcp.GenerateSettingsForAgent had just written to the
same /etc/codex/config.toml — so every Codex launch lost Orcabot + user MCP tools
(hook generation runs after MCP generation).

buildCodexSystemConfig is now section-aware: it rewrites only the managed top-level
keys (check_for_updates, notify — kept before every [table]) and the
[projects."<root>"] trust tables, and PRESERVES all other sections verbatim (notably
[mcp_servers.*]), stripping only stray managed keys a prior bug nested inside tables.
Regression test extended to assert an [mcp_servers.*] section survives regeneration
(and stays after notify).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
@robdmac
robdmac merged commit 98b47ed into main Jul 14, 2026
1 check passed
@robdmac
robdmac deleted the fix/desktop-sandbox-boot-status-isolation branch July 14, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant