fix: control v2 disconnected instantly, then shadow mode detached the viewer - #139
Merged
Merged
Conversation
Clicking to control always dropped the whole session and forced a reconnect. `set_input_ack_negotiated` is called only inside the `if control_protocol == ControlProtocol::V1` branch in connect.rs. The control v2 path negotiates InputAck with the host but never records that in the session diagnostics, so `observe_ack` still saw `negotiated == false` and rejected the host's first acknowledgement with "Host sent an input acknowledgement that was not negotiated". That marks the pointer feedback terminal/malformed, and the frontend treats a terminal feedback envelope as fatal and disconnects. The host emits that first ack the moment the input v2 stream opens, to prime pointer feedback, so every control attempt hit it. Control v2 could not have worked. Both rejection paths in `run_input_feedback_v2` also broke silently. A fatal disconnect left nothing in the log to explain itself, which is why this looked like a transport fault: the host reported only "connection lost" once Portal had already torn the endpoint down. They now name the mismatch. Verified against a live Bazzite host running the published 0.1.0: input v2 is accepted and the session survives instead of dying ~8ms later, and the rejection no longer appears.
…help With the input-ack disconnect fixed, the session survived long enough to reach the next failure: the host detached the viewer after ~25 seconds with reason="adaptive_recovery_detach". `RecoveryMutation::Detach` applied unconditionally. In shadow mode the controller computes a lower bitrate and never applies it, so a viewer under sustained pressure cannot recover -- the only remedy is switched off -- and the recovery state machine escalates to detach on schedule. Shadow mode disabled the remedy and kept the punishment. This is the documented default, not an exotic configuration. `docs/sigil-host-activation.md` step 5 writes `encoder_backend = "external-gst-launch"`, which is exactly what selects shadow mode, so a host provisioned by following the guide detaches any viewer that experiences sustained pressure. On the reference host that is 2560x1600@60 at 12000 kbps to a Wi-Fi client. Detaching is an action, so it is now gated on actuation being available, and says so when it declines. The duplicated actuation predicate becomes one `encoder_actuation_available` helper used by both the coordinator and the feedback session.
Merged
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.
Two defects found during the v0.1.0 end-to-end test against a live Bazzite host (#137, #140). The second only became visible once the first was fixed.
1. Control v2 disconnected on its first input ack
set_input_ack_negotiatedis called only inside theif control_protocol == ControlProtocol::V1branch. Control v2 negotiatesInputAckwith the host but never records it, soobserve_acksawnegotiated == falseand rejected the host's first acknowledgement:That marks pointer feedback terminal/malformed, which the frontend treats as fatal → full disconnect. The host sends that ack the instant the input stream opens, to prime pointer feedback, so control v2 could never have worked.
Both rejection paths in
run_input_feedback_v2also broke silently, so the fatal disconnect left nothing in the log to explain itself — which is why it presented as a transport fault. They now name the mismatch.2. Shadow mode detached viewers it had disabled the remedy for
With the session surviving, it then died after ~25s with
reason="adaptive_recovery_detach".RecoveryMutation::Detachapplied unconditionally. In shadow mode the controller computes a lower bitrate and never applies it, so a pressured viewer cannot recover by construction and the recovery machine escalates to detach on schedule.This is the documented default:
docs/sigil-host-activation.mdstep 5 writesencoder_backend = "external-gst-launch", which is exactly what selects shadow mode. Detaching is an action, so it's now gated on actuation being available, and logs when it declines.Also collapses the duplicated actuation predicate into one
encoder_actuation_availablehelper.Verified
Fix 1 against the live host: input v2 accepted and the session survives, versus dying ~8ms after accept.
Fix 2 has a regression test pinning that the activation guide's own backend yields shadow mode and never gains detach permission.
fmt,clippy -D warnings, 473 tests green.