diff --git a/src/main/index.ts b/src/main/index.ts index ecff01bd52d..5196b7eac01 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -229,6 +229,10 @@ const recoveryReloadInFlight = createWebContentsTimedFlag() let firstWindowStartupServicesReady: Promise = Promise.resolve() let managedWslCliReconciliationReady: Promise = Promise.resolve() let managedWslCliStartupBarrierReady: Promise = Promise.resolve() +// Why: the serve barrier fails open at its budget, so headless clients need the +// reconciliation state at ready time to know a WSL PTY launch may still race an +// un-migrated registration. 'settled' covers the off-Windows no-op fast path. +let managedWslCliReconciliationStatus: 'pending' | 'settled' | 'failed' = 'settled' // Why: GPU child crashes clustered right after launch indicate a broken driver; // track them so Orca can move this build onto software rendering. const gpuLaunchTimeMs = Date.now() @@ -1384,6 +1388,9 @@ async function printServeReady(options: ServeOptions): Promise { type: 'orca_server_ready', runtimeId: runtime.getRuntimeId(), endpoint, + // Why: the WSL reconciliation barrier fails open, so 'pending' warns clients + // it outlived the startup budget and a WSL PTY launch may still race a repair. + managedWslCliReconciliation: managedWslCliReconciliationStatus, pairing: pairing.available ? { url: pairing.pairingUrl, @@ -1610,6 +1617,7 @@ app.whenReady().then(async () => { // Why: managed WSL launchers live outside the Windows app bundle, so keep // their launcher and bridge contract synchronized across app updates. + managedWslCliReconciliationStatus = 'pending' managedWslCliReconciliationReady = reconcileManagedWslCliRegistrations({ isPackaged: app.isPackaged, userDataPath: getCanonicalUserDataPath(), @@ -1625,8 +1633,10 @@ app.whenReady().then(async () => { console.log(`[wsl-cli] Repaired managed registration in ${result.distro}.`) } } + managedWslCliReconciliationStatus = 'settled' }) .catch((error) => { + managedWslCliReconciliationStatus = 'failed' console.warn( '[wsl-cli] Managed registration reconciliation discovery failed:', error instanceof Error ? error.message : String(error) @@ -2092,7 +2102,11 @@ app.whenReady().then(async () => { if (serveOptions) { // Why: give managed WSL launchers a brief chance to migrate before headless // PTYs become reachable without letting slow repairs withhold all RPC readiness. + logStartupMilestone('wsl-cli-barrier-start') await managedWslCliStartupBarrierReady + logStartupMilestone('wsl-cli-barrier-resolved', { + reconciliation: managedWslCliReconciliationStatus + }) await startServeAgentHookServer() registerHeadlessPtyRuntime( runtime, diff --git a/src/main/startup/desktop-startup-ordering.test.ts b/src/main/startup/desktop-startup-ordering.test.ts index 6204f6f7336..b9adfc6b8f2 100644 --- a/src/main/startup/desktop-startup-ordering.test.ts +++ b/src/main/startup/desktop-startup-ordering.test.ts @@ -53,6 +53,22 @@ describe('startup ordering', () => { expect(barrier).not.toContain('managedWslCliReconciliationReady') }) + it('exposes managed WSL reconciliation status to headless serve clients and diagnostics', () => { + const source = readFileSync(join(process.cwd(), 'src/main/index.ts'), 'utf8') + + // Why: the barrier fails open, so the serve-ready payload must carry the + // reconciliation state and the bounded wait must be traceable via a milestone. + const readyStart = source.indexOf("type: 'orca_server_ready'") + const readyEnd = source.indexOf('pairing: pairing.available', readyStart) + const readyPayload = source.slice(readyStart, readyEnd) + expect(readyPayload).toContain('managedWslCliReconciliation: managedWslCliReconciliationStatus') + + expect(source).toContain("managedWslCliReconciliationStatus = 'pending'") + expect(source).toContain("managedWslCliReconciliationStatus = 'settled'") + expect(source).toContain("managedWslCliReconciliationStatus = 'failed'") + expect(source).toContain("logStartupMilestone('wsl-cli-barrier-resolved'") + }) + it('does not run the rate-limit quota fetch before the first window can show results', () => { const source = readFileSync(join(process.cwd(), 'src/main/index.ts'), 'utf8') const attachIndex = source.indexOf('rateLimits.attach(window)')