fix: sort child chats newest-first and prepend on creation (#24524)

GetChildChatsByParentIDs sorted created_at ASC, but the cache
helper appended new children to the end. On refetch the API and
cache agreed on oldest-first, putting the just-created child at
the bottom. Users expect newest first, matching the root-chat
sidebar convention.

- SQL: change child sort to created_at DESC, id DESC.
- Cache: prepend instead of append in addChildToParentInCache
  (renamed from appendChildToParentInCache to avoid leaking
  position semantics).
- Test: update ordering assertion to expect newest-first.

Refs #24404
This commit is contained in:
Mathias Fredriksson
2026-04-20 10:43:31 +00:00
committed by GitHub
parent df7e838c21
commit 467430d8fa
6 changed files with 19 additions and 19 deletions
+2 -2
View File
@@ -6881,8 +6881,8 @@ WHERE
ELSE chats.archived = $2 :: boolean
END
ORDER BY
chats.created_at ASC,
chats.id ASC
chats.created_at DESC,
chats.id DESC
`
type GetChildChatsByParentIDsParams struct {
+2 -2
View File
@@ -418,8 +418,8 @@ WHERE
ELSE chats.archived = sqlc.narg('archived') :: boolean
END
ORDER BY
chats.created_at ASC,
chats.id ASC;
chats.created_at DESC,
chats.id DESC;
-- name: InsertChat :one
INSERT INTO chats (
+3 -3
View File
@@ -1333,10 +1333,10 @@ func TestListChats(t *testing.T) {
}
require.Len(t, parent.Children, 2, "parent should embed 2 children")
// Children should be ordered by created_at ASC.
// Children are ordered by created_at DESC (newest first).
childIDs := []uuid.UUID{parent.Children[0].ID, parent.Children[1].ID}
require.Equal(t, child1.ID, childIDs[0])
require.Equal(t, child2.ID, childIDs[1])
require.Equal(t, child2.ID, childIDs[0])
require.Equal(t, child1.ID, childIDs[1])
// Verify each child has correct parent/root references.
for _, child := range parent.Children {
+6 -6
View File
@@ -4,7 +4,7 @@ import { API } from "#/api/api";
import type * as TypesGen from "#/api/typesGenerated";
import { buildOptimisticEditedMessage } from "./chatMessageEdits";
import {
appendChildToParentInCache,
addChildToParentInCache,
archiveChat,
cancelChatListRefetches,
chatCostSummary,
@@ -1703,8 +1703,8 @@ describe("mutation onMutate cancels pagination fetches", () => {
});
});
describe("appendChildToParentInCache", () => {
it("appends the child to the matching parent's children array", () => {
describe("addChildToParentInCache", () => {
it("prepends new child to the parent's children array", () => {
const queryClient = createTestQueryClient();
const parent = makeChat("parent-1");
seedInfiniteChats(queryClient, [parent]);
@@ -1713,7 +1713,7 @@ describe("appendChildToParentInCache", () => {
parent_chat_id: "parent-1",
root_chat_id: "parent-1",
});
appendChildToParentInCache(queryClient, child, "parent-1");
addChildToParentInCache(queryClient, child, "parent-1");
const result = readInfiniteChats(queryClient);
expect(result).toHaveLength(1);
@@ -1730,7 +1730,7 @@ describe("appendChildToParentInCache", () => {
parent_chat_id: "missing-parent",
root_chat_id: "missing-parent",
});
appendChildToParentInCache(queryClient, child, "missing-parent");
addChildToParentInCache(queryClient, child, "missing-parent");
const result = readInfiniteChats(queryClient);
expect(result).toHaveLength(1);
@@ -1747,7 +1747,7 @@ describe("appendChildToParentInCache", () => {
const parent = makeChat("parent-1", { children: [existingChild] });
seedInfiniteChats(queryClient, [parent]);
appendChildToParentInCache(queryClient, existingChild, "parent-1");
addChildToParentInCache(queryClient, existingChild, "parent-1");
const result = readInfiniteChats(queryClient);
expect(result?.[0].children).toHaveLength(1);
+3 -3
View File
@@ -103,11 +103,11 @@ export const readInfiniteChatsCache = (
};
/**
* Appends a child chat to its parent's `children` array across all
* Adds a child chat to its parent's `children` array across all
* infinite chat query caches. If the parent is not in any loaded page,
* the child is silently dropped (it will appear when the parent loads).
*/
export const appendChildToParentInCache = (
export const addChildToParentInCache = (
queryClient: QueryClient,
child: TypesGen.Chat,
parentId: string,
@@ -119,7 +119,7 @@ export const appendChildToParentInCache = (
// Avoid duplicates.
if (c.children?.some((ch) => ch.id === child.id)) return c;
changed = true;
return { ...c, children: [...(c.children ?? []), child] };
return { ...c, children: [child, ...(c.children ?? [])] };
});
return changed ? next : chats;
});
+3 -3
View File
@@ -10,7 +10,7 @@ import { toast } from "sonner";
import { API, watchChats } from "#/api/api";
import { getErrorMessage } from "#/api/errors";
import {
appendChildToParentInCache,
addChildToParentInCache,
archiveChat,
cancelChatListRefetches,
chatDiffContentsKey,
@@ -567,10 +567,10 @@ const AgentsPage: FC = () => {
// chat into every loaded page.
if (chatEvent.kind === "created") {
if (updatedChat.parent_chat_id) {
// Child chat: append to its parent's children
// Child chat: add to its parent's children
// array. If the parent is not in any loaded
// page, the child is silently dropped.
appendChildToParentInCache(
addChildToParentInCache(
queryClient,
updatedChat,
updatedChat.parent_chat_id,