Skip to content

feat: add execution binding foundation - #75

Merged
delete-cloud merged 7 commits into
mainfrom
feat/execution-binding-foundation
Apr 20, 2026
Merged

delete-cloud merged 7 commits into
mainfrom
feat/execution-binding-foundation

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Apr 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • persist typed execution binding metadata on UI sessions and default legacy records from repo_path
  • resolve workspace roots from bindings when creating, rehydrating, and checkpoint-restoring runtimes
  • inject the binding resolver into session-manager runtime paths with focused regression coverage

Test Plan

  • uv run pytest tests/ui/test_execution_binding.py -v
  • uv run pytest tests/ui/test_session_manager_public_api.py -k "binding" -v
  • uv run pytest tests/ui/test_session_manager_runtime.py -k "binding" -v

Summary by CodeRabbit

  • New Features

    • First-class session execution bindings (local/cloud) persisted on sessions with backward-compatible defaults.
    • PostgreSQL-backed session ownership with time-bounded leases and owner enforcement for runtime actions (run, restore, close).
  • Documentation

    • Added ADRs, checklist, and implementation plan describing Phase 2 ownership and execution-binding design.
  • Tests

    • Added unit tests covering binding serialization/resolution, owner-store behavior, and session manager owner checks.

delete-cloud and others added 6 commits April 19, 2026 18:19
Record the Phase 2 boundary after Phase 1 PG persistence and add an implementation checklist for owner, lease, fencing, routing, and failover work in the HTTP session stack.

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>
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>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Keep the workspace resolver stable on SessionManager and cover the missing cloud binding failures.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Copilot AI review requested due to automatic review settings April 20, 2026 00:35
@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c23a4804-2ce6-43f9-a687-d19b402cdd4f

📥 Commits

Reviewing files that changed from the base of the PR and between 0477524 and 768724c.

📒 Files selected for processing (4)
  • coding-agent/docs/adr/0014-separate-session-execution-binding-from-runtime-ownership.md
  • coding-agent/tests/ui/test_execution_binding.py
  • coding-agent/tests/ui/test_session_manager_public_api.py
  • coding-agent/tests/ui/test_session_manager_runtime.py
✅ Files skipped from review due to trivial changes (1)
  • coding-agent/docs/adr/0014-separate-session-execution-binding-from-runtime-ownership.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • coding-agent/tests/ui/test_session_manager_runtime.py
  • coding-agent/tests/ui/test_execution_binding.py
  • coding-agent/tests/ui/test_session_manager_public_api.py

📝 Walkthrough

Walkthrough

Introduces a first-class ExecutionBinding abstraction (local/cloud) and a BindingResolver; persists bindings on Session with backward-compatible defaults; adds a PostgreSQL-backed session owner store with atomic acquire/renew/release semantics and enforces ownership in session runtime paths. Adds ADRs, implementation plan, and tests covering bindings and owner store behavior.

Changes

Cohort / File(s) Summary
Execution Binding Models
src/coding_agent/ui/execution_binding.py
New ExecutionBinding base plus LocalExecutionBinding and CloudWorkspaceBinding with typed to_dict/from_dict serialization and validation.
Binding Resolver
src/coding_agent/ui/binding_resolver.py
New BindingResolver protocol and DefaultBindingResolver that resolves local bindings to a Path and tool config; cloud cases raise NotImplementedError.
Session Manager Integration
src/coding_agent/ui/session_manager.py
Adds execution_binding to Session (round-tripped via store), accepts binding_resolver in ctor, resolves workspace/tool config for agent creation and restore, and defaults legacy sessions to LocalExecutionBinding(repo_path).
Session Owner Store (PG)
src/agentkit/storage/pg.py, src/coding_agent/ui/session_owner_store.py
Adds PGSessionOwnerStore with atomic SQL for acquire/renew/release (lease, fencing token) and an app-layer SessionOwnerStore wrapper.
Tests: Execution Binding & Resolver
tests/ui/test_execution_binding.py
New tests for binding serialisation, error cases, and DefaultBindingResolver behavior for local and cloud bindings.
Tests: Session Manager Public & Runtime
tests/ui/test_session_manager_public_api.py, tests/ui/test_session_manager_runtime.py
Tests round-trip persistence of execution_binding, legacy repo_path fallback, resolved workspace usage in run_agent, and checkpoint restore preserving binding.
Documentation & Plans
docs/adr/0013-..., docs/adr/0014-..., docs/pg-phase2-..., docs/superpowers/plans/...
New ADRs and a Phase‑2 checklist/plan documenting ownership model (leases, fencing tokens), owner enforcement points, and incremental implementation plan and tests.

