Skip to content

fix: wire cloud workspace config - #112

Merged
delete-cloud merged 4 commits into
mainfrom
feat/adr-0019-provider-wiring
May 5, 2026
Merged

delete-cloud merged 4 commits into
mainfrom
feat/adr-0019-provider-wiring

Conversation

@delete-cloud

@delete-cloud delete-cloud commented May 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add an app-layer cloud workspace provider registry/factory seam
  • wire HTTP session-manager startup to build a cloud-aware binding resolver from config
  • add focused regressions for config-driven cloud binding behavior and fail-fast no-config behavior

Verification

  • uv run pytest tests/ui/test_execution_binding.py tests/ui/test_http_server.py -k "cloud_workspace_config or wires_cloud_client_factory or fails_fast_not_local_fallback or cloud" -v
  • uv run pytest tests/ui/test_execution_binding.py tests/ui/test_session_manager_runtime.py -k "cloud" -v
  • uv run pytest tests/ui/test_http_server_failover.py -k "events" -v
  • uv run pytest tests/ui/test_session_manager_public_api.py -k "ensure_session_runtime" -v
  • manual ASGI HTTP check: POST /sessions with cloud execution_binding returned 200 and session_id present

Summary by CodeRabbit

  • New Features

    • Added a workspace-provider registry so different cloud workspace providers can be registered and selected via configuration.
    • HTTP server now reads cloud workspace settings and can enable a cloud-aware binding resolver for session execution when configured.
  • Tests

    • Added tests covering provider registration, config validation, and cloud-binding behavior.

delete-cloud and others added 2 commits May 5, 2026 19:32
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@coderabbitai

coderabbitai Bot commented May 5, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@delete-cloud has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 1 second before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3cd9f89b-15e9-4e29-951d-42824d6a1ce1

📥 Commits

Reviewing files that changed from the base of the PR and between 4989dae and 9fa6b36.

📒 Files selected for processing (1)
  • coding-agent/tests/ui/test_execution_binding.py
📝 Walkthrough

Walkthrough

Adds a workspace-provider registry and factory resolver, exposes those symbols from the environment package, wires cloud-workspace config into the HTTP server's binding resolver construction, and adds tests and test doubles covering provider registration, resolution, and validation.

Changes

Workspace Provider Registry and HTTP Server Integration

Layer / File(s) Summary
Core Types & Protocol
coding-agent/src/coding_agent/environment/workspace_provider.py
Defines CloudWorkspaceClientFactory type alias and WorkspaceProvider protocol (build_cloud_client_factory(config)).
Registry Storage
coding-agent/src/coding_agent/environment/workspace_provider.py
Adds module-level _WORKSPACE_PROVIDERS: dict[str, WorkspaceProvider] for registered providers.
Registration API
coding-agent/src/coding_agent/environment/workspace_provider.py
Implements register_workspace_provider(name, provider) which normalizes names, validates non-empty, and stores provider.
Factory Resolution
coding-agent/src/coding_agent/environment/workspace_provider.py
Implements cloud_client_factory_from_config(config) which validates config["provider"], looks up provider, raises ValueError on issues, and returns the provider's build_cloud_client_factory(config) result.
Package Re-exports
coding-agent/src/coding_agent/environment/__init__.py
Switches to relative imports and expands __all__ to export CloudWorkspaceClientFactory, WorkspaceProvider, cloud_client_factory_from_config, and register_workspace_provider alongside existing exports.
Server Wiring
coding-agent/src/coding_agent/ui/http_server.py
Adds _load_cloud_workspace_config() and _build_binding_resolver(); when cloud_workspace.enabled is true, builds resolver using cloud_client_factory_from_config(...) and injects into SessionManager.
Tests & Fixtures
coding-agent/tests/ui/test_execution_binding.py, coding-agent/tests/ui/test_http_server.py
Adds FakeWorkspaceProvider and FakeCloudClient test doubles; tests provider registration, factory building, config forwarding, and validation for missing provider field.

Sequence Diagram

sequenceDiagram
    participant Server as HTTP Server
    participant Config as agent.toml Config
    participant Registry as Workspace Registry
    participant Provider as Workspace Provider
    participant Factory as CloudClientFactory
    participant Client as Cloud Workspace Client

    Server->>Config: load cloud_workspace settings
    Config-->>Server: returns config (enabled, provider, ...)
    Server->>Registry: cloud_client_factory_from_config(config)
    Registry->>Registry: validate provider name
    Registry->>Provider: lookup registered provider
    Provider->>Factory: build_cloud_client_factory(config)
    Factory-->>Registry: return CloudClientFactory
    Registry-->>Server: return CloudClientFactory
    Server->>Server: inject factory into SessionManager

    Note over Server,Factory: At execution time
    Server->>Factory: invoke with CloudWorkspaceBinding
    Factory->>Client: construct Cloud Workspace Client
    Client-->>Server: client ready for operations
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through code to plant a seed,

Providers queued up, a registry freed.
Config whispers the provider name,
Factories wake and play the game.
Hop—cloud clients spring to life with speed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% 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 'fix: wire cloud workspace config' directly summarizes the main change: wiring cloud workspace configuration into the application layer.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/adr-0019-provider-wiring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
coding-agent/src/coding_agent/ui/http_server.py (2)

165-180: ⚡ Quick win

_build_session_manager tests don't patch _build_binding_resolver, creating an environment-dependent test.

The three test_build_session_manager_* tests mock _load_storage_config but not _build_binding_resolver. If a developer's local agent.toml has cloud_workspace.enabled = true with an unregistered provider, _build_session_manager() will raise a ValueError from cloud_client_factory_from_config, causing those tests to fail despite being unrelated to cloud binding.

Add a monkeypatch.setattr for _build_binding_resolver (or _load_cloud_workspace_config) in all three tests:

monkeypatch.setattr(
    "coding_agent.ui.http_server._build_binding_resolver",
    lambda: DefaultBindingResolver(),
)
🤖 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 `@coding-agent/src/coding_agent/ui/http_server.py` around lines 165 - 180, The
tests for _build_session_manager are leaking local environment because they mock
_load_storage_config but not _build_binding_resolver; update each
test_build_session_manager_* to monkeypatch _build_binding_resolver to return a
simple resolver (e.g., DefaultBindingResolver()) so cloud provider discovery
isn't executed during tests; use
monkeypatch.setattr("coding_agent.ui.http_server._build_binding_resolver",
lambda: DefaultBindingResolver()) (or alternatively patch
_load_cloud_workspace_config) in all three tests to isolate them from local
agent.toml settings.

96-121: ⚡ Quick win

_load_cloud_workspace_config is a verbatim copy of _load_storage_config, causing a double agent.toml read.

The two functions differ only in the config-section key ("cloud_workspace" vs "storage") and the log message. Extracting a shared helper removes the duplication and halves the file-system reads on startup.

♻️ Proposed refactor
-def _load_storage_config() -> dict[str, Any]:
-    config_path = Path(__file__).resolve().parent.parent / "agent.toml"
-    try:
-        return cast(
-            dict[str, Any], load_agent_toml(config_path).extra.get("storage", {})
-        )
-    except (ConfigError, OSError) as exc:
-        if isinstance(exc, ConfigError):
-            detail = exc.args[0] if exc.args and isinstance(exc.args[0], str) else ""
-            if not detail.startswith("config file not found:"):
-                raise
-        logger.warning(
-            "Unable to load storage config from %s; using defaults",
-            config_path,
-            exc_info=True,
-        )
-        return {}
-
-
-def _load_cloud_workspace_config() -> dict[str, Any]:
-    config_path = Path(__file__).resolve().parent.parent / "agent.toml"
-    try:
-        return cast(
-            dict[str, Any], load_agent_toml(config_path).extra.get("cloud_workspace", {})
-        )
-    except (ConfigError, OSError) as exc:
-        if isinstance(exc, ConfigError):
-            detail = exc.args[0] if exc.args and isinstance(exc.args[0], str) else ""
-            if not detail.startswith("config file not found:"):
-                raise
-        logger.warning(
-            "Unable to load cloud workspace config from %s; using defaults",
-            config_path,
-            exc_info=True,
-        )
-        return {}
+def _load_config_section(section: str) -> dict[str, Any]:
+    config_path = Path(__file__).resolve().parent.parent / "agent.toml"
+    try:
+        return cast(
+            dict[str, Any], load_agent_toml(config_path).extra.get(section, {})
+        )
+    except (ConfigError, OSError) as exc:
+        if isinstance(exc, ConfigError):
+            detail = exc.args[0] if exc.args and isinstance(exc.args[0], str) else ""
+            if not detail.startswith("config file not found:"):
+                raise
+        logger.warning(
+            "Unable to load [%s] config from %s; using defaults",
+            section,
+            config_path,
+            exc_info=True,
+        )
+        return {}
+
+
+def _load_storage_config() -> dict[str, Any]:
+    return _load_config_section("storage")
+
+
+def _load_cloud_workspace_config() -> dict[str, Any]:
+    return _load_config_section("cloud_workspace")
🤖 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 `@coding-agent/src/coding_agent/ui/http_server.py` around lines 96 - 121, The
two functions _load_cloud_workspace_config and _load_storage_config duplicate
logic and cause agent.toml to be read twice; extract a small helper (eg.
_load_agent_toml_section(section: str) -> dict[str, Any]) that takes the section
key ("cloud_workspace" or "storage") and handles
load_agent_toml(config_path).extra.get(...), ConfigError/OSError handling and
logging, then make both _load_cloud_workspace_config and _load_storage_config
call that helper; keep _build_binding_resolver behavior unchanged (it should
call _load_cloud_workspace_config) and ensure cloud_client_factory_from_config
is still passed the returned dict.
coding-agent/tests/ui/test_http_server.py (1)

131-166: 💤 Low value

FakeCloudClient is defined but not referenced by any test in this file.

It duplicates the FakeCloudClient in test_execution_binding.py (with a different workspace_id). If it is needed for upcoming cloud-wiring tests in this file, consider sharing a single fixture via conftest.py instead.

🤖 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 `@coding-agent/tests/ui/test_http_server.py` around lines 131 - 166, The file
defines a duplicate FakeCloudClient class (FakeCloudClient with workspace_id)
that isn't used; remove this duplicate and reuse the existing shared fixture
instead: either import or move the canonical FakeCloudClient into a central test
fixture (conftest.py) and have tests in test_http_server.py reference that
fixture (or import the class) rather than defining another FakeCloudClient;
ensure the shared fixture provides the same attributes/methods (workspace_id,
workspace_url, default_cwd, read_file, write_file, replace_file, glob_files,
grep_search, apply_patch, run_command) so tests continue to work.
🤖 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.

Inline comments:
In `@coding-agent/tests/ui/test_execution_binding.py`:
- Around line 237-241: The test's pytest.raises(..., match=...) uses unescaped
dots so "cloud_workspace.provider" and "cloud_workspace.enabled" are treated as
regex wildcards; update the assertion in test_execution_binding.py to use a
literal match by escaping the dots (e.g., "cloud_workspace\\.provider" and
"cloud_workspace\\.enabled") or build the pattern via re.escape, keeping the
call to cloud_client_factory_from_config({"enabled": True}) and the same
ValueError expectation.

---

Nitpick comments:
In `@coding-agent/src/coding_agent/ui/http_server.py`:
- Around line 165-180: The tests for _build_session_manager are leaking local
environment because they mock _load_storage_config but not
_build_binding_resolver; update each test_build_session_manager_* to monkeypatch
_build_binding_resolver to return a simple resolver (e.g.,
DefaultBindingResolver()) so cloud provider discovery isn't executed during
tests; use
monkeypatch.setattr("coding_agent.ui.http_server._build_binding_resolver",
lambda: DefaultBindingResolver()) (or alternatively patch
_load_cloud_workspace_config) in all three tests to isolate them from local
agent.toml settings.
- Around line 96-121: The two functions _load_cloud_workspace_config and
_load_storage_config duplicate logic and cause agent.toml to be read twice;
extract a small helper (eg. _load_agent_toml_section(section: str) -> dict[str,
Any]) that takes the section key ("cloud_workspace" or "storage") and handles
load_agent_toml(config_path).extra.get(...), ConfigError/OSError handling and
logging, then make both _load_cloud_workspace_config and _load_storage_config
call that helper; keep _build_binding_resolver behavior unchanged (it should
call _load_cloud_workspace_config) and ensure cloud_client_factory_from_config
is still passed the returned dict.

In `@coding-agent/tests/ui/test_http_server.py`:
- Around line 131-166: The file defines a duplicate FakeCloudClient class
(FakeCloudClient with workspace_id) that isn't used; remove this duplicate and
reuse the existing shared fixture instead: either import or move the canonical
FakeCloudClient into a central test fixture (conftest.py) and have tests in
test_http_server.py reference that fixture (or import the class) rather than
defining another FakeCloudClient; ensure the shared fixture provides the same
attributes/methods (workspace_id, workspace_url, default_cwd, read_file,
write_file, replace_file, glob_files, grep_search, apply_patch, run_command) so
tests continue to work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 014dcba5-67a8-4d1f-ba74-5ed47744c3fe

📥 Commits

Reviewing files that changed from the base of the PR and between a8b52b0 and dad6cbc.

📒 Files selected for processing (5)
  • coding-agent/src/coding_agent/environment/__init__.py
  • coding-agent/src/coding_agent/environment/workspace_provider.py
  • coding-agent/src/coding_agent/ui/http_server.py
  • coding-agent/tests/ui/test_execution_binding.py
  • coding-agent/tests/ui/test_http_server.py

Comment thread coding-agent/tests/ui/test_execution_binding.py Outdated
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

@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)
coding-agent/tests/ui/test_execution_binding.py (1)

196-208: 💤 Low value

Missing return type annotation on build_cloud_client_factory.

Line 200 omits the return type for build_cloud_client_factory, leaving it as implicitly None in tools that rely on annotations (mypy, pyright). The method clearly returns Callable[[CloudWorkspaceBinding], FakeCloudClient].

🔧 Proposed fix
+from collections.abc import Callable
+
 class FakeWorkspaceProvider:
     def __init__(self) -> None:
         self.seen_configs: list[dict[str, object]] = []

-    def build_cloud_client_factory(self, config: dict[str, object]):
+    def build_cloud_client_factory(
+        self, config: dict[str, object]
+    ) -> Callable[[CloudWorkspaceBinding], FakeCloudClient]:
🤖 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 `@coding-agent/tests/ui/test_execution_binding.py` around lines 196 - 208, The
method build_cloud_client_factory is missing a return type annotation; update
its signature to return a callable of type Callable[[CloudWorkspaceBinding],
FakeCloudClient] (i.e., add -> Callable[[CloudWorkspaceBinding],
FakeCloudClient]) and ensure typing.Callable is imported if not already present;
reference the build_cloud_client_factory function and the CloudWorkspaceBinding
and FakeCloudClient types when making the change.
🤖 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 `@coding-agent/tests/ui/test_execution_binding.py`:
- Around line 196-208: The method build_cloud_client_factory is missing a return
type annotation; update its signature to return a callable of type
Callable[[CloudWorkspaceBinding], FakeCloudClient] (i.e., add ->
Callable[[CloudWorkspaceBinding], FakeCloudClient]) and ensure typing.Callable
is imported if not already present; reference the build_cloud_client_factory
function and the CloudWorkspaceBinding and FakeCloudClient types when making the
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9e093ed0-e7d7-473f-9eaa-e9bb6d94cab8

📥 Commits

Reviewing files that changed from the base of the PR and between dad6cbc and 4989dae.

📒 Files selected for processing (1)
  • coding-agent/tests/ui/test_execution_binding.py

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
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