Skip to content

fix(web): order session messages by creation time#17149

Open
ShenAC-SAC wants to merge 4 commits intoanomalyco:devfrom
ShenAC-SAC:codex/fix-frontend-message-order
Open

fix(web): order session messages by creation time#17149
ShenAC-SAC wants to merge 4 commits intoanomalyco:devfrom
ShenAC-SAC:codex/fix-frontend-message-order

Conversation

@ShenAC-SAC
Copy link

@ShenAC-SAC ShenAC-SAC commented Mar 12, 2026

Issue for this PR

Closes #17012

Follow-up to #17010.

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

This fixes the web frontend side of the cross-machine message ordering bug.

#17010 fixed the backend/prompt loop logic by avoiding assumptions based on message ID ordering. The frontend still had several places where it implicitly treated message.id as a chronological key.

That breaks when user message IDs are generated on the client, assistant message IDs are generated on the server, and those machines are not using the same clock source. In that setup, message IDs can be lexically out of order even when time.created is correct.

This PR updates the web UI to:

  • order messages chronologically using time.created
  • render assistant replies by parentID instead of relying on array position after ID sorting
  • update session timeline, context, revert, and redo behavior to follow chronological ordering instead of message.id comparisons
  • add regression tests for cases where assistant IDs sort before their parent user IDs

How did you verify your code works?

I verified this locally in the fork workspace with:

  • cd packages/util && bun test src/message.test.ts
  • cd packages/util && bun run typecheck
  • cd packages/ui && bun run typecheck
  • cd packages/app && bun run typecheck

I also reproduced the issue scenario conceptually by using the case where the browser and opencode server run on different machines, so client-generated user IDs and server-generated assistant IDs no longer share a reliable global time source.

Screenshots / recordings

Not included. This change is focused on the message ordering and rendering logic that determines whether assistant replies appear at all in cross-machine web sessions.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Mar 12, 2026
@github-actions
Copy link
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Mar 12, 2026
@github-actions
Copy link
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@ShenAC-SAC
Copy link
Author

Adding a quick note on the split with #17010:

  • fix(session): use parentID instead of message ID ordering in prompt loop #17010 fixes the backend / prompt-loop side by removing assumptions that parent-child message relationships can be inferred from message ID ordering.
  • This PR fixes the web frontend side by removing the remaining places where the UI still treated message.id as a chronological key for rendering, timeline traversal, and revert/redo behavior.

So the two PRs are meant to be complementary:

Both are needed when the browser and opencode server run on different machines and therefore do not share a reliable global clock for message ID generation.

@sjawhar
Copy link

sjawhar commented Mar 12, 2026

We adopted this PR's sortMessages/selectAssistants/splitMessages utilities in our fork. The time.created-first ordering + role rank tiebreaker approach works well for frontend rendering. One note for the maintainers: we also found that time_created in the backend DB is client-provided for user messages (set from msg.time.created in updateMessage), so the same ordering approach isn't safe for backend chronological comparisons — we used index-based position instead for backend fixes to fork/revert/prompt-wrapping.

@ShenAC-SAC
Copy link
Author

We adopted this PR's sortMessages/selectAssistants/splitMessages utilities in our fork. The time.created-first ordering + role rank tiebreaker approach works well for frontend rendering. One note for the maintainers: we also found that time_created in the backend DB is client-provided for user messages (set from msg.time.created in updateMessage), so the same ordering approach isn't safe for backend chronological comparisons — we used index-based position instead for backend fixes to fork/revert/prompt-wrapping.

Thanks — that’s helpful context.
This PR is only intended to fix the frontend rendering / interaction side. I agree the backend should not simply adopt the same time.created ordering approach for fork/revert/prompt-wrapping.
That’s also why I treated the fixes as split:

  • backend loop correctness separately
  • frontend rendering / traversal separately
    The frontend can use time.created for presentation order, while backend session operations likely need a stronger ordering primitive such as position / parent relationships.

Sign up for free to 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.

Web sessions can loop indefinitely when user message IDs are generated on the client

2 participants