Sequence Diagram(s)

sequenceDiagram
    participant SM as SessionManager
    participant SOS as SessionOwnerStore
    participant BR as BindingResolver
    participant CA as create_agent

    SM->>SOS: _assert_owner(session_id)
    SOS-->>SM: owner_id, fencing_token / success or conflict

    SM->>BR: resolve_workspace_root(execution_binding)
    BR-->>SM: workspace_root (Path)

    SM->>BR: resolve_tool_config(execution_binding)
    BR-->>SM: tool_config (dict)

    SM->>CA: create_agent(workspace_root, tool_config)
    CA-->>SM: agent instance
Loading
sequenceDiagram
    participant SM as SessionManager
    participant PG as PGSessionOwnerStore
    participant DB as PostgreSQL

    SM->>PG: acquire(session_id, owner_id, lease_ttl)
    PG->>DB: INSERT/UPDATE session_owners (atomic)
    DB-->>PG: fencing_token / result
    PG-->>SM: success or conflict

    SM->>PG: renew(session_id, owner_id, lease_ttl)
    PG->>DB: conditional UPDATE lease_expires_at
    DB-->>PG: renewed or stale
    PG-->>SM: success or stale error

    SM->>PG: release(session_id, owner_id)
    PG->>DB: conditional DELETE
    DB-->>PG: released
    PG-->>SM: success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • Issue #70 — Implements the same ExecutionBinding, BindingResolver, session owner store, and SessionManager owner-enforcement features described here.

Possibly related PRs

  • PR #69 — Related PostgreSQL store changes (adds PG-backed stores/schema and pool/codec wiring) that touch src/agentkit/storage/pg.py alongside this PR.

Poem

🐰 I bind the path, I hold the key,
Owners lease where runtimes be—
Local roots and cloud‑day dreams,
Tokens guard the ordered streams,
A rabbit cheers this tidy scheme! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add execution binding foundation' clearly and specifically describes the primary change, which is introducing the foundational execution binding abstraction for decoupling execution location from ownership.

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

✨ 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 feat/execution-binding-foundation

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a first-class “execution binding” abstraction for sessions, persists it in session metadata (with legacy fallback from repo_path), and routes runtime creation/rehydration/restore through a binding resolver with targeted regression tests.

Changes:

  • Add ExecutionBinding dataclasses (local + cloud skeleton) plus DefaultBindingResolver.
  • Persist execution_binding in Session store data and resolve workspace_root from bindings in run_agent, ensure_session_runtime, and checkpoint restore.
  • Add/extend UI tests to cover binding round-trips, legacy fallback behavior, and runtime workspace root resolution.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
coding-agent/src/coding_agent/ui/session_manager.py Persists/rehydrates execution_binding, resolves workspace root via resolver in runtime paths.
coding-agent/src/coding_agent/ui/execution_binding.py Adds typed binding models and (de)serialization helpers.
coding-agent/src/coding_agent/ui/binding_resolver.py Adds resolver interface + default local resolver (cloud unimplemented).
coding-agent/tests/ui/test_execution_binding.py New tests for binding (de)serialization and resolver behavior.
coding-agent/tests/ui/test_session_manager_public_api.py Adds session metadata persistence tests for execution bindings + legacy fallback.
coding-agent/tests/ui/test_session_manager_runtime.py Adds runtime/restore tests to ensure workspace_root comes from bindings; adjusts task cancellation assertions.
coding-agent/docs/adr/0014-separate-session-execution-binding-from-runtime-ownership.md ADR documenting the binding abstraction decision and acceptance criteria.
coding-agent/docs/adr/0013-define-phase2-multi-instance-session-ownership-for-pg-http-sessions.md ADR documenting Phase 2 ownership/lease/fencing direction.
coding-agent/docs/pg-phase2-multi-instance-checklist.md Checklist for multi-instance ownership implementation.
coding-agent/docs/superpowers/plans/2026-04-19-session-execution-binding-and-thin-phase2.md Detailed implementation plan for execution binding + Phase 2 substrate.
coding-agent/.opencode/prompts/session-execution-binding-phase2-task-packet.md Task packet prompt for implementing this work incrementally.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +187 to +190
workspace_root = (
repo_path_raw if repo_path_raw is not None else str(Path.cwd())
)
execution_binding = LocalExecutionBinding(workspace_root=workspace_root)

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

When hydrating legacy session metadata without an explicit execution_binding, the fallback workspace_root uses repo_path_raw or str(Path.cwd()) without resolving to an absolute path. This can yield relative workspace roots (and inconsistent behavior vs create_session, which resolves repo_path/cwd). Consider resolving (e.g., Path(...).resolve()) for both repo_path_raw and cwd before constructing LocalExecutionBinding, and keep repo_path consistent if you still persist it for backward-compat.

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +28
- Add an `ExecutionBinding` dataclass/protocol in `coding_agent.ui.execution_binding` with at least two concrete variants:
- `LocalExecutionBinding(workspace_root: Path)` for local filesystem mode.
- `CloudWorkspaceBinding(workspace_url: str, workspace_id: str)` for remote workspace mode.

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

ADR-0014 states LocalExecutionBinding(workspace_root: Path), but the implementation uses workspace_root: str (and the binding resolver converts it to Path). Please align the ADR wording with the code (or change the code to store a Path) to avoid misleading future implementers about the binding type and serialization expectations.

Copilot uses AI. Check for mistakes.
Comment on lines +352 to +353
fake_task = FakeTask()
session.task = cast(asyncio.Task[None], cast(object, fake_task))

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

The double cast cast(asyncio.Task[None], cast(object, fake_task)) is redundant and makes the test harder to read. A single cast to asyncio.Task (or assigning to a typed variable + type: ignore[assignment]) should be sufficient while still keeping fake_task available for assertions.

Copilot uses AI. Check for mistakes.
Comment on lines +382 to +383
fake_task = FakeTask()
session.task = cast(asyncio.Task[None], cast(object, fake_task))

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

Same as above: the extra cast(object, fake_task) is redundant here and reduces readability. Prefer a single cast (or a targeted type ignore) while keeping the separate fake_task variable for assertions.

Copilot uses AI. Check for mistakes.

@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: 3

🧹 Nitpick comments (1)
coding-agent/src/coding_agent/ui/session_manager.py (1)

181-190: Consider adding a warning when falling back to Path.cwd().

When deserializing a session that has neither execution_binding nor repo_path, the code falls back to Path.cwd(). This could silently produce unexpected behavior if the session is restored in a different working directory than where it was created.

Consider logging a warning in this edge case to aid debugging:

💡 Proposed enhancement
         else:
+            import logging
+            _logger = logging.getLogger(__name__)
             workspace_root = (
                 repo_path_raw if repo_path_raw is not None else str(Path.cwd())
             )
+            if repo_path_raw is None:
+                _logger.warning(
+                    "Session %s has no execution_binding or repo_path; defaulting to cwd",
+                    data.get("id"),
+                )
             execution_binding = LocalExecutionBinding(workspace_root=workspace_root)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@coding-agent/src/coding_agent/ui/session_manager.py` around lines 181 - 190,
When deserializing in session_manager, if both execution_binding is missing and
repo_path_raw is None the code falls back to Path.cwd() without notice; update
the branch that constructs LocalExecutionBinding(workspace_root=workspace_root)
to emit a warning via the logger (e.g., use self.logger.warning or module
logger) indicating we are falling back to Path.cwd() and include context
(session id or user if available), referencing execution_binding, repo_path_raw,
and LocalExecutionBinding so callers can locate and review the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@coding-agent/tests/ui/test_execution_binding.py`:
- Around line 13-52: Replace hardcoded "/tmp/repo" and the endswith check with
pytest's tmp_path fixture and assert exact resolved path: in
test_local_binding_round_trip and test_local_resolver_returns_absolute_path
create the LocalExecutionBinding using tmp_path / "repo" (cast to str), use that
same exact path when asserting restored.workspace_root and when comparing
resolver.resolve_workspace_root(binding) (convert to str) and
resolver.resolve_tool_config(binding) to ensure precise equality; also update
test_local_binding_requires_string_workspace_root to use tmp_path for
consistency if needed.

In `@coding-agent/tests/ui/test_session_manager_public_api.py`:
- Around line 161-204: Replace hardcoded "/tmp/..." paths with temporary
directories created at test runtime: in
test_session_metadata_round_trips_local_execution_binding, create a temp dir
(e.g., with tempfile.TemporaryDirectory() or pytest tmp_path) and pass its path
to LocalExecutionBinding(workspace_root=tmp_dir); in
test_session_metadata_defaults_missing_binding_to_local_from_repo_path, create a
temp dir and use its path for the stored "repo_path" value instead of
"/tmp/legacy-repo". Ensure the temp dir is cleaned up (use context manager or
pytest fixture) so tests remain deterministic and avoid Ruff S108.

In `@coding-agent/tests/ui/test_session_manager_runtime.py`:
- Around line 1032-1144: Replace hardcoded "/tmp" paths in the tests with
pytest's tmp_path (or a tempdir) so Ruff S108 is avoided: update
test_run_agent_uses_resolved_workspace_root_from_binding and
test_restore_checkpoint_preserves_execution_binding to accept a tmp_path fixture
and set session.repo_path and LocalExecutionBinding(workspace_root=...) to
str(tmp_path / "not-used-directly") / str(tmp_path / "bound-workspace")
(respectively), and update the assertions to compare
captured_kwargs["workspace_root"] and session.execution_binding.workspace_root
against Path(tmp_path / "...").resolve() or the string form as appropriate;
reference LocalExecutionBinding, SessionManager.run_agent, and
SessionManager._restore_checkpoint to locate changes.

---

Nitpick comments:
In `@coding-agent/src/coding_agent/ui/session_manager.py`:
- Around line 181-190: When deserializing in session_manager, if both
execution_binding is missing and repo_path_raw is None the code falls back to
Path.cwd() without notice; update the branch that constructs
LocalExecutionBinding(workspace_root=workspace_root) to emit a warning via the
logger (e.g., use self.logger.warning or module logger) indicating we are
falling back to Path.cwd() and include context (session id or user if
available), referencing execution_binding, repo_path_raw, and
LocalExecutionBinding so callers can locate and review the change.
🪄 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: f3cc185f-538e-4cd5-9570-62850016b4dc

📥 Commits

Reviewing files that changed from the base of the PR and between 9ffe246 and 0477524.

📒 Files selected for processing (11)
  • coding-agent/.opencode/prompts/session-execution-binding-phase2-task-packet.md
  • coding-agent/docs/adr/0013-define-phase2-multi-instance-session-ownership-for-pg-http-sessions.md
  • coding-agent/docs/adr/0014-separate-session-execution-binding-from-runtime-ownership.md
  • coding-agent/docs/pg-phase2-multi-instance-checklist.md
  • coding-agent/docs/superpowers/plans/2026-04-19-session-execution-binding-and-thin-phase2.md
  • coding-agent/src/coding_agent/ui/binding_resolver.py
  • coding-agent/src/coding_agent/ui/execution_binding.py
  • coding-agent/src/coding_agent/ui/session_manager.py
  • coding-agent/tests/ui/test_execution_binding.py
  • coding-agent/tests/ui/test_session_manager_public_api.py
  • coding-agent/tests/ui/test_session_manager_runtime.py

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@delete-cloud
delete-cloud merged commit 7aa5d0d into main Apr 20, 2026
1 check passed
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.

2 participants