import type { EnvironmentProject, EnvironmentThreadShell,} from "@t3tools/client-runtime/state/shell";import { threadSearchMatchKey } from "@t3tools/client-runtime/state/thread-search";import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts";import { describe, expect, it } from "vite-plus/test"; import { buildHomeProjectScopes, buildHomeThreadGroups, sortHomeProjectScopes,} from "./homeThreadList"; function makeProject( input: Partial<EnvironmentProject> & Pick<EnvironmentProject, "environmentId" | "id" | "title">,): EnvironmentProject { return { workspaceRoot: `/workspaces/${input.id}`, repositoryIdentity: null, defaultModelSelection: null, scripts: [], createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-01T00:00:00.000Z", ...input, };} function makeThread( input: Partial<EnvironmentThreadShell> & Pick<EnvironmentThreadShell, "environmentId" | "id" | "projectId" | "title">,): EnvironmentThreadShell { return { modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurn: null, createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-01T00:00:00.000Z", archivedAt: null, session: null, latestUserMessageAt: null, hasPendingApprovals: false, hasPendingUserInput: false, hasActionableProposedPlan: false, ...input, settledOverride: input.settledOverride ?? null, settledAt: input.settledAt ?? null, };} const NOW = Date.parse("2026-06-29T00:00:00.000Z"); function buildGroups( projects: ReadonlyArray<EnvironmentProject>, threads: ReadonlyArray<EnvironmentThreadShell>, overrides: Partial<Parameters<typeof buildHomeThreadGroups>[0]> = {},) { return buildHomeThreadGroups({ projects, threads, environmentId: null, searchQuery: "", projectSortOrder: "updated_at", threadSortOrder: "updated_at", projectGroupingMode: "repository", now: NOW, ...overrides, });} describe("buildHomeThreadGroups", () => { it("builds one v2 scope for the same repository across environments", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const projects = [ makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-local"), title: "t3code", repositoryIdentity, }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote"), title: "t3code", repositoryIdentity, }), ]; const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); expect(scopes).toHaveLength(1); expect(scopes[0]?.title).toBe("t3code"); expect(scopes[0]?.projects).toEqual(projects); expect(scopes[0]?.projectRefs).toEqual( projects.map((project) => ({ environmentId: project.environmentId, projectId: project.id, })), ); }); it("routes stale duplicate project refs through the canonical repository group", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const local = makeProject({ id: ProjectId.make("project-local"), environmentId: localEnvironmentId, title: "t3code", workspaceRoot: "/workspaces/t3code", repositoryIdentity, }); const stale = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-stale"), title: "t3code", workspaceRoot: "/remote/t3code", updatedAt: "2026-06-01T00:00:00.000Z", }); const canonicalRemote = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-canonical-remote"), title: "t3code", workspaceRoot: "/remote/t3code/", repositoryIdentity, updatedAt: "2026-06-02T00:00:00.000Z", }); const projects = [local, stale, canonicalRemote]; const staleThread = makeThread({ environmentId: remoteEnvironmentId, id: ThreadId.make("thread-stale-project-ref"), projectId: stale.id, title: "Still visible", updatedAt: "2026-06-03T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); const groups = buildGroups(projects, [staleThread]); expect(scopes).toHaveLength(1); expect(scopes[0]?.projects.map((project) => project.id)).toEqual([ local.id, canonicalRemote.id, ]); expect(scopes[0]?.projectRefs.map((projectRef) => projectRef.projectId)).toEqual([ local.id, stale.id, canonicalRemote.id, ]); expect(groups).toHaveLength(1); expect(groups[0]?.threads.map((thread) => thread.id)).toEqual([staleThread.id]); expect(groups[0]?.newThreadTarget?.id).toBe(canonicalRemote.id); }); it("keeps repository identity from an older duplicate when the freshness winner lacks it", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const projects = [ makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-local"), title: "t3code", repositoryIdentity, }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote-with-identity"), title: "t3code", workspaceRoot: "/remote/t3code", repositoryIdentity, updatedAt: "2026-06-01T00:00:00.000Z", }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote-fresh"), title: "t3code", workspaceRoot: "/remote/t3code/", updatedAt: "2026-06-02T00:00:00.000Z", }), ]; const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); expect(scopes).toHaveLength(1); expect(scopes[0]?.representative.id).toBe(ProjectId.make("project-local")); expect(scopes[0]?.projects.map((project) => project.id)).toContain( ProjectId.make("project-remote-fresh"), ); expect(scopes[0]?.projectRefs).toHaveLength(3); }); it("sorts v2 project scopes by their grouped thread activity", () => { const environmentId = EnvironmentId.make("environment-1"); const olderProject = makeProject({ environmentId, id: ProjectId.make("project-older"), title: "Older project", }); const newerProject = makeProject({ environmentId, id: ProjectId.make("project-newer"), title: "Newer project", }); const scopes = buildHomeProjectScopes({ projects: [newerProject, olderProject], environmentId: null, projectGroupingMode: "separate", }); expect( sortHomeProjectScopes({ scopes, threads: [ makeThread({ environmentId, id: ThreadId.make("thread-older-project"), projectId: olderProject.id, title: "Most recently active", updatedAt: "2026-06-03T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("thread-newer-project"), projectId: newerProject.id, title: "Less recently active", updatedAt: "2026-06-02T00:00:00.000Z", }), ], pendingTasks: [], projectSortOrder: "updated_at", }).map((scope) => scope.representative.id), ).toEqual([olderProject.id, newerProject.id]); }); it("sorts invalid project creation timestamps after valid ones", () => { const environmentId = EnvironmentId.make("environment-1"); const invalidProject = makeProject({ environmentId, id: ProjectId.make("project-invalid"), title: "A invalid timestamp", createdAt: "invalid", }); const validProject = makeProject({ environmentId, id: ProjectId.make("project-valid"), title: "Z valid timestamp", createdAt: "2026-06-02T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects: [invalidProject, validProject], environmentId: null, projectGroupingMode: "separate", }); expect( sortHomeProjectScopes({ scopes, threads: [], pendingTasks: [], projectSortOrder: "created_at", }).map((scope) => scope.representative.id), ).toEqual([validProject.id, invalidProject.id]); }); it("uses the freshest member when a grouped scope has no activity", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const olderMember = makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-older-member"), title: "t3code", updatedAt: "2026-06-01T00:00:00.000Z", repositoryIdentity, }); const newerMember = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-newer-member"), title: "t3code", updatedAt: "2026-06-03T00:00:00.000Z", repositoryIdentity, }); const otherProject = makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-other"), title: "other", updatedAt: "2026-06-02T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects: [olderMember, newerMember, otherProject], environmentId: null, projectGroupingMode: "repository", }); expect( sortHomeProjectScopes({ scopes, threads: [], pendingTasks: [], projectSortOrder: "updated_at", })[0]?.key, ).toBe(scopes.find((scope) => scope.projects.length === 2)?.key); }); it("does not merge unrelated repositories that share a title", () => { const environmentId = EnvironmentId.make("environment-1"); const projects = ["one", "two"].map((name) => makeProject({ environmentId, id: ProjectId.make(`project-${name}`), title: "app", repositoryIdentity: { canonicalKey: `github.com/example/${name}`, locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: `git@github.com:example/${name}.git`, }, }, }), ); expect( buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }), ).toHaveLength(2); }); it("uses the physical project title for a singleton scope", () => { const project = makeProject({ environmentId: EnvironmentId.make("environment-1"), id: ProjectId.make("project-1"), title: "local-worktree-name", repositoryIdentity: { canonicalKey: "github.com/pingdotgg/t3code", displayName: "codething-mvp", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }, }); const scopes = buildHomeProjectScopes({ projects: [project], environmentId: null, projectGroupingMode: "repository", }); const groups = buildGroups( [project], [ makeThread({ environmentId: project.environmentId, id: ThreadId.make("thread-1"), projectId: project.id, title: "Thread", }), ], ); expect(scopes[0]?.title).toBe("local-worktree-name"); expect(groups[0]?.title).toBe("local-worktree-name"); }); it("sorts the newest thread first regardless of snapshot order", () => { const environmentId = EnvironmentId.make("environment-1"); const project = makeProject({ environmentId, id: ProjectId.make("project-1"), title: "T3 Code", }); const threads = [ makeThread({ environmentId, id: ThreadId.make("thread-old"), projectId: project.id, title: "Older thread", updatedAt: "2026-06-02T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("thread-new"), projectId: project.id, title: "Newer thread", updatedAt: "2026-06-03T00:00:00.000Z", }), ]; expect(buildGroups([project], threads)[0]?.threads.map((thread) => thread.id)).toEqual([ "thread-new", "thread-old", ]); }); it("supports independent project and thread creation-time sorting", () => { const environmentId = EnvironmentId.make("environment-1"); const olderProject = makeProject({ environmentId, id: ProjectId.make("project-older"), title: "Older project", }); const newerProject = makeProject({ environmentId, id: ProjectId.make("project-newer"), title: "Newer project", }); const threads = [ makeThread({ environmentId, id: ThreadId.make("old-created"), projectId: olderProject.id, title: "Updated recently", createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-05T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("new-created"), projectId: olderProject.id, title: "Created recently", createdAt: "2026-06-04T00:00:00.000Z", updatedAt: "2026-06-04T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("newest-project-thread"), projectId: newerProject.id, title: "Newest project", createdAt: "2026-06-06T00:00:00.000Z", }), ]; const groups = buildGroups([olderProject, newerProject], threads, { projectSortOrder: "created_at", threadSortOrder: "created_at", projectGroupingMode: "separate", }); expect(groups.map((group) => group.representative.id)).toEqual([ "project-newer", "project-older", ]); expect(groups[1]?.threads.map((thread) => thread.id)).toEqual(["new-created", "old-created"]); }); it("filters both projects and threads to one environment", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const projects = [ makeProject({Lines 1–500 of 746. Load more
import type { EnvironmentProject, EnvironmentThreadShell, } from "@t3tools/client-runtime/state/shell"; import { threadSearchMatchKey } from "@t3tools/client-runtime/state/thread-search"; import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts"; import { describe, expect, it } from "vite-plus/test"; import { buildHomeProjectScopes, buildHomeThreadGroups, sortHomeProjectScopes, } from "./homeThreadList"; function makeProject( input: Partial<EnvironmentProject> & Pick<EnvironmentProject, "environmentId" | "id" | "title">, ): EnvironmentProject { return { workspaceRoot: `/workspaces/${input.id}`, repositoryIdentity: null, defaultModelSelection: null, scripts: [], createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-01T00:00:00.000Z", ...input, }; } function makeThread( input: Partial<EnvironmentThreadShell> & Pick<EnvironmentThreadShell, "environmentId" | "id" | "projectId" | "title">, ): EnvironmentThreadShell { return { modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" }, runtimeMode: "full-access", interactionMode: "default", branch: null, worktreePath: null, latestTurn: null, createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-01T00:00:00.000Z", archivedAt: null, session: null, latestUserMessageAt: null, hasPendingApprovals: false, hasPendingUserInput: false, hasActionableProposedPlan: false, ...input, settledOverride: input.settledOverride ?? null, settledAt: input.settledAt ?? null, }; } const NOW = Date.parse("2026-06-29T00:00:00.000Z"); function buildGroups( projects: ReadonlyArray<EnvironmentProject>, threads: ReadonlyArray<EnvironmentThreadShell>, overrides: Partial<Parameters<typeof buildHomeThreadGroups>[0]> = {}, ) { return buildHomeThreadGroups({ projects, threads, environmentId: null, searchQuery: "", projectSortOrder: "updated_at", threadSortOrder: "updated_at", projectGroupingMode: "repository", now: NOW, ...overrides, }); } describe("buildHomeThreadGroups", () => { it("builds one v2 scope for the same repository across environments", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const projects = [ makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-local"), title: "t3code", repositoryIdentity, }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote"), title: "t3code", repositoryIdentity, }), ]; const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); expect(scopes).toHaveLength(1); expect(scopes[0]?.title).toBe("t3code"); expect(scopes[0]?.projects).toEqual(projects); expect(scopes[0]?.projectRefs).toEqual( projects.map((project) => ({ environmentId: project.environmentId, projectId: project.id, })), ); }); it("routes stale duplicate project refs through the canonical repository group", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const local = makeProject({ id: ProjectId.make("project-local"), environmentId: localEnvironmentId, title: "t3code", workspaceRoot: "/workspaces/t3code", repositoryIdentity, }); const stale = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-stale"), title: "t3code", workspaceRoot: "/remote/t3code", updatedAt: "2026-06-01T00:00:00.000Z", }); const canonicalRemote = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-canonical-remote"), title: "t3code", workspaceRoot: "/remote/t3code/", repositoryIdentity, updatedAt: "2026-06-02T00:00:00.000Z", }); const projects = [local, stale, canonicalRemote]; const staleThread = makeThread({ environmentId: remoteEnvironmentId, id: ThreadId.make("thread-stale-project-ref"), projectId: stale.id, title: "Still visible", updatedAt: "2026-06-03T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); const groups = buildGroups(projects, [staleThread]); expect(scopes).toHaveLength(1); expect(scopes[0]?.projects.map((project) => project.id)).toEqual([ local.id, canonicalRemote.id, ]); expect(scopes[0]?.projectRefs.map((projectRef) => projectRef.projectId)).toEqual([ local.id, stale.id, canonicalRemote.id, ]); expect(groups).toHaveLength(1); expect(groups[0]?.threads.map((thread) => thread.id)).toEqual([staleThread.id]); expect(groups[0]?.newThreadTarget?.id).toBe(canonicalRemote.id); }); it("keeps repository identity from an older duplicate when the freshness winner lacks it", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const projects = [ makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-local"), title: "t3code", repositoryIdentity, }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote-with-identity"), title: "t3code", workspaceRoot: "/remote/t3code", repositoryIdentity, updatedAt: "2026-06-01T00:00:00.000Z", }), makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-remote-fresh"), title: "t3code", workspaceRoot: "/remote/t3code/", updatedAt: "2026-06-02T00:00:00.000Z", }), ]; const scopes = buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }); expect(scopes).toHaveLength(1); expect(scopes[0]?.representative.id).toBe(ProjectId.make("project-local")); expect(scopes[0]?.projects.map((project) => project.id)).toContain( ProjectId.make("project-remote-fresh"), ); expect(scopes[0]?.projectRefs).toHaveLength(3); }); it("sorts v2 project scopes by their grouped thread activity", () => { const environmentId = EnvironmentId.make("environment-1"); const olderProject = makeProject({ environmentId, id: ProjectId.make("project-older"), title: "Older project", }); const newerProject = makeProject({ environmentId, id: ProjectId.make("project-newer"), title: "Newer project", }); const scopes = buildHomeProjectScopes({ projects: [newerProject, olderProject], environmentId: null, projectGroupingMode: "separate", }); expect( sortHomeProjectScopes({ scopes, threads: [ makeThread({ environmentId, id: ThreadId.make("thread-older-project"), projectId: olderProject.id, title: "Most recently active", updatedAt: "2026-06-03T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("thread-newer-project"), projectId: newerProject.id, title: "Less recently active", updatedAt: "2026-06-02T00:00:00.000Z", }), ], pendingTasks: [], projectSortOrder: "updated_at", }).map((scope) => scope.representative.id), ).toEqual([olderProject.id, newerProject.id]); }); it("sorts invalid project creation timestamps after valid ones", () => { const environmentId = EnvironmentId.make("environment-1"); const invalidProject = makeProject({ environmentId, id: ProjectId.make("project-invalid"), title: "A invalid timestamp", createdAt: "invalid", }); const validProject = makeProject({ environmentId, id: ProjectId.make("project-valid"), title: "Z valid timestamp", createdAt: "2026-06-02T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects: [invalidProject, validProject], environmentId: null, projectGroupingMode: "separate", }); expect( sortHomeProjectScopes({ scopes, threads: [], pendingTasks: [], projectSortOrder: "created_at", }).map((scope) => scope.representative.id), ).toEqual([validProject.id, invalidProject.id]); }); it("uses the freshest member when a grouped scope has no activity", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const repositoryIdentity = { canonicalKey: "github.com/pingdotgg/t3code", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }; const olderMember = makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-older-member"), title: "t3code", updatedAt: "2026-06-01T00:00:00.000Z", repositoryIdentity, }); const newerMember = makeProject({ environmentId: remoteEnvironmentId, id: ProjectId.make("project-newer-member"), title: "t3code", updatedAt: "2026-06-03T00:00:00.000Z", repositoryIdentity, }); const otherProject = makeProject({ environmentId: localEnvironmentId, id: ProjectId.make("project-other"), title: "other", updatedAt: "2026-06-02T00:00:00.000Z", }); const scopes = buildHomeProjectScopes({ projects: [olderMember, newerMember, otherProject], environmentId: null, projectGroupingMode: "repository", }); expect( sortHomeProjectScopes({ scopes, threads: [], pendingTasks: [], projectSortOrder: "updated_at", })[0]?.key, ).toBe(scopes.find((scope) => scope.projects.length === 2)?.key); }); it("does not merge unrelated repositories that share a title", () => { const environmentId = EnvironmentId.make("environment-1"); const projects = ["one", "two"].map((name) => makeProject({ environmentId, id: ProjectId.make(`project-${name}`), title: "app", repositoryIdentity: { canonicalKey: `github.com/example/${name}`, locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: `git@github.com:example/${name}.git`, }, }, }), ); expect( buildHomeProjectScopes({ projects, environmentId: null, projectGroupingMode: "repository", }), ).toHaveLength(2); }); it("uses the physical project title for a singleton scope", () => { const project = makeProject({ environmentId: EnvironmentId.make("environment-1"), id: ProjectId.make("project-1"), title: "local-worktree-name", repositoryIdentity: { canonicalKey: "github.com/pingdotgg/t3code", displayName: "codething-mvp", locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: "git@github.com:pingdotgg/t3code.git", }, }, }); const scopes = buildHomeProjectScopes({ projects: [project], environmentId: null, projectGroupingMode: "repository", }); const groups = buildGroups( [project], [ makeThread({ environmentId: project.environmentId, id: ThreadId.make("thread-1"), projectId: project.id, title: "Thread", }), ], ); expect(scopes[0]?.title).toBe("local-worktree-name"); expect(groups[0]?.title).toBe("local-worktree-name"); }); it("sorts the newest thread first regardless of snapshot order", () => { const environmentId = EnvironmentId.make("environment-1"); const project = makeProject({ environmentId, id: ProjectId.make("project-1"), title: "T3 Code", }); const threads = [ makeThread({ environmentId, id: ThreadId.make("thread-old"), projectId: project.id, title: "Older thread", updatedAt: "2026-06-02T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("thread-new"), projectId: project.id, title: "Newer thread", updatedAt: "2026-06-03T00:00:00.000Z", }), ]; expect(buildGroups([project], threads)[0]?.threads.map((thread) => thread.id)).toEqual([ "thread-new", "thread-old", ]); }); it("supports independent project and thread creation-time sorting", () => { const environmentId = EnvironmentId.make("environment-1"); const olderProject = makeProject({ environmentId, id: ProjectId.make("project-older"), title: "Older project", }); const newerProject = makeProject({ environmentId, id: ProjectId.make("project-newer"), title: "Newer project", }); const threads = [ makeThread({ environmentId, id: ThreadId.make("old-created"), projectId: olderProject.id, title: "Updated recently", createdAt: "2026-06-01T00:00:00.000Z", updatedAt: "2026-06-05T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("new-created"), projectId: olderProject.id, title: "Created recently", createdAt: "2026-06-04T00:00:00.000Z", updatedAt: "2026-06-04T00:00:00.000Z", }), makeThread({ environmentId, id: ThreadId.make("newest-project-thread"), projectId: newerProject.id, title: "Newest project", createdAt: "2026-06-06T00:00:00.000Z", }), ]; const groups = buildGroups([olderProject, newerProject], threads, { projectSortOrder: "created_at", threadSortOrder: "created_at", projectGroupingMode: "separate", }); expect(groups.map((group) => group.representative.id)).toEqual([ "project-newer", "project-older", ]); expect(groups[1]?.threads.map((thread) => thread.id)).toEqual(["new-created", "old-created"]); }); it("filters both projects and threads to one environment", () => { const localEnvironmentId = EnvironmentId.make("environment-local"); const remoteEnvironmentId = EnvironmentId.make("environment-remote"); const projects = [ makeProject({