COR-2 (useCallEmbed): useCallJoined only reset `joined` when the embed became
undefined. Answering a 2nd call swaps the embed A->B directly (embed stays
truthy), so `joined` stayed true and call B rendered as already-joined,
skipping the loading/watchdog UI. Re-seed from `embed?.joined ?? false` on
every embed identity change.
COR-4 (ClientNonUIFeatures): the notify-dedupe used one Map<roomId,eventId>
slot shared by the main-timeline and per-thread paths, so a thread reply
overwrote the room's slot and a re-fired main message (decrypt/edit re-emit,
common in E2EE) then mismatched and double-notified. Key the slot by
`${roomId}|${threadId ?? 'main'}` so each path dedupes independently.
Both verified correct by two review passes (no missed-notification or
missed-join regressions).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>