From 67701cf87dfa573e0729f2a57f351c475542fad3 Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Tue, 6 Oct 2026 00:56:33 -0400 Subject: [PATCH] fix(runtime): join the resize repaint's seed without per-PTY seed state Replaces the rendererSeedsByPtyId map with a check over existing state. If an on-data seed was already considered before the subscribe's hydrate, the subscribe awaits the model's write chain and reads the outcome from the hydration state, the installed model and the provider-preferred flag. Claude-Session: lane-host-hydrate --- .../orca-runtime-fit-override-listeners.ts | 2 -- ...me-maybe-hydrate-headless-from-renderer.ts | 27 ++++++++++++++----- ...time-serialize-headless-terminal-buffer.ts | 1 - ...ty-exit-per-pty-map-reaper-ratchet.test.ts | 4 --- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/runtime/orca-runtime-fit-override-listeners.ts b/src/main/runtime/orca-runtime-fit-override-listeners.ts index f738c81eec5..5ef17ce4c2e 100644 --- a/src/main/runtime/orca-runtime-fit-override-listeners.ts +++ b/src/main/runtime/orca-runtime-fit-override-listeners.ts @@ -130,8 +130,6 @@ export class OrcaRuntimeWithFitOverrideListeners extends OrcaRuntimeWithStopRequ // Absent → hydration has not been considered yet for this PTY. // See docs/mobile-prefer-renderer-scrollback.md. protected headlessHydrationState = new Map() - /** The model's renderer seed, resolving true once written; lives as long as the model. */ - protected rendererSeedsByPtyId = new Map>() // Why: mobile-fit overrides are keyed by ptyId (not terminal handle) because // handles can be reissued while the PTY identity is stable. In-memory only — diff --git a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts index 37eaf950d82..6125d41dc03 100644 --- a/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts +++ b/src/main/runtime/orca-runtime-maybe-hydrate-headless-from-renderer.ts @@ -110,19 +110,32 @@ export class OrcaRuntimeWithMaybeHydrateHeadlessFromRenderer extends OrcaRuntime } } }) - const seed = state.writeChain.then(() => seeded) - this.rendererSeedsByPtyId.set(ptyId, seed) - return seed + return state.writeChain.then(() => seeded) } /** Public: a phone fit resizes the PTY, but an idle TUI sends no byte to hydrate on. Resolves * true once the model holds the pane's screen reflowed onto the PTY's current grid. */ hydrateHeadlessTerminalFromRenderer(ptyId: string): Promise { - // Why join: a resize repaint can run the on-data seed first; its result answers this caller too. + const priorHydration = this.headlessHydrationState.get(ptyId) + const seed = this.maybeHydrateHeadlessFromRenderer(ptyId) + if (seed) { + return seed + } + // Why: a resize repaint can run the on-data seed before this call; join its outcome instead. + return priorHydration ? this.readRendererSeedOutcome(ptyId) : Promise.resolve(false) + } + + private async readRendererSeedOutcome(ptyId: string): Promise { + const state = this.headlessTerminals.get(ptyId) + if (!state) { + return false + } + await state.writeChain + // Why: a failed seed also ends 'done'; treated as seeded, since that model is on the PTY grid. return ( - this.maybeHydrateHeadlessFromRenderer(ptyId) ?? - this.rendererSeedsByPtyId.get(ptyId) ?? - Promise.resolve(false) + this.headlessHydrationState.get(ptyId) === 'done' && + this.headlessTerminals.get(ptyId) === state && + !this.providerSnapshotPreferredPtys.has(ptyId) ) } diff --git a/src/main/runtime/orca-runtime-serialize-headless-terminal-buffer.ts b/src/main/runtime/orca-runtime-serialize-headless-terminal-buffer.ts index 5d985f2c78a..673c31167f4 100644 --- a/src/main/runtime/orca-runtime-serialize-headless-terminal-buffer.ts +++ b/src/main/runtime/orca-runtime-serialize-headless-terminal-buffer.ts @@ -75,7 +75,6 @@ export class OrcaRuntimeWithSerializeHeadlessTerminalBuffer extends OrcaRuntimeW protected disposeHeadlessTerminal(ptyId: string): void { this.headlessHydrationState.delete(ptyId) - this.rendererSeedsByPtyId.delete(ptyId) const state = this.headlessTerminals.get(ptyId) if (!state) { return diff --git a/src/main/runtime/pty-exit-per-pty-map-reaper-ratchet.test.ts b/src/main/runtime/pty-exit-per-pty-map-reaper-ratchet.test.ts index e8362c0fb94..8f1f6035dd8 100644 --- a/src/main/runtime/pty-exit-per-pty-map-reaper-ratchet.test.ts +++ b/src/main/runtime/pty-exit-per-pty-map-reaper-ratchet.test.ts @@ -41,10 +41,6 @@ const CLEARED_BY_REAPER_HELPER: Record