Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,8 @@ export const SessionListPane = React.memo(function SessionListPane({
const { foreignRows, markersByLaneId } = useCrossMachineLaneUnion(
crossMachineSyncActive,
allSessionsUnfiltered,
workLaneSortMode,
workLaneOrder,
);
const [createLaneOpen, setCreateLaneOpen] = useState(false);
const [settleUndo, setSettleUndo] = useState<{ ids: string[]; count: number } | null>(null);
Expand Down
155 changes: 155 additions & 0 deletions apps/desktop/src/renderer/state/crossMachineLanes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,88 @@ describe("offline machines stay in the sidebar, dimmed", () => {
.toEqual(["lane-online", "lane-offline"]);
});

it("uses stable Work ordering unless activity mode is selected", () => {
const rows = buildCrossMachineLaneRows({
localLanes: [],
machines: {
"target-studio": {
machineId: "target-studio",
machineName: "Mac Studio (12)",
targetId: "target-studio",
projectId: "project-a",
online: true,
lanes: [
makeLane({ id: "lane-old", name: "Old", createdAt: "2026-07-20T10:00:00.000Z" }),
makeLane({ id: "lane-new", name: "New", createdAt: "2026-07-21T10:00:00.000Z" }),
],
sessions: [
makeSession({
id: "session-old",
laneId: "lane-old",
lastActivityAt: "2026-07-30T10:00:00.000Z",
}),
makeSession({
id: "session-new",
laneId: "lane-new",
lastActivityAt: "2026-07-29T10:00:00.000Z",
}),
],
prs: [],
lastSyncedAtMs: Date.now(),
error: null,
},
},
});

expect(orderCrossMachineRows(rows).map((row) => row.lane.id))
.toEqual(["lane-new", "lane-old"]);
expect(orderCrossMachineRows(rows, "activity").map((row) => row.lane.id))
.toEqual(["lane-old", "lane-new"]);
expect(orderCrossMachineRows([...rows].reverse()).map((row) => row.lane.id))
.toEqual(["lane-new", "lane-old"]);
});

it("uses composite machine/lane keys for shared foreign manual order", () => {
const rows = buildCrossMachineLaneRows({
localLanes: [],
machines: {
"target-studio": {
machineId: "target-studio",
machineName: "Mac Studio (12)",
targetId: "target-studio",
projectId: "project-a",
online: true,
lanes: [makeLane({ id: "shared-lane", name: "Studio Shared" })],
sessions: [],
prs: [],
lastSyncedAtMs: Date.now(),
error: null,
},
"target-laptop": {
machineId: "target-laptop",
machineName: "MacBook Pro (97)",
targetId: "target-laptop",
projectId: "project-a",
online: true,
lanes: [makeLane({ id: "shared-lane", name: "Laptop Shared" })],
sessions: [],
prs: [],
lastSyncedAtMs: Date.now(),
error: null,
},
},
});
const manualOrder = ["target-laptop:shared-lane", "target-studio:shared-lane"];
const toCompositeIds = (ordered: readonly typeof rows[number][]) =>
ordered.map((row) => `${row.machineId}:${row.lane.id}`);

expect(toCompositeIds(orderCrossMachineRows(rows, "manual", manualOrder)))
.toEqual(manualOrder);
expect(toCompositeIds(orderCrossMachineRows([...rows].reverse(), "manual", manualOrder)))
.toEqual(manualOrder);
expect(new Set(toCompositeIds(rows))).toEqual(new Set(manualOrder));
});

it("forgets a machine outright only when asked to", () => {
useAppStore.getState().mergeCrossMachineLanes({
machineId: "target-studio",
Expand Down Expand Up @@ -1201,6 +1283,79 @@ describe("cross-machine refresh scheduling", () => {
stop();
});

it("does not restart the foreign poll for every active session event", async () => {
vi.useFakeTimers();
const emitters: {
session: (() => void) | null;
lane: (() => void) | null;
} = { session: null, lane: null };
const callAction = vi.fn(async (
_targetId: string,
_projectId: string,
request: { domain: string; action: string },
) => ({
result: request.domain === "lane"
? { lanes: [] }
: request.domain === "pr"
? { prs: [] }
: { sessions: [] },
}));
window.ade = {
sessions: {
onChanged: vi.fn((listener: () => void) => {
emitters.session = listener;
return () => { emitters.session = null; };
}),
},
lanes: {
onLifecycleEvent: vi.fn((listener: () => void) => {
emitters.lane = listener;
return () => { emitters.lane = null; };
}),
},
remoteRuntime: {
callAction,
getConnectionSnapshot: vi.fn(async () => ({
connections: [{
state: "connected",
target: { id: "target-studio", name: "Mac Studio (12)", hostname: "studio" },
projects: [{
projectId: "project-a",
rootPath: "/repo-a",
displayName: "Repo A",
gitOriginUrl: "git@github.com:acme/repo-a.git",
}],
}],
connectedCount: 1,
})),
onConnectionSnapshotChanged: vi.fn(() => () => {}),
},
} as unknown as typeof window.ade;

const stop = startCrossMachineLaneSync({
scopeKey: "local:/repo-a",
repoDisplayName: "Repo A",
repoOriginUrl: "git@github.com:acme/repo-a.git",
boundTargetId: null,
boundProjectId: null,
});
await Promise.resolve();
await vi.advanceTimersByTimeAsync(400);
const initialCallCount = callAction.mock.calls.length;
expect(initialCallCount).toBe(3);

emitters.session?.();
emitters.lane?.();
emitters.session?.();
await vi.advanceTimersByTimeAsync(2_000);
expect(callAction).toHaveBeenCalledTimes(initialCallCount);

await vi.advanceTimersByTimeAsync(8_500);
expect(callAction.mock.calls.length).toBeGreaterThan(initialCallCount);

stop();
});

it("re-reads lanes on their own slow cadence, and immediately for an unseen lane", async () => {
vi.useFakeTimers();
const requests: Array<{ domain: string; action: string }> = [];
Expand Down
Loading
Loading