"Cut after this airs": scheduled safe cut for match-end reactions#33
Merged
Conversation
Streamers finishing a match on a 30s delay had to count the delay in their head before cutting, or risk clipping their win/lose reaction off the delayed output. Now they press one button at the moment the reaction ends: the controller records that live-edge timestamp and fires the normal IDR-aligned cut once the slowest live destination has aired past it. Nothing before the press ever gets clipped, and the downstream connection survives the cut like any manual one. Details: - Controller: safe_cut_input_ts mark + schedule/cancel/pending/remaining API. maybe_fire_safe_cut runs in each pump's throttled cut-check and gates on the slowest live consumer, with a compare_exchange so exactly one pump fires. The mark clears on manual cut, disarm, and publisher reconnect (a mark on the old session's timeline could never be reached). current_delay_ms now shares the slowest-consumer helper. - Web: POST /cut-after (409 without an active delay) and POST /cut-after/cancel; /state gains safe_cut_pending + safe_cut_remaining_ms. - Dashboard: the button appears under Cut only while a delay is active, turns into a live countdown while pending (tap again to cancel), and carries a hovercard explaining the press -> airs -> auto-cut flow. The setup wizard and the onboarding tour both introduce it. - Tests: 5 controller tests (refusal, schedule/cancel, slowest-consumer gate, clears) plus e2e scenario F driving the endpoint against a live ffmpeg stream. 234 tests green; fmt + clippy -D warnings clean; all 6 e2e scenarios pass locally.
The "Cut after this airs" button rendered as its own full-width row
under Cut delay, so the whole control column grew and shrank as the
delay state changed. Feedback from live use: the layout jump is worse
than the bigger button.
Now both buttons share one row at a 3:1 split. The compact button
reads "Cut after" (countdown state: "~27s x") and the full story
stays in the hovercard; the hero subtext carries the readable
countdown ("Auto-cut armed - going live in ~27s...") since it never
affects layout.
Also fixes a real bug the restyle surfaced: .cutafter-wrap declared
display:block, which overrides the UA's [hidden] rule - the wrap
would not collapse when hidden. Added an explicit [hidden] override.
This was referenced Jul 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new ⏱ Cut after this airs button, visible while a delay is active. Press it the moment your match reaction ends: InstantClone records that exact live-edge timestamp, lets everything up to it reach your viewers, then fires the normal IDR-aligned cut to live on its own.
Why
The whole point of running a competitive delay is stream-snipe protection, but ending it gracefully was manual: finish the match, react, then count 30 seconds in your head before hitting Cut so the reaction isn't clipped off the delayed output. Get it wrong in one direction and viewers lose the reaction; wrong in the other and you sit on a dead delay. Now it's one press at a moment you already recognize.
How it works
maybe_fire_safe_cut, which fires only when the slowest live destination has aired past the mark, so a fast destination can never cut a slow one short of it. A compare-exchange makes exactly one pump the firing pump; the cut itself is the samestop_delay->compute_delay_cutpath as a manual Cut, so it inherits the IDR alignment and connection preservation.POST /cut-after(409 when no delay is active) andPOST /cut-after/cancel./stategainssafe_cut_pendingandsafe_cut_remaining_ms.UX
Verification
cargo fmt --check,cargo clippy --all-targets -- -D warnings, andcargo test --releaseall clean: 234 passed (5 new controller tests: refusal without an active delay, schedule/cancel round-trip, slowest-consumer firing gate, clearing on manual cut/disarm, clearing on publisher reconnect).