mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 16:02:43 +00:00
Only retain a rollback obligation a retry could still discharge
Dismissal removes the pending creation entry before invoking the deferred rollback, so a re-armed hook there could never run again and would strand the attempt, and the workspace it references, for the session. Retry keeps the entry, so it is the only caller that can re-attempt. Gate the retained obligation on the entry still being present.
This commit is contained in:
@@ -288,6 +288,19 @@ describe('worktree creation cancellation', () => {
|
||||
expect(finalRetry).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('does not arm an unreachable cleanup when a dismissal rollback fails', async () => {
|
||||
await withWorktreeCreationCancellation('creation', async (attempt) => {
|
||||
attempt.worktree = worktree
|
||||
})
|
||||
state.removeWorktree.mockResolvedValue({ ok: false, error: 'Host unavailable' })
|
||||
// Dismissal removes the pending entry first, so nothing could ever invoke a
|
||||
// re-armed hook again — holding the attempt would strand it for the session.
|
||||
delete state.pendingWorktreeCreations.creation
|
||||
expect(cancelActiveWorktreeCreation('creation')).toBe(true)
|
||||
await vi.waitFor(() => expect(state.removeWorktree).toHaveBeenCalledOnce())
|
||||
await vi.waitFor(() => expect(cancelActiveWorktreeCreation('creation')).toBe(false))
|
||||
})
|
||||
|
||||
it('lets retry proceed once an unidentifiable workspace has been reported', async () => {
|
||||
const unstamped = makeWorktree({ id: 'repo::/workspace', repoId: 'repo', hostId: 'ssh:owner' })
|
||||
await withWorktreeCreationCancellation('creation', async (attempt) => {
|
||||
|
||||
@@ -45,7 +45,14 @@ export async function withWorktreeCreationCancellation(
|
||||
// would let the next retry create a second workspace beside the first, so
|
||||
// keep the obligation until a removal actually discharges it. An unidentified
|
||||
// workspace is exempt: no call was made, so retrying can never discharge it.
|
||||
if (deferred && !outcome.ok && outcome.retryable) {
|
||||
// The entry check separates retry (kept, so it can re-attempt) from dismissal
|
||||
// (already gone, so a re-armed hook could never run again).
|
||||
if (
|
||||
deferred &&
|
||||
!outcome.ok &&
|
||||
outcome.retryable &&
|
||||
useAppStore.getState().pendingWorktreeCreations[creationId]
|
||||
) {
|
||||
attempt.cleanupAfterSettlement = () => cleanup(deferred)
|
||||
} else {
|
||||
releaseActiveWorktreeCreation(creationId, attempt)
|
||||
|
||||
Reference in New Issue
Block a user