core: retire the share at Join's session teardown, not after ticket parse

Gemini's review of the S2 branch found the one hole the harness had not
covered (P2, verified reachable): Join tears the old session down —
deliberately killing the share host — BEFORE validating the ticket, and
an invalid ticket exits the arm early, skipping the late
`current_sharing = None`. The killed host's stdout EOF then passed the
staleness gate and the user got a spurious "Screen share ended
unexpectedly" on top of "invalid room ticket". Pre-S2 the stale value
was toothless on this path; the fault handler gave it teeth.

The share now dies where the session does: cleared unconditionally right
after the teardown block, ahead of every early exit. The live gate grew
a third half — share, Join with a garbage ticket, then require silence
after the ticket error — and the mutant restoring the old placement is
killed by exactly that assertion (spurious re-emitted ScreenShareStopped).

Also Gemini's P3: the test's temp dir is now dropped by a guard, so an
assertion panic no longer leaks the fake-pixelpass scripts in /tmp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 04:05:17 -04:00
co-authored by Claude Fable 5
parent 3df2378831
commit b9803f93fb
2 changed files with 72 additions and 5 deletions
+9 -2
View File
@@ -1733,6 +1733,13 @@ async fn run_core_loop(
net.file_router.clear();
*current_room.lock().unwrap() = None;
}
// Any advertised share died with that session — deliberately —
// so retire it HERE, before the invalid-ticket early exit below
// can skip it. Left populated, the killed host's stdout EOF
// would pass the ScreenShareHostFault staleness gate and
// surface as a spurious "ended unexpectedly" error on top of
// the ticket error (Gemini review of S2, P2-1).
current_sharing = None;
// If a network-mode / identity change was deferred while a call was
// active, rebuild the persistent stack now — after the old session is
@@ -1828,8 +1835,8 @@ async fn run_core_loop(
secret_key.clone(),
));
// Fresh join starts not sharing; clear any stale share ticket.
current_sharing = None;
// (The share was already retired beside the session teardown
// above; a fresh join starts not sharing.)
let self_state =
presence.to_state(is_muted.load(Ordering::Relaxed), endpoint.addr(), None);