fix(terminal): migrate hidden delivery gate default

This commit is contained in:
Brennan Benson
2026-08-13 02:02:16 -07:00
parent 96b1d4b557
commit 7bb2d65529
6 changed files with 58 additions and 12 deletions
+1 -2
View File
@@ -31,8 +31,7 @@ const droppedSinceHiddenPtys = new Set<string>()
let droppedHiddenDeliveryChars = 0
let droppedHiddenDeliveryChunks = 0
/** Gate kill switches, both read main-side: the gate only operates under main
* side-effect authority AND the gate-specific setting (both default on). */
/** The gate operates only when main side-effect authority and the gate setting are enabled. */
export function isHiddenPtyDeliveryGateEnabled(
settings: HiddenPtyDeliveryGateSettings | null | undefined
): boolean {
+37
View File
@@ -7933,6 +7933,43 @@ 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 () => {
+11
View File
@@ -3290,6 +3290,15 @@ 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.
@@ -3503,6 +3512,8 @@ 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,
+5
View File
@@ -42,6 +42,11 @@ 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)
+2 -10
View File
@@ -311,17 +311,9 @@ export function getDefaultSettings(homedir: string): GlobalSettings {
terminalHiddenWorktreeRetentionBudget: true,
browserGuestWorktreeRetentionBudget: true,
terminalMainSideEffectAuthority: true,
// 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.
// Why: keep hidden-pane xterm state synchronized while rendering remains suspended.
terminalHiddenDeliveryGate: false,
terminalHiddenDeliveryGateDefaultedOffForAllUsers: true,
terminalModelQueryAuthority: true,
defaultTuiAgent: null,
disabledTuiAgents: [...DEFAULT_DISABLED_TUI_AGENTS],
+2
View File
@@ -3009,6 +3009,8 @@ 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.