fix(web): give each EMA step-up attempt ownership of its command retry

Copilot round 5. `pendingStepUpRetryRef` is shared, and the EMA branch
dismisses the prompt *before* its awaits — so a newer command can open
its own step-up and install its own retry while the first authorization
is in flight. Every arm that read or cleared the ref afterwards was
acting on that newer operation: running it under the wrong
authorization and reporting its failure to the wrong source, or
deleting it outright.

The attempt now takes its retry out of the ref before any await, and no
arm touches the ref again. That covers all three sites at once and
removed the interactive-arm clear added last round — the value is
already out by then. The standard step-up path redirects and clears
synchronously, so it has no such window.

Also rewrites the `writeOAuthResumeSnapshot` docstring, which still
described the byte-comparison token it no longer returns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Quk1hBUtyhkXowwMXzAnAY
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-29 03:03:02 -04:00
commit 1634437b69

View file

@ -182,8 +182,13 @@ export function restoreTabUiFromSnapshot(
}
/**
* Persist the snapshot, returning the exact serialization stored — the
* "attempt token" {@link clearOwnOAuthResumeSnapshot} matches against (#2165).
* Persist the snapshot, returning the **`attemptId`** it was stored under —
* the identifier {@link clearOwnOAuthResumeSnapshot} matches on (#2165), not
* the serialized snapshot and not anything to compare bytes against.
*
* A fresh id is minted per write unless the caller supplied one, so two
* redirect attempts are always distinguishable even when their snapshots are
* byte-identical.
*
* `undefined` when nothing was stored (no `window`, privacy mode, quota), so a
* caller holding one knows a clear is meaningful.