Skip to content

Keep the invite path retryable through host session rollover - #118

Merged
thetangstr merged 1 commit into
mainfrom
fix/invite-rollover-retry
Sep 17, 2026
Merged

thetangstr merged 1 commit into
mainfrom
fix/invite-rollover-retry

Conversation

@thetangstr

Copy link
Copy Markdown
Owner

Summary

  • invite() now requires INVITATION_MIN_RUNWAY_MS (30s) of remaining invitation window before minting; less returns V2TransientCoordinatorError → retryable HANDSHAKE_TEMPORARILY_UNAVAILABLE before any state exists.
  • The same 30s runway invariant is enforced atomically at invitation-store commit time: create() forwards a commitGuard evaluated inside the store's serialized write; if the clock drops below the runway mid-create, the commit is refused with V2InvitationWindowUnavailableError → converted to V2TransientCoordinatorError. No invitation record, no role state.
  • Replaces the previous post-create expiry throw, which ran after the record was already persisted (orphan).
  • Pairs with the host-side rotation fix (clockchain-handshake-v2): the host exits at window close so current refreshes; clients retry into the fresh session.

Regression coverage

  • Already-expired current discovery → retryable + zero role state
  • Near-expiry (5s remaining) → retryable, create never invoked, zero role state
  • Runway crossing during create (31s → 10s remaining) → retryable, create ran once, record absent from invitation store, zero role state

Test plan

  • Focused coordinator/invitation-store/public-server: 50/50
  • Root npm test: 663 total (627 workspace + 36 infra), 0 fail; npm run build clean; git diff --check clean

Reviewed commit: 0bc59ba2381e257517164f3543df4cd78d9bbcc4 (independent review: APPROVED, zero findings)

Generated with Devin

An idle host session rendezvous window lapses 120s after open while the
session stays "current" until the 10-minute deadline, so a fresh client
could fetch discovery for a window that was already gone or nearly gone.
Mint an invitation only when the remaining window retains at least 30s
of runway for the Responder claim to land; anything less returns
V2TransientCoordinatorError (retryable) before any state is created.
The same 30s invariant is enforced atomically at invitation-store
commit time: create passes a commitGuard evaluated inside the
serialized write, so a clock that drops below the runway during create
is refused with V2InvitationWindowUnavailableError — converted to
V2TransientCoordinatorError — and neither an invitation record nor role
state is left behind.

Constraint: the 120s invitation window is a protocol invariant enforced
  by the monitor; rotation, not extension, is the fix surface. The 90s
  agreement duration stays separate.
Rejected: filter expired sessions out of /v1/discovery/current | leaves
  a dead window with nothing to rendezvous on and cannot rotate the host
Rejected: keep a post-create expiry throw in the coordinator | the
  invitation record is already persisted at that point, so rejecting
  there orphans state
Rejected: weaker commitGuard of now() < expiry | a commit at 29s
  remaining would silently violate the 30s runway guarantee the
  precheck established
Confidence: high
Scope-risk: narrow — invite() runway guard, store commitGuard, tests
Directive: INVITATION_MIN_RUNWAY_MS must stay well below the 120s
  window; if responder claim latency grows, prefer faster rotation over
  shrinking the runway. Never gate expiry outside the commitGuard —
  only the serialized write can refuse the commit atomically.
Tested: coordinator suite 21/21 incl. near-expiry (5s remaining) and
  runway-crossing-during-create cases; the crossing test proves create
  ran once, the record is absent from the invitation store, and no role
  state exists; root npm test 663 total, 0 fail; build + git diff
  --check clean
Not-tested: live relay round-trip during production rollover
@thetangstr
thetangstr merged commit 730d484 into main Sep 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant