Overview: add an Unread section with durable per-session read tracking
Agreed behavior
The operator requested a second Overview section for sessions with output they have not read, and approved this model:
- Running → Unread → Remaining sessions, using the existing cards/tiles without duplicates.
- New human-facing assistant output makes a session unread; tool activity and status changes do not.
- Automatically mark output read when the operator opens the session or expanded conversation and its latest reply is actually visible. Merely displaying an Overview thumbnail is insufficient.
- Background tabs and reading older messages do not clear newer unread output.
- Read state survives refreshes and is shared across the operator's tabs/devices. A new reply racing with an acknowledgement remains unread unless its own version is subsequently acknowledged.
- Add a compact Mark all read action in the Unread section header. This is an explicit manual acknowledgement of that section's current replies, without opening each conversation.
- On first rollout, existing replies start read; track new output from the one-time initial baseline onward. The operator approved this baseline and requested the bulk action in the follow-up discussion.
Implement one focused PR. This is the unread slice of review items U10/F13, not full-transcript search, a notification system or a general Overview redesign. Desktop toolbar relocation was discussed separately but has not been approved.
Current code and integration points
Checked main at ef08e843d1c67fcc4e1c463415819ea351734d6d on 2026-09-08:
- Overview already has manual/grouped and ranked layouts plus card/tile views. Ranked blocks intentionally share a stable parent to avoid losing drafts and scroll when a session moves between sections.
- MetaDigest exposes assistant text/time, but digestPrompt/digestAssistant reset fields on a new prompt and can update timestamps for mirrored assistant records. They are not already a durable unread cursor.
- ConversationView has follow-latest and measured viewport state. readingPosition stores local scroll anchors, not read receipts. Remote-agent
lastSeenAt describes connectivity, not operator reading.
- No existing operator read-marker implementation or overlapping Unread issue was found. Recheck main before implementation. Coordinate #118 pin/group behavior if it lands, #119 reader architecture, #124 refresh behavior, and #127 mode-switch position restoration.
Requirements
1. Identify the output, not merely the time a pane opened
- Maintain a lightweight latest-output identity/version and last-acknowledged position per session. Identify sessions by immutable ID and account for source/run generation, not names, paths or browser wall-clock time alone.
- Qualifying output is human-facing assistant content, including a later visible update to a streaming answer. User prompts, tool traffic, reasoning-only records, terminal redraws, heartbeats and repeated delivery of the same output do not create a new unread reply.
- Distinguish separate replies with identical text, same-timestamp replies, and revisions to one streaming reply. Do not hash only clipped Overview text or use whole-trace revision changes that also include tool/status noise.
- Sending a prompt must not silently erase earlier unread state because the existing digest clears its assistant fields. Map presentation and server metadata to the same output identity so an acknowledgement can specify exactly what was displayed.
- Reuse existing trace parsing/metadata work and bounded reader windows. Do not add a full-history browser scan or a per-session full-trace poll merely to draw Overview. Publish compact output/read metadata with the existing refresh paths.
2. Automatic acknowledgement requires output actually presented
- The latest reply must be loaded, rendered and visible in a deliberately opened session conversation or expanded Overview conversation, with the app in the foreground and that view active. Use actual presentation/viewport state, not component mount, route selection, a successful fetch or the
At latest boolean by itself.
- An inline/truncated tile or card preview, an obscured/inactive pane, a background tab, a search showing older matches and a scrolled-back reader do not acknowledge unseen newer output. If content is truncated, do not acknowledge the omitted part as viewed.
- While a user is actively following the latest reply, acknowledge newly rendered output once it becomes visible. A newer version arriving after that observation requires its own observation; never send “mark whatever is latest on the server read.”
- Keep acknowledgements independent from scroll restoration. A saved bottom flag or switching Reader/Terminal is not evidence that a particular reply was shown. For terminal views, do not equate opening a socket or painting a cached screen with reading the latest reply. Only acknowledge there if the implementation can reliably map visible live output to that same reply/version; otherwise preserve unread and document the limitation, with Reader/expanded conversation as the supported automatic read surfaces.
- This is a practical “shown to the operator” rule, not a claim to detect attention or comprehension. Do not add eye tracking, telemetry, per-token writes or arbitrary dwell timers.
- The explicit Mark all read action below is a separate user-directed path: it can acknowledge the chosen current replies without opening them. Do not weaken automatic visibility checks merely because this manual action exists.
3. Durable, race-safe state shared across devices
- Store acknowledgements server-side for Agent Manager's existing single operator; no new account/authentication system. Browser-only storage may cache state, but is not its authority.
- Apply acknowledgement updates monotonically within the correct source generation. Concurrent/late requests cannot move read progress backward or clear output newer than the acknowledged version. Validate session/output association and reject invalid/future acknowledgements.
- Publish truthful persistence outcomes. An error cannot masquerade as a durable successful acknowledgement. Retain/retry the observed version without promoting it to a newer unseen one; avoid success toasts on every read. Use a small scoped store/API, not the deferred app-wide persistence redesign.
- Reconcile other tabs/devices through existing metadata refresh. Stale responses must not regress read state or cause flickering unread cards. Coalesce redundant acknowledgements and bound pending state.
- Renaming/moving a session preserves its read state. Replacing its trace or starting a new run must not inherit an acknowledgement that incorrectly covers new output. Missing/failed metadata is not proof that everything has been read.
4. Overview sections with stable interaction state
- Order eligible sessions as Running, Unread, then Remaining, in both tile and list presentations. A running session appears only in Running; retain its unread state so it moves to Unread if it finishes with unseen output.
- Respect existing state/search/hidden/archive filters; do not resurrect hidden sessions through the new section. Keep group names recognizable. Preserve manual order/group relationships where applicable and the selected sort within the relevant sections; avoid discarding configured ordering as a side effect.
- Reuse current cards, labels and restrained styling. Show an Unread count; omit the empty Unread section. No duplicate cards, new sidebar status system, sound/push notifications or automatic focus changes.
- Moving a card between sections must not remount away its draft, attachments, disclosure, focus or reading position. Marking an expanded conversation read must not close it or move the user's reading surface out from under them. Preserve the stable-parent behavior the ranked feed already documents.
5. Compact Mark all read action
- Place a small text button labelled Mark all read beside the Unread heading/count, using existing button/focus styles. An optional existing check glyph is fine, but do not use an unexplained icon-only control or add a toolbar/menu for this one action. Support keyboard activation and a comfortable touch target while keeping the visual treatment compact.
- Scope the action to all sessions represented in the current Unread section at activation, including entries below the viewport. Respect current search/state/hidden/archive filters; do not silently clear filtered-out sessions or unread state held by the separate Running section. Its accessible description/tooltip should make that section scope clear.
- Snapshot the target session IDs, source generations and latest output versions represented by that section when clicked. A new reply or streaming revision arriving during the request remains unread, even for a targeted session. Do not implement a blanket server-side “everything read up to now” operation, nor rescan newer output during commit/retry.
- Apply through the same durable, monotonic acknowledgement path used by automatic reading, preferably as a bounded batch. Repeated activation while pending must not duplicate work. An explicit Retry uses the original captured targets/versions; a later fresh activation may deliberately capture a new batch.
- Keep persistence results truthful. On total failure, keep/restore the targeted unread state and offer a compact retryable error. If the implementation permits partial success, identify failed targets and only clear the acknowledged versions. Do not lose newer output when reconciling an optimistic result or updating other devices.
- Use one deliberate click, without an extra confirmation dialog, new undo/recovery feature or per-session success toasts. The control is absent with the empty section and disabled while its batch is pending. If clearing the section removes the keyboard-focused control, give focus a stable nearby Overview destination; do not close an expanded conversation or move focus in another pane.
Agreed rollout baseline
Existing replies start read. Take a one-time shared baseline of existing output and start tracking new output, so rollout does not mark the entire historical fleet unread.
Initialize once, not on every browser/device visit. Distinguish unknown/failed metadata from a real baseline; capture exact observed output versions rather than a later wall-clock cutoff. Output arriving after that captured baseline must remain eligible for Unread, including output racing with baseline persistence. Persist initialization and test its behavior across restart and new-device visits.
Required tests and acceptance
| Area |
Cases and assertions |
| Output identity |
New assistant content, streaming revisions, same-text distinct replies, timestamp ties, mirrored events, a new prompt and source replacement. Tool/status/reasoning noise does not create false unread; real new output is not lost. |
| Read conditions |
Foreground latest reply visible in Reader and expanded Overview clears only that version. Inline previews, background/obscured/inactive views, older history, loading, errors and filtered search do not clear newer output. Exercise real presentation handlers, not test-only acknowledgement logic. |
| Races |
Reply B arrives while acknowledgement for A is pending; duplicate/reordered requests; stale meta; two tabs reading different versions; failure/retry. B remains unread until shown, and acknowledged progress does not regress. |
| Persistence and rollout |
Existing replies start read once; subsequent output becomes unread. Test reload/restart/new devices, renamed sessions and new source generations, delayed metadata and output racing with baseline creation. Inject marker-write failure in isolated storage. |
| Overview behavior |
Running → Unread → Remaining, no duplicates, correct counts, empty section, both views, manual/group ordering, sorts, filters, hidden/archive rules and any landed pin behavior. |
| Mark all read scope and races |
Include all section rows, even below the viewport; exclude sessions outside the current filters (including default hidden/archive exclusions) and the separate Running section. Snapshot IDs/versions; new replies, streaming updates, filter changes, repeated activation and retry during the request cannot broaden the captured acknowledgement. |
| Mark all read feedback |
Pointer and Enter/Space activation; compact narrow-screen layout, accessible name/scope, pending control, empty section, stable focus after removal, total/partial persistence failures and cross-device convergence. A manual bulk acknowledgement does not require opening replies and does not weaken automatic read checks. |
| Interaction/performance |
Focus/draft/attachments/open conversation/scroll survive section changes and Reader/Terminal toggles. No eager per-session trace reads or writes every poll/render; request and retained-state counts stay bounded. |
Add deterministic model/API tests and browser tests using real Overview/reader components with fixture output. Cover the supported transcript formats and document any format/surface whose output cannot yet be identified reliably rather than guessing it read. Run focused tests, default web/server suites and web build/typecheck; report exact results and unrelated baseline failures separately.
Use isolated state and fake harness output; no real prompts, live transcript scans, production marker writes or deployment. Open one focused PR with the read/output contract, confirmed rollout policy, screenshots of the three sections and regression evidence. Do not bundle toolbar relocation, full-history search, general polling or the separate mode-switch fix into this PR.
Overview: add an Unread section with durable per-session read tracking
Agreed behavior
The operator requested a second Overview section for sessions with output they have not read, and approved this model:
Implement one focused PR. This is the unread slice of review items U10/F13, not full-transcript search, a notification system or a general Overview redesign. Desktop toolbar relocation was discussed separately but has not been approved.
Current code and integration points
Checked main at
ef08e843d1c67fcc4e1c463415819ea351734d6don 2026-09-08:lastSeenAtdescribes connectivity, not operator reading.Requirements
1. Identify the output, not merely the time a pane opened
2. Automatic acknowledgement requires output actually presented
At latestboolean by itself.3. Durable, race-safe state shared across devices
4. Overview sections with stable interaction state
5. Compact Mark all read action
Agreed rollout baseline
Existing replies start read. Take a one-time shared baseline of existing output and start tracking new output, so rollout does not mark the entire historical fleet unread.
Initialize once, not on every browser/device visit. Distinguish unknown/failed metadata from a real baseline; capture exact observed output versions rather than a later wall-clock cutoff. Output arriving after that captured baseline must remain eligible for Unread, including output racing with baseline persistence. Persist initialization and test its behavior across restart and new-device visits.
Required tests and acceptance
Add deterministic model/API tests and browser tests using real Overview/reader components with fixture output. Cover the supported transcript formats and document any format/surface whose output cannot yet be identified reliably rather than guessing it read. Run focused tests, default web/server suites and web build/typecheck; report exact results and unrelated baseline failures separately.
Use isolated state and fake harness output; no real prompts, live transcript scans, production marker writes or deployment. Open one focused PR with the read/output contract, confirmed rollout policy, screenshots of the three sections and regression evidence. Do not bundle toolbar relocation, full-history search, general polling or the separate mode-switch fix into this PR.