mirror of
https://github.com/stablyai/orca.git
synced 2026-09-25 16:02:38 +00:00
* fix(mobile): survive connection-migration cutovers during worktree create A worktree.create in flight when the mobile transport migrates (relay/direct hand-off on shoddy cellular, relay lease rotation, relay recovery) rejects with "RPC interrupted by connection migration" even though the host completed it — leaving the Create Workspace modal stuck while the worktree exists on desktop. A naive retry hits a name collision and spawns a duplicate. Mirror the existing mobile terminal-create idempotency: worktree.create now accepts an optional clientMutationId that the host dedupes (in-flight + brief post-success TTL), and mobile mints one key per candidate name and re-issues the create on a cutover so the retry reconciles instead of duplicating. * fix(mobile): gate worktree cutover replay by capability * fix(mobile): await worktree replay capability
58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import type { RpcClient } from '../transport/rpc-client'
|
|
import { LogicalClientCutoverError } from '../transport/stable-logical-rpc-client'
|
|
import { readNewWorktreeRuntimeCapabilities } from './worktree-create-capability'
|
|
|
|
function statusClient(outcomes: Array<'cutover' | 'error' | string[]>): RpcClient {
|
|
let call = 0
|
|
return {
|
|
sendRequest: async () => {
|
|
const outcome = outcomes[Math.min(call, outcomes.length - 1)]!
|
|
call += 1
|
|
if (outcome === 'cutover') {
|
|
throw new LogicalClientCutoverError()
|
|
}
|
|
if (outcome === 'error') {
|
|
throw new Error('offline')
|
|
}
|
|
return {
|
|
id: '1',
|
|
ok: true,
|
|
result: { capabilities: outcome },
|
|
_meta: { runtimeId: 'r' }
|
|
}
|
|
}
|
|
} as unknown as RpcClient
|
|
}
|
|
|
|
describe('readNewWorktreeRuntimeCapabilities', () => {
|
|
it('reads task and idempotent-create support from status.get', async () => {
|
|
await expect(
|
|
readNewWorktreeRuntimeCapabilities(
|
|
statusClient([['mobile.tasks.v1', 'worktree.create-idempotency.v1']])
|
|
)
|
|
).resolves.toEqual({
|
|
tasksSupported: true,
|
|
idempotentWorktreeCreateSupported: true
|
|
})
|
|
})
|
|
|
|
it('retries the safe status probe after a connection cutover', async () => {
|
|
await expect(
|
|
readNewWorktreeRuntimeCapabilities(
|
|
statusClient(['cutover', ['worktree.create-idempotency.v1']])
|
|
)
|
|
).resolves.toEqual({
|
|
tasksSupported: false,
|
|
idempotentWorktreeCreateSupported: true
|
|
})
|
|
})
|
|
|
|
it('fails closed when capability detection is unavailable', async () => {
|
|
await expect(readNewWorktreeRuntimeCapabilities(statusClient(['error']))).resolves.toEqual({
|
|
tasksSupported: false,
|
|
idempotentWorktreeCreateSupported: false
|
|
})
|
|
})
|
|
})
|