fix(workspace): claim detached sessions to source project (#26413)

This commit is contained in:
Kit Langton
2026-05-08 20:25:53 -04:00
committed by GitHub
parent d19f7bc77c
commit 21d055be19
2 changed files with 36 additions and 2 deletions

View File

@@ -5,7 +5,6 @@ import { asc } from "drizzle-orm"
import { eq } from "drizzle-orm"
import { inArray } from "drizzle-orm"
import { Project } from "@/project/project"
import { Instance } from "@/project/instance"
import { BusEvent } from "@/bus/bus-event"
import { GlobalBus } from "@/bus/global"
import { Auth } from "@/auth"
@@ -646,7 +645,7 @@ export const layer = Layer.effect(
// "claim" this session so any future events coming from
// the old workspace are ignored
SyncEvent.claim(input.sessionID, input.workspaceID ?? Instance.project.id)
SyncEvent.claim(input.sessionID, input.workspaceID ?? previous.projectID)
}
}

View File

@@ -848,6 +848,41 @@ describe("workspace CRUD", () => {
})
})
test("sessionWarp detaches to the source project when invoked from a workspace instance", async () => {
await withInstance(async () => {
const projectID = Instance.project.id
await using workspaceTmp = await tmpdir({ git: true })
const previousType = unique("warp-detach-workspace-instance")
const previous = workspaceInfo(projectID, previousType)
insertWorkspace(previous)
registerAdapter(projectID, previousType, localAdapter(workspaceTmp.path, { createDir: false }).adapter)
const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))
attachSessionToWorkspace(session.id, previous.id)
const workspaceProjectID = await WithInstance.provide({
directory: workspaceTmp.path,
fn: async () => {
const id = Instance.project.id
expect(id).not.toBe(projectID)
await warpWorkspaceSession({ workspaceID: null, sessionID: session.id })
return id
},
})
expect(
Database.use((db) =>
db
.select({ workspaceID: SessionTable.workspace_id })
.from(SessionTable)
.where(eq(SessionTable.id, session.id))
.get(),
)?.workspaceID,
).toBeNull()
expect(sessionSequenceOwner(session.id)).toBe(projectID)
expect(sessionSequenceOwner(session.id)).not.toBe(workspaceProjectID)
})
})
it.live("sessionWarp syncs previous remote history, replays it, steals, and claims the sequence", () => {
const calls: FetchCall[] = []
let historySessionID: SessionID | undefined