diff --git a/src/main/orcad/orcad-entry.ts b/src/main/orcad/orcad-entry.ts index 6c0e9bbad227..9b15ed4657a0 100644 --- a/src/main/orcad/orcad-entry.ts +++ b/src/main/orcad/orcad-entry.ts @@ -12,6 +12,7 @@ */ import process from 'node:process' import { setAppEnvironment, type AppEnvironment } from '../../shared/app-environment' +import { HEADLESS_RUNTIME_WINDOW_ID } from '../../shared/runtime-session-contracts' import { setSecretStore, type SecretStore } from '../../shared/secret-store' import type { ServeReadiness } from '../server/serve-readiness' import { resolveOrcadInstallRoot, resolveOrcadPath, resolveUserDataPath } from './orcad-app-paths' @@ -291,6 +292,10 @@ async function startOrcadRuntime( // Recovery binds terminal and dispatch identities; only now can startup observations be fenced. observedStatusCapture.attach(runtime) + // Why: nothing else publishes a graph on this host, so `graphStatus` would stay 'unavailable' + // and every graph-gated RPC — terminal create, tab inventory — would fail or hang forever. + runtime.syncWindowGraph(HEADLESS_RUNTIME_WINDOW_ID, { tabs: [], leaves: [] }) + const bindHost = resolveOrcadBindHost(options.bind) rpc = new OrcaRuntimeRpcServer({ runtime, diff --git a/src/main/startup/headless-pty-hydration-ordering.test.ts b/src/main/startup/headless-pty-hydration-ordering.test.ts index c761ccf0409f..25e609a444d7 100644 --- a/src/main/startup/headless-pty-hydration-ordering.test.ts +++ b/src/main/startup/headless-pty-hydration-ordering.test.ts @@ -53,6 +53,20 @@ describe('headless PTY registry hydration ordering', () => { expect(readiness).toBeGreaterThan(rpc) }) + it('publishes the orcad headless graph after PTY registration and before RPC', () => { + const source = readFileSync(join(process.cwd(), 'src/main/orcad/orcad-entry.ts'), 'utf8') + const handlersAndHydration = source.indexOf('await registerHeadlessPtyRuntime(') + const sentinel = source.indexOf( + 'runtime.syncWindowGraph(HEADLESS_RUNTIME_WINDOW_ID, { tabs: [], leaves: [] })', + handlersAndHydration + ) + const rpc = source.indexOf('rpc = new OrcaRuntimeRpcServer({', sentinel) + + expect(handlersAndHydration).toBeGreaterThanOrEqual(0) + expect(sentinel).toBeGreaterThan(handlersAndHydration) + expect(rpc).toBeGreaterThan(sentinel) + }) + it('starts the orcad hook owner after Store hydration and before daemon PTY recovery', () => { const source = readFileSync(join(process.cwd(), 'src/main/orcad/orcad-entry.ts'), 'utf8') const cleanup = source.indexOf('registerCleanup(async () => {')