Skip to content

Sidebar can miss unread Completed status for newly finished threads #3131

Description

@paulbettner

With a newly completed background thread, when completed, the sidebar entry switched to the already-viewed/idle state instead of showing the unread Completed dot/status.

My agent claims to have tracked it down to visit-state seeding:

New thread UI state can be seeded from the thread activity timestamp:

seedVisitedAt: thread.updatedAt ?? thread.createdAt

For a just-completed thread, updatedAt can be equal to or later than latestTurn.completedAt. The sidebar's unread-completion check uses a strict comparison:

latestTurn.completedAt > lastVisitedAt

That means if lastVisitedAt is seeded to completedAt or later, the completed thread is treated as already viewed even when the user has not opened it.

Minimal bad state:

latestTurn: {
  state: "completed",
  completedAt: "2026-04-13T00:05:00.000Z",
},
updatedAt: "2026-04-13T00:05:00.000Z",
threadLastVisitedAtById: {
  [threadKey]: "2026-04-13T00:05:00.000Z",
}

Agent's suggested fix: for first-observed completed threads after initial bootstrap, seed visit state before latestTurn.completedAt so they appear unread. Initial bootstrap history should still seed as read, so old completed threads do not all show as newly completed.

And suggested regression coverage:

  • first-seen completed thread-upserted shows unread Completed
  • same case with updatedAt > completedAt
  • post-bootstrap snapshot introducing a completed thread shows unread Completed
  • initial bootstrap historical completed threads stay read
  • non-completed settled turns do not show Completed

Hope that's helpful! Thanks for the great work on t3 code ❤️

Activity

  1. juliusmarminge commented on Sep 5, 2026

    @juliusmarminge
    Member

    Reproduced the missing unread indicator in the Linux web client on September 5, 2026, using current main eced382b.

    I created a background thread through the real WebSocket command API without opening it, then completed a turn through a local deterministic Codex app-server fixture. The server persisted the completion and the client received it, but the sidebar showed no Done indicator. The thread had no recorded visit. No paid model was invoked.

    The current cause is narrower than the original seedVisitedAt explanation: that seeding code is gone, but the unread selector still treats a missing visit timestamp as read. The tested unread-state, sidebar, shell, and ingestion files match the linked main revision. Unrelated local audit integrations were present in the isolated test app.

    Missing indicator after the background completion:

    Completed background thread with no Done indicator

    Positive control, not a fix: using the existing Mark unread action immediately shows Done. It survives a page reload, then opening the thread clears it normally.

    The same thread after the existing Mark unread action

    Keeping this open. The earlier #3235 is closed and not editable by maintainers. A replacement needs to preserve initial historical threads as read while identifying new background completions after bootstrap, without overwriting genuine visits. Native desktop and mobile were not exercised.

    GPT 6 Astra via Codex in T3 Code.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.🚧 In Progress

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions