docs: add tickets dir + first two open defects

zebra-report didn't have a defect tracker before; until now everything
lived in commit-message archaeology and signal-server log greps. Two
known defects make this the right moment to start one:

  0001: fedora chrome cannot hear speakers (P0, in-progress)
    P0 because every invariant (RTP arriving, worklet started,
    ctxState=running) is green but the user hears nothing — silent
    regression is trust-eroding. Primary hypothesis is that
    AudioContext.destination always routed to system-default while
    the speaker-output picker only setSinkId'd <audio> elements.
    Fix landed in 467146a (applySinkToAudioCtx), awaiting fox retest
    in production.

  0002: fedora firefox cannot share camera (High, open)
    Camera publish fails on firefox; chrome same machine works.
    No telemetry yet; ticket enumerates the candidates to narrow
    on the next attempt.

README.md spells out the format (status / priority / surface / first
seen, then symptom / telemetry / hypothesis / fix / status notes)
and the resolution loop (telemetry → failing test → fix → stamp →
deploy → update ticket).
This commit is contained in:
Russell Ballestrini 2026-06-07 11:24:17 -04:00
parent 467146a14a
commit e321a7cee1
No known key found for this signature in database
3 changed files with 246 additions and 0 deletions

View file

@ -0,0 +1,104 @@
# 0001: fedora chrome cannot hear speakers
**Status:** in-progress
**Priority:** P0 (silent regression — every invariant green except audible output)
**Surface:** fedora chrome — observed under both listener and speaker roles
**First seen:** 2026-06-04 (or earlier — "for days")
**Reporter:** fox (blanka-chrome identity)
## Symptom
On fedora chrome, no audio from any speaker in the room is audible. Affects
every role chrome takes: as listener, no mic audio plays. As speaker, no
peer mic audio plays through the mesh+SFU path. Same chrome can publish
mic + camera + screen fine — only the receive-side audio is silent.
Other browsers on the same machine (firefox) hear the room correctly once
the same code is loaded — so the codebase is not uniformly broken.
## Telemetry / Repro
Signal-server log (`/var/log/zebra-spaces-signal.log`,
`CLIENT_LOG actor_handle="blanka-chrome"`) on 2026-06-06 ~23:24 UTC:
```
· role=speaker sub=connected/connected pub=connected/connected
mesh=2 sListen=0 streamMode=0 muted=1 xcr=off
aud.recv pkt=308 lost=0 bytes=197428 jitter=0.0080 level=0.000 jbuf=? lp=0.2s
aud.recv pkt=22075 lost=3 bytes=44147 jitter=0.3210 level=0.000 jbuf=? lp=0.9s
aud.recv pkt=44242 lost=4 bytes=751664 jitter=0.0260 level=0.000 jbuf=? lp=0.3s
```
- RTP arriving at ~50 pps on the active speaker (real Opus voice traffic)
- `level=0.000` on EVERY receiver
- `mesh=2` (both mesh peers connected)
- `sub=connected` and `pub=connected` (both SFU PCs healthy)
Earlier in the session:
```
audio attach uuid=2d4a target=0.5s (current pool=3)
audio via AudioContext 2d4a target=0.5s ctxState=running
jitter-buffer installed uuid=2d4a target=0.5s
jitter-buffer started uuid=2d4a target=0.5s
mesh stream swapped into worklet for 2d4a — buffer cushion now applies to mesh too
```
Every attach invariant green. Worklet `started` event fired. ctxState=running.
Yet the user hears nothing.
User report: chrome tab indicator shows NO audio playing.
## Hypothesis
**Primary (deployed 2026-06-06, awaiting retest):** the speaker-output
dropdown in the page calls `setSinkId` on `<audio>` elements only. The
worklet path (every listener AND every speaker monitor since c3ff58c routed
mesh through the worklet) runs through `audioCtx.destination`, which always
emits to the **system default** sink. If the user picks a non-default sink
in the dropdown, the worklet keeps emitting to the wrong device — silence
on the device they're monitoring.
Fox 2026-06-06 confirmed picking a non-default sink on fedora chrome. Firefox
defaults to system default which happens to match the right speaker for him,
which is why firefox music started working without needing this fix.
**Secondary (not yet ruled out):**
- AudioContext.setSinkId() may not be honored on this chrome version /
flag set → silent fallthrough to default
- PulseAudio per-app default routing for chrome may override the picked
sink at the OS layer
- The worklet's `started=true` may flip back to `false` on a network blip
and never re-arm if the queue can't refill to target (rearm guard fires)
## Fix
**Landed:** `467146a` 2026-06-06 — `applySinkToAudioCtx()` calls
`audioCtx.setSinkId(speakerDeviceId)` on creation and in the dropdown's
fan-out. Diagnostic log line now includes `sink=...` so the next
telemetry session shows which sink the worklet is routed to.
**Deployed:** `0f36e02` to www.unturf.com — blanka-chrome needs page reload
(it's a code-shell change, not SDP-level).
**Pinned by tests:**
- `audioCtx.setSinkId is called on creation so the worklet routes to the
user-picked sink (not just system default)`
- `audioCtx without setSinkId support (older browser) does not throw —
silent fallback to default`
## Status notes
- **2026-06-07 (open):** awaiting fox retest after reload. Next telemetry
should show `sink=<device-id>` (not `default`) on the `audio via
AudioContext` line. If chrome still silent with `sink=<correct-id>`,
hypothesis shifts to PulseAudio per-app routing or chrome's WebAudio
sink negotiation.
- **2026-06-06:** the Jun 5 cascade + flushSfuStreams prefix-match fix
(`0ce1339`) helped firefox but not chrome. Telemetry above ruled out
the FSM / attach defects.
- **2026-06-04 → 2026-06-05:** earlier cascade commits (2e74b92,
f479878, 144dd15, 61b21a6) tightened the audio attach path but did
not address this defect (they targeted double-audio on
fxhp-android-firefox).

View file

@ -0,0 +1,71 @@
# 0002: fedora firefox cannot share camera
**Status:** open
**Priority:** High (feature unavailable — speakers expect to share camera)
**Surface:** fedora firefox — speaker or host attempting to publish camera
**First seen:** 2026-06-06
**Reporter:** fox
## Symptom
On fedora firefox, the "share camera" button does not result in a camera
publish. Same firefox can share screen and publish mic — only camera
publish fails. Same machine + chrome publishes camera fine.
## Telemetry / Repro
Not yet captured.
Repro candidates to try:
1. Open fedora firefox → enter a room as speaker
2. Click "share camera"
3. Note whether the browser permission prompt appears
4. If granted, note whether the camera tile renders locally
5. Grab the in-page signal log + browser console output
Things to specifically look for:
- `getUserMedia` errors (`NotFoundError`, `NotReadableError`,
`OverconstrainedError`, `NotAllowedError`)
- Whether `navigator.mediaDevices.enumerateDevices()` lists the camera
pre-permission (label is empty, deviceId present)
- Whether the camera deviceId saved in `localStorage` (`CAM_DEV_KEY`)
matches a device firefox still recognizes (deviceIds rotate across
sessions on chrome but should be stable on firefox)
## Hypothesis
Candidates, none yet confirmed:
1. **Saved cameraDeviceId pointing at a stale / removed device.** Firefox
would `OverconstrainedError` and the page's catch logs `err`. Fix
would be: on `OverconstrainedError`, retry with `deviceId` unset and
re-prompt the picker — same shape as the mic-label-fallback at
line ~5041.
2. **Firefox v4l2 backend conflicting with chrome's hold on the camera.**
If chrome was using the camera, firefox can't open it (single-consumer
on most fedora kernels). User-visible workaround: close chrome's
camera-using tab.
3. **Firefox MediaTrackConstraints quirk** — possibly the page's
`videoConstraints` (line ~4493) demanding a resolution / framerate
the camera doesn't support → `OverconstrainedError`. Fix: relax the
constraints to `{ ideal }` for non-essential fields.
4. **Permission state stuck.** Firefox's permission prompt is gated per
origin + device; a previous "block" decision can persist.
`about:permissions` would show this. Not a code issue if so.
5. **PipeWire / v4l2loopback fakery on the system** — if the camera
device is actually a virtual capture (OBS Virtual Cam etc), firefox
may reject it where chrome accepts.
## Fix
(none yet — needs telemetry)
## Status notes
- **2026-06-07 (open):** ticket created. Next step is to capture the
firefox console / signal log when the share-camera button is clicked
and see which of the hypotheses fits.

71
docs/tickets/README.md Normal file
View file

@ -0,0 +1,71 @@
# Tickets
Defect tracker for zebra-report. Anything that takes more than 15 minutes
or needs to be reproduced across browsers, devices, or sessions lives here
as a numbered markdown file.
## File format
Each ticket is `docs/tickets/NNNN-slug.md` where NNNN is monotonically
increasing. New tickets get the next number.
```markdown
# NNNN: Short title
**Status:** open | in-progress | blocked | fixed | won't-fix
**Priority:** P0 (silent regression / data loss) | High | Medium | Low
**Surface:** browser / OS / role (e.g. "fedora chrome listener")
**First seen:** YYYY-MM-DD (reporter's local date)
**Reporter:** handle
## Symptom
What the user sees. Plain language, no implementation details.
## Telemetry / Repro
What the signal log / browser console / pcap shows. Concrete lines if
available. How to reproduce.
## Hypothesis
What we think is going on. List candidates if not yet narrowed.
## Fix
What was tried, what landed, what commit. Empty until something ships.
## Status notes
Running log of attempts, observations, retests. Newest at the top.
```
## Priority
- **P0** — silent regression: the app appears to work but doesn't (no error
surfaced, audio routes nowhere, video goes black). Trust-eroding because
the user can't tell what's wrong.
- **High** — feature unavailable (can't share camera, can't enter room).
Visible error or empty state.
- **Medium** — degraded UX with workaround (slight latency, wrong tile order).
- **Low** — cosmetic, log noise, tooling.
## How tickets are resolved
1. Reproduce or grab telemetry from the signal-server log
(`/var/log/zebra-spaces-signal.log` — grep `CLIENT_LOG` + the
reporter's handle).
2. Write a failing test in `test/` that pins the contract the defect
violates. The test failing IS the diagnosis.
3. Fix the code so the test passes. Commit with the test name in the
message body.
4. `make stamp`, push both `zebra-report` and the deploy repo
(`www.unturf.com/zebra-report/`).
5. Update the ticket Status to `fixed`, link the commit, note what to
re-test in production.
## Open
| # | Title | Priority | Surface | Status |
|---|-------|----------|---------|--------|
| [0001](0001-fedora-chrome-cannot-hear-speakers.md) | fedora chrome cannot hear speakers | P0 | fedora chrome (any role) | in-progress |
| [0002](0002-fedora-firefox-cannot-share-camera.md) | fedora firefox cannot share camera | High | fedora firefox (speaker/host) | open |
## Fixed
(none yet)