---
title: "Channel timeline pages use Host-owned opaque cursors"
version: "en"
---

> Documentation Index
> Fetch the complete documentation index at: https://botharness.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Channel timeline pages use Host-owned opaque cursors

> Status: Accepted

# Channel timeline pages use Host-owned opaque cursors

For #143, the durable Channel log's append order is the timeline order. The Host exposes chronological latest, older, newer, and around windows through `botharness/channelTimeline`; each page carries opaque before/after anchors and `hasOlder`/`hasNewer`, while the independent Channel revision remains the live-delivery watermark from ADR-0054. The current cursor encodes Channel id plus the anchor message's id and timestamp, but the Client must never decode it or assume NDJSON offsets. This keeps the read contract stable when #46 replaces NDJSON with SQLite. A message-id-only query would expose no page direction or boundary state; an ever-growing full-history Client snapshot would make long-running Channels expensive.

The Client keeps one contiguous visible window. Older pages prepend without changing the reader's viewport, newer pages extend an around window, and live committed messages append only while that window reaches the tail; otherwise the revision and latest Channel preview advance without silently stitching across unseen history. Near the bottom the viewport follows new content; away from the bottom it preserves reading position and offers a new-message/return-to-latest control. Failed page reads retain the window and allow an explicit retry. Visual grouping of adjacent messages is a Client projection only: the same concrete sender, short gap, bounded count, one bottom-aligned avatar and group timestamp. It never merges durable messages.
The reopening anchor is a profile-scoped Human read position owned by the Channel Host, not browser-local scroll state or a DSH SessionEvent. The Client reports only committed messages actually visible in its viewport through `channelMarkRead`; the Host validates Channel membership and advances the position monotonically, storing it atomically beside the current Channel log. `channelReadPosition` supplies the message id for an `around` page on reopen. An absent, corrupt, or expired anchor falls back to latest. Streaming drafts and local optimistic echoes never count as read. This V1 single-Human/profile marker is not a global unread counter or a second Inbox authority; #126 may consume or refine it, and #46 moves its persistence with Channel facts without changing the Client contract.

The UI interaction reference is [mero-mero at `a346d6adfb4f2f96991dd95e47b3b056823c297c`](https://github.com/mero-mero/mero-mero/tree/a346d6adfb4f2f96991dd95e47b3b056823c297c), particularly `packages/web/src/components/chat/MessageList.tsx`, `MessageBubble.tsx`, `useConversationViewport.ts`, and `conversation-viewport-controller.ts`. BotHarness adapts the grouping, attached corners, bottom avatar, hover action, and prepend/follow behavior to DSH primitives and its own Channel authority; it does not copy the reference's game-specific message model. Right-click Copy and Locate are available in this slice. Durable reply-to relationships, quoted previews, and the hover Reply action remain #145 so the UI never offers a nonfunctional reply control.

Source: https://botharness.ai/dev/adr/0061-channel-timeline-uses-opaque-cursors/index.mdx
