Skip to content

perf(mobile): pause elapsed-time timers on hidden thread screens - #15397

Merged
juliusmarminge merged 1 commit into
t3code/mobile-perf/compact-thread-snapshotsfrom
t3code/mobile-perf/pause-hidden-thread-timers
Oct 8, 2026
Merged

juliusmarminge merged 1 commit into
t3code/mobile-perf/compact-thread-snapshotsfrom
t3code/mobile-perf/pause-hidden-thread-timers

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Problem

Mobile keeps thread routes mounted after you navigate away. The working-duration label (WorkingTimer) and the live subagent status (ProviderSubagentBar) each ran an ungated 1 s setInterval. It kept firing while the screen was unfocused, while the app was backgrounded or inactive, and re-rendered the hidden screen 60 times a minute.

Fix

A new hook, useVisibleSecondClock(enabled), ticks once a second only while enabled && focused && AppState === "active". When ticking resumes it re-reads Date.now() in a layout effect, so the label shows the true elapsed time on the first frame after you return. Both labels use the hook. Nothing else changes: the visible tick rate and interval phase stay the same.

The hook also re-reads AppState.currentState after its listener is attached. This closes a small race where a change between the first render and the passive subscription would otherwise be missed. A focused regression test covers it, and that test fails without the re-read.

jsdom is now declared as an apps/mobile devDependency, matching apps/web's ^30.0.1. The new test file uses it. Before this, it resolved only through the workspace's Vite+ peer. The lockfile change is three lines on the mobile importer and pulls in no new packages.

Before / after

Measured with an instrumented Vitest test that uses fake timers in jsdom. Navigation focus and AppState are mocked, and each scenario runs for 60 simulated seconds. "Before" is the parent commit's timer code, copied verbatim.

Timer Scenario Before After
WorkingTimer, ProviderSubagentBar (live) visible (focused, active) 60 commits/min 60 commits/min
both unfocused retained route 60 commits/min 0
both focused, app background 60 commits/min 0
both focused, app inactive 60 commits/min 0
WorkingTimer 600 s background → active 600 commits while hidden 0, label shows +600 s on first active render

Evidence (sanitized):

Screenshots don't apply: the visible label looks the same before and after. The change is in how often it ticks while hidden.

Limits

  • These numbers count fake-clock JS interval callbacks and React commits. They are not OS wakeups, CPU time, or battery measurements.
  • No device or simulator proof yet, which is why this PR is a draft. Still needed: a profile on a real iOS/Android device (for example Instruments Energy Log or Android Battery Historian/Perfetto) showing JS-thread wakeups while a working thread sits backgrounded or on an unfocused retained route, before vs after.
  • This covers only these two labels. It is not a full mobile battery audit.

Verification

  • vp test run apps/mobile/src/features/threads/use-visible-second-clock.test.tsx: 9/9 pass, including the new AppState race test, which fails without the fix.
  • Measurement harness: 18/18 scenarios pass.
  • apps/mobile typecheck passes. Targeted lint shows 0 errors (the warnings are already on base, in untouched code). Format passes.

Stack: #15096 → #15248 → #15385 → this PR.

Implemented with Claude Opus 5.5 (1M context) in Claude Code via T3 Code.

🤖 Generated with Claude Code


Devin Review

@juliusmarminge
juliusmarminge added this pull request to stack #15249 October 3, 2026 23:58
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 3, 2026
@juliusmarminge juliusmarminge added macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews and removed size:M 30-99 changed lines (additions + deletions). labels Oct 3, 2026 — with Cursor
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 20.9 KiB — 29.3 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.2 KiB — 29.3 KiB ✅
Claude Live turn messages — 2 — 8 ✅

Baseline: unavailable · PR result: bca4197 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/pause-hidden-thread-timers branch from 56fac3d to 4b291fc Compare October 4, 2026 23:25
@github-actions github-actions Bot added the size:M 30-99 changed lines (additions + deletions). label Oct 4, 2026
@juliusmarminge
juliusmarminge marked this pull request as ready for review October 5, 2026 16:30
@juliusmarminge
juliusmarminge marked this pull request as draft October 5, 2026 16:31
@macroscopeapp

macroscopeapp Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — The PR introduces a bounded production hook that pauses hidden-screen timer updates and adds focused tests, but it also adds two line-level oxlint suppression directives for the new state updates. Those static-analysis overrides require human review.

No code changes detected at bca4197. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 2b1edf7d-a492-491b-9c09-8d51e0e4e90d
📥 Commits

Reviewing files that changed from the base of the PR and between 918cf5f and 4b291fc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • apps/mobile/package.json
  • apps/mobile/src/features/threads/ProviderSubagentBar.tsx
  • apps/mobile/src/features/threads/floating-working-control.tsx
  • apps/mobile/src/features/threads/use-visible-second-clock.test.tsx
  • apps/mobile/src/features/threads/use-visible-second-clock.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

The mobile app adds useVisibleSecondClock, which updates its timestamp only while enabled, focused, and active. Two thread components use the hook, and tests cover its ticking, pause, resume, and cleanup behavior.

Changes

Visible second clock

Layer / File(s) Summary
Clock lifecycle and validation
apps/mobile/src/features/threads/use-visible-second-clock.ts, apps/mobile/src/features/threads/use-visible-second-clock.test.tsx, apps/mobile/package.json
Adds a clock hook that tracks focus and app activity, starts a one-second interval when enabled, focused, and active, and refreshes the timestamp when ticking resumes. Tests cover ticking, pauses, resumes, and cleanup. Adds jsdom as a development dependency.
Thread component adoption
apps/mobile/src/features/threads/ProviderSubagentBar.tsx, apps/mobile/src/features/threads/floating-working-control.tsx
Both components use useVisibleSecondClock instead of maintaining local timestamp intervals.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ProviderSubagentBar
  participant WorkingTimer
  participant useVisibleSecondClock
  participant NavigationFocus
  participant ReactNativeAppState
  participant Interval
  ProviderSubagentBar->>useVisibleSecondClock: Request timestamp with live state
  WorkingTimer->>useVisibleSecondClock: Request timestamp enabled
  useVisibleSecondClock->>NavigationFocus: Track screen focus
  useVisibleSecondClock->>ReactNativeAppState: Track app activity
  useVisibleSecondClock->>Interval: Run ticks when enabled, focused, and active
  Interval->>useVisibleSecondClock: Update timestamp once per second
  useVisibleSecondClock->>ProviderSubagentBar: Provide timestamp
  useVisibleSecondClock->>WorkingTimer: Provide timestamp
Loading

Merge Risk: ⚪ Minimal · up to 4b291

The timer change is mergeable after normal checks; no actionable issue remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 4 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the mobile performance change: pausing elapsed-time timers on hidden thread screens.
Description check ✅ Passed The description explains the problem, implementation, measured results, verification, and limitations. It does not include the required Scope and approval section or explain why the change qualifies f…
Full details: Docstring Coverage

