fix(terminal): stop re-running composer prompt on user-initiated splits (#778)

This commit is contained in:
Neil 2026-04-17 16:48:54 -07:00 committed by GitHub
parent 2931f66e78
commit 451b6e652f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 7 deletions

View file

@ -19,13 +19,12 @@ export function connectPanePty(
let disposed = false
let connectFrame: number | null = null
let startupInjectTimer: ReturnType<typeof setTimeout> | null = null
// Why: setup commands must only run once — in the initial pane of the tab.
// Capture and clear the startup reference synchronously so that panes
// created later by splits or layout restoration cannot re-execute the
// setup script, which would be confusing and potentially destructive.
// Note: this intentionally mutates `deps` so the caller's object no
// longer carries the startup payload — preventing any later consumer
// from accidentally replaying it.
// Why: startup commands must only run once — in the pane they were
// targeted at. Capture `deps.startup` into a local and clear the field on
// the (already spread-copied) `deps` so nothing else inside this function
// can accidentally re-read it. The caller is responsible for clearing its
// own outer reference, since `deps` here is a shallow copy and our
// mutation does not propagate back.
const paneStartup = deps.startup ?? null
deps.startup = undefined

View file

@ -293,6 +293,18 @@ export function useTerminalPaneLifecycle({
...ptyDeps,
restoredLeafId
})
// Why: connectPanePty receives a spread copy of ptyDeps, so the
// `deps.startup = undefined` it performs internally only clears its
// local copy. If we don't also clear the outer ptyDeps.startup here,
// a later user-initiated splitPane (e.g. Cmd+D, context-menu "Split
// Right") fires onPaneCreated again with the original startup still
// attached — which re-runs the initial composer prompt in the newly
// created pane. Clearing here ensures the initial-startup payload is
// consumed exactly once, by the first pane. Setup/issue splits
// inject their own payload via splitPaneWithOneShotStartup, which
// sets deps.startup immediately before splitPane() and is therefore
// unaffected by this clear.
ptyDeps.startup = null
panePtyBindings.set(pane.id, panePtyBinding)
scheduleRuntimeGraphSync()
queueResizeAll(true)