Skip to content

feat: persist desktop provider sessions - #49

Merged
slashdevcorpse merged 6 commits into
mainfrom
slashdevcorpse/persist-sessions
Jul 21, 2026
Merged

feat: persist desktop provider sessions#49
slashdevcorpse merged 6 commits into
mainfrom
slashdevcorpse/persist-sessions

Conversation

@slashdevcorpse

@slashdevcorpse slashdevcorpse commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • keep Super Synara's desktop backend and provider runtimes warm when the last window closes, then reopen the existing backend on relaunch
  • recover persisted provider bindings after process loss so durable Codex cursors can cold-resume after updates, explicit quit, force termination, or reboot
  • make idle runtime teardown atomic and race-safe across foreground work, background work, tasks, generation changes, and adapter state
  • add production-path desktop persistence fixtures plus a native two-launch force-termination smoke on Windows and macOS

Persistence boundary

A normal window close keeps the desktop process, backend, and provider runtimes alive. An explicit quit, update/relaunch, force termination, or reboot reconstructs sessions from durable runtime cursor and payload state on the next launch.

A hard process or power loss cannot preserve an in-memory active process or unfinished turn. Startup recovery deliberately normalizes orphaned starting/running bindings to stopped before cursor-backed resume.

Validation

  • provider lifecycle/reaper/directory/startup reconciliation: 96 tests passed
  • desktop suite: 432 tests passed
  • desktop persistence fixture: 3 tests passed
  • workflow contracts: 18 tests passed
  • workflow validation: 4 allowed and 4 disabled workflows passed
  • native Windows two-launch production smoke passed: launch A armed durable state, the Electron process tree was force-stopped, launch B restored the same state, and all child processes were confirmed gone
  • native Codex resume regression passed with codex-cli 0.144.5
  • Node syntax checks and git diff origin/main --check passed

The required formatting, lint, typecheck, platform build, and release-smoke gates are left to the repository's protected CI workflow.


Summary by cubic

Persist provider sessions in the desktop app: keep the backend and provider runtimes warm after the last window closes, and restore them on relaunch. Relaunch reuses the existing backend and focuses an existing window or defers creating a new one until the backend is ready; CI runs a two-launch persistence smoke with isolated userData, enforces absolute SYNARA_DESKTOP_PERSISTENCE_SMOKE_USER_DATA, tightens readiness/timeouts and output capture, and confirms full process-tree cleanup.

  • New Features

    • Warm-close keeps the desktop process, backend, and provider runtimes alive; reopen decisions use a new window lifecycle policy and defer window creation until backend readiness.
    • Startup recovery rebuilds provider bindings from durable runtime cursors and normalizes orphaned in-flight state; hardened for interrupted launches and adapter failures.
    • Two-launch persistence smoke on Windows and macOS with isolated userData; adds an offline server fixture that seeds/arms durable state backed by production SQLite.
  • Refactors

    • Added atomic idle teardown via stopRuntimeSessionIfIdle and generation-aware conditional lifecycle leases to avoid races across foreground/background work.
    • Extracted and tested window lifecycle policy; reaper uses conditional idle stop and logs when safe cleanup isn’t available.
    • Enforced absolute persistence-smoke userData override with a clear log prefix; tightened smoke harness isolation/validation; workflow contracts require the CI persistence step and OS-specific homes; improved Windows process-tree confirmation with explicit taskkill race verification.

Written for commit 0185b22. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added automated desktop “two-launch” persistence smoke coverage in CI for Windows and macOS, with a dedicated persistence smoke test command.
  • Bug Fixes
    • Improved crash/orphaned session recovery to preserve resume state.
    • Refined desktop relaunch behavior to consistently focus existing windows and create/reopen only after backend readiness.
  • Reliability
    • Strengthened idle teardown and persistence verification to fail fast, enforce stricter isolation checks, and improve process-tree shutdown confirmation.
  • Tests / CI
    • Expanded smoke and integration test coverage for desktop persistence flows and teardown correctness.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds two-launch desktop persistence smoke coverage, isolated Electron userData handling, lifecycle-aware window reopening, provider crash recovery, conditional idle runtime cleanup, and Windows/macOS CI workflow enforcement.

Changes

Desktop persistence and runtime recovery

Layer / File(s) Summary
Provider lifecycle recovery and conditional idle stops
apps/server/src/provider/...
Provider startup reconciliation, lifecycle leases, idle-sensitive work tracking, atomic idle stops, and reaper behavior are updated with recovery and concurrency tests.
Offline persistence fixture and crash-recovery assertions
apps/server/integration/desktopPersistenceSmokeFixture.*, apps/server/src/provider/Layers/ProviderService.test.ts
A SQLite-backed fixture implements isolated seed, arm, and assert modes with lifecycle-lock validation and crash-recovered binding checks.
Desktop lifecycle and persistence-smoke orchestration
apps/desktop/src/..., apps/desktop/scripts/..., apps/desktop/package.json, apps/desktop/turbo.jsonc, package.json
Electron reopen and keep-alive policies, persistence-specific userData paths, readiness supervision, process-tree teardown, and the two-launch smoke command are added.
Desktop lifecycle and smoke validation coverage
apps/desktop/**/*.test.*
Tests cover lifecycle decisions, userData isolation, readiness evidence, Windows and POSIX teardown, output retention, polling, and smoke sequencing cleanup.
Native CI persistence smoke wiring
.github/workflows/ci.yml, scripts/lib/workflow-contracts.*
Windows and macOS native jobs run persistence smoke, while workflow contracts validate placement, environment, timeout, uniqueness, and fail-closed behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant SmokeScript
  participant Electron
  participant SmokeFixture
  participant SQLite
  CI->>SmokeScript: run persistence smoke
  SmokeScript->>SmokeFixture: seed fixture
  SmokeScript->>Electron: launch and await readiness
  SmokeScript->>SmokeFixture: arm persisted runtime
  SmokeScript->>Electron: force-stop process tree
  SmokeScript->>SmokeScript: launch second instance
  SmokeScript->>SmokeFixture: assert recovered state
  SmokeFixture->>SQLite: read durable bindings
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.41% 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
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: persisting desktop provider sessions.
Description check ✅ Passed The description clearly covers what changed and why, with validation details; the template headings are different but the required substance is present.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

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

🧹 Nitpick comments (1)
apps/server/src/provider/providerLifecycleCoordinator.ts (1)

107-144: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

runConditional generation-preservation logic looks correct. advance() is lazy and rollback only fires after a commit, so a failing operation that never advanced leaves the current generation untouched.

Optional: the failure-rollback body (Lines 134-138) duplicates the identical block in run (Lines 95-99). Consider extracting a small restorePreviousGeneration(threadId, previousGeneration) helper to keep the two lifecycle paths in sync.

As per coding guidelines: "avoid duplicating logic across files and prefer reusable modules over local shortcuts."

🤖 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 `@apps/server/src/provider/providerLifecycleCoordinator.ts` around lines 107 -
144, Extract the duplicated generation-restoration logic from runConditional and
run into a shared restorePreviousGeneration helper, accepting threadId and
previousGeneration. Replace both inline rollback blocks with this helper while
preserving the existing delete-versus-set behavior.

Source: Coding guidelines

🤖 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 `@apps/server/src/provider/providerLifecycleCoordinator.ts`:
- Around line 107-144: Extract the duplicated generation-restoration logic from
runConditional and run into a shared restorePreviousGeneration helper, accepting
threadId and previousGeneration. Replace both inline rollback blocks with this
helper while preserving the existing delete-versus-set behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d0ee220-9e04-46d3-8294-0cd702163715

📥 Commits

Reviewing files that changed from the base of the PR and between b135e0a and 2ae75ed.

📒 Files selected for processing (22)
  • .github/workflows/ci.yml
  • apps/desktop/package.json
  • apps/desktop/scripts/session-persistence-smoke.mjs
  • apps/desktop/scripts/smoke-test-lifecycle.mjs
  • apps/desktop/scripts/smoke-test.test.mjs
  • apps/desktop/src/desktopUserDataProfile.test.ts
  • apps/desktop/src/desktopUserDataProfile.ts
  • apps/desktop/src/desktopWindowLifecycle.test.ts
  • apps/desktop/src/desktopWindowLifecycle.ts
  • apps/desktop/src/main.ts
  • apps/desktop/turbo.jsonc
  • apps/server/integration/desktopPersistenceSmokeFixture.test.ts
  • apps/server/integration/desktopPersistenceSmokeFixture.ts
  • apps/server/src/provider/Layers/ProviderService.test.ts
  • apps/server/src/provider/Layers/ProviderService.ts
  • apps/server/src/provider/Layers/ProviderSessionReaper.test.ts
  • apps/server/src/provider/Layers/ProviderSessionReaper.ts
  • apps/server/src/provider/Services/ProviderService.ts
  • apps/server/src/provider/providerLifecycleCoordinator.ts
  • package.json
  • scripts/lib/workflow-contracts.test.ts
  • scripts/lib/workflow-contracts.ts

@slashdevcorpse
slashdevcorpse force-pushed the slashdevcorpse/persist-sessions branch from 140956f to 92e1a3b Compare July 20, 2026 22:42

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread apps/desktop/scripts/session-persistence-smoke.mjs Outdated
Comment thread apps/desktop/scripts/session-persistence-smoke.mjs Outdated
Comment thread apps/server/src/provider/Layers/ProviderService.ts
Comment thread apps/desktop/src/main.ts Outdated
Comment thread scripts/lib/workflow-contracts.ts

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread apps/server/src/provider/Layers/ProviderService.ts Outdated
Comment thread apps/desktop/src/main.ts
Comment thread apps/server/integration/desktopPersistenceSmokeFixture.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 9 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/desktop/scripts/session-persistence-smoke.mjs Outdated
@slashdevcorpse
slashdevcorpse force-pushed the slashdevcorpse/persist-sessions branch from 087214b to dec624a Compare July 20, 2026 23:49
@slashdevcorpse
slashdevcorpse merged commit 250d837 into main Jul 21, 2026
10 of 11 checks 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.

1 participant