Skip to content

Sessions and views (L2)

1. View snapshot shape

Every view snapshot has a common envelope:

ViewSnapshot { viewId, descriptor, revision, lifecycle, readWatermark, coverage, data, pendingMutations, error }

lifecycle is a runtime-supplied UI affordance (loading, ready, updating, error), not a local-replica certainty model. readWatermark identifies the runtime state sequence or store read watermark used to produce the data; renderer components do not interpret it for freshness (the runtime uses it for resume, diagnostics, query-window requests, or coverage-proof construction). coverage is the forward-compatible coverage seam: in bundled authority mode it asserts the returned finite view state is complete for its descriptor, window request, and read watermark; a future replica may replace that with structural coverage/gap/ freshness data without making the renderer evaluate queries locally.

For mailList, data carries rows, cursor/window continuation, total/known-count metadata, and row-level pending markers. For messageDetail/conversation, the runtime projection plus token metadata. For compose/settings, editable runtime state and any pending settlement fields.

1.1 Mail-list data shape

A mailList snapshot uses MailListViewState:

MailListViewState { scope, projectionKind, sort, windowRequest, rows, continuation, readWatermark, coverage, knownTotalCount, pendingMutations, anchor }

Rows use MailListRowState:

MailListRowState { rowKey, resourceRef, projection, sortKey, orderKey, pendingMarkers }

projectionKind is message or conversation. projection is the runtime row display state: a MessageSummaryState for message rows or a conversation row/envelope projection. continuation carries opaque beforeCursor/afterCursor plus hasBefore/hasAfter. anchor reports whether a requested anchor row was kept, moved, removed, or unknown.

1.2 View frames

View subscriptions emit full-state frames:

Frame Meaning
view.snapshot Complete current view state. Sent on subscribe and resume fallback.
view.replace Complete replacement for a newer ViewRevision.
view.error Runtime could not produce the view state. Carries the adapter error shape.
view.closed Runtime closed or released the view.

Full replacement frames are the baseline; row-patch frames may be added later only if they stay runtime-authored and the full-replacement path remains available.

A UI link is one renderer connection. The link registry tracks active views, subscriptions, and transient renderer capabilities. Bundled mode may have one main link plus focused windows; each window talks to the same authority server.

2.2 View registry

The view registry maps ViewId to runtime-owned view state — canonical descriptor, latest revision, latest snapshot, subscribers, and cleanup policy. It owns current view windows and pushes updates to subscribers. When a renderer closes a view or disconnects, the runtime may release active-view state after a grace period; reopening creates or reuses runtime state per the view policy.

3. View operation flow

3.1 Opening a query view

UI openLinkView(mailList descriptor)
 -> runtime canonicalizes QueryScope and window request
 -> query/view service reads SQLite projection
 -> view registry stores current ViewSnapshot
 -> runtime returns or streams view.snapshot
 -> background sync or provider status may later update the view

No provider call is required to render the initial local window. The snapshot carries stable row IDs, row projections, ordering metadata, cursor/window continuation, row pending markers, and list-level loading/error affordances. The UI may preserve scroll anchor and selection but does not compute row membership or replacement rows.

3.2 Applying state assertions

state assertion appended
 -> event bus broadcasts assertion
 -> view registry determines affected active views
 -> query/view service recomputes affected windows
 -> runtime streams replacement state to subscribers

The runtime may use dependency analysis to skip views that cannot be affected (an optimization; correctness comes from authority-server query evaluation). Affected subscriptions receive view.replace with a new ViewRevision; a subscriber that resumes behind the latest revision receives a fresh view.snapshot instead of replayed stale frames. The targeted-update slice watches coalesced message.updated assertions and inspects facets such as changes.keywords to decide whether open mailList views need replacement.

The authoritative projection is written only by sync and authoritative provider-outcome application; a pending mutation's local effect is folded over it at read time, not written into it (replication L1 §5). A pending mutation is retired when the confirmation watermark passes it; because state-assertion effects are idempotent, the base-updated-and-still-folded interval is a visual no-op, so retirement cannot flicker the row.

3.3 Conversation and detail views

Conversation and message detail views use the same token model as list views. If a message assertion changes ConversationRef, bodyToken, or attachmentToken for a visible view, the runtime refreshes the affected view and streams the result. A view may enter loading/updating while body, attachment, or envelope data refreshes; the renderer shows the runtime-supplied affordance and does not compare tokens itself.

3.4 Account status views

The accountStatus view serves account state the same way: its snapshot is the folded account overview — config (the only authority for existence/settings) joined with the supervisor's runtime overview (status, push, sync progress, last error). The renderer renders that served snapshot; it does not reconstruct account status by patching account.status_changed deltas, which a renderer can miss across a reconnect or late subscription and thereby strand an account stale.

The supervisor centralizes every runtime-overview write at one commit point; that commit, and any account config change (account.created/updated/deleted), is the recompute signal. The view registry recomputes the folded overview and streams view.replace; data-equality suppression elides no-op recomputes. On open or resume the renderer receives the current snapshot, so a missed intermediate change is not observable. The durable account.status_changed event remains the status audit trail; it is history, not the renderer's read path.

4. Assertions

ID Sev. Assertion
view-coverage-seam MUST View snapshots carry a runtime-authored coverage field; bundled authority mode reports complete finite-view coverage for its descriptor/window/read watermark.
read-watermark-runtime-owned MUST Renderer components do not interpret readWatermark for mail freshness; it is runtime-owned metadata for resume, diagnostics, requests, or coverage-proof construction.
mail-list-window-payload MUST Mail-list view snapshots carry MailListViewState with rows, continuation, read watermark, coverage, lifecycle, pending mutations, and anchor state.
mail-list-row-payload MUST Mail-list rows carry stable row keys, resource refs, runtime projections, opaque ordering metadata, and pending markers.
view-subscriptions-full-state MUST View subscriptions deliver full replacement snapshots for the current view revision.
ui-link-view-registry MUST Active renderer views are registered with the runtime, not repaired independently by UI components.
open-view-local-read MUST Opening a query view reads local runtime projections before any provider round trip.
state-assertion-updates-views MUST Runtime state assertions drive recomputation and streaming of affected active views.
account-status-served MUST Account config and runtime status reach the renderer as a served accountStatus view snapshot (folded config + runtime overview), not as delta status events the renderer patches.
account-status-recompute MUST The runtime recomputes and re-serves the accountStatus view on any account config change or runtime-overview change (status, push, sync progress, error), with data-equality suppression for no-op recomputes.