@@ -21,6 +21,7 @@ import { instanceRouterMiddleware } from "../../src/server/routes/instance/httpa
2121import { workspaceRouterMiddleware } from "../../src/server/routes/instance/httpapi/middleware/workspace-routing"
2222import { resetDatabase } from "../fixture/db"
2323import { disposeAllInstances , tmpdirScoped } from "../fixture/fixture"
24+ import { withFixedWorkspaceID } from "../fixture/flag"
2425import { waitGlobalBusEvent } from "./global-bus"
2526import { testEffect } from "../lib/effect"
2627
@@ -204,16 +205,10 @@ describe("HttpApi instance context middleware", () => {
204205 } ) ,
205206 )
206207
207- it . live ( "uses configured workspace id instead of routing to requested workspaces " , ( ) =>
208+ it . live ( "uses configured workspace id instead of routing to the requested workspace " , ( ) =>
208209 Effect . gen ( function * ( ) {
209- const originalWorkspaceID = Flag . OPENCODE_WORKSPACE_ID
210210 const fixedWorkspaceID = WorkspaceID . ascending ( )
211- Flag . OPENCODE_WORKSPACE_ID = fixedWorkspaceID
212- yield * Effect . addFinalizer ( ( ) =>
213- Effect . sync ( ( ) => {
214- Flag . OPENCODE_WORKSPACE_ID = originalWorkspaceID
215- } ) ,
216- )
211+ yield * withFixedWorkspaceID ( fixedWorkspaceID )
217212
218213 const dir = yield * tmpdirScoped ( { git : true } )
219214 const project = yield * Project . use . fromDirectory ( dir )
@@ -238,6 +233,66 @@ describe("HttpApi instance context middleware", () => {
238233 } ) ,
239234 )
240235
236+ it . live ( "falls through to local instead of MissingWorkspace when configured workspace id is set" , ( ) =>
237+ Effect . gen ( function * ( ) {
238+ const fixedWorkspaceID = WorkspaceID . ascending ( )
239+ yield * withFixedWorkspaceID ( fixedWorkspaceID )
240+
241+ const dir = yield * tmpdirScoped ( { git : true } )
242+ yield * Project . use . fromDirectory ( dir )
243+ yield * serveProbe ( )
244+
245+ // Reference a workspace id that is not registered locally. Without the
246+ // configured env override, this would short-circuit to a 500
247+ // MissingWorkspace response. With the env set, planRequest must skip the
248+ // MissingWorkspace branch and fall through to Local with the configured
249+ // workspace id.
250+ const unknownWorkspaceID = WorkspaceID . ascending ( )
251+ const response = yield * HttpClientRequest . get ( `/probe?workspace=${ unknownWorkspaceID } ` ) . pipe (
252+ HttpClientRequest . setHeader ( "x-opencode-directory" , dir ) ,
253+ HttpClient . execute ,
254+ )
255+
256+ expect ( response . status ) . toBe ( 200 )
257+ expect ( yield * response . json ) . toMatchObject ( {
258+ directory : dir ,
259+ workspaceID : fixedWorkspaceID ,
260+ } )
261+ } ) ,
262+ )
263+
264+ it . live ( "keeps configured workspace id on control-plane routes without remote routing" , ( ) =>
265+ Effect . gen ( function * ( ) {
266+ const fixedWorkspaceID = WorkspaceID . ascending ( )
267+ yield * withFixedWorkspaceID ( fixedWorkspaceID )
268+
269+ const dir = yield * tmpdirScoped ( { git : true } )
270+ const project = yield * Project . use . fromDirectory ( dir )
271+ const workspaceDir = path . join ( dir , ".workspace-local" )
272+ const workspace = yield * createLocalWorkspace ( {
273+ projectID : project . project . id ,
274+ type : "instance-context-fixed-workspace-control-plane" ,
275+ directory : workspaceDir ,
276+ } )
277+ // /session is matched by isLocalWorkspaceRoute, so shouldStayOnControlPlane
278+ // is true. Combined with the env override, the route must stay Local with
279+ // the configured workspace id (not divert to the requested workspace's
280+ // local directory).
281+ yield * serveProbe ( "/session" )
282+
283+ const response = yield * HttpClientRequest . get ( `/session?workspace=${ workspace . id } ` ) . pipe (
284+ HttpClientRequest . setHeader ( "x-opencode-directory" , dir ) ,
285+ HttpClient . execute ,
286+ )
287+
288+ expect ( response . status ) . toBe ( 200 )
289+ expect ( yield * response . json ) . toMatchObject ( {
290+ directory : dir ,
291+ workspaceID : fixedWorkspaceID ,
292+ } )
293+ } ) ,
294+ )
295+
241296 it . live ( "preserves selected workspace id on instance disposal events" , ( ) =>
242297 Effect . gen ( function * ( ) {
243298 const dir = yield * tmpdirScoped ( { git : true } )
0 commit comments