Skip to content

fix: route workspace tree to host when sandbox_mode is native/local#490

Open
sami-marreed wants to merge 1 commit into
mainfrom
fix/484-workspace-tree-native-mode
Open

fix: route workspace tree to host when sandbox_mode is native/local#490
sami-marreed wants to merge 1 commit into
mainfrom
fix/484-workspace-tree-native-mode

Conversation

@sami-marreed

@sami-marreed sami-marreed commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Bug fix

Fixes #484

Summary

/api/workspace/tree was taking the OpenSandbox path whenever opensandbox_sandbox=true, even with default sandbox_mode=native (and shell tools off). That caused noisy import errors and 503s when the optional OpenSandbox SDK was not installed.

Workspace routing now follows sandbox_mode: native/local use the host thread workspace; OpenSandbox is only required when mode is actually opensandbox-backed.

Testing

  • Verified fix locally; tests pass
  • uv run pytest tests/unit/test_workspace_sandbox.py — 31 passed
  • uv run pytest tests/unit/test_workspace_upload.py — 16 passed

Summary by CodeRabbit

  • Bug Fixes
    • Workspace behavior now correctly respects the selected sandbox mode.
    • Native and local modes consistently use the native workspace, even when shell tools are disabled or sandbox settings are enabled.
    • Other modes continue to select sandbox-backed workspaces when configured.

Avoid OpenSandbox SDK calls (and 503s) for /api/workspace/tree when
opensandbox_sandbox is true but sandbox_mode is native or local.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Workspace tree backing decisions now account for sandbox_mode, selecting native handling for native and local modes. Tests use a shared settings helper and cover the updated mode combinations.

Changes

Workspace backing selection

Layer / File(s) Summary
Sandbox and native backing predicates
src/cuga/backend/server/workspace_sandbox.py
Sandbox backing now excludes native and local modes, while native backing recognizes those modes independently of shell tool enablement.
Workspace backing test coverage
tests/unit/test_workspace_sandbox.py
Tests use shared settings construction and cover native mode without shell tools plus local mode with the OpenSandbox flag enabled.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested labels: readability: good, complexity: medium

Suggested reviewers: offerakrabi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: routing workspace tree to the host in native/local sandbox modes.
Linked Issues check ✅ Passed The code and tests address #484 by routing /api/workspace/tree by sandbox_mode and avoiding OpenSandbox dependency use in native/local modes.
Out of Scope Changes check ✅ Passed The changes stay focused on workspace tree routing and related tests, with no obvious unrelated additions.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/484-workspace-tree-native-mode

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added complexity: medium Moderate scope — multiple files or non-trivial logic readability: good Clear PR goal and description; easy to review labels Jul 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/cuga/backend/server/workspace_sandbox.py (1)

29-32: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Duplicated mode-resolution logic uses a fallback default that contradicts the project's documented default.

The mode = str(getattr(settings.advanced_features, "sandbox_mode", "opensandbox") or "opensandbox") snippet is duplicated verbatim in both functions. More importantly, project docs state "That preset turns on skills for the run and uses [advanced_features] sandbox_mode in settings.toml (default native)." The getattr/or fallback here defaults to "opensandbox" instead of "native". In the normal case the real Settings object always carries a value so this fallback rarely fires, but if sandbox_mode is ever missing/None/empty (partial settings, older schema, a different mock), this silently falls back to OpenSandbox routing — the exact class of bug (#484) this PR fixes.

Extracting a single helper also removes the duplication risk of the two copies drifting out of sync.

♻️ Suggested consolidation
+def _resolve_sandbox_mode() -> str:
+    return str(getattr(settings.advanced_features, "sandbox_mode", "native") or "native")
+
+
 def workspace_tree_is_sandbox_backed() -> bool:
     ...
     if not bool(getattr(settings.advanced_features, "opensandbox_sandbox", False)):
         return False
-    mode = str(getattr(settings.advanced_features, "sandbox_mode", "opensandbox") or "opensandbox")
+    mode = _resolve_sandbox_mode()
     return mode not in ("native", "local")


 def workspace_tree_is_native_backed() -> bool:
     ...
-    mode = str(getattr(settings.advanced_features, "sandbox_mode", "opensandbox") or "opensandbox")
+    mode = _resolve_sandbox_mode()
     if mode in ("native", "local"):
         return True

Please confirm what the real Settings schema's default for advanced_features.sandbox_mode is, to make sure the fallback here matches it.

Also applies to: 42-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cuga/backend/server/workspace_sandbox.py` around lines 29 - 32,
Consolidate the duplicated sandbox-mode resolution used by both functions into
one shared helper, and make its missing, None, or empty-value fallback match the
Settings schema’s documented default of "native". Update both callers to use
this helper while preserving the existing OpenSandbox-enabled check and
native/local routing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/cuga/backend/server/workspace_sandbox.py`:
- Around line 29-32: Consolidate the duplicated sandbox-mode resolution used by
both functions into one shared helper, and make its missing, None, or
empty-value fallback match the Settings schema’s documented default of "native".
Update both callers to use this helper while preserving the existing
OpenSandbox-enabled check and native/local routing behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 264b00c2-dad8-4f15-ad1a-fd8428601265

📥 Commits

Reviewing files that changed from the base of the PR and between f3511e9 and b5ab0d4.

📒 Files selected for processing (2)
  • src/cuga/backend/server/workspace_sandbox.py
  • tests/unit/test_workspace_sandbox.py

@offerakrabi

Copy link
Copy Markdown
Collaborator

PR Review: #490 — fix: route workspace tree to host when sandbox_mode is native/local

Summary

Verdict: merge with fixes. The core fix is correct and minimal: gating workspace_tree_is_sandbox_backed() on both opensandbox_sandbox and sandbox_mode directly addresses the reported 503s. The refactoring of the test helper into _settings() removes eight lines of repeated class boilerplate and makes all four new tests visibly more readable. test_workspace_tree_uses_native_when_sandbox_mode_native_without_shell_tools directly reproduces the exact config from the issue report.

Strengths:

  • workspace_tree_is_sandbox_backed() fix is minimal: two lines, correct semantics, and the function now has a docstring explaining the intent.
  • All four new @pytest.mark.unit tests assert both predicates (is_native_backed and is_sandbox_backed) in tandem, verifying they stay mutually consistent rather than testing in isolation.
  • test_workspace_tree_uses_native_when_sandbox_mode_native_without_shell_tools directly exercises the reported bug config (opensandbox_sandbox=True, sandbox_mode=native, shell tools off), giving an explicit regression guard.
  • 47 tests pass with no regressions.

What to resolve before merge:

Minor:

  1. MIN-1 — workspace_tree_is_native_backed() now routes any sandbox_mode=native/local config to fetch_native_workspace_tree, which silently skips the ensure_thread_workspace_seeded() call that fetch_host_workspace_tree makes. For deployments using workspace fixture seeding (SEED_MODES env var), the first tree poll on a new thread will return an empty workspace instead of pre-populated fixtures.

Issues

[MIN-1] workspace_tree_is_native_backed() now routes sandbox_mode=native/local to fetch_native_workspace_tree, which skips ensure_thread_workspace_seeded() that fetch_host_workspace_tree calls
src/cuga/backend/server/workspace_sandbox.py:42-44

What's wrong: The new guard at lines 42–44 (if mode in ("native", "local"): return True) fires before the workspace_tree_is_sandbox_backed() check, and it fires for any config with sandbox_mode=native/local — including opensandbox_sandbox=False with no shell tools, which previously fell through to main.py:3511 → fetch_host_workspace_tree(). fetch_host_workspace_tree (in workspace_upload.py:280) calls ensure_thread_workspace_seeded(tid) before scanning; fetch_native_workspace_tree (in workspace_sandbox.py:176) does not. These two functions scan the same physical directory (<cwd>/cuga_workspace/<thread_id>/), so the outputs are identical in the common case. But the seeding call is silently dropped.

What it can cause: When SEED_MODES is configured (a CRM or CI fixture-injection env var), the first workspace-tree API call for a new thread normally seeds fixture files into the thread directory. After this PR, any deployment with sandbox_mode=native/local (regardless of opensandbox_sandbox) skips that seeding entirely. The workspace tree panel will appear empty on first load instead of showing the pre-seeded fixtures. The root cause is invisible from the API response — it just returns an empty tree with no error.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complexity: medium Moderate scope — multiple files or non-trivial logic readability: good Clear PR goal and description; easy to review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /api/workspace/tree logs opensandbox import error and returns 503 in native sandbox mode

2 participants