Skip to content

chore: trace device permission state transitions#2168

Merged
oliverlaz merged 2 commits intomainfrom
permission-propmts-tracing
Mar 20, 2026
Merged

chore: trace device permission state transitions#2168
oliverlaz merged 2 commits intomainfrom
permission-propmts-tracing

Conversation

@oliverlaz
Copy link
Member

@oliverlaz oliverlaz commented Mar 20, 2026

💡 Overview

Enhances our tracing with information about device permission state transitions.
This should help us pinpoint device permission-related issues faster.

Summary by CodeRabbit

  • Refactor
    • Enhanced permission-state tracing for camera and microphone flows, capturing previous and new states for better diagnostics.
    • Device permission and stream flows now carry diagnostic context through prompts and retries, improving logging and observability on errors.
    • Device list handling now recalculates when call context changes to keep permission observables in sync.

@changeset-bot
Copy link

changeset-bot bot commented Mar 20, 2026

⚠️ No Changeset found

Latest commit: 177f8fd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: faebf2bf-7096-43dc-a842-b628e0c28098

📥 Commits

Reviewing files that changed from the base of the PR and between 4fe2d32 and 177f8fd.

📒 Files selected for processing (1)
  • packages/client/src/devices/devices.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/client/src/devices/devices.ts

📝 Walkthrough

Walkthrough

Threads an optional Tracer through browser permission acquisition and device manager state, emitting trace events on permission state transitions for camera and microphone flows.

Changes

Cohort / File(s) Summary
Browser permission core
packages/client/src/devices/BrowserPermission.ts
Added tracer: Tracer | undefined to BrowserPermissionConfig. setState now emits an optional trace event keyed navigator.mediaDevices.${queryName}.permission with { previous, state } before updating internal state and notifying listeners.
Device manager state & constructors
packages/client/src/devices/CameraManagerState.ts, packages/client/src/devices/MicrophoneManagerState.ts
Constructors now accept tracer: Tracer | undefined and pass it into getVideoBrowserPermission(tracer) / getAudioBrowserPermission(tracer). Constructor signatures were updated accordingly.
Device managers
packages/client/src/devices/CameraManager.ts, packages/client/src/devices/MicrophoneManager.ts
Managers now pass call.tracer into their respective State constructors (e.g., new CameraManagerState(call.tracer)).
Device utilities & streams
packages/client/src/devices/devices.ts
getAudioBrowserPermission and getVideoBrowserPermission lazy factories now accept optional tracer. Permission creation, device enumeration, and stream prompting propagate the tracer. Error paths use a devices logger; getVideoStream retry preserves tracer.
Sample app hooks
sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx
useVideoDevices / useAudioDevices now read call via useCall() and pass call?.tracer into permission creation. Permission observable memoization depends on [call] to refresh when call changes.

Sequence Diagram

sequenceDiagram
    participant React as DevicesDash Hook
    participant Manager as Device Manager
    participant State as ManagerState
    participant Devices as devices.ts Factory
    participant BrowserPerm as BrowserPermission
    participant Tracer as Tracer

    React->>Manager: init (pass call.tracer)
    Manager->>State: new ManagerState(tracer)
    State->>Devices: get*BrowserPermission(tracer)
    Devices->>BrowserPerm: create BrowserPermission({ tracer })
    BrowserPerm->>Tracer: hold tracer reference

    Note over BrowserPerm,Tracer: On permission change
    BrowserPerm->>Tracer: emit("navigator.mediaDevices.<query>.permission", { previous, state })
    Tracer->>Tracer: record trace event
    BrowserPerm->>State: notify listeners of state change
    State->>Manager: update state
    Manager->>React: expose updated device/permission state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hop with a tracer tucked under my paw,

Emitting each change that the browsers saw.
From cam to mic, every state I trace,
Hopping through code with jubilant grace.
Observatory carrots mark every place.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides a brief overview of the changes but is missing the Implementation notes and Ticket sections required by the template. Add Implementation notes section explaining the technical approach and include a Ticket reference link to the Linear issue.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding tracing for device permission state transitions across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch permission-propmts-tracing
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use TruffleHog to scan for secrets in your code with verification capabilities.

