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:
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.
2. Links and view registry¶
2.1 Link registry¶
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. |