mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-07 00:44:49 +00:00
* Revert "Restore cached conversations before reconnecting (#4421)"
This reverts commit 1c51b52d07.
* fix(app): restore provider loading on native
* fix(app): preserve native navigators across appearance changes
Reapply the isolated appearance boundaries and native menu transform origin from #4421 so appearance hydration preserves native navigator and gesture host lifetimes.
* fix(app): retain cached baselines during live updates
Read the timeline concurrently with agent preparation and reconcile display-only cache rows beneath the live head using the existing reducer. Merge cached directory entries beneath live state while preserving the existing checkpoint race guard.
233 lines
7.9 KiB
TypeScript
233 lines
7.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
import type { WorkspaceDescriptorPayload } from "@getpaseo/protocol/messages";
|
|
import {
|
|
normalizeProjectDescriptor,
|
|
normalizeWorkspaceDescriptor,
|
|
useSessionStore,
|
|
} from "@/stores/session-store";
|
|
import {
|
|
clearWorkspaceArchivePending,
|
|
markWorkspaceArchivePending,
|
|
} from "@/contexts/session-workspace-upserts";
|
|
import { WorkspaceDirectoryReplica } from "./workspace-replica";
|
|
|
|
function workspace(id: string, projectId = "project"): WorkspaceDescriptorPayload {
|
|
return {
|
|
id,
|
|
projectId,
|
|
projectDisplayName: projectId,
|
|
projectRootPath: `/repo/${projectId}`,
|
|
workspaceDirectory: `/repo/${projectId}/${id}`,
|
|
projectKind: "git",
|
|
workspaceKind: "worktree",
|
|
name: id,
|
|
title: id,
|
|
status: "done",
|
|
activityAt: null,
|
|
statusEnteredAt: null,
|
|
archivingAt: null,
|
|
diffStat: null,
|
|
scripts: [],
|
|
};
|
|
}
|
|
|
|
it("commits workspace and project-parent state with filtered removals", () => {
|
|
const serverId = "workspace-replica";
|
|
const store = useSessionStore.getState();
|
|
store.initializeSession(serverId, null as unknown as DaemonClient);
|
|
const replica = new WorkspaceDirectoryReplica(serverId);
|
|
const empty = normalizeProjectDescriptor({
|
|
projectId: "empty",
|
|
projectDisplayName: "Empty",
|
|
projectRootPath: "/repo/empty",
|
|
projectKind: "git",
|
|
});
|
|
replica.commitSnapshot(
|
|
{
|
|
workspaces: new Map([
|
|
["kept", normalizeWorkspaceDescriptor(workspace("kept"))],
|
|
["filtered", normalizeWorkspaceDescriptor(workspace("filtered", "filtered-project"))],
|
|
]),
|
|
projects: new Map([[empty.projectId, empty]]),
|
|
},
|
|
[{ kind: "remove", id: "filtered", removedProjectId: "filtered-project" }],
|
|
);
|
|
|
|
const session = useSessionStore.getState().sessions[serverId];
|
|
expect(Array.from(session?.workspaces.keys() ?? [])).toEqual(["kept"]);
|
|
expect(Array.from(session?.projects.keys() ?? [])).toEqual(["empty"]);
|
|
store.clearSession(serverId);
|
|
});
|
|
|
|
it("commits the authoritative snapshot before buffered project updates", () => {
|
|
const serverId = "project-update-replica";
|
|
const store = useSessionStore.getState();
|
|
store.initializeSession(serverId, null as unknown as DaemonClient);
|
|
const replica = new WorkspaceDirectoryReplica(serverId);
|
|
const attachedMain = normalizeWorkspaceDescriptor(workspace("attached-main", "attached"));
|
|
const attachedFeature = normalizeWorkspaceDescriptor(workspace("attached-feature", "attached"));
|
|
const removed = normalizeWorkspaceDescriptor(workspace("removed", "removed"));
|
|
const unrelated = normalizeWorkspaceDescriptor(workspace("unrelated", "unrelated"));
|
|
const staleAttachedProject = normalizeProjectDescriptor({
|
|
projectId: "attached",
|
|
projectDisplayName: "Stale attached project",
|
|
projectRootPath: "/repo/attached",
|
|
projectKind: "git",
|
|
});
|
|
const removedProject = normalizeProjectDescriptor({
|
|
projectId: "removed",
|
|
projectDisplayName: "Removed project",
|
|
projectRootPath: "/repo/removed",
|
|
projectKind: "git",
|
|
});
|
|
const unchangedEmptyProject = normalizeProjectDescriptor({
|
|
projectId: "unchanged-empty",
|
|
projectDisplayName: "Unchanged empty project",
|
|
projectRootPath: "/repo/unchanged-empty",
|
|
projectKind: "git",
|
|
});
|
|
|
|
replica.commitSnapshot(
|
|
{
|
|
workspaces: new Map([
|
|
[attachedMain.id, attachedMain],
|
|
[attachedFeature.id, attachedFeature],
|
|
[removed.id, removed],
|
|
[unrelated.id, unrelated],
|
|
]),
|
|
projects: new Map([
|
|
[staleAttachedProject.projectId, staleAttachedProject],
|
|
[removedProject.projectId, removedProject],
|
|
[unchangedEmptyProject.projectId, unchangedEmptyProject],
|
|
]),
|
|
},
|
|
[
|
|
{
|
|
kind: "upsert",
|
|
project: {
|
|
projectId: "attached",
|
|
projectKey: "remote:github.com/acme/attached",
|
|
projectDisplayName: "Renamed attached project",
|
|
projectCustomName: "Personal name",
|
|
projectRootPath: "/moved/attached",
|
|
projectKind: "directory",
|
|
},
|
|
},
|
|
{
|
|
kind: "upsert",
|
|
project: {
|
|
projectId: "new-empty",
|
|
projectDisplayName: "New empty project",
|
|
projectRootPath: "/repo/new-empty",
|
|
projectKind: "directory",
|
|
},
|
|
},
|
|
{ kind: "remove", projectId: "removed" },
|
|
],
|
|
);
|
|
|
|
const session = useSessionStore.getState().sessions[serverId];
|
|
expect(session?.workspaces.get(attachedMain.id)).toMatchObject({
|
|
projectDisplayName: "Renamed attached project",
|
|
projectCustomName: "Personal name",
|
|
projectRootPath: "/moved/attached",
|
|
projectKind: "directory",
|
|
});
|
|
expect(session?.workspaces.get(attachedFeature.id)).toMatchObject({
|
|
projectDisplayName: "Renamed attached project",
|
|
projectRootPath: "/moved/attached",
|
|
});
|
|
expect(session?.workspaces.has(removed.id)).toBe(false);
|
|
expect(session?.workspaces.get(unrelated.id)).toBe(unrelated);
|
|
expect(Array.from(session?.projects.keys() ?? [])).toEqual([
|
|
"attached",
|
|
"unchanged-empty",
|
|
"new-empty",
|
|
]);
|
|
expect(session?.projects.get("unchanged-empty")).toBe(unchangedEmptyProject);
|
|
expect(session?.projects.get("new-empty")).toMatchObject({
|
|
projectDisplayName: "New empty project",
|
|
projectRootPath: "/repo/new-empty",
|
|
});
|
|
store.clearSession(serverId);
|
|
});
|
|
|
|
it("preserves unchanged project identity when another project changes", () => {
|
|
const serverId = "project-identity";
|
|
const store = useSessionStore.getState();
|
|
store.initializeSession(serverId, null as unknown as DaemonClient);
|
|
const replica = new WorkspaceDirectoryReplica(serverId);
|
|
const first = normalizeProjectDescriptor({
|
|
projectId: "first",
|
|
projectDisplayName: "First",
|
|
projectRootPath: "/repo/first",
|
|
projectKind: "git",
|
|
});
|
|
const second = normalizeProjectDescriptor({
|
|
projectId: "second",
|
|
projectDisplayName: "Second",
|
|
projectRootPath: "/repo/second",
|
|
projectKind: "git",
|
|
});
|
|
replica.commitSnapshot(
|
|
{
|
|
workspaces: new Map(),
|
|
projects: new Map([
|
|
["first", first],
|
|
["second", second],
|
|
]),
|
|
},
|
|
[],
|
|
);
|
|
const previousSecond = useSessionStore.getState().sessions[serverId]?.projects.get("second");
|
|
|
|
replica.commitSnapshot(
|
|
{
|
|
workspaces: new Map(),
|
|
projects: new Map([
|
|
["first", { ...first, projectDisplayName: "Updated" }],
|
|
["second", { ...second }],
|
|
]),
|
|
},
|
|
[],
|
|
);
|
|
|
|
expect(useSessionStore.getState().sessions[serverId]?.projects.get("second")).toBe(
|
|
previousSecond,
|
|
);
|
|
store.clearSession(serverId);
|
|
});
|
|
|
|
it("does not invent a null-key project from a workspace update", () => {
|
|
const serverId = "workspace-before-project-update";
|
|
const store = useSessionStore.getState();
|
|
store.initializeSession(serverId, null as unknown as DaemonClient);
|
|
const replica = new WorkspaceDirectoryReplica(serverId);
|
|
|
|
replica.applyDelta({ kind: "upsert", workspace: workspace("main", "fresh-project") });
|
|
|
|
const session = useSessionStore.getState().sessions[serverId];
|
|
expect(session?.workspaces.has("main")).toBe(true);
|
|
expect(session?.projects.has("fresh-project")).toBe(false);
|
|
store.clearSession(serverId);
|
|
});
|
|
|
|
it("does not restore a targeted cached workspace while its archive is pending", () => {
|
|
const serverId = "cached-workspace-during-archive";
|
|
const workspaceId = "archived-workspace";
|
|
const store = useSessionStore.getState();
|
|
store.initializeSession(serverId, null as unknown as DaemonClient);
|
|
const replica = new WorkspaceDirectoryReplica(serverId);
|
|
markWorkspaceArchivePending({ serverId, workspaceId });
|
|
|
|
try {
|
|
replica.commitCachedWorkspace(normalizeWorkspaceDescriptor(workspace(workspaceId)), undefined);
|
|
|
|
expect(useSessionStore.getState().sessions[serverId]?.workspaces.has(workspaceId)).toBe(false);
|
|
} finally {
|
|
clearWorkspaceArchivePending({ serverId, workspaceId });
|
|
store.clearSession(serverId);
|
|
}
|
|
});
|