Files
orca/src/main/runtime/settle-before-deadline.ts
T
OrcaWin 7a809a0ecd fix(worktree): stop the PTY gate from wedging workspace removal (#11960)
Destructive worktree removal proves every PTY is physically dead before it
touches the filesystem. When a stop RPC failed, it re-listed the provider to
check whether the PTY had actually exited — but on the *same* deadline the
sweeps had just spent. A slow-but-successful sweep therefore reached the
re-list with ~0ms of budget, timed out, and reported "unverifiable", which the
gate treated as "still live". `--force` never reached this gate, so the
workspace became permanently unremovable and every retry failed identically.

- Verification now gets its own 2s window instead of inheriting an exhausted
  deadline, so an already-exited PTY can be proven exited.
- The verdict distinguishes live / unverifiable / exited, and the error names
  the blocking PTY ids plus the escape hatch instead of only the worktree.
- `--force` propagates to the gate (`allowUnverifiedStop`), downgrading an
  unproven stop to a loud warning so removal can always complete.

Extracts `settleBeforeDeadline` and the verification logic into their own
modules to stay under max-lines.
2026-08-02 17:33:50 -07:00

51 lines
1.4 KiB
TypeScript

/**
* Races `run()` against an absolute deadline (epoch ms).
*
* Without `failClosedError` the call is best-effort: a timeout or a rejection
* resolves to `fallback`. With it, both surface as a rejection so destructive
* callers can block on unproven work — `failClosedOnRunError` narrows which
* rejections count (some are benign sentinels).
*/
export async function settleBeforeDeadline<T>(
run: () => Promise<T>,
fallback: T,
deadline: number,
failClosedError?: Error,
failClosedOnRunError: (error: unknown) => boolean = () => true
): Promise<T> {
const remaining = deadline - Date.now()
if (remaining <= 0) {
if (failClosedError) {
throw failClosedError
}
return fallback
}
return new Promise((resolve, reject) => {
let settled = false
const finish = (value: T): void => {
if (settled) {
return
}
settled = true
clearTimeout(timer)
resolve(value)
}
const fail = (error: unknown): void => {
if (settled) {
return
}
settled = true
clearTimeout(timer)
reject(error)
}
const timer = setTimeout(
() => (failClosedError ? fail(failClosedError) : finish(fallback)),
remaining
)
timer.unref?.()
void run().then(finish, (error: unknown) =>
failClosedError && failClosedOnRunError(error) ? fail(error) : finish(fallback)
)
})
}