# fix\(server\): agent recordings honor the browser frame rate setting · PR \#23895 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23895)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: fix/issue-19442

Target branch: main

Head commit: 6e20dc3b8f00161c0e4cf163f39b5fd52cfa59ca

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/23895)

[Commits](https://git.cafe/pingdotgg/t3code/pull/23895/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/23895/diffs?format=markdown)

## Description

---

## Problem

Selecting 60 fps in Browser recording settings still encoded agent preview recordings with a fixed 30 fps capture limit. Reproduced on Linux desktop with the reporter’s animation-frame counter and actual agent recording start/stop calls.

## Change

Send the existing recording-rate setting with the existing browser-profile report. The server uses the latest explicitly reported rate for each new recording’s capture stream and bitrate, retaining the default of 30 until a client reports. Older reports can omit the field. Existing settings exports remain compatible.

## Scope and approval

Fixes #19442

Triaged bug via the automated issue flow. Existing Browser recording configuration now applies to the server encoder used by agents. No new RPC, tool input, permission, persisted setting or recording option.

## Verification

- Focused server/browser tests: **43 passed**; contracts preview/settings tests: **27 passed** after merging main.
- Contracts, server, web and desktop typechecks passed; changed-file lint/format passed; final desktop build passed.
- Two independent reviewers returned MERGE on the complete final diff.
- [Archived replay fixture](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/41742f725ca312b1/issue-19442-replay-fixture.tar.gz): manual reproduction pages and recording-analysis script, preserved outside the source tree.

Actual Electron recordings, Linux/KVM, Xvfb, GPU disabled, one light theme:

| Content viewport | Before / control | After at 60 | Observed result |
|---|---|---|---|
| 1280×800 | [Before recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/73851235a4dbe4fa/before.mp4), built from [11adcb0e25](https://git.cafe/pingdotgg/t3code/commit/11adcb0e25) | [After recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e854f5906b41d5ec/after60.mp4), built from [07d7b3c836](https://git.cafe/pingdotgg/t3code/commit/07d7b3c836) | 20.79 → 29.62 fps average; busiest one-second window 28 → 37 frames |
| 800×450 | [30 fps control](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/dac6c21d2d92a80b/after30-small.mp4), after build | [60 fps recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/1e1eb41f4647936c/after60-small.mp4), same after build | 29.14 → 45.54 fps average; median frame interval 32.22 → 19.77 ms |

Steps: open the counter fixture, start recording, wait four seconds, stop, and inspect decoded timestamps with ffprobe. Source animation frames stayed approximately 60 Hz. The setting is a maximum; these runs do not demonstrate sustained 60 fps. Later commits preserve the captured encoder source and add compatibility exports, tests and replay files.

Spatial samples show different counter values and codec noise; the small 60 fps control also includes the representative click’s blue pointer. These are capture-content differences, not additional UI changes. Full-frame diffs use absolute RGB ×8 at zero threshold and strict max-channel delta >16/255; they inspect spatial content and do not prove cadence.

| 1280×800, light | Before | After | Diff0 | DiffT16 |
|---|---|---|---|---|
| Complete frames at ≥1 s | ![Before](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/bceef3633d06b971/before-after-a-t1.png)<br>[11adcb0e25](https://git.cafe/pingdotgg/t3code/commit/11adcb0e25) | ![After](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/20dbea587861d8a7/before-after-b-t1.png)<br>[07d7b3c836](https://git.cafe/pingdotgg/t3code/commit/07d7b3c836) | ![Diff0 amplified](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/4f7a794217015db2/before-after-diff0amplified.png) | ![DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/807602be883d8298/before-after-diffT16.png) |

| 800×450, light, both after implementation | 30 fps control | 60 fps control | Diff0 | DiffT16 |
|---|---|---|---|---|
| Complete frames at ≥1 s | ![30 fps control](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/03574af397068348/30-vs-60-control-a-t1.png)<br>[07d7b3c836](https://git.cafe/pingdotgg/t3code/commit/07d7b3c836) | ![60 fps control](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/bc36bcf068111c7e/30-vs-60-control-b-t1.png)<br>[07d7b3c836](https://git.cafe/pingdotgg/t3code/commit/07d7b3c836) | ![Control Diff0 amplified](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/f497eab7f795483d/30-vs-60-control-diff0amplified.png) | ![Control DiffT16](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a8c5631c31a65d8e/30-vs-60-control-diffT16.png) |

[800×450 control frames and both diffs, pixel measurements and capture environment](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/3fa540f5b839c990/supplementary-index.md).

[Full evidence index, lifecycle recordings, measurements and limitations](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/b9fb0e0d5b06aa94/evidence-index.md). Actual setting reset/selection, duplicate start, changing settings during recording, subsequent recording and immediate idle export were exercised.

Independent audit of the rebuilt desktop repeated the 800×450 recording at **44.55 fps**, peak **53 frames/second**, with source RAF at 60 Hz: [actual recording](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/701942a8ea8d518e/audit-rebuild60-small.mp4), [probe](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/0f10a1ba8e694a7e/audit-rebuild-probe.json), [runtime responses](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/e4856c7fc68ca944/audit-rebuild-transcript.md). The audited app was built at [877c7da3d5](https://git.cafe/pingdotgg/t3code/commit/877c7da3d5); [3bb66f26c9](https://git.cafe/pingdotgg/t3code/commit/3bb66f26c9) only changes the replay probe.

Additional [live web and lifecycle evidence](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/29c9bec8cb901dd3/CE-live-evidence.md) at [3bb66f26c9](https://git.cafe/pingdotgg/t3code/commit/3bb66f26c9) confirms the normal web reporter sends 60, a server-owned page exports, the encoder retains 60 after client disconnect, and a restarted server selects 30 with no client connected. All three recordings exported; their delivered cadence is routing/lifecycle evidence, not a sustained-60 claim.

Not exercised live: permission/hydration transitions, multi-client/multi-environment delivery, or the reporter’s Wayland compositor. Existing guards and destination routing are unchanged; focused tests cover default, explicit rates, omitted-field retention and updates. The most recent explicit client report controls the environment’s rate for subsequent recordings.

Implemented and verified with GPT-6.1 Sol and Claude Opus 5.5 through T3 Code’s Codex and Claude Code harnesses.

