fix(wsl): main's background start preps its host env without waking the distro, on both providers (E9) (#36149)

* fix(wsl): main's background start preps its host env without waking the distro (E9)

* fix(wsl): daemon-off background starts prep their env without waking the distro (E9)
This commit is contained in:
Neil
2026-10-29 16:29:48 -07:00
committed by GitHub
parent 17a681b8f8
commit d063703de2
4 changed files with 108 additions and 3 deletions
@@ -4,6 +4,7 @@ import { wslDistroHoldMessage } from '../../../../shared/wsl-distro-stopped-hold
import { prepareRuntimePtySpawn } from './spawn-preflight'
import { createRuntimePtySpawnState, type RuntimePtySpawnArgs } from './spawn-state'
import { makeRuntimePtySpawnTestDeps } from './spawn-preflight-test-deps-fixture'
import { isWslUserActFor } from '../../../wsl/wsl-user-act-scope'
const mocks = vi.hoisted(() => ({
readWslDistroRunningState: vi.fn<() => Promise<'running' | 'stopped' | 'unverifiable'>>()
@@ -16,8 +17,11 @@ vi.mock('../../../wsl-background-distro-gate', async (importOriginal) => ({
const WSL_CWD = '\\\\wsl.localhost\\Ubuntu\\home\\me\\repo'
const hostPlatform = process.platform
async function prepare(args: Partial<RuntimePtySpawnArgs>): Promise<void> {
const ctx = createRuntimePtySpawnState(makeRuntimePtySpawnTestDeps(), {
async function prepare(
args: Partial<RuntimePtySpawnArgs>,
deps = makeRuntimePtySpawnTestDeps()
): Promise<void> {
const ctx = createRuntimePtySpawnState(deps, {
cols: 120,
rows: 40,
cwd: WSL_CWD,
@@ -61,3 +65,26 @@ describe("main's background start in a stopped WSL distro (E9)", () => {
await expect(prepare({})).resolves.toBeUndefined()
})
})
describe('host env prep after the startup hold (E9)', () => {
// Prep runs after the hold read; the distro may stop in between, so only a user act may wake it.
async function userActDuringPrep(args: Partial<RuntimePtySpawnArgs>): Promise<boolean[]> {
mocks.readWslDistroRunningState.mockResolvedValue('running')
const seen: boolean[] = []
const deps = makeRuntimePtySpawnTestDeps()
deps.prepareCodexResumeHome = () => {
seen.push(isWslUserActFor('Ubuntu'))
return null
}
await prepare({ command: 'codex', ...args }, deps)
return seen
}
it('gives a background start no user-act scope', async () => {
expect(await userActDuringPrep({})).toEqual([false])
})
it("keeps the user's start a user act", async () => {
expect(await userActDuringPrep({ wslUserAct: true })).toEqual([true])
})
})
@@ -152,6 +152,8 @@ export async function prepareRuntimePtySpawn(
mintedSessionId: ctx.requestedSessionId === undefined,
resumeOnSelectedAccount: false,
emptySessionIdSkipsHostEnv: true,
// The distro may stop after the hold read: a background start's prep must not wake it (E9).
wslPassiveRestore: args.wslUserAct !== true,
requestedAgentTeamsPath: ctx.requestedAgentTeamsPath,
provider: ctx.provider,
deps: ctx.deps
@@ -0,0 +1,75 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { buildLocalPtySpawnEnvironment } from './local-pty-spawn-environment'
import type { LocalPtyLaunchPlan } from './local-pty-launch-plan'
import type { PtySpawnOptions } from './pty-provider-contract'
import { runAsWslLaunchUserAct } from '../wsl/wsl-background-exec-gate'
import { isWslUserActFor } from '../wsl/wsl-user-act-scope'
const plan: LocalPtyLaunchPlan = {
startupAgentRecognition: null,
defaultCwd: '',
cwd: '\\\\wsl.localhost\\Ubuntu\\home\\me\\repo',
wslInfo: null,
worktreeWslContext: undefined,
preferredWslContext: undefined,
launchWslContext: undefined,
shellPath: 'wsl.exe',
shellArgs: [],
effectiveCwd: '',
validationCwd: '',
startupCommandDeliveredInShellArgs: false,
windowsFallbackAttempts: [],
shellReadyLaunch: null,
getFallbackShellReadyConfig: undefined,
primaryPreLaunchEnv: {},
isWslShell: true,
launchWslDistro: 'Ubuntu'
}
const hostPlatform = process.platform
beforeEach(() => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
})
afterEach(() => {
Object.defineProperty(process, 'platform', { configurable: true, value: hostPlatform })
})
// The daemon-off builder wraps its env prep the way provider/local-configure.ts does.
async function userActDuringDaemonOffPrep(spawn: Partial<PtySpawnOptions>): Promise<boolean[]> {
const seen: boolean[] = []
await buildLocalPtySpawnEnvironment({
id: 'pane',
spawn: { cols: 80, rows: 24, ...spawn },
getOptions: () => ({
buildSpawnEnv: (_id, baseEnv, ctx) =>
runAsWslLaunchUserAct(
{
isWsl: ctx?.isWsl === true,
passiveRestore: ctx?.wslPassiveRestore === true,
distro: ctx?.wslDistro
},
async () => {
seen.push(isWslUserActFor('Ubuntu'))
return baseEnv
}
)
}),
plan
})
return seen
}
describe("daemon-off env prep for main's background start (STA-12870, E9)", () => {
it('gives a background start no user-act scope', async () => {
expect(await userActDuringDaemonOffPrep({ wslBackgroundStart: true })).toEqual([false])
})
it('gives a passive restore no user-act scope', async () => {
expect(await userActDuringDaemonOffPrep({ wslPassiveRestore: true })).toEqual([false])
})
it("keeps the user's start a user act", async () => {
expect(await userActDuringDaemonOffPrep({})).toEqual([true])
})
})
@@ -47,7 +47,8 @@ export function buildLocalPtySpawnEnvironment(args: {
shellPath: plan.shellPath,
isWsl: plan.isWslShell,
wslDistro: plan.launchWslDistro,
wslPassiveRestore: spawn.wslPassiveRestore === true
// Main's background starts get the same E9 prep as a passive restore (STA-12870).
wslPassiveRestore: spawn.wslPassiveRestore === true || spawn.wslBackgroundStart === true
})
}