Keep remote agent status in sync after completion and approvals (#34729)

* fix(remote): reconcile stale paired agent status and pane titles

Adapt the remote completion repair from #25205 while preserving sparse runtime pane slots through existing PTY bindings.

Co-authored-by: Manan Chataut <mananchataut@gmail.com>

* fix(remote): preserve pane identity when reconciling moved titles

Use mounted manager identities and retained PTY bindings ahead of replay order; keep fresh byte-stream ownership on the correct moved pane.

* fix(remote): release byte status fence after host approval

* fix(remote): tighten paired title removal and test the timing fence

- Share the byte-write timing decision (resolveRendererOwnedAgentStatusTiming) between pty-connection and the flap test so reverting it fails a test.
- Only delete a saved pane title when its durable leaf is absent from both the host layout and local panes; unresolved or surface-less slots are kept.
- Share the client-fence predicate (clientAgentStatusFencesHost) between status and title reconciliation.
- Cover removed-leaf spinner fallback in worktree status and a client split absent from a pre-split host snapshot.

* fix(remote): correct fence predicate and title test types

---------

Co-authored-by: Manan Chataut <mananchataut@gmail.com>
This commit is contained in:
Neil
2026-10-28 01:19:22 -07:00
committed by GitHub
co-authored by Manan Chataut
parent c665754a8e
commit 96cd21bbc4
14 changed files with 975 additions and 16 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import type { TerminalLayoutSnapshot } from './terminal-tab-types'
import {
isRuntimePaneTitleOrphan,
resolveRuntimePaneTitleForLeaf,
resolveRuntimePaneTitleLeafResolution
} from './runtime-pane-title-leaf-id'
@@ -60,3 +61,16 @@ describe('resolveRuntimePaneTitleLeafResolution', () => {
})
})
})
describe('isRuntimePaneTitleOrphan', () => {
it('never treats numeric slots as removed panes', () => {
for (const runtimePaneId of ['0', '-1', '2', '3']) {
expect(isRuntimePaneTitleOrphan(runtimePaneId, new Set([LEAF_A]))).toBe(false)
}
})
it('flags only a durable leaf id the layout no longer has', () => {
expect(isRuntimePaneTitleOrphan(LEAF_B, new Set([LEAF_A, LEAF_B]))).toBe(false)
expect(isRuntimePaneTitleOrphan(LEAF_B, new Set([LEAF_A]))).toBe(true)
})
})
+8
View File
@@ -125,6 +125,14 @@ export function resolveRuntimePaneTitleLeafIdFromRoot(
return leafIds[numericPaneId - FIRST_PANE_ID] ?? null
}
/** Numeric slots survive pane closes with gaps, so only absent durable leaf ids prove removal. */
export function isRuntimePaneTitleOrphan(
runtimePaneId: string,
layoutLeafIds: ReadonlySet<string>
): boolean {
return isTerminalLeafId(runtimePaneId) && !layoutLeafIds.has(runtimePaneId)
}
/**
* Resolve a sparse live runtime pane slot through the tab's current PTY bindings.
* PaneManager ids survive closes with gaps, while the live PTY list retains order.