mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
refactor(orchestration): name the disposed close error once instead of in both predicates
isTransient and isMissing both matched disposed, so the two read as disjoint while one silently shadowed the other. A disposed endpoint is genuinely both, and only the host observation decides which; pin the certified-exit outcome that had no coverage.
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
function isTransientWorkerTerminalCloseError(reason: string): boolean {
|
||||
return /disposed|not connected|unavailable/i.test(reason)
|
||||
return /not connected|unavailable/i.test(reason)
|
||||
}
|
||||
|
||||
/** The close found nothing to close. Against a host-certified exit that is the goal state, not new
|
||||
* doubt: a retry only aims the same dead handle at the same absent terminal, forever. */
|
||||
function isMissingWorkerTerminalCloseError(reason: string): boolean {
|
||||
return /handle_stale|stale handle|disposed|not found|no such terminal/i.test(reason)
|
||||
return /handle_stale|stale handle|not found|no such terminal/i.test(reason)
|
||||
}
|
||||
|
||||
/** A disposed endpoint is genuinely both: nothing is left to close, and it may return on
|
||||
* reconnect. Only the host observation can say which, so it is named once here instead of
|
||||
* being spelled into two predicates that then read as if they were disjoint. */
|
||||
function isDisposedWorkerTerminalCloseError(reason: string): boolean {
|
||||
return /disposed/i.test(reason)
|
||||
}
|
||||
|
||||
export function classifyWorkerTerminalCloseError(error: unknown): {
|
||||
@@ -14,10 +21,11 @@ export function classifyWorkerTerminalCloseError(error: unknown): {
|
||||
alreadyGone: boolean
|
||||
} {
|
||||
const reason = error instanceof Error ? error.message : String(error)
|
||||
const disposed = isDisposedWorkerTerminalCloseError(reason)
|
||||
return {
|
||||
reason,
|
||||
transient: isTransientWorkerTerminalCloseError(reason),
|
||||
alreadyGone: isMissingWorkerTerminalCloseError(reason)
|
||||
transient: disposed || isTransientWorkerTerminalCloseError(reason),
|
||||
alreadyGone: disposed || isMissingWorkerTerminalCloseError(reason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,6 +212,17 @@ describe('orchestration worker release recovery', () => {
|
||||
expect(runtime.closeTerminal).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('settles a disposed endpoint as released once the host certified the exit', async () => {
|
||||
setup()
|
||||
const { dispatchId } = await startSettledWorker()
|
||||
vi.spyOn(runtime, 'getTerminalLivenessVerdict').mockReturnValue({ status: 'exited' })
|
||||
vi.mocked(runtime.closeTerminal).mockRejectedValueOnce(new Error('Multiplexer disposed'))
|
||||
|
||||
await expect(
|
||||
call('orchestration.workerRelease', { dispatch: dispatchId })
|
||||
).resolves.toMatchObject({ state: 'released', processAction: 'closed_exited_terminal' })
|
||||
})
|
||||
|
||||
it('defers instead of settling unknown while inventory is incomplete', async () => {
|
||||
setup()
|
||||
const { dispatchId } = await startSettledWorker()
|
||||
|
||||
Reference in New Issue
Block a user