Repository navigation
Sync thread and DM read state across a person's devices - #832
Merged
Merged
Conversation
Read markers lived only in each client's local storage and were sent as `read_at` on discovery / DM listing, so reading a thread on the desktop left it unread on the phone (and the other way round). Backend - New read_markers table (migration 084): one marker per workspace, person (email) and session id (thread name or canonical dm:<a>,<b>). Markers only move forward; future times are clamped to the server clock. - GET /v1/read-state returns the caller's markers (optionally only those changed after `since`); POST /v1/read-state moves them forward, marks the caller's DM / mention signals up to the marker read, and publishes workspace.read_state.updated on the workspace event stream. - /v1/discover and /v1/events/conversations merge the stored markers with any markers the client still sends (newer wins), so unread counts follow the server for every client and older clients keep working. - The SSE stream delivers read-state events only to the person they belong to, never to agents, machines or other members. Web workspace (also the launcher, which embeds it) - Opening a thread / DM, and keeping the open one read while the window is focused, reports the read (batched). First-seen baselines are not reported. - Newer markers from discovery and DM listing are adopted locally, so a badge cleared on another device clears here on the next poll.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reading a thread or DM on the desktop did not clear its unread badge on the phone. Read markers lived only in each client's local storage (web
oa-read-<workspace>, mobile SharedPreferences) and were sent per request asread_at; the server computed counts from whatever the device sent and stored nothing. Each device therefore had its own read state.The server now stores one read marker per person (by email, not per device) and per thread / DM, and computes unread counts from it.
Backend
read_markerstable (migration084):(workspace_id, user_email, session_id)→read_at,updated_at(unix ms).session_idis the client session id: the thread name, or the canonicaldm:<a>,<b>(two addresses sorted and comma-joined). Markers only move forward, and future times are clamped to the server clock.GET /v1/read-state?network=&since=: the caller's markers (hidden private threads left out).sincereturns only the markers changed after that server time.POST /v1/read-state{network, markers: {session_id: read_at_ms | null}}(null= server now): moves the markers forward, marks the caller's DM / @mention signals up to the marker as read, and publishesworkspace.read_state.updatedon the workspace event stream for each marker that moved. Token-only callers getpersisted: falseand nothing is stored./v1/discoverand/v1/events/conversations: merge the stored markers with anyread_atthe client still sends, and the newer one wins.read_at/unread_countin the response are based on the merged marker. Clients that send nothing get the stored markers, and older clients keep working unchanged.access_token). Agents, machines and other members never get them. Channel-scoped streams do not carry them.Web workspace (and the launcher, which embeds the same bundle)
Needs agreement before deploy
This adds a table (migration
084_read_markers). Perworkspace/AGENTS.md, the migration has not been run anywhere. It needs the owner's agreement before it is applied, and preview shares the production database. The new code reads the table on every discovery / DM poll from a signed-in person, so the migration must be applied before this backend ships.Test plan
workspace/backend: newtests/test_read_state.py(14 tests): per-person sharing, merge with markers the client sends, markers only moving forward, future-time clamp, DM counts, clearing signals up to the marker, event payload, stream delivery only to the owner, hidden threads, token-only callers, input validation.workspace/backendfull suite: 34 failed / 6 errors, the same set as ondevelopon this machine. That includestest_migration_schema, whose init SQL is still stamped015.workspace/frontend:tsc --noEmit;vitest runpasses except 8 tests inbrowser-thumbnail/browser-view-keyboard, which also fail ondevelop; newlib/read-state.test.ts.ruffadds no new findings inapp/.