feat: restore production chat reads, honest Settings GET, and granted chat conversation composition - #12914
Conversation
Mount GET /v1/chat-messages on the production Firebase/PostgreSQL entry behind an explicit chat.read grant. Missing or revoked grants stay 403. Granted empty history is an honest empty page with existing attachment capabilities. Assistant rows without a unique terminal generation event are 503. POST and SSE stay unmounted. Verification: backends/example-platform `bun run check:deployed` passed (282 tests, 1 skipped real PostgreSQL without Docker). Migration 55 is not applied to DEV from this environment.
Missing chat.read and conversations/memories/tasks grants now surface typed account-unavailable copy instead of a generic load failure or an empty successful list.
Verified Firebase credentials no longer 404 on GET /v1/settings. Signed-out stays a null envelope; signed-in stays 503 until an owner-backed profile and entitlement producer exists.
The production unique-terminal projector fail-closes to 503 when frame_json is a JSON string. Pass the serialized frame as text so postgres.js does not double-encode the jsonb fixture. Failure-Class: none
First-page GET /v1/conversations includes chat:chat-main when the same credential holds chat.read and main-session messages exist. Missing grants and empty history never invent that row. Later cursor pages keep the Listen sequence. Failure-Class: none
Opening chat:chat-main reads GET /v1/chat-messages instead of a title-only pane. Other chat session ids stay unavailable rather than showing the main transcript. Grant denial keeps typed copy. Failure-Class: none
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b9c0e58b-013a-4b71-85c7-68baed3e6b49) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59fc4c4d12
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -0,0 +1,273 @@ | |||
| CREATE TABLE omi_memory.chat_messages ( | |||
There was a problem hiding this comment.
Mark unresolved chat schema names for mechanical renaming
Add SQL-compatible domain-pending(...) annotations to migrations 0055 and 0056. This migration introduces the unresolved sender, type, session, revision, hash, and source vocabulary throughout the durable schema, but the markers currently exist only in the TypeScript adapters; that leaves these SQL identifiers invisible to the required mechanical rename process.
AGENTS.md reference: backends/example-platform/AGENTS.md:L10-L13
Useful? React with 👍 / 👎.
| if (omiBackend == null) { | ||
| throw new Error('Native transport unavailable'); | ||
| } | ||
| const messages = await loadChatHistory(omiBackend); |
There was a problem hiding this comment.
Preserve pagination when loading conversation chat history
When the main chat contains more than 50 messages, loadChatHistory fetches only the newest page and returns just its message array, so this hook discards olderCursor and hasOlder. ChatConversationHistory consequently provides no way to load the omitted messages, making all history beyond the newest 50 entries inaccessible from the newly added conversation detail.
Useful? React with 👍 / 👎.
| if (!chatAuthorization.authorized) { | ||
| return chatAuthorization.outcome === "authorization" | ||
| ? listen | ||
| : failed(503, "unavailable"); |
There was a problem hiding this comment.
Return unauthorized when the second identity check fails
If a first-page conversation read crosses the Firebase token's expiry boundary, the initial conversations.read authorization can succeed while this subsequent chat.read authorization returns authentication. This branch maps every result except missing authorization to 503, so the client treats an expired session as a transient service outage and does not trigger its 401 reauthentication flow; preserve the established 401 unauthorized response for this outcome.
Useful? React with 👍 / 👎.
| if (!sessionIds.has(row.id)) items.push(row); | ||
| } | ||
| items.push(...sessions); | ||
| items.sort(compareItems); |
There was a problem hiding this comment.
Preserve the listen cursor order when adding chat
Do not re-sort the returned listen page independently of its cursor. PostgreSQL pages recordings by ascending durable conversation_sequence, and the runtime saves the next cursor against that original sequence, but this line reorders only the first page by descending updatedAt; after the client appends later pages, recordings can appear in a different order from the cursor chain, with an old injected chat row also preceding newer recordings that have not yet been fetched. Either preserve the listen rows' order when inserting the chat item or paginate the combined ordering.
Useful? React with 👍 / 👎.
| )} | ||
| {selected.source === 'chat' && | ||
| selected.id === MAIN_CHAT_CONVERSATION_ID && ( | ||
| <ChatConversationHistory key={selected.id} /> |
There was a problem hiding this comment.
Reload the selected chat when its conversation changes
When a conversations refresh returns chat:chat-main with a newer updatedAt, React preserves this child because its key remains the conversation ID, while useChatConversationHistory reloads only when active or its private retry counter changes. The list can therefore show new chat activity while the open detail continues displaying the old message snapshot, and the loaded state offers no manual reload action; pass the conversation revision/timestamp into the hook or include it in the component key, as the recording transcript path already does.
Useful? React with 👍 / 👎.
Summary
Production New-backend now serves
GET /v1/chat-messagesfrom PostgreSQL under an explicitchat.readgrant, mountsGET /v1/settingswithout inventing a signed-in profile, and composes granted main-chat sessions onto the firstGET /v1/conversationspage. Openingchat:chat-mainloads that persisted history instead of a title-only pane. Missing grants and missing producers stay visible failures. Empty successful transcripts, token-claim identities, and emptychat:chat-mainrows are not used to satisfy the screens.POST /v1/chat-messagesand generation SSE stay unmounted (404{error:"not_found"}).chat_messagesandchat_generation_events. Migration 0056 addsread_chat_conversation_sessions(). Both update the checksummed manifest.GET /v1/settingsstays{identity:null,entitlement:null}. A verified Firebase identity without an owner-backed producer is 503, not a 200 profile minted from token claims.chat:chat-mainonly when the same credential holdschat.readand main-session messages exist. Later cursor pages keep the Listen sequence and do not repeat that row.chat:chat-mainreadsGET /v1/chat-messages. Otherchat:session ids stay unavailable rather than showing the main transcript. Grant denial keeps typed copy.$n::text::jsonbso postgres.js does not double-encode terminals into JSON strings (which correctly fail closed as 503).Why this boundary
Chat history ownership is the
chat.readgrant, not Firebase identity and notmemories.read. Writes still need a source-owned entitlement producer; this PR does not invent quotas or mount admission. Settings identity and entitlements still need an owner-backed producer; this PR does not project Firebase claims as a profile. Conversation composition uses a secondchat.readauthorization rather than reading chat tables underconversations.read.Verification
From
backends/example-platform:GitHub
Validate backend workerpassed onec68aaa925, including real PostgreSQL 18.4 (test:postgres) for the jsonb fixture andchat:chat-maincomposition.React Native:
The skipped local tests are
chat-messages.real.test.tsand the conversation-chat composition case inconversations.real.test.ts, which need Docker/OMI_TEST_POSTGRES_URL. This Cloud VM has no Docker. CItest:postgresis the real-PG proof.Not verified here: applying migrations 55-56 to based-hardware-dev, Cloud Run deploy, live Firebase grants, Apple Debug builds, ScreenCaptureKit, physical BLE/iPad.
Do not deploy this entry against a database that still has only migrations 1–54: readiness compares the full checksummed manifest and will stay unready.
Remaining (not this PR)