Skip to content

feat: prepare session lifecycle for Roost adapter - #1179

Closed
Lampese wants to merge 2 commits into
mainfrom
lody-roost
Closed

Lampese wants to merge 2 commits into
mainfrom
lody-roost

Conversation

@Lampese

@Lampese Lampese commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Problem / pressure

Lody's session orchestration currently reaches through SessionDocument and Loro-specific history details while also coordinating queue promotion, steer delivery, ACP streaming, Edit & Resend, and session shutdown. Adding Roost for future new sessions without an explicit boundary would duplicate those rules in the adapter and would make retries, late provider output, and partial writes difficult to recover safely.

The change must preserve the existing logical session contract while allowing old sessions to remain Loro-backed and leaving Roost disabled until its adapter is ready. It must also avoid changing the current user-visible queue, steer, streaming, edit, or message-order behavior.

Summary

  • Added the SessionBackend contract and per-session backend factory. Missing discriminators continue to mean loro, and the current new-session selector remains loro.
  • Routed production session history, queue promotion, steer reconciliation, assistant output, fork handling, and lifecycle cleanup through the backend boundary.
  • Added durable Loro-side queue-promotion receipts with prepared, history_accepted, activation_published, and queue_consumed recovery states.
  • Added stable operation identities for queue, steer, ACP notifications, and partially persisted batches so retries do not append duplicate logical turns or output items.
  • Made assistant targets explicit across batching, finalization, late callbacks, shutdown, and deletion, so output remains attached to the run that owns it.
  • Added CLI and renderer composition seams. A future Roost selection fails closed until a Roost factory is registered; it does not construct a Loro history reader or writer.
  • Added backend contract, lifecycle, queue recovery, fork, ACP batching, and renderer factory tests, plus the transition architecture and history-write specifications.
  • Roost is not a dependency in this change, the Roost adapter is not implemented, and no Roost Rust code is modified.

Visual explanation

The queue promotion path now has a stable operation identity and durable recovery points. A retry resumes the first missing stage instead of appending another logical turn.

sequenceDiagram
    participant W as Dispatch watcher
    participant B as SessionBackend
    participant C as Loro control metadata
    participant H as Loro history
    participant Q as Message queue

    W->>B: promoteQueuedTurn(item, entry, operationId)
    B->>C: record prepared
    B->>H: accept user turn if absent
    B->>C: record history_accepted
    B->>C: publish activation
    B->>C: record activation_published
    B->>Q: consume exact queue row
    B->>C: record queue_consumed
    Note over W,C: A retry reuses operationId and completes only missing stages
Loading

Before / after

Before After
Session callers depended directly on Loro-specific document/history surfaces. Callers use SessionBackend; storage-specific readers, writers, and segments stay behind the backend.
Queue promotion crossed history, activation, and queue writes without a shared recovery contract. Queue promotion carries a stable operation ID and durable receipts, and startup recovery resumes incomplete stages.
Flush-time target lookup could rebind a late ACP notification to the current turn. Each notification is stamped with its assistant target before buffering and keeps that identity across retries and finalization.
Backend choice was implicit while a session was opened. A persisted discriminator fixes the choice for the session; legacy sessions remain Loro and an unavailable selected backend fails closed.
Renderer composition assumed a Loro SessionData implementation. Renderer composition has a backend factory seam, while the current selector still creates the existing Loro implementation.
The change set had no reusable adapter-level queue contract. A backend-independent contract and real Loro failure tests define the behavior a future Roost adapter must satisfy.

Test plan

  • CLI TypeScript checking passed.
  • CLI targeted tests passed: 353 tests.
  • Components TypeScript checking passed.
  • Components targeted tests passed: 74 tests.
  • Oxfmt checking passed.
  • git diff --check passed.
  • Documentation validation completed with errors: [].
  • The full pnpm check was not run because pnpm is unavailable in the environment.

This change does not benchmark or optimize long-conversation performance. It keeps the existing Loro path and user-visible behavior unchanged; performance comparison is a later adapter validation step after Roost exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants