Four parallel code-reading passes over lotus @ e36aef8a (audio pipeline, widget action bus & security, in-call UI/mobile, build/publish/upstream-sync), scoped to git diff v0.20.1..lotus. Every finding was verified against the code before filing; the P6-2 items already open in cinny's LOTUS_TODO.md were not refiled. Host-side halves of cross-cutting problems are filed in LotusGuild/cinny and cross-referenced from the relevant issue here.
Labelling: one priority: *, one type label, one area: * per issue; milestones High (due 2026-10-15) and Medium & Low (due 2026-11-30).
#20call_state can emit up to 8 messages/second; dedupe runs after the throttle performancewidget-actions
#21lotusTransparent/lotusTheme provide no contrast guard for an arbitrary host background uxmobile-ui
#22 Published npm version consumed by cinny has no corresponding git tag bugbuild-publish
#23 Upstream fork is 6 minor releases behind with no cherry-picks; hotspot files have heavy upstream churn tech-debtupstream-sync
Low
#24 Denoise worklet node ignores its channelCount: 1 (no channelCountMode: "explicit") bugdenoise
#25 Dry/wet floor sums an undelayed dry copy with the delayed denoised path (comb filtering) bugdenoise
#26 Denoise external-module contracts are untyped any and break silently on an asset bump tech-debtdenoise
#27 Audio-output sink is never restored when the selected device becomes undefined again bugaudio-pipeline
#28 Decoration URLs derive from an unvalidated arbitrary profile field securitydecorations
#29 Pinning during a screenshare removes the PiP even when the pin is a third party uxwidget-actions
#30 Manual pin resolves by userId only, so multi-device users pin an arbitrary device bugwidget-actions
#31lotusCallState=1 in standalone (non-widget) mode builds the whole pipeline for nothing performancewidget-actions
#32 Landscape filmstrip rail-shrink query is height-only, not orientation- or device-aware uxmobile-ui
#33lotusFlag/lotusParam URL cache is captured once at module load, not re-read tech-debtmobile-ui
#34 CI workflow has no concurrency guard, unlike the cinny workflow it's modeled on performancebuild-publish
Fix-first recommendation
#1 — reimplement deafen as EC's global output mute (setAudioEnabled$) instead of per-participant setVolume; this single change also fixes the first-joiner race and the known soundboard-audio divergence.
#2 — one-line fix on the denoise fallback path (processedTrack = undefined), prevents a permanently dead mic.
#5 + #6 — wire pnpm lint and pnpm test:unit into CI and add the first tests under src/lotus/, so everything else here has regression protection.
#3 + #16 — make focus_participant force spotlight mode and clear the pin on leave/hangup (pair with cinny#56 for the unpin UI).
#15 — strictOriginCheck = true on the widget transport; one line, closes the inbound-action origin gap.
#23 — schedule the upstream rebase before the conflict surface grows further.
Checked and found sound
The 98fbdbd5 inject double-publish fix under every interleaving; denoise survival across LiveKit reconnects on the success path; safeMediaUrl/safeAssetBase scheme and origin checks; all lotus module teardowns; every toWidget handler replies before gating; decoration roster capping and ref-counting; upstream capability negotiation unchanged; pnpm audit --prod clean; no Lotus credentials in Dockerfile/config; knip/tsc/vitest globs include src/lotus/*.
Status 2026-09-13
All 34 issues closed. Shipped as 0.25.0-lotus.1 (upstream v0.25.0 merge + first fix round) and 0.25.0-lotus.2 (remaining 24), both CI-published; cinny pinned to 0.25.0-lotus.2 at ba0cc3e4. Host-side halves (request_state, focus by device id, soundboard muted reason) landed in cinny f50f50be.
# Element Call fork audit 2026-09 — tracking index
Four parallel code-reading passes over `lotus` @ e36aef8a (audio pipeline, widget action bus & security, in-call UI/mobile, build/publish/upstream-sync), scoped to `git diff v0.20.1..lotus`. Every finding was verified against the code before filing; the P6-2 items already open in cinny's `LOTUS_TODO.md` were not refiled. Host-side halves of cross-cutting problems are filed in LotusGuild/cinny and cross-referenced from the relevant issue here.
**Labelling:** one `priority: *`, one type label, one `area: *` per issue; milestones **High** (due 2026-10-15) and **Medium & Low** (due 2026-11-30).
**Totals:** 34 issues — 6 high, 17 medium, 11 low.
**By area:** denoise 8, widget-actions 8, decorations 4, build-publish 4, mobile-ui 3, livekit 2, soundboard-inject 2, deafen 1, upstream-sync 1, audio-pipeline 1.
## High
- [ ] #1 Deafen via per-participant setVolume is undone by EC volume controls and clobbers tile volumes on undeafen `bug` `deafen`
- [ ] #2 Denoise restart fallback hands LiveKit the raw mic as `processedTrack`, which later gets stopped `bug` `denoise`
- [ ] #3 `focus_participant` is a silent no-op in EC's default grid layout `bug` `widget-actions`
- [ ] #4 In-call avatar decoration is sized 1:1 with the avatar (no inset like the host) and has no onError fallback `bug` `decorations`
- [ ] #5 Fork CI never runs the test/lint/typecheck suite before publish `bug` `build-publish`
- [ ] #6 Nothing in CI or the test suite exercises `src/lotus/*` at all `tech-debt` `build-publish`
## Medium
- [ ] #7 Denoise init blocks LiveKit's track-change lock, freezing mute/unmute for seconds `ux` `denoise`
- [ ] #8 Model load failure silently publishes raw audio while the host UI says denoise is on `bug` `denoise`
- [ ] #9 Denoise graph keeps running (full ML inference) while the mic is muted `performance` `denoise`
- [ ] #10 Racing `LocalTrackPublished` events start two denoise processors (two AudioContexts, two wasm inits) `performance` `denoise`
- [ ] #11 Sending `null` does not clear a quality cap, contrary to the host contract `bug` `livekit`
- [ ] #12 Screenshare bitrate cap is applied per simulcast layer, so the aggregate can be N× the cap `bug` `livekit`
- [ ] #13 Soundboard clips are published to peers even while the local mic is muted `ux` `soundboard-inject`
- [ ] #14 Each soundboard clip creates a new AudioContext, against a per-document limit of ~6 `performance` `soundboard-inject`
- [ ] #15 Incoming `io.lotus.*` toWidget actions are accepted from any origin `security` `widget-actions`
- [ ] #16 Spotlight pin can never be cleared once set `ux` `widget-actions`
- [ ] #17 Decoration roster and spotlight pin are lost on an EC handler remount and never re-pushed `bug` `decorations`
- [ ] #18 Torn-down lotus actions pile up in the LazyEventEmitter backlog and replay stale `bug` `widget-actions`
- [ ] #19 In-call avatar decoration (animated APNG) ignores prefers-reduced-motion, unlike the host renderer `a11y` `decorations`
- [ ] #20 `call_state` can emit up to 8 messages/second; dedupe runs after the throttle `performance` `widget-actions`
- [ ] #21 `lotusTransparent`/`lotusTheme` provide no contrast guard for an arbitrary host background `ux` `mobile-ui`
- [ ] #22 Published npm version consumed by cinny has no corresponding git tag `bug` `build-publish`
- [ ] #23 Upstream fork is 6 minor releases behind with no cherry-picks; hotspot files have heavy upstream churn `tech-debt` `upstream-sync`
## Low
- [ ] #24 Denoise worklet node ignores its `channelCount: 1` (no `channelCountMode: "explicit"`) `bug` `denoise`
- [ ] #25 Dry/wet floor sums an undelayed dry copy with the delayed denoised path (comb filtering) `bug` `denoise`
- [ ] #26 Denoise external-module contracts are untyped `any` and break silently on an asset bump `tech-debt` `denoise`
- [ ] #27 Audio-output sink is never restored when the selected device becomes undefined again `bug` `audio-pipeline`
- [ ] #28 Decoration URLs derive from an unvalidated arbitrary profile field `security` `decorations`
- [ ] #29 Pinning during a screenshare removes the PiP even when the pin is a third party `ux` `widget-actions`
- [ ] #30 Manual pin resolves by userId only, so multi-device users pin an arbitrary device `bug` `widget-actions`
- [ ] #31 `lotusCallState=1` in standalone (non-widget) mode builds the whole pipeline for nothing `performance` `widget-actions`
- [ ] #32 Landscape filmstrip rail-shrink query is height-only, not orientation- or device-aware `ux` `mobile-ui`
- [ ] #33 `lotusFlag`/`lotusParam` URL cache is captured once at module load, not re-read `tech-debt` `mobile-ui`
- [ ] #34 CI workflow has no `concurrency` guard, unlike the cinny workflow it's modeled on `performance` `build-publish`
## Fix-first recommendation
1. #1 — reimplement deafen as EC's global output mute (`setAudioEnabled$`) instead of per-participant `setVolume`; this single change also fixes the first-joiner race and the known soundboard-audio divergence.
2. #2 — one-line fix on the denoise fallback path (`processedTrack = undefined`), prevents a permanently dead mic.
3. #5 + #6 — wire `pnpm lint` and `pnpm test:unit` into CI and add the first tests under `src/lotus/`, so everything else here has regression protection.
4. #3 + #16 — make `focus_participant` force spotlight mode and clear the pin on leave/hangup (pair with cinny#56 for the unpin UI).
5. #15 — `strictOriginCheck = true` on the widget transport; one line, closes the inbound-action origin gap.
6. #23 — schedule the upstream rebase before the conflict surface grows further.
## Checked and found sound
The `98fbdbd5` inject double-publish fix under every interleaving; denoise survival across LiveKit reconnects on the success path; `safeMediaUrl`/`safeAssetBase` scheme and origin checks; all lotus module teardowns; every toWidget handler replies before gating; decoration roster capping and ref-counting; upstream capability negotiation unchanged; `pnpm audit --prod` clean; no Lotus credentials in Dockerfile/config; knip/tsc/vitest globs include `src/lotus/*`.
## Status 2026-09-13
All 34 issues closed. Shipped as `0.25.0-lotus.1` (upstream v0.25.0 merge + first fix round) and `0.25.0-lotus.2` (remaining 24), both CI-published; cinny pinned to `0.25.0-lotus.2` at ba0cc3e4. Host-side halves (request_state, focus by device id, soundboard muted reason) landed in cinny f50f50be.
jared
self-assigned this 2026-09-12 02:13:45 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Element Call fork audit 2026-09 — tracking index
Four parallel code-reading passes over
lotus@e36aef8a(audio pipeline, widget action bus & security, in-call UI/mobile, build/publish/upstream-sync), scoped togit diff v0.20.1..lotus. Every finding was verified against the code before filing; the P6-2 items already open in cinny'sLOTUS_TODO.mdwere not refiled. Host-side halves of cross-cutting problems are filed in LotusGuild/cinny and cross-referenced from the relevant issue here.Labelling: one
priority: *, one type label, onearea: *per issue; milestones High (due 2026-10-15) and Medium & Low (due 2026-11-30).Totals: 34 issues — 6 high, 17 medium, 11 low.
By area: denoise 8, widget-actions 8, decorations 4, build-publish 4, mobile-ui 3, livekit 2, soundboard-inject 2, deafen 1, upstream-sync 1, audio-pipeline 1.
High
bugdeafenprocessedTrack, which later gets stoppedbugdenoisefocus_participantis a silent no-op in EC's default grid layoutbugwidget-actionsbugdecorationsbugbuild-publishsrc/lotus/*at alltech-debtbuild-publishMedium
uxdenoisebugdenoiseperformancedenoiseLocalTrackPublishedevents start two denoise processors (two AudioContexts, two wasm inits)performancedenoisenulldoes not clear a quality cap, contrary to the host contractbuglivekitbuglivekituxsoundboard-injectperformancesoundboard-injectio.lotus.*toWidget actions are accepted from any originsecuritywidget-actionsuxwidget-actionsbugdecorationsbugwidget-actionsa11ydecorationscall_statecan emit up to 8 messages/second; dedupe runs after the throttleperformancewidget-actionslotusTransparent/lotusThemeprovide no contrast guard for an arbitrary host backgrounduxmobile-uibugbuild-publishtech-debtupstream-syncLow
channelCount: 1(nochannelCountMode: "explicit")bugdenoisebugdenoiseanyand break silently on an asset bumptech-debtdenoisebugaudio-pipelinesecuritydecorationsuxwidget-actionsbugwidget-actionslotusCallState=1in standalone (non-widget) mode builds the whole pipeline for nothingperformancewidget-actionsuxmobile-uilotusFlag/lotusParamURL cache is captured once at module load, not re-readtech-debtmobile-uiconcurrencyguard, unlike the cinny workflow it's modeled onperformancebuild-publishFix-first recommendation
setAudioEnabled$) instead of per-participantsetVolume; this single change also fixes the first-joiner race and the known soundboard-audio divergence.processedTrack = undefined), prevents a permanently dead mic.pnpm lintandpnpm test:unitinto CI and add the first tests undersrc/lotus/, so everything else here has regression protection.focus_participantforce spotlight mode and clear the pin on leave/hangup (pair with cinny#56 for the unpin UI).strictOriginCheck = trueon the widget transport; one line, closes the inbound-action origin gap.Checked and found sound
The
98fbdbd5inject double-publish fix under every interleaving; denoise survival across LiveKit reconnects on the success path;safeMediaUrl/safeAssetBasescheme and origin checks; all lotus module teardowns; every toWidget handler replies before gating; decoration roster capping and ref-counting; upstream capability negotiation unchanged;pnpm audit --prodclean; no Lotus credentials in Dockerfile/config; knip/tsc/vitest globs includesrc/lotus/*.Status 2026-09-13
All 34 issues closed. Shipped as
0.25.0-lotus.1(upstream v0.25.0 merge + first fix round) and0.25.0-lotus.2(remaining 24), both CI-published; cinny pinned to0.25.0-lotus.2at ba0cc3e4. Host-side halves (request_state, focus by device id, soundboard muted reason) landed in cinny f50f50be.