Skip to content

feat(acp): use sqlite local durability - #556

Merged
delete-cloud merged 1 commit into
mainfrom
feat/acp-sqlite-local-durability
Jun 5, 2026
Merged

delete-cloud merged 1 commit into
mainfrom
feat/acp-sqlite-local-durability

Conversation

@delete-cloud

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

Copy link
Copy Markdown
Owner

Summary

  • use SQLite tape/checkpoint/runtime stores for local ACP stdio sessions
  • add SQLite-backed local session owner leases with fencing semantics
  • acquire ACP session ownership on load/resume and renew local ACP owner leases during process lifetime

Tests

  • uv run pytest tests/ui/test_session_owner_store.py tests/cli/test_local_runtime.py tests/cli/test_entrypoint_contract.py tests/acp tests/ui/test_session_manager_owner_checks.py tests/coding_agent/test_sqlite_runtime_store.py tests/agentkit/storage/test_sqlite.py -q
  • uv run ruff check src/coding_agent/acp/server.py src/coding_agent/server/session_manager.py src/coding_agent/server/stores/session_owner_store.py src/coding_agent/cli/acp_command.py src/coding_agent/cli/local_runtime.py tests/acp/test_server.py tests/acp/test_compat_harness.py tests/ui/test_session_owner_store.py tests/cli/test_entrypoint_contract.py tests/cli/test_local_runtime.py
  • uv run ruff format --check src/coding_agent/acp/server.py src/coding_agent/server/session_manager.py src/coding_agent/server/stores/session_owner_store.py src/coding_agent/cli/acp_command.py src/coding_agent/cli/local_runtime.py tests/acp/test_server.py tests/acp/test_compat_harness.py tests/ui/test_session_owner_store.py tests/cli/test_entrypoint_contract.py tests/cli/test_local_runtime.py
  • git diff --check

Summary by CodeRabbit

Release Notes

  • New Features

    • Added session ownership management with automatic lease renewal to prevent concurrent access conflicts.
    • Introduced monotonic fencing tokens to ensure ordered takeover behavior when session leases expire.
    • Local CLI now uses SQLite for persistent session storage and durability.
  • Tests

    • Added comprehensive test coverage for session ownership lifecycle, lease renewal, and takeover scenarios.

@coderabbitai

coderabbitai Bot commented Jun 5, 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 48 minutes and 12 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: ac8d722a-8da1-42cf-8920-f26f1eb0174e

📥 Commits

Reviewing files that changed from the base of the PR and between 08943e6 and dbe1cfa.

📒 Files selected for processing (12)
  • coding-agent/docs/adr/0067-local-sqlite-durable-tape-runtime.md
  • coding-agent/src/coding_agent/acp/server.py
  • coding-agent/src/coding_agent/cli/acp_command.py
  • coding-agent/src/coding_agent/cli/local_runtime.py
  • coding-agent/src/coding_agent/server/session_manager.py
  • coding-agent/src/coding_agent/server/stores/session_owner_store.py
  • coding-agent/tests/acp/test_compat_harness.py
  • coding-agent/tests/acp/test_server.py
  • coding-agent/tests/cli/test_entrypoint_contract.py
  • coding-agent/tests/cli/test_local_runtime.py
  • coding-agent/tests/ui/test_session_manager_owner_checks.py
  • coding-agent/tests/ui/test_session_owner_store.py
📝 Walkthrough

Walkthrough

This PR introduces SQLite-backed session ownership with fencing-token lease management for local ACP/CLI durability. It implements a multi-layer feature: a new SQLiteSessionOwnerStore provides thread-safe lease persistence, SessionManager enforces ownership checks on mutations, the ACP server acquires ownership before load/resume operations, local runtime manages background lease renewal, and the CLI command wires everything with generated owner credentials.

Changes

Local Session Ownership Implementation

Layer / File(s) Summary
Architecture & Design Documentation
docs/adr/0067-local-sqlite-durable-tape-runtime.md
ADR 0067 documents the shift to SQLite-first local durability, session ownership with fencing tokens, lease takeover semantics, and implementation order including contract tests and restart/concurrency behavior validation.
SQLite Session Ownership Store
src/coding_agent/server/stores/session_owner_store.py
SQLiteSessionOwnerStore class provides thread-safe lease acquire/renew/release/lookup using SQLite transactions, ISO-8601 datetime persistence, and fencing-token enforcement to prevent stale owner actions and validate increasing takeover tokens.
SessionManager Ownership Methods
src/coding_agent/server/session_manager.py
SessionManager.acquire_session_owner() wraps internal owner acquisition; update_session_mcp_servers and update_session_additional_directories now call _assert_owner() to enforce ownership checks before mutations.
ACP Server Session Ownership Integration
src/coding_agent/acp/server.py
AcpSessionManager protocol adds acquire_session_owner() method; handle_message catches SessionOwnershipConflictError and returns JSON-RPC error code -32000; _session_load and _session_resume acquire ownership before updating session state.
Local Runtime Ownership & Lease Renewal
src/coding_agent/cli/local_runtime.py
LocalCliSessionManager protocol gains acquire_session_owner() and start_owner_lease_renewal(); ServerBackedLocalCliSessionManager manages ownership configuration, implements background asyncio lease-renewal task, delegates additional-directory updates, and releases owned sessions on close.
CLI Integration & Wiring
src/coding_agent/cli/acp_command.py
_run_acp_stdio configures tracing, reads data_dir from AGENT_DATA_DIR, initializes local session manager with SQLite tape/checkpoint/runtime stores, creates SQLiteSessionOwnerStore, generates acp-stdio:UUID owner_id and nanosecond-precision fencing_token, and starts lease renewal.
Comprehensive Test Coverage
tests/acp/test_compat_harness.py, tests/acp/test_server.py, tests/cli/test_local_runtime.py, tests/cli/test_entrypoint_contract.py, tests/ui/test_session_owner_store.py
Test harness and managers support ownership acquisition; ACP server tests verify ownership-conflict error handling and call ordering; local runtime tests validate delegation and renewal lifecycle; session owner store tests exercise acquire/renew/release/takeover with fencing validation; CLI contract test confirms end-to-end tracing and ownership setup.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • delete-cloud/Coding-Agent-Loop#79: Implements PostgreSQL-backed session ownership enforcement at the UI layer (PGSessionOwnerStore), complementing this PR's SQLite-backed local implementation with server-side fencing validation.

Poem

🐰 A rabbit's ode to session leases:
SQLite locks with fencing's might,
Local ownership takes the night—
Leases renewed with token grace,
Takeovers claim their rightful place,
Sessions owned, no conflicts bloom,
Multi-process finds its room.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% 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 summarizes the main change: introducing SQLite-backed local durability for ACP sessions, which is the core objective of the PR.
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/acp-sqlite-local-durability

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.

@delete-cloud
delete-cloud force-pushed the feat/acp-sqlite-local-durability branch from 08943e6 to 3f1190e Compare June 5, 2026 05:20

@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 (2)
coding-agent/tests/cli/test_entrypoint_contract.py (1)

149-226: ⚡ Quick win

Remove redundant imports inside the test function.

CliRunner is already imported at line 10 and main is already imported at line 14 (as from coding_agent.__main__ import main). The local re-imports on lines 152 and 155 are unnecessary.

♻️ Remove redundant imports
 def test_acp_stdio_disables_agentkit_tracing(
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
-    from click.testing import CliRunner
-
     from coding_agent.cli import acp_command
-    from coding_agent.cli.main import main
 
     calls: list[tuple[str, object]] = []
🤖 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/cli/test_entrypoint_contract.py` around lines 149 - 226,
In test_acp_stdio_disables_agentkit_tracing remove the two redundant local
imports "from click.testing import CliRunner" and "from coding_agent.cli.main
import main" and rely on the existing module-level imports (CliRunner and main)
already imported earlier; keep the test's use of acp_command and all
monkeypatches the same, just delete those two local import statements so there
are no duplicate imports.
coding-agent/tests/cli/test_local_runtime.py (1)

110-125: 💤 Low value

Consider using deterministic synchronization instead of timing-based sleep.

The test relies on asyncio.sleep(0.01) with a 1ms renewal interval to assert that renewals occurred. Under CI load or slow environments, this could be flaky. Consider using an asyncio.Event or counter-based wait to make the test deterministic.

♻️ Optional: Make renewal assertion deterministic
 async def test_server_backed_local_cli_renews_owner_leases_until_close() -> None:
     manager = object.__new__(ServerBackedLocalCliSessionManager)
     delegate = FakeDelegate()
     manager._delegate = delegate
     manager.owner_store = object()
     manager.owner_renew_interval_seconds = 0.001
     manager._owner_renew_task = None
 
     await manager.start_owner_lease_renewal()
-    await asyncio.sleep(0.01)
+    # Wait until at least one renewal has occurred
+    for _ in range(100):
+        if delegate.renew_owner_leases_called > 0:
+            break
+        await asyncio.sleep(0.001)
     await manager.close()
 
     assert delegate.renew_owner_leases_called > 0
🤖 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/cli/test_local_runtime.py` around lines 110 - 125, Replace
the timing-based sleep with deterministic synchronization: modify the
FakeDelegate used in
test_server_backed_local_cli_renews_owner_leases_until_close so that its
renew_owner_leases implementation signals an asyncio.Event (or increments an
awaitable counter and sets an Event when >0), then in the test await that Event
with a short timeout after calling manager.start_owner_lease_renewal(), proceed
to manager.close(), and assert the same conditions
(delegate.renew_owner_leases_called > 0, manager._owner_renew_task is None,
delegate.release_owned_sessions_called is True, delegate.close_called is True);
reference FakeDelegate,
ServerBackedLocalCliSessionManager.start_owner_lease_renewal, and manager.close
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/cli/test_entrypoint_contract.py`:
- Around line 149-226: In test_acp_stdio_disables_agentkit_tracing remove the
two redundant local imports "from click.testing import CliRunner" and "from
coding_agent.cli.main import main" and rely on the existing module-level imports
(CliRunner and main) already imported earlier; keep the test's use of
acp_command and all monkeypatches the same, just delete those two local import
statements so there are no duplicate imports.

In `@coding-agent/tests/cli/test_local_runtime.py`:
- Around line 110-125: Replace the timing-based sleep with deterministic
synchronization: modify the FakeDelegate used in
test_server_backed_local_cli_renews_owner_leases_until_close so that its
renew_owner_leases implementation signals an asyncio.Event (or increments an
awaitable counter and sets an Event when >0), then in the test await that Event
with a short timeout after calling manager.start_owner_lease_renewal(), proceed
to manager.close(), and assert the same conditions
(delegate.renew_owner_leases_called > 0, manager._owner_renew_task is None,
delegate.release_owned_sessions_called is True, delegate.close_called is True);
reference FakeDelegate,
ServerBackedLocalCliSessionManager.start_owner_lease_renewal, and manager.close
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ee5d5664-ad47-4935-a457-12c8ed5acd1c

📥 Commits

Reviewing files that changed from the base of the PR and between 25ff47f and 08943e6.

📒 Files selected for processing (11)
  • coding-agent/docs/adr/0067-local-sqlite-durable-tape-runtime.md
  • coding-agent/src/coding_agent/acp/server.py
  • coding-agent/src/coding_agent/cli/acp_command.py
  • coding-agent/src/coding_agent/cli/local_runtime.py
  • coding-agent/src/coding_agent/server/session_manager.py
  • coding-agent/src/coding_agent/server/stores/session_owner_store.py
  • coding-agent/tests/acp/test_compat_harness.py
  • coding-agent/tests/acp/test_server.py
  • coding-agent/tests/cli/test_entrypoint_contract.py
  • coding-agent/tests/cli/test_local_runtime.py
  • coding-agent/tests/ui/test_session_owner_store.py

Add ADR-0067 for SQLite-first local ACP storage, wire ACP stdio to SQLite tape/checkpoint/runtime stores, and add a local SQLite session owner store for owner/fencing enforcement.\n\nVerification:\n- uv run pytest tests/ui/test_session_owner_store.py tests/cli/test_local_runtime.py tests/cli/test_entrypoint_contract.py tests/acp tests/ui/test_session_manager_owner_checks.py tests/coding_agent/test_sqlite_runtime_store.py tests/agentkit/storage/test_sqlite.py -q\n- uv run ruff check src/coding_agent/server/stores/session_owner_store.py src/coding_agent/cli/acp_command.py src/coding_agent/cli/local_runtime.py tests/ui/test_session_owner_store.py tests/cli/test_entrypoint_contract.py tests/cli/test_local_runtime.py tests/acp/test_server.py\n- uv run ruff format --check src/coding_agent/server/stores/session_owner_store.py src/coding_agent/cli/acp_command.py src/coding_agent/cli/local_runtime.py tests/ui/test_session_owner_store.py tests/cli/test_entrypoint_contract.py tests/cli/test_local_runtime.py tests/acp/test_server.py\n- git diff --check
@delete-cloud
delete-cloud force-pushed the feat/acp-sqlite-local-durability branch from 3f1190e to dbe1cfa Compare June 5, 2026 05:25
@delete-cloud
delete-cloud merged commit bf67321 into main Jun 5, 2026
1 check passed
@delete-cloud
delete-cloud deleted the feat/acp-sqlite-local-durability branch June 5, 2026 05:26
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