mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(orcad): skip the moved shell-history pass on starts with nothing to install (#29556)
Every orcad start ran installPendingOrcadMigrationShellHistory, which paid a process inventory with an empty worktree list. That inventory could leave a surviving daemon session unfiled and drop its restored orchestration receipt before any client connected. Return early unless a committed migration still holds a received shell history, and refresh against the host's real worktrees when one does. Follow-up to #28902.
This commit is contained in:
@@ -227,12 +227,20 @@ describe('orcad migration shell history through the store', () => {
|
||||
const reloaded = bashHostStore()
|
||||
expect(hostHistory(DORMANT_WORKTREE_ID, 'bash_history')).toBeNull()
|
||||
expect(existsSync(receivedPath(input, blobs[0].descriptor.key))).toBe(true)
|
||||
expect(reloaded.hasPendingOrcadMigrationShellHistory()).toBe(true)
|
||||
|
||||
reloaded.installPendingOrcadMigrationReceivedBlobs({ worktreeHasLiveShells: () => true })
|
||||
expect(hostHistory(DORMANT_WORKTREE_ID, 'bash_history')).toBeNull()
|
||||
expect(reloaded.hasPendingOrcadMigrationShellHistory()).toBe(true)
|
||||
|
||||
reloaded.installPendingOrcadMigrationReceivedBlobs({ worktreeHasLiveShells: noLiveShells })
|
||||
expect(hostHistory(DORMANT_WORKTREE_ID, 'bash_history')).toBe(BASH)
|
||||
expect(existsSync(receivedPath(input, blobs[0].descriptor.key))).toBe(false)
|
||||
// Every later orcad start skips the post-attach pass and its terminal inventory.
|
||||
expect(reloaded.hasPendingOrcadMigrationShellHistory()).toBe(false)
|
||||
})
|
||||
|
||||
it('reports nothing pending on a host that never received a migration', () => {
|
||||
expect(bashHostStore().hasPendingOrcadMigrationShellHistory()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
+17
@@ -1,3 +1,4 @@
|
||||
import { ORCAD_MIGRATION_BLOB_KIND_CONTRACTS } from '../../../shared/orcad-migration-blob-transfer'
|
||||
import type { OrcadMigrationManifest } from '../../../shared/orcad-migration-manifest'
|
||||
import type { PersistedState } from '../../../shared/persisted-state-types'
|
||||
import { parsePaneKey } from '../../../shared/stable-pane-id'
|
||||
@@ -5,6 +6,7 @@ import { getHistoryRoot } from '../../terminal-history-paths'
|
||||
import type { StoreRuntimeState } from '../loading-store/store-runtime-state'
|
||||
import { findOrcadMigrationImportReceipt } from './orcad-catalog-receipt-ledger'
|
||||
import { installOrcadMigrationReceivedBlobs } from './orcad-migration-blob-installers'
|
||||
import { listOrcadMigrationReceivedBlobNames } from './orcad-migration-blob-staging'
|
||||
import type { OrcadMigrationRequestIdentity } from './orcad-migration-staged-file'
|
||||
|
||||
type OrcadMigrationBlobInstallRuntime = Pick<
|
||||
@@ -72,6 +74,21 @@ export class OrcadMigrationBlobInstallPersistence {
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether any committed migration still holds a received shell history; only the runtime installs those. */
|
||||
hasPendingOrcadMigrationShellHistory(): boolean {
|
||||
const { runtime } = this[orcadMigrationBlobInstallContext]
|
||||
return [
|
||||
...(runtime.state.orcadMigrationImportReceipts ?? []),
|
||||
...(runtime.state.orcadMigrationEvictedReceipts ?? [])
|
||||
].some((receipt) =>
|
||||
listOrcadMigrationReceivedBlobNames(
|
||||
runtime.orcadMigrationBlobRoot,
|
||||
receipt,
|
||||
'shell-history'
|
||||
).some((key) => ORCAD_MIGRATION_BLOB_KIND_CONTRACTS['shell-history'].key.test(key))
|
||||
)
|
||||
}
|
||||
|
||||
/** Retries a history install that waited on this worktree's shells; a no-op for any other. */
|
||||
installDeferredOrcadMigrationShellHistory(
|
||||
worktreeId: string,
|
||||
|
||||
@@ -108,13 +108,21 @@ export abstract class OrcaRuntimeWithMigrationCatalog extends OrcaRuntimeWithRes
|
||||
*/
|
||||
async installPendingOrcadMigrationShellHistory(): Promise<void> {
|
||||
const store = this.store
|
||||
if (!store?.installPendingOrcadMigrationReceivedBlobs) {
|
||||
// Why: runs on every orcad start; with nothing to install it must not pay a process inventory.
|
||||
if (
|
||||
!store?.installPendingOrcadMigrationReceivedBlobs ||
|
||||
!store.hasPendingOrcadMigrationShellHistory?.()
|
||||
) {
|
||||
return
|
||||
}
|
||||
// Daemon shells that outlived a restart count as live only once the inventory recorded them.
|
||||
// Why the real worktrees: an empty list re-files sessions it cannot resolve and drops their
|
||||
// restored orchestration receipts.
|
||||
if (
|
||||
this.ptyController?.listProcesses &&
|
||||
!(await this.refreshPtyWorktreeRecordsWithControllerInventory([]))
|
||||
!(await this.refreshPtyWorktreeRecordsWithControllerInventory([
|
||||
...(await this.getResolvedWorktreeMap()).values()
|
||||
]))
|
||||
) {
|
||||
console.warn('[orcad-migration] shell history waits: the terminal inventory did not answer')
|
||||
return
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { OrcaRuntimeService } from './orca-runtime-test-mocks.spec'
|
||||
import { store } from './orca-runtime-test-fixtures.spec'
|
||||
import { store, TEST_WORKTREE_ID, TEST_WORKTREE_PATH } from './orca-runtime-test-fixtures.spec'
|
||||
|
||||
type RuntimeInternals = {
|
||||
recordPtyWorktree: (ptyId: string, worktreeId: string, state?: { connected?: boolean }) => unknown
|
||||
restoredOrchestrationAuthorityByPtyId: Map<string, unknown>
|
||||
buildResolvedWorktreeFromId: (worktreeId: string) => unknown
|
||||
getResolvedWorktreeMap: () => Promise<Map<string, unknown>>
|
||||
}
|
||||
|
||||
function internalsOf(runtime: OrcaRuntimeService): RuntimeInternals {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these members are declared on OrcaRuntimeService's inheritance chain.
|
||||
return runtime as unknown as RuntimeInternals
|
||||
}
|
||||
|
||||
describe('a moved shell history that waited on live shells', () => {
|
||||
@@ -13,8 +21,7 @@ describe('a moved shell history that waited on live shells', () => {
|
||||
...store,
|
||||
installDeferredOrcadMigrationShellHistory: installDeferred
|
||||
})
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: recordPtyWorktree is declared on OrcaRuntimeService's inheritance chain.
|
||||
const internals = runtime as unknown as RuntimeInternals
|
||||
const internals = internalsOf(runtime)
|
||||
internals.recordPtyWorktree('pty-1', 'repo-1::/srv/app', { connected: true })
|
||||
internals.recordPtyWorktree('pty-2', 'repo-1::/srv/app', { connected: true })
|
||||
|
||||
@@ -32,21 +39,29 @@ describe('a moved shell history that waited on live shells', () => {
|
||||
const installPending = vi.fn()
|
||||
const runtime = new OrcaRuntimeService({
|
||||
...store,
|
||||
installPendingOrcadMigrationReceivedBlobs: installPending
|
||||
installPendingOrcadMigrationReceivedBlobs: installPending,
|
||||
hasPendingOrcadMigrationShellHistory: () => true
|
||||
})
|
||||
runtime.setPtyController({
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess: async () => null,
|
||||
listProcesses: vi.fn(async () => [
|
||||
{ id: 'daemon-pty', cwd: '/srv/app', title: 'zsh', worktreeId: 'repo-1::/srv/app' }
|
||||
{ id: 'daemon-pty', cwd: '/srv/app', title: 'zsh', worktreeId: 'repo-1::/srv/app' },
|
||||
// No provider owner: only the host's real worktrees can file this shell by its cwd.
|
||||
{ id: 'unowned-pty', cwd: TEST_WORKTREE_PATH, title: 'zsh' }
|
||||
])
|
||||
})
|
||||
const internals = internalsOf(runtime)
|
||||
vi.spyOn(internals, 'getResolvedWorktreeMap').mockResolvedValue(
|
||||
new Map([[TEST_WORKTREE_ID, internals.buildResolvedWorktreeFromId(TEST_WORKTREE_ID)]])
|
||||
)
|
||||
|
||||
await runtime.installPendingOrcadMigrationShellHistory()
|
||||
|
||||
const [options] = installPending.mock.calls[0]
|
||||
expect(options.worktreeHasLiveShells('repo-1::/srv/app')).toBe(true)
|
||||
expect(options.worktreeHasLiveShells(TEST_WORKTREE_ID)).toBe(true)
|
||||
expect(options.worktreeHasLiveShells('repo-1::/srv/other')).toBe(false)
|
||||
})
|
||||
|
||||
@@ -54,7 +69,8 @@ describe('a moved shell history that waited on live shells', () => {
|
||||
const installPending = vi.fn()
|
||||
const runtime = new OrcaRuntimeService({
|
||||
...store,
|
||||
installPendingOrcadMigrationReceivedBlobs: installPending
|
||||
installPendingOrcadMigrationReceivedBlobs: installPending,
|
||||
hasPendingOrcadMigrationShellHistory: () => true
|
||||
})
|
||||
runtime.setPtyController({
|
||||
write: () => true,
|
||||
@@ -71,4 +87,37 @@ describe('a moved shell history that waited on live shells', () => {
|
||||
expect(installPending).not.toHaveBeenCalled()
|
||||
warn.mockRestore()
|
||||
})
|
||||
|
||||
it('costs a start with nothing pending no inventory and keeps restored receipts', async () => {
|
||||
const installPending = vi.fn()
|
||||
const runtime = new OrcaRuntimeService({
|
||||
...store,
|
||||
installPendingOrcadMigrationReceivedBlobs: installPending,
|
||||
hasPendingOrcadMigrationShellHistory: () => false
|
||||
})
|
||||
const listProcesses = vi.fn(async () => [{ id: 'daemon-pty', cwd: '/elsewhere', title: 'zsh' }])
|
||||
runtime.setPtyController({
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess: async () => null,
|
||||
listProcesses
|
||||
})
|
||||
const receipt = {
|
||||
ptyId: 'daemon-pty',
|
||||
worktreeId: TEST_WORKTREE_ID,
|
||||
terminalHandle: 'term_daemon',
|
||||
paneKey: 'tab-1:1',
|
||||
processIncarnation: 'daemon-pty:inc-1',
|
||||
hostScope: { kind: 'local' }
|
||||
}
|
||||
internalsOf(runtime).restoredOrchestrationAuthorityByPtyId.set('daemon-pty', receipt)
|
||||
|
||||
await runtime.installPendingOrcadMigrationShellHistory()
|
||||
|
||||
expect(listProcesses).not.toHaveBeenCalled()
|
||||
expect(installPending).not.toHaveBeenCalled()
|
||||
expect(internalsOf(runtime).restoredOrchestrationAuthorityByPtyId.get('daemon-pty')).toBe(
|
||||
receipt
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -65,6 +65,7 @@ export type RuntimeStore = {
|
||||
installOrcadMigrationReceivedBlobs?: Store['installOrcadMigrationReceivedBlobs']
|
||||
installPendingOrcadMigrationReceivedBlobs?: Store['installPendingOrcadMigrationReceivedBlobs']
|
||||
installDeferredOrcadMigrationShellHistory?: Store['installDeferredOrcadMigrationShellHistory']
|
||||
hasPendingOrcadMigrationShellHistory?: Store['hasPendingOrcadMigrationShellHistory']
|
||||
peekOrcadMigrationTerminalRestoreHint?: Store['peekOrcadMigrationTerminalRestoreHint']
|
||||
consumeOrcadMigrationTerminalRestoreHint?: Store['consumeOrcadMigrationTerminalRestoreHint']
|
||||
persistPtyBinding?: Store['persistPtyBinding']
|
||||
|
||||
Reference in New Issue
Block a user