Skip to content

fix(backfill): store full UIMessage in chat_messages.parts - #627

Merged
sweetmantech merged 1 commit into
testfrom
fix/backfill-parts-full-uimessage
May 29, 2026
Merged

fix(backfill): store full UIMessage in chat_messages.parts#627
sweetmantech merged 1 commit into
testfrom
fix/backfill-parts-full-uimessage

Conversation

@sweetmantech

Copy link
Copy Markdown
Contributor

Summary

The chat-workflow cutover (recoupable/app#1747) needs migrated history to read back through the workflow path. That surfaced a shape mismatch in the Phase 2 backfill:

  • Native writes store the entireUIMessage in chat_messages.partspersistLatestUserMessage.ts:42 and persistAssistantMessage.ts:27 both do parts: message as never.
  • The read pathgetSessionChatHandler.ts:44 returns messages: rows.map(r => r.parts) and expects each to already be a UIMessage.
  • The backfill wrote parts: content.parts — just the bare parts array — so migrated rows came back with no id/role and wouldn't render.

This stores { id, role, parts } to match the native shape.

Data already corrected

The ~46k already-migrated rows were fixed in place on prod via a one-time, reversible SQL reconstruction from the existing columns (the original array is preserved nested under parts):

UPDATE chat_messages
SET parts = jsonb_build_object('id', id, 'role', role, 'parts', parts)
WHERE jsonb_typeof(parts) ='array'; -- targets only backfilled rows; native rows are objects

Post-update verification: 46,616 object rows / 0 array rows; 0 rows missing id/role/parts keys; 0 rows where inner id/role diverge from the columns.

This code change keeps the owed pre-promotion straggler re-run consistent (write-once, so it only adds new rows in the correct shape).

Test plan

  • migrateRoom unit test asserts parts is the full { id, role, parts } UIMessage. (4 tests green)
  • tsc --noEmit clean on changed files; eslint clean.
  • Prod data reconciled + verified (counts above).

🤖 Generated with Claude Code

The workflow persists the entire UIMessage in the parts column
(persistLatestUserMessage / persistAssistantMessage), and the read path
(getSessionChatHandler) returns parts verbatim as a UIMessage. The Phase 2
backfill wrote only the bare parts array, so migrated history came back
without id/role and wouldn't render through the workflow read path.
Store { id, role, parts } to match the native shape. The already-migrated
~46k rows were corrected in place via a one-time SQL reconstruction
(jsonb_build_object from the id/role/parts columns); this fix keeps the
owed straggler re-run consistent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
apiReadyReadyPreviewMay 29, 2026 4:20pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (2)
  • scripts/backfill/__tests__/migrateRoom.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by none
  • scripts/backfill/migrateRoom.ts is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 95435fab-d7fa-46bf-a82e-b0020583e696

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/backfill-parts-full-uimessage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-aicubic-dev-aiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
participant B as Backfill Script
participant DB as Database
participant R as Read Path
participant W as Native Write Path
Note over B,W: Chat Messages Data Flow
B->>B: Read migrated rows
B->>DB: Insert chat_messages row
Note over B,DB: parts: { id, role, parts } (full UIMessage)
alt Native write path
W->>DB: insert chat_messages.parts as UIMessage
Note over W,DB: persistLatestUserMessage / persistAssistantMessage
W->>W: parts = message as never (full UIMessage)
end
DB-->>R: Return rows
R->>R: messages = rows.map(r => r.parts)
Note over R: Expects each .parts to be UIMessage
alt parts is full UIMessage object
R->>R: Extract id, role, parts from object
R-->>R: Render correctly
else parts is bare array (legacy backfill)
R->>R: Missing id and role
R-->>R: Render failure (no id/role)
end
Note over B,R: Fix: Backfill now stores { id, role, parts }<br/>matching native shape
opt Production data fix (already applied)
B->>DB: UPDATE chat_messages
Note over B,DB: Reconstruct from columns<br/>where parts is array
DB-->>B: Verify: 0 array rows<br/>0 missing id/role/parts
end
Loading

Auto-approved: This is a focused, low-risk fix to a backfill script that corrects the stored shape of chat messages to match the production read path, has a corresponding test update, and the production data was already reconciled via a separate SQL command.

Re-trigger cubic

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@sweetmantech