Explanation

Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 4 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@juliusmarminge
juliusmarminge marked this pull request as ready for review October 5, 2026 16:37
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/pause-hidden-thread-timers branch 2 times, most recently from f9fe9bd to f583268 Compare October 5, 2026 17:03
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/pause-hidden-thread-timers branch from f583268 to 0b66b28 Compare October 7, 2026 02:34
Retained thread routes kept their 1s working/subagent timers running while
unfocused or backgrounded. useVisibleSecondClock ticks only while enabled,
focused and active, and re-reads the clock before paint on resume.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/pause-hidden-thread-timers branch from 0b66b28 to bca4197 Compare October 8, 2026 05:25
@juliusmarminge
juliusmarminge merged commit eb17e0d into main Oct 8, 2026
34 of 54 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/mobile-perf/pause-hidden-thread-timers branch October 8, 2026 05:30
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 8, 2026
## What's Changed
* chore(review): remove the custom Approvability check by @esthor in pingdotgg/t3code#17018
* perf(mobile): show the cached thread list sooner and stop the freeze after it by @juliusmarminge in pingdotgg/t3code#16713
* fix(server): replaying a command no longer freezes the server by @SunkenInTime in pingdotgg/t3code#17041
* fix(server): opening a long thread no longer blocks the server for a second by @SunkenInTime in pingdotgg/t3code#17029
* fix(server,web,mobile): thread links reference the thread id, not a baked title by @juliusmarminge in pingdotgg/t3code#17017
* fix(web): messages with quotes or links no longer collapse when short by @flamboh in pingdotgg/t3code#16624
* refactor(web): PR loading skeleton shares the detail panel's layout by @flamboh in pingdotgg/t3code#15583
* fix(web): PR file headers keep the file name in narrow panels by @flamboh in pingdotgg/t3code#15562
* fix(web): PR timeline no longer shifts when its scrollbar appears by @flamboh in pingdotgg/t3code#15588
* fix(server): held queued wakes no longer keep delegated tasks running by @juliusmarminge in pingdotgg/t3code#17028
* fix(server): a message sent during a rollback no longer undoes it by @t3dotgg in pingdotgg/t3code#17079
* fix(web): PR file stats ignore the hide-whitespace toggle by @flamboh in pingdotgg/t3code#16162
* perf(mobile): omit duplicated turn items from bounded thread snapshots by @juliusmarminge in pingdotgg/t3code#15385
* perf(mobile): pause elapsed-time timers on hidden thread screens by @juliusmarminge in pingdotgg/t3code#15397
* fix(mobile): pause hidden home thread list updates by @juliusmarminge in pingdotgg/t3code#15705
* fix(mobile): restore file viewer insets and glass header by @juliusmarminge in pingdotgg/t3code#17073
* fix(web): PR code toolbar no longer overlaps in narrow panels by @flamboh in pingdotgg/t3code#15561
* fix(web): PR commit menu no longer stretches across the window by @flamboh in pingdotgg/t3code#15560
* fix(web): command palette scrollbar no longer clipped at the top by @flamboh in pingdotgg/t3code#17035
* fix(web): Usage breadcrumb stays centered on small viewports by @flamboh in pingdotgg/t3code#15552
* fix(mobile): stop refreshing Git status on streamed thread updates by @juliusmarminge in pingdotgg/t3code#15893
* fix(mobile): skip move indexes for empty and single-thread sections by @juliusmarminge in pingdotgg/t3code#16115
* perf(mobile): reuse encoded rows in shell cache saves by @juliusmarminge in pingdotgg/t3code#16129
* perf(mobile): skip showcase subscriptions in normal builds by @juliusmarminge in pingdotgg/t3code#16131
* perf(mobile): remove unused Home project sorting by @juliusmarminge in pingdotgg/t3code#16177
* perf(mobile): reduce move-menu index allocations by @juliusmarminge in pingdotgg/t3code#16256
* perf(mobile): skip impossible thread-key lookups by @juliusmarminge in pingdotgg/t3code#16263
* fix(mobile): collect UI runtime garbage on iOS memory warnings by @juliusmarminge in pingdotgg/t3code#16296
* fix(mobile): stop Git sheet refresh loop by @juliusmarminge in pingdotgg/t3code#16305
* fix(mobile): refresh Git status after reconnect by @juliusmarminge in pingdotgg/t3code#16329
* fix(mobile): update the iOS Git header menu when status changes by @juliusmarminge in pingdotgg/t3code#16330
* perf(mobile): reuse the settled sort when settled rows are unchanged by @juliusmarminge in pingdotgg/t3code#16369
* perf(mobile): highlight source files in small batches that keep grammar state by @juliusmarminge in pingdotgg/t3code#16729
* feat(web): quote chips show what you said about the quote by @flamboh in pingdotgg/t3code#15703
* feat(web): projectless threads show their machine in the sidebar by @flamboh in pingdotgg/t3code#17022
* fix(lineage): keep agent effort and speed after completion by @Bil0000 in pingdotgg/t3code#16925
* fix(mobile): align diff scrolling with glass headers by @juliusmarminge in pingdotgg/t3code#17085
* fix(web): workspace page headers can no longer grow past the top-bar height by @maria-rcks in pingdotgg/t3code#17086
* feat(mobile): redesign the Add environment sheet by @juliusmarminge in pingdotgg/t3code#17092


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261008.2801...v0.0.46-nightly.20261008.2813

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261008.2813
github-actions Bot added a commit to davidvanderklay/t3code-flake that referenced this pull request Oct 8, 2026
## What's Changed
* chore(review): remove the custom Approvability check by @esthor in pingdotgg/t3code#17018
* perf(mobile): show the cached thread list sooner and stop the freeze after it by @juliusmarminge in pingdotgg/t3code#16713
* fix(server): replaying a command no longer freezes the server by @SunkenInTime in pingdotgg/t3code#17041
* fix(server): opening a long thread no longer blocks the server for a second by @SunkenInTime in pingdotgg/t3code#17029
* fix(server,web,mobile): thread links reference the thread id, not a baked title by @juliusmarminge in pingdotgg/t3code#17017
* fix(web): messages with quotes or links no longer collapse when short by @flamboh in pingdotgg/t3code#16624
* refactor(web): PR loading skeleton shares the detail panel's layout by @flamboh in pingdotgg/t3code#15583
* fix(web): PR file headers keep the file name in narrow panels by @flamboh in pingdotgg/t3code#15562
* fix(web): PR timeline no longer shifts when its scrollbar appears by @flamboh in pingdotgg/t3code#15588
* fix(server): held queued wakes no longer keep delegated tasks running by @juliusmarminge in pingdotgg/t3code#17028
* fix(server): a message sent during a rollback no longer undoes it by @t3dotgg in pingdotgg/t3code#17079
* fix(web): PR file stats ignore the hide-whitespace toggle by @flamboh in pingdotgg/t3code#16162
* perf(mobile): omit duplicated turn items from bounded thread snapshots by @juliusmarminge in pingdotgg/t3code#15385
* perf(mobile): pause elapsed-time timers on hidden thread screens by @juliusmarminge in pingdotgg/t3code#15397
* fix(mobile): pause hidden home thread list updates by @juliusmarminge in pingdotgg/t3code#15705
* fix(mobile): restore file viewer insets and glass header by @juliusmarminge in pingdotgg/t3code#17073
* fix(web): PR code toolbar no longer overlaps in narrow panels by @flamboh in pingdotgg/t3code#15561
* fix(web): PR commit menu no longer stretches across the window by @flamboh in pingdotgg/t3code#15560
* fix(web): command palette scrollbar no longer clipped at the top by @flamboh in pingdotgg/t3code#17035
* fix(web): Usage breadcrumb stays centered on small viewports by @flamboh in pingdotgg/t3code#15552
* fix(mobile): stop refreshing Git status on streamed thread updates by @juliusmarminge in pingdotgg/t3code#15893
* fix(mobile): skip move indexes for empty and single-thread sections by @juliusmarminge in pingdotgg/t3code#16115
* perf(mobile): reuse encoded rows in shell cache saves by @juliusmarminge in pingdotgg/t3code#16129
* perf(mobile): skip showcase subscriptions in normal builds by @juliusmarminge in pingdotgg/t3code#16131
* perf(mobile): remove unused Home project sorting by @juliusmarminge in pingdotgg/t3code#16177
* perf(mobile): reduce move-menu index allocations by @juliusmarminge in pingdotgg/t3code#16256
* perf(mobile): skip impossible thread-key lookups by @juliusmarminge in pingdotgg/t3code#16263
* fix(mobile): collect UI runtime garbage on iOS memory warnings by @juliusmarminge in pingdotgg/t3code#16296
* fix(mobile): stop Git sheet refresh loop by @juliusmarminge in pingdotgg/t3code#16305
* fix(mobile): refresh Git status after reconnect by @juliusmarminge in pingdotgg/t3code#16329
* fix(mobile): update the iOS Git header menu when status changes by @juliusmarminge in pingdotgg/t3code#16330
* perf(mobile): reuse the settled sort when settled rows are unchanged by @juliusmarminge in pingdotgg/t3code#16369
* perf(mobile): highlight source files in small batches that keep grammar state by @juliusmarminge in pingdotgg/t3code#16729
* feat(web): quote chips show what you said about the quote by @flamboh in pingdotgg/t3code#15703
* feat(web): projectless threads show their machine in the sidebar by @flamboh in pingdotgg/t3code#17022
* fix(lineage): keep agent effort and speed after completion by @Bil0000 in pingdotgg/t3code#16925
* fix(mobile): align diff scrolling with glass headers by @juliusmarminge in pingdotgg/t3code#17085
* fix(web): workspace page headers can no longer grow past the top-bar height by @maria-rcks in pingdotgg/t3code#17086
* feat(mobile): redesign the Add environment sheet by @juliusmarminge in pingdotgg/t3code#17092


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261008.2801...v0.0.46-nightly.20261008.2813

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261008.2813
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant