Skip to content

Prepare local runtimes through LocalDaemonExecutor - #439

Merged
delete-cloud merged 1 commit into
mainfrom
dogfood/local-runtime-preparation-executor
Jun 2, 2026
Merged

delete-cloud merged 1 commit into
mainfrom
dogfood/local-runtime-preparation-executor

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Jun 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add a LocalDaemonRuntimePreparation API on LocalDaemonExecutor
  • route local ensure_session_runtime and replace_session_runtime_config rebuilds through the executor preparation boundary
  • keep cloud/non-local runtime builders on the existing compatibility path
  • add PR-17 task packet and regressions for executor-owned preparation

ADR-0058 Direction

This moves local REPL/runtime rebuild setup one step away from SessionManager-owned runtime construction and toward executor-owned local runtime preparation. Turn lifecycle persistence hooks and checkpoint restore are intentionally left for later slices.

Tests

  • uv run pytest tests/coding_agent/test_local_daemon_executor.py -v
  • uv run pytest tests/ui/test_session_manager_runtime.py -k "ensure_session_runtime_uses_default_run_target_workspace or replace_session_runtime_config_uses_default_run_target_workspace" -v
  • uv run pytest tests/ui/test_session_manager_runtime.py -v
  • uv run pytest tests/cli/test_repl.py -k "switch or model or checkpoint" -v
  • uv run pytest tests/ui/test_http_server_failover.py -k "events or event_queue or owner_change or stale_owner or stream or queue_registration" -v
  • uv run pytest tests/ui/test_session_manager_public_api.py -k "event_queue or cleanup or teardown" -v
  • uv run pytest tests/ui/test_session_manager_runtime.py -k "event_queues or clear_sessions or close_session or shutdown_session_runtime or cleanup" -v
  • uv run ruff check src/coding_agent/executors/local_daemon.py src/coding_agent/executors/init.py src/coding_agent/server/session_manager.py tests/coding_agent/test_local_daemon_executor.py tests/ui/test_session_manager_runtime.py
  • git diff --check

Summary by CodeRabbit

  • Refactor
    • Enhanced local runtime preparation workflow by centralizing initialization logic through a single execution path, improving reliability and consistency in workspace target handling across local and cloud runtime scenarios.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@delete-cloud, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 44 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0bda2062-8753-4a5c-97b0-4d5601c3374e

📥 Commits

Reviewing files that changed from the base of the PR and between d63f548 and 3a815d4.

📒 Files selected for processing (6)
  • coding-agent/.opencode/prompts/tasks/local-daemon-pr17-runtime-preparation-boundary.md
  • coding-agent/src/coding_agent/executors/__init__.py
  • coding-agent/src/coding_agent/executors/local_daemon.py
  • coding-agent/src/coding_agent/server/session_manager.py
  • coding-agent/tests/coding_agent/test_local_daemon_executor.py
  • coding-agent/tests/ui/test_session_manager_runtime.py
📝 Walkthrough

Walkthrough

This PR establishes a runtime preparation boundary for local-daemon workspaces by adding a prepare_runtime() method to LocalDaemonExecutor and routing SessionManager's local runtime setup through it, ensuring non-turn initialization is separated from turn execution.

Changes

Local Daemon Executor Runtime Preparation Boundary

Layer / File(s) Summary
Task specification
.opencode/prompts/tasks/local-daemon-pr17-runtime-preparation-boundary.md
Defines the preparation boundary goal: moving local REPL/runtime rebuild preparation behind LocalDaemonExecutor so SessionManager no longer constructs non-turn local runtime setup directly. Scope, acceptance criteria, and test commands are enumerated.
LocalDaemonExecutor preparation API
src/coding_agent/executors/__init__.py, src/coding_agent/executors/local_daemon.py
Introduces LocalDaemonRuntimePreparation dataclass bundling request and runtime_provider, exports it publicly, implements LocalDaemonExecutor.prepare_runtime() to validate targets and delegate to the provider, and refactors execute_runtime() to use the new method.
SessionManager local-daemon routing
src/coding_agent/server/session_manager.py
Adds _is_local_daemon_run_target() and _runtime_preparation_request() helpers; refactors _build_session_runtime() to detect local-daemon targets and route them through LocalDaemonExecutor.prepare_runtime() while preserving the direct build path for non-local targets.
Executor unit tests
tests/coding_agent/test_local_daemon_executor.py
Tests prepare_runtime() return value and runtime provider invocation (positive path), and validates rejection of non-local-daemon targets with RunExecutorTargetError (negative path).
SessionManager integration tests
tests/ui/test_session_manager_runtime.py
Extended RecordingLocalDaemonExecutor to track prepare_runtime() calls; updated workspace resolution tests for ensure_session_runtime() and replace_session_runtime_config() to inject the recording executor and assert preparation targets match session.default_run_target.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • delete-cloud/Coding-Agent-Loop#436: Both PRs refactor the LocalDaemonExecutor runtime lifecycle; this PR adds the preparation boundary while #436 modifies execution error handling.
  • delete-cloud/Coding-Agent-Loop#427: Both PRs route local runtime initialization through LocalDaemonExecutor; this PR adds the earlier prepare_runtime() stage while #427 added the turn execution path.

Poem

🐰 A daemon stands ready, prepared for the call,
No runtime built without crossing its wall,
SessionManager delegates, boundaries align,
Local paths routed through proper design,
Prep work and turns now know their place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.35% 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 'Prepare local runtimes through LocalDaemonExecutor' directly and clearly summarizes the main change: routing local runtime preparation through the LocalDaemonExecutor instead of SessionManager.
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 dogfood/local-runtime-preparation-executor

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
coding-agent/src/coding_agent/server/session_manager.py (1)

4707-4722: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Build from the validated preparation request, not session.default_run_target.

LocalDaemonExecutor.prepare_runtime() validates LocalDaemonRuntimePreparation.request, but this callback drops that request and _build_session_runtime_direct() re-reads session.default_run_target. That means the executor can validate one target and the provider can still prepare a different runtime if session state changes before execution. Please thread the RunRequest/RunTarget into the direct builder and resolve the environment from that validated request instead of mutable session state.

Suggested direction
-    async def _build_session_runtime_direct(
+    async def _build_session_runtime_direct(
         self,
         session: Session,
         *,
+        target: RunTarget | None = None,
         model_name: str | None = None,
         provider_name: str | None = None,
         base_url: str | None = None,
         max_steps: int | None = None,
         approval_policy: ApprovalPolicy | None = None,
     ) -> tuple[Any, Any, PipelineAdapter]:
@@
-        environment = self._resolve_environment_for_run_target(session.default_run_target)
+        environment = self._resolve_environment_for_run_target(
+            session.default_run_target if target is None else target
+        )
@@
-        async def prepare_runtime(_: RunRequest) -> LocalDaemonRuntimeBinding:
+        async def prepare_runtime(request: RunRequest) -> LocalDaemonRuntimeBinding:
             pipeline, ctx, adapter = await self._build_session_runtime_direct(
                 session,
+                target=request.target,
                 model_name=model_name,
                 provider_name=provider_name,
                 base_url=base_url,
                 max_steps=max_steps,
                 approval_policy=approval_policy,

Also applies to: 4765-4786

🤖 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/server/session_manager.py` around lines 4707 -
4722, The code is using mutable session.default_run_target instead of the
validated preparation request; update LocalDaemonExecutor.prepare_runtime to
pass the validated RunRequest/RunTarget into _build_session_runtime_direct and
change _build_session_runtime_direct to accept that RunRequest/RunTarget
parameter; then use that passed-in run target when calling
_resolve_environment_for_run_target and _environment_workspace_root (instead of
session.default_run_target) and forward the same validated target/context into
_create_agent_for_session (where workspace_root and environment are computed),
and apply the same threading change for the duplicate block around the other
occurrence (~4765-4786) so the prepared-and-validated request is always used
instead of mutable session state.
🧹 Nitpick comments (3)
coding-agent/tests/ui/test_session_manager_runtime.py (3)

876-878: 💤 Low value

Consider more explicit assertions.

The list comprehension assertion is correct but could be clearer by separating the length check from the target comparison. This makes test failures more informative.

📊 Suggested alternative
-    assert [preparation.request.target for preparation in local_executor.preparations] == [
-        session.default_run_target
-    ]
+    assert len(local_executor.preparations) == 1
+    assert local_executor.preparations[0].request.target == session.default_run_target

This makes it immediately clear whether the failure is due to the wrong number of preparations or the wrong target.

🤖 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_session_manager_runtime.py` around lines 876 -
878, The test currently uses a single list-comprehension assertion that mixes
count and value checks; change it to two explicit assertions after creating the
session (session_id = await manager.create_session()): first assert the number
of preparations (e.g., assert len(preparations) == expected_count) and then
assert the specific target value (e.g., assert preparations[0].target ==
"expected_target"), referencing the preparations produced by the FakeAdapter to
make failures clearer.

943-945: 💤 Low value

Consider more explicit assertions.

Same suggestion as lines 876-878: separate the length check from the target comparison for clearer test failure messages.

📊 Suggested alternative
-    assert [preparation.request.target for preparation in local_executor.preparations] == [
-        session.default_run_target
-    ]
+    assert len(local_executor.preparations) == 1
+    assert local_executor.preparations[0].request.target == session.default_run_target
🤖 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_session_manager_runtime.py` around lines 943 -
945, The test currently compares a collection to the expected AgentRunRecord in
one step; change it to two explicit assertions so failures are clearer: first
assert the length (e.g., assert len(runs) == 1) and then assert the contents
match (e.g., compare runs[0] to previous_run or assert specific fields),
referencing the existing AgentRunRecord instance named previous_run and the
collection variable runs in test_session_manager_runtime.py to locate where to
split the checks.

345-350: 💤 Low value

Consider adding return type annotation.

The prepare_runtime method override is missing a return type annotation. While this is test code and not strictly required, adding the annotation would improve consistency with the base class interface and make the test double's contract clearer.

📝 Suggested enhancement
 async def prepare_runtime(
     self,
     preparation: LocalDaemonRuntimePreparation,
-):
+) -> LocalExecutionBinding:
     self.preparations.append(preparation)
     return await preparation.runtime_provider.prepare_runtime(preparation.request)
🤖 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_session_manager_runtime.py` around lines 345 -
350, The test double method prepare_runtime should include an explicit return
type to match the base class contract: add the appropriate coroutine return
annotation (e.g., -> Awaitable[RuntimeType] or -> Coroutine[Any, Any,
RuntimeType] or the concrete Runtime type used in the codebase) to the
prepare_runtime signature; update the signature on the
LocalDaemonRuntimePreparation-handling method prepare_runtime to include that
return type and ensure imports (typing.Awaitable/Coroutine or the runtime type)
are added if necessary so the override's type matches the
runtime_provider.prepare_runtime return type.
🤖 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/.opencode/prompts/tasks/local-daemon-pr17-runtime-preparation-boundary.md`:
- Around line 47-50: The pytest invocation in the prompt uses a direct path to
tests/ui/test_session_manager_runtime.py which is outside the documented suites;
update the second command in local-daemon-pr17-runtime-preparation-boundary.md
to call the appropriate suite-level pytest entry (e.g., run pytest against the
tests/coding_agent/ or the repo's defined suite that includes ui tests) or add a
short justification comment explaining why that specific test file must be
invoked directly; change the line that currently references
tests/ui/test_session_manager_runtime.py to the suite-level invocation per the
repo guideline (tests/coding_agent/ or the agreed suite) or add the
justification text inline so reviewers understand the intentional exception.

In `@coding-agent/tests/coding_agent/test_local_daemon_executor.py`:
- Around line 355-396: Add a new async test that mirrors
test_local_daemon_executor_rejects_runtime_preparation_for_non_local_target but
flips the mismatch: construct a RunRequest whose target.executor is a
LocalDaemon executor ref (e.g., LocalDaemonExecutorRef or the local-daemon
binding used by LocalDaemonExecutor) while target.workspace is a non-local
CloudWorkspaceRef, then call
LocalDaemonExecutor().prepare_runtime(LocalDaemonRuntimePreparation(...)) and
assert it raises RunExecutorTargetError (use pytest.raises with an appropriate
match for the local-workspace requirement). Ensure you use the same
FakeRuntimeProvider and FakeRuntimeAdapter setup and assert provider.requests
and adapter.prompts remain empty; this will cover the _validate_request_target()
branch that rejects local-daemon targets paired with non-local workspaces.

---

Outside diff comments:
In `@coding-agent/src/coding_agent/server/session_manager.py`:
- Around line 4707-4722: The code is using mutable session.default_run_target
instead of the validated preparation request; update
LocalDaemonExecutor.prepare_runtime to pass the validated RunRequest/RunTarget
into _build_session_runtime_direct and change _build_session_runtime_direct to
accept that RunRequest/RunTarget parameter; then use that passed-in run target
when calling _resolve_environment_for_run_target and _environment_workspace_root
(instead of session.default_run_target) and forward the same validated
target/context into _create_agent_for_session (where workspace_root and
environment are computed), and apply the same threading change for the duplicate
block around the other occurrence (~4765-4786) so the prepared-and-validated
request is always used instead of mutable session state.

---

Nitpick comments:
In `@coding-agent/tests/ui/test_session_manager_runtime.py`:
- Around line 876-878: The test currently uses a single list-comprehension
assertion that mixes count and value checks; change it to two explicit
assertions after creating the session (session_id = await
manager.create_session()): first assert the number of preparations (e.g., assert
len(preparations) == expected_count) and then assert the specific target value
(e.g., assert preparations[0].target == "expected_target"), referencing the
preparations produced by the FakeAdapter to make failures clearer.
- Around line 943-945: The test currently compares a collection to the expected
AgentRunRecord in one step; change it to two explicit assertions so failures are
clearer: first assert the length (e.g., assert len(runs) == 1) and then assert
the contents match (e.g., compare runs[0] to previous_run or assert specific
fields), referencing the existing AgentRunRecord instance named previous_run and
the collection variable runs in test_session_manager_runtime.py to locate where
to split the checks.
- Around line 345-350: The test double method prepare_runtime should include an
explicit return type to match the base class contract: add the appropriate
coroutine return annotation (e.g., -> Awaitable[RuntimeType] or ->
Coroutine[Any, Any, RuntimeType] or the concrete Runtime type used in the
codebase) to the prepare_runtime signature; update the signature on the
LocalDaemonRuntimePreparation-handling method prepare_runtime to include that
return type and ensure imports (typing.Awaitable/Coroutine or the runtime type)
are added if necessary so the override's type matches the
runtime_provider.prepare_runtime return type.
🪄 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: 6b42751b-86db-46d5-8575-5ae9001677cc

📥 Commits

Reviewing files that changed from the base of the PR and between 289630f and d63f548.

📒 Files selected for processing (6)
  • coding-agent/.opencode/prompts/tasks/local-daemon-pr17-runtime-preparation-boundary.md
  • coding-agent/src/coding_agent/executors/__init__.py
  • coding-agent/src/coding_agent/executors/local_daemon.py
  • coding-agent/src/coding_agent/server/session_manager.py
  • coding-agent/tests/coding_agent/test_local_daemon_executor.py
  • coding-agent/tests/ui/test_session_manager_runtime.py

@delete-cloud
delete-cloud force-pushed the dogfood/local-runtime-preparation-executor branch 3 times, most recently from 9bc0c9e to a04c734 Compare June 2, 2026 18:19
@delete-cloud
delete-cloud force-pushed the dogfood/local-runtime-preparation-executor branch from a04c734 to 3a815d4 Compare June 2, 2026 18:24
@delete-cloud
delete-cloud merged commit 5e679b1 into main Jun 2, 2026
1 check passed
@delete-cloud
delete-cloud deleted the dogfood/local-runtime-preparation-executor branch June 2, 2026 18:28
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