diff --git a/src/main/ipc/pty-hidden-delivery-gate.ts b/src/main/ipc/pty-hidden-delivery-gate.ts index 0fa58e6f909..43fa8567b88 100644 --- a/src/main/ipc/pty-hidden-delivery-gate.ts +++ b/src/main/ipc/pty-hidden-delivery-gate.ts @@ -31,7 +31,8 @@ const droppedSinceHiddenPtys = new Set() let droppedHiddenDeliveryChars = 0 let droppedHiddenDeliveryChunks = 0 -/** The gate operates only when main side-effect authority and the gate setting are enabled. */ +/** Gate kill switches, both read main-side: the gate only operates under main + * side-effect authority AND the gate-specific setting (both default on). */ export function isHiddenPtyDeliveryGateEnabled( settings: HiddenPtyDeliveryGateSettings | null | undefined ): boolean { diff --git a/src/main/persistence.test.ts b/src/main/persistence.test.ts index 96f697c976d..17a73fa0e48 100644 --- a/src/main/persistence.test.ts +++ b/src/main/persistence.test.ts @@ -7933,43 +7933,6 @@ describe('Store', () => { expect(store.getSettings().experimentalActivity).toBe(true) }) - it('disables the generated hidden PTY delivery gate for existing profiles', async () => { - writeDataFile({ - schemaVersion: 1, - repos: [], - worktreeMeta: {}, - settings: { terminalHiddenDeliveryGate: true }, - ui: {}, - githubCache: { pr: {}, issue: {} }, - workspaceSession: {} - }) - - const store = await createStore() - - expect(store.getSettings().terminalHiddenDeliveryGate).toBe(false) - expect(store.getSettings().terminalHiddenDeliveryGateDefaultedOffForAllUsers).toBe(true) - }) - - it('preserves hidden PTY delivery gate opt-ins after the default-off migration', async () => { - writeDataFile({ - schemaVersion: 1, - repos: [], - worktreeMeta: {}, - settings: { - terminalHiddenDeliveryGate: true, - terminalHiddenDeliveryGateDefaultedOffForAllUsers: true - }, - ui: {}, - githubCache: { pr: {}, issue: {} }, - workspaceSession: {} - }) - - const store = await createStore() - - expect(store.getSettings().terminalHiddenDeliveryGate).toBe(true) - expect(store.getSettings().terminalHiddenDeliveryGateDefaultedOffForAllUsers).toBe(true) - }) - // ── worktree-card property migration ─────────────────────────────── it('adds split-out default card properties for legacy detailed profiles', async () => { diff --git a/src/main/persistence.ts b/src/main/persistence.ts index 975d558cec8..f9fee511d39 100644 --- a/src/main/persistence.ts +++ b/src/main/persistence.ts @@ -3290,15 +3290,6 @@ export class Store { const migratedExperimentalActivity = experimentalActivityDefaultedOffForAllUsers ? (parsed.settings?.experimentalActivity ?? false) : false - const terminalHiddenDeliveryGateDefaultedOffForAllUsers = - parsed.settings?.terminalHiddenDeliveryGateDefaultedOffForAllUsers === true - // Why: the old generated true is indistinguishable from an opt-in, so reset it once before preserving later overrides. - const migratedTerminalHiddenDeliveryGate = terminalHiddenDeliveryGateDefaultedOffForAllUsers - ? (parsed.settings?.terminalHiddenDeliveryGate ?? false) - : false - if (!terminalHiddenDeliveryGateDefaultedOffForAllUsers) { - this.loadNeedsSave = true - } const autoRenameBranchFromWorkDefaultedOn = parsed.settings?.autoRenameBranchFromWorkDefaultedOn === true // Why: default-on rollout activates old profiles once, but a later Settings opt-out survives reloads. @@ -3512,8 +3503,6 @@ export class Store { ...migratedTerminalTuiScrollSensitivity.settings, experimentalActivity: migratedExperimentalActivity, experimentalActivityDefaultedOffForAllUsers: true, - terminalHiddenDeliveryGate: migratedTerminalHiddenDeliveryGate, - terminalHiddenDeliveryGateDefaultedOffForAllUsers: true, // Why: compact worktree cards graduated from Experimental; preserve the old opt-in for rollout-era profiles. compactWorktreeCards: loadedCompactWorktreeCards, experimentalCompactWorktreeCards: undefined, diff --git a/src/shared/constants.test.ts b/src/shared/constants.test.ts index bd3e799793e..ea41c8a6af9 100644 --- a/src/shared/constants.test.ts +++ b/src/shared/constants.test.ts @@ -42,11 +42,6 @@ describe('getDefaultSettings', () => { expect(getDefaultSettings('/tmp').terminalCursorStyleDefaultedToBlock).toBe(true) }) - it('keeps hidden PTY bytes flowing to the renderer by default', () => { - expect(getDefaultSettings('/tmp').terminalHiddenDeliveryGate).toBe(false) - expect(getDefaultSettings('/tmp').terminalHiddenDeliveryGateDefaultedOffForAllUsers).toBe(true) - }) - it('allows OSC 52 clipboard writes by default for new settings', () => { expect(getDefaultSettings('/tmp').terminalAllowOsc52Clipboard).toBe(true) expect(getDefaultSettings('/tmp').terminalAllowOsc52ClipboardDefaultedOnForAllUsers).toBe(true) diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 95d85109557..ec0f8358005 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -311,9 +311,17 @@ export function getDefaultSettings(homedir: string): GlobalSettings { terminalHiddenWorktreeRetentionBudget: true, browserGuestWorktreeRetentionBudget: true, terminalMainSideEffectAuthority: true, - // Why: keep hidden-pane xterm state synchronized while rendering remains suspended. + // Why default off: dropping renderer-bound bytes leaves the renderer's xterm + // a second emulator that has seen a different stream than the model, and a + // gap can strand any carried state — a swallowed `ESC[22m` leaves bold on + // every later cell, a swallowed `ESC(B` leaves line-drawing on (STA-4042). + // Every recovery path then has to reconstruct state it cannot actually know. + // Feeding the emulator unconditionally and suspending only the *rendering* + // (pane-manager suspendRendering + WebGL retention already do this) removes + // the gap class outright rather than repairing it after the fact. The gate + // stays available for anyone who needs to trade that correctness for the + // hidden-pane IPC/parse saving. terminalHiddenDeliveryGate: false, - terminalHiddenDeliveryGateDefaultedOffForAllUsers: true, terminalModelQueryAuthority: true, defaultTuiAgent: null, disabledTuiAgents: [...DEFAULT_DISABLED_TUI_AGENTS], diff --git a/src/shared/types.ts b/src/shared/types.ts index 29ad921082f..4d9a2e9b810 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -3009,8 +3009,6 @@ export type GlobalSettings = { terminalMainSideEffectAuthority?: boolean /** Kill switch for main's hidden-delivery gate (Phase 4): drops PTY bytes to hidden views after model ingestion; requires terminalMainSideEffectAuthority. */ terminalHiddenDeliveryGate?: boolean - /** One-shot guard for defaulting the hidden-delivery gate off for existing profiles. */ - terminalHiddenDeliveryGateDefaultedOffForAllUsers?: boolean /** Kill switch for main's model query responder (Phase 5); active only when both Phase-4 gates are also on. */ terminalModelQueryAuthority?: boolean /** Which agent to pre-select in the new-workspace composer.