Add a TruffleHog config file (e.g. trufflehog-config.yml, trufflehog.yml) to your project to customize detectors and scanning behavior. The tool runs only when a config file is present.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx (2)

99-111: Inconsistent tracer usage in device enumeration.

The devices$ observable is created without passing the tracer (getVideoDevices() on line 101), while permission$ correctly receives call?.tracer. This means device enumeration won't benefit from the tracer while permission state transitions will.

For consistency, consider passing the tracer to getVideoDevices as well:

♻️ Suggested fix
 function useVideoDevices() {
   const call = useCall();
-  const devices$ = useMemo(() => getVideoDevices(), []);
+  const devices$ = useMemo(() => getVideoDevices(call?.tracer), [call]);
   const permission$ = useMemo(
     () => getVideoBrowserPermission(call?.tracer).asObservable(),
     [call],
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx` around
lines 99 - 111, In useVideoDevices(), getVideoDevices() is called without the
tracer while getVideoBrowserPermission uses call?.tracer, causing inconsistent
tracing; update the devices$ creation to pass the tracer (use
getVideoDevices(call?.tracer) or equivalent) so both observables use the same
tracer source (references: function useVideoDevices, variable devices$, and
function getVideoDevices).

113-125: Same inconsistency for audio devices.

Similarly, getAudioDevices() on line 115 should receive the tracer for consistent tracing coverage.

♻️ Suggested fix
 function useAudioDevices() {
   const call = useCall();
-  const devices$ = useMemo(() => getAudioDevices(), []);
+  const devices$ = useMemo(() => getAudioDevices(call?.tracer), [call]);
   const permission$ = useMemo(
     () => getAudioBrowserPermission(call?.tracer).asObservable(),
     [call],
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx` around
lines 113 - 125, The useAudioDevices hook calls getAudioDevices() without
passing the tracer, causing inconsistent tracing; update useAudioDevices (the
devices$ memo) to call getAudioDevices with the current tracer (e.g.,
getAudioDevices(call?.tracer)) so it mirrors the permission$ call that uses
getAudioBrowserPermission(call?.tracer), keeping tracing consistent across audio
device and permission observables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client/src/devices/devices.ts`:
- Line 319: The recursive call in getVideoStream is missing the tracer
parameter, so retries aren't traced; update the recursive invocation
getVideoStream(relaxedConstraints) to pass the same tracer used by the current
call (mirror how getAudioStream forwards tracer) so the retry path receives and
propagates the tracer context.

---

Nitpick comments:
In `@sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx`:
- Around line 99-111: In useVideoDevices(), getVideoDevices() is called without
the tracer while getVideoBrowserPermission uses call?.tracer, causing
inconsistent tracing; update the devices$ creation to pass the tracer (use
getVideoDevices(call?.tracer) or equivalent) so both observables use the same
tracer source (references: function useVideoDevices, variable devices$, and
function getVideoDevices).
- Around line 113-125: The useAudioDevices hook calls getAudioDevices() without
passing the tracer, causing inconsistent tracing; update useAudioDevices (the
devices$ memo) to call getAudioDevices with the current tracer (e.g.,
getAudioDevices(call?.tracer)) so it mirrors the permission$ call that uses
getAudioBrowserPermission(call?.tracer), keeping tracing consistent across audio
device and permission observables.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18fa15ee-57cc-4475-9799-797a8287bbb3

📥 Commits

Reviewing files that changed from the base of the PR and between 4a95b9c and 4fe2d32.

📒 Files selected for processing (7)
  • packages/client/src/devices/BrowserPermission.ts
  • packages/client/src/devices/CameraManager.ts
  • packages/client/src/devices/CameraManagerState.ts
  • packages/client/src/devices/MicrophoneManager.ts
  • packages/client/src/devices/MicrophoneManagerState.ts
  • packages/client/src/devices/devices.ts
  • sample-apps/react/react-dogfood/components/Inspector/DevicesDash.tsx

@oliverlaz oliverlaz merged commit e4203a3 into main Mar 20, 2026
20 checks passed
@oliverlaz oliverlaz deleted the permission-propmts-tracing branch March 20, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants