Skip to content

Restore live sessions immediately after app resume - #4160

Merged
boudra merged 6 commits into
mainfrom
investigate-relay-timeline-reconnect-lag
Sep 6, 2026
Merged

Restore live sessions immediately after app resume#4160
boudra merged 6 commits into
mainfrom
investigate-relay-timeline-reconnect-lag

Conversation

@boudra

@boudra boudra commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Linked issue

Refs #2991. This covers queues stranded after an unobserved turn completion; the original report has not been reproduced directly.

Type of change

  • Bug fix
  • New feature
  • Enhancement
  • Refactor
  • Docs

Reasoning

Returning to the app could show cached chat while host directories and running indicators waited for reconnect backoff and catch-up. This reconnects every registered host immediately on foreground, restores cached directory/chat/turn state, and preserves live output received before catch-up completes.

Directory updates now persist only touched rows, with serialization deferred until flush. Replacing a directory baseline preserves pending timeline writes, fixing a race that discarded newly accepted chat-cache changes.

The scope is limited to these behavior and persistence improvements. The session-data facade and broad read-hook migration have been removed; a full data-layer ownership redesign is deferred. Against the rebased main: 65 files, +2208/-1158; production code is 32 files and net +285 lines, down from 133 files and net +992.

Goals

  • Reconnect every registered host immediately on foreground while preserving healthy background sockets.
  • Restore agents, workspaces, projects, timelines, and turn liveness from the client cache.
  • Preserve visible live output and pending timeline writes during directory catch-up.
  • Persist touched rows and serialize them during the deferred flush.
  • Drain queued messages when the authoritative turn becomes idle.

Non-goals

  • Redesign the data-layer ownership boundary or migrate all global-store reads.
  • Probe sockets that still report connected for silent half-open failures.
  • Change the daemon protocol or relay transport.
  • Persist queued messages across process termination or encrypt the local cache.

Supersedes

QA

The pending timeline-write regression failed before the fix and passes afterward. A failing-first lifecycle regression also verifies mounting while inactive/backgrounded applies the current state before bootstrap. Added coverage also verifies that clearing a timeline survives directory baseline replacement.

The sidebar and shortcut fixture failures found by CI are fixed by seeding through the existing workspace owner; all seven tests pass on two consecutive focused runs. All 29 startup/lifecycle tests pass.

Focused app coverage passed for cache, directory synchronization, host lifecycle, turn state, activity presentation, subagents, archive, and workspace behavior. Selected command output:

# packages/app
npx vitest run src/runtime/replica-cache/index.test.ts --bail=1
Tests  24 passed (24)
npx vitest run src/runtime/host-runtime.test.ts --bail=1
Tests  69 passed (69)

# packages/client
npx vitest run src/daemon-client.test.ts --bail=1
Tests  118 passed (118)

# packages/app
npm run test:e2e -- agent-timeline-resume.spec.ts --project=browser --workers=1
2 passed (59.2s)

The provider-acknowledgement reconnect and queued-cancellation journeys also pass three consecutive runs each (--repeat-each=3: 6 passed in 1.7m). They verify that submission settlement survives disconnect, the last known running turn remains visible offline and becomes idle after authoritative catch-up, and the next queued turn remains interruptible.

The browser tests use an isolated daemon: a long disconnected run resumes into one latest-tail update with older history still reachable; an unchanged resume preserves a scrolled-up timeline.

Root npm run typecheck, npm run lint, npm run format, and git diff --check pass. Lint reports zero warnings and errors.

Browser resume behavior is verified. Android loaded the current bundle, but resume/catch-up/queue/cache-recovery journeys remain unverified because another session reserved the emulator during setup. iOS and packaged Electron were not retested. Native foreground behavior and older cache hydration remain the main manual QA gaps.

Checklist

  • One focused change
  • npm run typecheck passes
  • npm run lint passes
  • npm run format passes
  • QA evidence
  • Tests added or updated where it made sense

Foregrounding now reconnects every host without waiting for scheduled backoff, while cached turns, timelines, agents, and workspaces remain internally consistent until authoritative catch-up arrives. Replica persistence is keyed and incremental so ordinary updates avoid whole-directory serialization.
Stream turn closure now wakes queued-message delivery, while same-turn snapshots retain local cancellation state. Offline liveness remains visible until an authoritative close arrives.
Directory baseline replacement must preserve independently accepted timeline changes until the deferred flush. Keep the reconnect and persistence fixes while removing the session-data facade and broad read-hook migration.
@boudra
boudra force-pushed the investigate-relay-timeline-reconnect-lag branch from 6760ab2 to 54d94c3 Compare September 5, 2026 12:09
@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores cached host/session state and immediately reconnects registered hosts when the app returns to the foreground.

  • Centralizes agent, workspace, project, turn-liveness, and archive mutations through host-runtime-owned replicas.
  • Preserves live turn state and pending timeline writes across cache hydration and directory catch-up.
  • Changes replica persistence to write touched rows during deferred flushes.
  • Adds lifecycle, cache-race, queue-draining, and browser resume coverage.
  • Removes implementation-focused persistence prose identified in the previous review.

Confidence Score: 5/5

The PR appears safe to merge; the previous lifecycle and documentation findings are resolved, and no new actionable defect was established.

The lifecycle binding now applies AppState.currentState before host bootstrap, and the implementation-focused cache mutation paragraph was removed from docs/data-model.md. The remaining documentation describes cross-module contracts and failure consequences rather than duplicating local implementation details.

Important Files Changed

Filename Overview
packages/app/src/runtime/host-runtime.ts Coordinates foreground reconnection, replica ownership, persistence, and serialized queue draining.
packages/app/src/runtime/directory-sync/agent-replica.ts Moves agent mutations and turn-liveness reconciliation behind the directory replica while preserving newer live state.
packages/app/src/runtime/replica-cache/index.ts Persists keyed row mutations with deferred serialization and preserves pending timeline writes during baseline replacement.
packages/app/src/navigation/host-runtime-bootstrap.ts Applies the current native app state before bootstrap and forwards subsequent lifecycle transitions.
packages/client/src/daemon-client.ts Updates reconnect and catch-up behavior supporting immediate restoration after resume.
docs/data-model.md Describes the durable replica row model while removing the previously flagged implementation-level flush description.
docs/timeline-sync.md Documents queue dispatch and turn-liveness lifecycle contracts.

Sequence Diagram

sequenceDiagram
  participant App as App lifecycle
  participant Runtime as Host runtime
  participant Cache as Replica cache
  participant Directory as Directory replica
  participant Daemon as Daemon

  App->>Runtime: foreground / setAppVisible(true)
  Runtime->>Daemon: reconnect registered hosts immediately
  Runtime->>Cache: hydrate cached host rows
  Cache-->>Directory: agents, workspaces, projects, timelines
  Daemon-->>Directory: authoritative snapshot and deltas
  Directory->>Directory: preserve newer live turn state
  Directory->>Cache: persist touched rows and checkpoint
  Directory-->>Runtime: notify when authoritative turn becomes idle
  Runtime->>Runtime: drain queued message
Loading

Reviews (4): Last reviewed commit: "docs: remove cache implementation recap" | Re-trigger Greptile

Comment thread packages/app/src/app/_layout.tsx Outdated
Initialize visibility before host bootstrap so a background mount cannot leave reconnect enabled. Seed sidebar fixtures through the workspace owner so cache hydration preserves their rows.
Keep submission acknowledgement separate from turn liveness: disconnect preserves the running turn until authoritative catch-up. Wait for confirmed running chrome before exercising queued cancellation so the alternate-send shortcut cannot race turn startup.
Comment thread docs/data-model.md Outdated
@boudra
boudra merged commit efd6023 into main Sep 6, 2026
24 checks passed
@boudra
boudra deleted the investigate-relay-timeline-reconnect-lag branch September 6, 2026 08:18
Legoless added a commit to Legoless/paseo that referenced this pull request Sep 9, 2026
Port of getpaseo#4160 and getpaseo#4444. Flush the replica cache when hidden, reconnect
every host immediately on resume, and keep daemon reconnect backoff
running while backgrounded. Disconnect no longer clears turn liveness
so queued messages still drain after a backgrounded turn finishes.
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