Issue #23: boot lock-screen silent-capture self-heal (re-open ladder)
Corrected diagnosis (Ed: the machine's OWN speakers audibly play the Windows tune and NVDA at the boot lock screen — the endpoint is NOT silent): a loopback capture attached in the first seconds of boot can land on an audio-engine mix the logon-session audio path was never wired into. Callbacks flow (fed by our own silence keepalive) but carry none of the audio that is audibly playing, and Windows fires no device event about it — the previously-shipped device-change reopen never triggers. Signing in re-plumbs the session audio into the graph, which is why sound starts instantly with zero change on our side; a capture opened after sign-out (graph fully live) works at the lock screen, matching Jonathan's reports exactly. Fix: while sending, the 15s capture pulse now drives a self-heal — if the capture has heard only silence since it opened (pre-encode peak < 0.001 on every pulse), or its callbacks freeze, tear down and re-open the capture so it re-attaches to the live graph. Capped at 3 attempts per sending stint; the first real audio ends the ladder so a quiet-but-healthy capture is never churned. Ladder refills on Resume() and on power resume (wake re-plumbs the graph like boot). Every re-open is logged with the attempt count, so Jonathan's next log shows either "re-open recovered audio" (fixed) or three silent re-opens (deeper Windows routing issue, and we know exactly where we stand). Decision core (ShouldReopenSilentCapture) is pure and pinned by a new self-test. Gate: 39/39. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1003bbfdf2
commit
c72169c7f5
@@ -68,6 +68,7 @@ internal static class SelfTest
|
||||
RunStep(results, "Service send host (headless stream + yield)", ServiceSendHostStream);
|
||||
RunStep(results, "Service network presence (reachable + shell teardown)", ServiceNetworkPresenceReachable);
|
||||
RunStep(results, "Service reachability-gated sending (drop dead peers, re-arm recovered)", ServiceReachabilityGating);
|
||||
RunStep(results, "Service silent-capture self-heal (issue #23 boot re-open ladder)", ServiceSilentCaptureSelfHeal);
|
||||
RunStep(results, "Send-app capture change-detection (catch an app the instant it opens)", SendAppCaptureChangeDetection);
|
||||
RunStep(results, "Remembered applications list is global + clearable", RememberedApplicationsGlobal);
|
||||
RunStep(results, "Send-app lists semantics (ticked → Active, out of Remembered)", SendAppListSemantics);
|
||||
@@ -1547,6 +1548,25 @@ internal static class SelfTest
|
||||
return "reachable armed; long-unreachable dropped; grace-window kept; recovery re-arms (issues #8/#15)";
|
||||
}
|
||||
|
||||
/// <summary>Issue #23 boot self-heal decision core: a capture that has heard only silence since it
|
||||
/// opened (or whose callbacks froze) gets re-opened, capped at 3 attempts per stint, and the ladder
|
||||
/// ends for good once real audio has been heard. The scenario: at the boot lock screen the machine's
|
||||
/// speakers audibly play (Windows tune, NVDA) but a capture attached in the first seconds of boot
|
||||
/// taps an engine mix the logon-session audio was never wired into — re-attaching lands on the live
|
||||
/// graph.</summary>
|
||||
private static string? ServiceSilentCaptureSelfHeal()
|
||||
{
|
||||
Check(ServiceSendHost.ShouldReopenSilentCapture(stalled: false, everHeardAudio: false, attemptsSoFar: 0),
|
||||
"a capture that has never heard audio must be re-opened");
|
||||
Check(ServiceSendHost.ShouldReopenSilentCapture(stalled: true, everHeardAudio: true, attemptsSoFar: 0),
|
||||
"a stalled capture must be re-opened even after audio has been heard");
|
||||
Check(!ServiceSendHost.ShouldReopenSilentCapture(stalled: false, everHeardAudio: true, attemptsSoFar: 0),
|
||||
"a healthy capture that has heard real audio must be left alone");
|
||||
Check(!ServiceSendHost.ShouldReopenSilentCapture(stalled: false, everHeardAudio: false, attemptsSoFar: 3),
|
||||
"the re-open ladder must stop at the attempt cap");
|
||||
return "silent-since-open and stalled captures re-open; heard-audio healthy captures don't; capped at 3";
|
||||
}
|
||||
|
||||
/// <summary>The fix for "a saved app that launches later never gets captured": the send engine
|
||||
/// re-applies capture whenever the ticked apps' running process ids change. This tests the pure
|
||||
/// change-detector that drives it — the signature is stable while nothing changes (so we don't churn),
|
||||
|
||||
Reference in New Issue
Block a user