mirror of
https://github.com/stablyai/orca.git
synced 2026-10-23 00:03:52 +00:00
wip(ssh): hoist migration — function proven correct, wiring still unresolved
Direct unit test of hoistSshPartitionsIntoLocalSession PASSES, so the migration
logic is right. Wiring it into load does not take effect: tried three points
(mid-loadState, on the final loadState result, and after
normalizePersistedPaneIdentityState with partitions captured pre-normalization).
The local session still has no hoisted pane on reopen.
The oracle now pins the intermediate step too ('the migration did not hoist the
pane into the local session'), which is what localises the remaining failure to
wiring rather than logic.
NOT SHIPPABLE. Next: instrument this.sshPartitionsAtLoad at the constructor
hoist call to find where the partitions are actually lost.
This commit is contained in:
+19
-10
@@ -2859,8 +2859,12 @@ export type PtyBindingSourceExpectation = {
|
||||
* `mayCreate: false`, so the binding is refused and rolled back — for every pane the upgrading
|
||||
* user had open. Local always wins; the partition only supplies panes local does not already know.
|
||||
*/
|
||||
export function hoistSshPartitionsIntoLocalSession(state: PersistedState): boolean {
|
||||
const partitions = state.workspaceSessionsByHostId
|
||||
export function hoistSshPartitionsIntoLocalSession(
|
||||
state: PersistedState,
|
||||
/** Passed explicitly: pane-identity normalization prunes partitions before this runs. */
|
||||
sourcePartitions?: PersistedState['workspaceSessionsByHostId']
|
||||
): boolean {
|
||||
const partitions = sourcePartitions ?? state.workspaceSessionsByHostId
|
||||
if (!partitions) {
|
||||
return false
|
||||
}
|
||||
@@ -2943,6 +2947,8 @@ export class Store {
|
||||
private readonly sshRemotePtyLeaseMutationVersions = new WeakMap<SshRemotePtyLease, number>()
|
||||
private readonly protectedSecrets = new ProtectedSecretPersistence()
|
||||
private loadNeedsSave = false
|
||||
/** SSH host partitions as they were on disk, before pruning. Used once, by the hoist migration. */
|
||||
private sshPartitionsAtLoad: PersistedState['workspaceSessionsByHostId'] | undefined
|
||||
private settingsChangeListeners = new Set<
|
||||
(
|
||||
updates: Partial<GlobalSettings>,
|
||||
@@ -2971,6 +2977,13 @@ export class Store {
|
||||
const loaded = this.load()
|
||||
const normalized = normalizePersistedPaneIdentityState(loaded)
|
||||
this.state = normalized.state
|
||||
// Why: earlier builds wrote SSH pane membership into `ssh:<target>` partitions; this build's
|
||||
// binding writer is local-only and its reattach refuses to create, so an unhoisted partition
|
||||
// means every pane is refused and the user's tabs are discarded. Runs after pane-identity
|
||||
// normalization, which rebuilds the session and would otherwise drop the hoisted panes.
|
||||
if (hoistSshPartitionsIntoLocalSession(this.state, this.sshPartitionsAtLoad)) {
|
||||
this.loadNeedsSave = true
|
||||
}
|
||||
// Why: activeView is a frequent, tiny preference; keeping it beside the
|
||||
// profile avoids serializing the multi-MB recovery store on navigation.
|
||||
this.activeViewPreference = new ActiveViewPreference(this.dataFile, this.state.ui?.activeView)
|
||||
@@ -3984,6 +3997,10 @@ export class Store {
|
||||
this.loadNeedsSave = true
|
||||
}
|
||||
|
||||
// Captured here because later load steps and pane-identity normalization prune host
|
||||
// partitions; the SSH hoist below still needs the panes they held.
|
||||
this.sshPartitionsAtLoad = result.workspaceSessionsByHostId
|
||||
|
||||
const migrated = this.migrateTabSwitchKeybindings(
|
||||
this.migrateTelemetry(result, fileExistedOnLoad),
|
||||
fileExistedOnLoad
|
||||
@@ -4002,14 +4019,6 @@ export class Store {
|
||||
migrated.githubCache = readGithubCacheSnapshot(this.dataFile) ?? migrated.githubCache
|
||||
}
|
||||
|
||||
// Why: earlier builds wrote SSH pane membership into `ssh:<target>` partitions; this build's
|
||||
// binding writer is local-only and its reattach refuses to create, so an unhoisted partition
|
||||
// means every pane is refused and the user's tabs are discarded. Runs on the final state so a
|
||||
// later migration cannot replace the session object out from under it.
|
||||
if (hoistSshPartitionsIntoLocalSession(migrated)) {
|
||||
this.loadNeedsSave = true
|
||||
}
|
||||
|
||||
logPersistenceStartupMilestone('persistence-load-done', {
|
||||
repos: migrated.repos.length,
|
||||
workspaceSessionBytes: Buffer.byteLength(JSON.stringify(migrated.workspaceSession))
|
||||
|
||||
@@ -92,6 +92,11 @@ describe('an SSH pane whose membership was persisted into the ssh partition', ()
|
||||
|
||||
// The upgrade launch.
|
||||
const store = await reopenStore()
|
||||
// The migration must have folded the pane into the plane the binder actually reads.
|
||||
expect(
|
||||
store.getWorkspaceSession().tabsByWorktree?.[WORKTREE] ?? [],
|
||||
'the migration did not hoist the pane into the local session'
|
||||
).toHaveLength(1)
|
||||
|
||||
// The reattach bind: no hostId, refuses to create.
|
||||
const bound = store.persistPtyBinding({
|
||||
@@ -126,4 +131,19 @@ describe('an SSH pane whose membership was persisted into the ssh partition', ()
|
||||
|
||||
expect(bound, 'the pre-mayCreate path did not keep the pane either').toBe(true)
|
||||
})
|
||||
|
||||
// Isolates the migration from its wiring: does the function itself hoist?
|
||||
it('hoists the partition when called directly', async () => {
|
||||
await createStore()
|
||||
const { hoistSshPartitionsIntoLocalSession } = await import('./persistence')
|
||||
const state = {
|
||||
workspaceSession: { ...getDefaultWorkspaceSession() },
|
||||
workspaceSessionsByHostId: { [SSH_PARTITION]: paneSession() }
|
||||
} as never as Parameters<typeof hoistSshPartitionsIntoLocalSession>[0]
|
||||
|
||||
const changed = hoistSshPartitionsIntoLocalSession(state)
|
||||
|
||||
expect(changed, 'the function reported no change').toBe(true)
|
||||
expect(state.workspaceSession?.tabsByWorktree?.[WORKTREE] ?? []).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user