Problem
Today's promotion path for a contested seat (one with a real queue behind it) is a manual claimOpenSeat race among the top 3 ranked requests (issue #14's MVP policy) — a real race condition among simultaneous clickers, and no way to skip an absent/unready top candidate without someone else noticing and clicking first.
Scope narrowed (2026-08-21)
This issue no longer covers the truly-uncontested case — direct join on an empty seat with no queue at all is now #27, split out after real-device testing found it was both separable (no dependency on this issue's readiness signal) and high-priority (real friction hit immediately). This issue is now specifically the harder remaining problem: automatic ranked promotion when a real queue exists.
Design
Automatic ranked promotion, no manual race: when a seat with a real queue behind it opens, the highest-ranked eligible candidate is automatically attempted server-side — no claimOpenSeat button, no client-side race. "Eligible" factors in #22's readiness signal (has this candidate actually prepared local media) rather than just rank; an unready top candidate yields via time-graduated eligibility (a bounded grace period, then the next-ranked candidate is attempted) rather than blocking the seat indefinitely. The server independently validates every attempt regardless of which client triggered it — event_speakers/claim_speaker_seat (issue #13) stays the sole source of truth, never a client's own claim of readiness or rank.
Depends on
#22 (composer mic-request/candidate readiness) — this dependency is no longer soft now that the readiness-independent case has moved to #27. Automatic promotion's unready-yields-eligibility behavior needs #22's real readiness signal to mean anything.
Blocks
#24 (fresh next-speaker ranking) and #25 (retention voting) — voting needs a working promotion path to hand off to after an eviction.
Problem
Today's promotion path for a contested seat (one with a real queue behind it) is a manual
claimOpenSeatrace among the top 3 ranked requests (issue #14's MVP policy) — a real race condition among simultaneous clickers, and no way to skip an absent/unready top candidate without someone else noticing and clicking first.Scope narrowed (2026-08-21)
This issue no longer covers the truly-uncontested case — direct join on an empty seat with no queue at all is now #27, split out after real-device testing found it was both separable (no dependency on this issue's readiness signal) and high-priority (real friction hit immediately). This issue is now specifically the harder remaining problem: automatic ranked promotion when a real queue exists.
Design
Automatic ranked promotion, no manual race: when a seat with a real queue behind it opens, the highest-ranked eligible candidate is automatically attempted server-side — no
claimOpenSeatbutton, no client-side race. "Eligible" factors in #22's readiness signal (has this candidate actually prepared local media) rather than just rank; an unready top candidate yields via time-graduated eligibility (a bounded grace period, then the next-ranked candidate is attempted) rather than blocking the seat indefinitely. The server independently validates every attempt regardless of which client triggered it —event_speakers/claim_speaker_seat(issue #13) stays the sole source of truth, never a client's own claim of readiness or rank.Depends on
#22 (composer mic-request/candidate readiness) — this dependency is no longer soft now that the readiness-independent case has moved to #27. Automatic promotion's unready-yields-eligibility behavior needs #22's real readiness signal to mean anything.
Blocks
#24 (fresh next-speaker ranking) and #25 (retention voting) — voting needs a working promotion path to hand off to after an eviction.