Skip to content

fix(client-runtime): stop query polling after the last reader leaves - #25266

Open
saphid wants to merge 8 commits into
pingdotgg:mainfrom
saphid:fix/idle-query-polling-20261026
Open

saphid wants to merge 8 commits into
pingdotgg:mainfrom
saphid:fix/idle-query-polling-20261026

Conversation

@saphid

@saphid saphid commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Shared query caches retain recurring polling after the final reader leaves. For example, navigating away from a mobile PR detail can leave linked-thread refreshes running during the five-minute cache lifetime, although the screen has no reader.

Change

The refreshIntervalMs timer wraps the query outside the factory's idle TTLs, so it stops when the final reader unmounts while cached data remains warm. A returning reader catches up a missed poll. Main now revalidates stale answers on every mount; that behavior is preserved. A registry-local build counter prevents a duplicate request when a signal or cold read already rebuilt the query. Final results suppress automatic interval, signal and remount refresh; explicit refresh remains available.

The existing weak cache/member link preserves identity until cache expiry. Only runtime.ts and its test file change. No UI, RPC or transport change. Maintainer approval of the shared-factory direction remains pending.

Known limit: consumers that add their own idle TTL above a polled query still keep it mounted for that TTL. createUrls and the project-favicon helpers in assets.ts hold asset URL queries (30-minute poll) for 60 minutes, which allows up to two idle refreshes. PR linked-threads has no wrapper, and linked-summary's wrapper lasts 5 s.

Scope and approval

This changes shared polling ownership in the query factory, so it needs maintainer review of the direction. No UI, setting, RPC contract or transport change is included. The fix is in the shared query factory, so it applies to mobile, web and desktop query consumers alike. Audit tracker: #189.

Verification

Current candidate 2e5282d, tree 1232461b96a78c8ec90487bbf5d26071ed4ea345, integrates main a472342 without rewriting prior author commits.

  • CI=true TMPDIR=/private/tmp vp run check:changed upstream/main passed format, lint, knip, affected-package typechecks and 59/59 shared query lifecycle tests. All commands retained their actual exit status. No repo-wide test run.
  • Fresh independent read-only review by GPT-6.1 Sol/high, Codex through T3 Code: no actionable P1/P2/P3 findings in the full frozen contribution. Task node:delegated-task:command%3Amcp%3A05f1a96a-5b49-41a2-bc99-96140e5986df%3Adelegate-task%3Afinish-t3-four-repairs-review-20261114 completed. It inspected source and test receipts without rerunning the suites.
  • Fresh readback: all eight required checks are satisfied (five success, three skipped). Macroscope correctness passed. Macroscope approvability remains neutral/needs human approval; no current human approval is claimed. Earlier captures do not certify this head. Native/client, remote/relay and paid-provider runtime proof were not repeated for this conflict repair.
Historical verification and retained author credits (earlier revisions)

Verification

At head 3431a69, merged with main 5959787:

  • CI=true vp test run on runtime.test.ts, filesystem.test.ts, assets.test.ts and pullRequests.test.ts: 143 tests passed, including main's folder-picker reopen test from fix(client-runtime): reopening a folder picker refreshes its listing #23289 (unchanged).
  • CI=true vp run check:changed: format, lint, knip, typecheck (7 packages) and tests passed.
  • With main's runtime.ts, five idle-polling regressions fail: "polls only for real readers…" (interval, interval and signal), "stops polling independently of cache TTL (3600000)", "cancels an obsolete periodic query when its session is replaced", and "queries afresh once after a polled query's cache expires". The new test "shows the cached answer without querying when remounted past its stale time" passes on main and fails on this PR's earlier runtime, which re-checked stale data on every remount.
  • Two remount cases from review fail without the catch-up fix (3 executions instead of 2): a synchronous failure after an idle signal and more than one interval, and a polled revalidateOnRemount query.

These are fixture query-operation counts, not wire-traffic or energy measurements. Maria's verification report covered real web request counts at the earlier a64ca2a head, which used a different implementation, and was not repeated. Current-head native iOS/Android, Electron, remote/relay/tunnel, battery impact and Hermes performance are unverified.

Created with: original implementation and independent review in separate GPT-6.1 Sol/high sessions through Codex in T3 Code. Rework onto main's revalidateOnRemount design: Claude Opus 5.5 through Claude Code in T3 Code, with independent review by GPT-6.1 Sol/high through Codex in T3 Code.

Current-main conflict repair: GPT-6.1 Sol/high in Codex through T3 Code.

@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 26, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — Polling now stops when the last reader leaves and catches up when a reader returns. The new globalDate:off diagnostic suppression requires human review under the explicit suppression policy.

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

@coderabbitai

coderabbitai Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • Resume reviews
  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

The runtime tracks signal revisions for cached answers and separates cached query state from live revalidation. Mounts can trigger revalidation for eligible stale results. Periodic refresh follows the mounted reader lifecycle. Tests cover cache retention and expiry, polling, final results, and session replacement.

Changes: Query Refresh and Cache Lifecycle

Layer / File(s) Summary
Cached query and signal refresh
packages/client-runtime/src/state/runtime.ts, packages/client-runtime/src/state/runtime.test.ts
The runtime tracks signal revisions and refreshes cached non-final answers when signals change. Tests cover idle signal changes, cache expiry, and query timing options.
Live revalidation and polling
packages/client-runtime/src/state/runtime.ts, packages/client-runtime/src/state/runtime.test.ts
The runtime schedules revalidation for eligible stale results and applies periodic refresh to the live query. Tests cover revalidation, polling, final results, and session replacement.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Reader
  participant LiveWrapper as Live query wrapper
  participant CachedQuery as Cached query
  participant QuerySource as Query source
  Reader->>LiveWrapper: Mount and read
  LiveWrapper->>CachedQuery: Read cached result
  LiveWrapper->>QuerySource: Schedule eligible revalidation
  LiveWrapper->>QuerySource: Refresh on interval while mounted
Loading

Possibly related issues

Suggested reviewers: t3dotgg, juliusmarminge

Merge Risk: Minimal · up to 9afc2

No actionable code issue remains in this review. Maintainer approval is still pending before merge.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Description check Warning The description follows the repository template's Problem, Change, Scope, and Verification sections, and it states the problem, the mechanism, the approval status, and the test evidence. It is detaile… Link the issue that shows the bug in the Scope section, or explain why a small obvious bug needs no issue. If a maintainer asked for this PR, link that request. Resolve or state the status of the pending maintainer approval for the shared-f…
Passed Passed checks: 3 passed

Check name Status Explanation
Title check Passed The title names the main behavior change: query polling stops after the last reader leaves. The raw summary confirms the change to refresh and polling ownership in the query runtime, so the title matc…
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.
Full details: Title check

Explanation

The title names the main behavior change: query polling stops after the last reader leaves. The raw summary confirms the change to refresh and polling ownership in the query runtime, so the title matches the changeset. The title is specific, uses a conventional commit format, and is a single line.

Full details: Description check

Explanation

The description follows the repository template's Problem, Change, Scope, and Verification sections, and it states the problem, the mechanism, the approval status, and the test evidence. It is detailed and specific, but the Verification section is long and mixes current-head results with historical revisions. The Scope section links audit tracker #189 but does not link a bug issue that shows the bug. It also says maintainer approval of the shared-factory direction is pending, so the change is not a settled bug fix under the template's scope rule. The description also has no screenshots or recordings. The template says these are required only for UI changes, and this PR states that it has no UI change, so they are not needed.

Resolution

Link the issue that shows the bug in the Scope section, or explain why a small obvious bug needs no issue. If a maintainer asked for this PR, link that request. Resolve or state the status of the pending maintainer approval for the shared-factory direction. Trim the Verification section to the results for the current head, and move the historical revision details into a short note. State clearly which checks were not run, as the description already does for native, remote/relay, and battery checks.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between ae264c9 and 9afc2fd.

Files selected for processing: 2

  • packages/client-runtime/src/state/runtime.test.ts
  • packages/client-runtime/src/state/runtime.ts

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

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

thanks @saphid. separating the cached answer and signal revision from the live polling subscription fixes the ownership bug while preserving cached reopen behavior. i kept your implementation and all regression coverage, and merged current main (323d9611ff49) without rebasing. head: a64ca2ae9f55423eb78d6f104f51119082bf8a5a.

two independent shared-query reviews found no correctness blockers or safe substantial simplification. the build counter prevents duplicate synchronous revalidation; replacing the tracked cache with stock swr would retain source subscriptions. the diff remains two files, +438/-24, with no changes to your runtime patch.

verification:

  • reproduced the bug on current main with the existing-file regression cases before restoring the fix; all 47 runtime tests then passed.
  • blacksmith vp run check:changed passed format, lint, both knip passes, seven affected-package typechecks, and all 47 tests.
  • real web client, system chrome, live git.cafe provider: open this pr, leave via back for 30 seconds, then reopen. websocket requests for pullRequests.linkedThreads continued three times offscreen on main and zero times with the fix. both polled while visible; the fixed query refreshed and resumed polling on reopen. this is request-count verification, not a battery or latency benchmark.
  • all 29 ci checks are green. the automatic review found no actionable findings; its latest pass skipped the unchanged source. both independent source reviews approve this head.

unverified: native ios/android, electron, remote/relay/tunnel runtime, physical battery impact. an already-started request may complete under the retained cache lifetime. maintainer approval of the shared-query lifetime change remains for maria's review, as the approvability comment requests.

matching full-window captures show the same live pr, with no intended appearance change.

before: current main displaying pr 25266

after: the fixed client displaying the same pr

fixed client: leave pr detail and reopen it

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

Verified exact head a64ca2ae9f55423eb78d6f104f51119082bf8a5a as-is on a disposable Linux VM. No source changes or pushes were needed.

Started github/main (d2429f733c) in a separate worktree, stopped it, then started this exact PR head. Both used vp i, a background vp run dev, isolated state, repository Playwright and system Google Chrome. Added a real git project through the UI and completed real Codex gpt-6.1-sol turns through the web composer on both versions. No mocks, fixtures, demo data, or accelerated timers.

Observed actual pullRequests.linkedThreads WebSocket requests for this live GitCafe PR:

  • Main: 3 requests during 31 seconds visible; 3 more during 31 seconds after closing the PR panel.
  • PR: 3 requests during 31 seconds visible; 0 during 31 seconds after closing.
  • A second same-client close/reopen cycle also produced 0 idle requests. Clicking the sidebar PR badge reopened the panel, sent one immediate refresh and resumed polling 10 seconds later.
  • Nearby behavior: manual PR refresh worked; linking the real Codex thread to this PR updated the sidebar badge and the actual linked-threads RPC response. The same sidebar link reopened the PR successfully.

These are real request counts, not battery or latency measurements. The recording preserves the full idle interval and shows elapsed time.

Blacksmith verification on this head passed: 132 focused tests across runtime, assets, pullRequests, fileMetadata, threadFamilies and olderSettledThreads; vp run check:changed 323d9611ff49521357fdc205f667614602ec46b6 passed format, lint, both knip passes, seven package typechecks and 47 runtime tests. CI reports 29/29 successful checks.

Command and request transcript

before: main displaying live PR 25266

after: exact PR head displaying the same live PR

exact PR head: close for 31 seconds, reopen through the sidebar, and resume polling

Not verified

Native iOS/Android, Electron, remote/relay/tunnel connections, providers other than Codex, physical battery impact and Hermes performance were not exercised on this Linux web-client pass. Already-started requests may still finish under the existing cache policy. The separate maintainer scope approval mentioned in the PR description remains pending.

maria-rcks and others added 4 commits November 4, 2026 07:49
…esign

Merge origin/main and keep main's query structure from pingdotgg#23289. The polling
timer now sits outside every idle TTL, so it stops with the last reader and
catches up on a missed poll when a reader returns. Queries without
revalidateOnRemount still show their cached answer on remount.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…bles a remount refresh

Catch up only from a settled success, and queue the catch-up on the registry
scheduler behind any revalidation the remount started, so a signal refresh or
revalidateOnRemount check that already ran cancels it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Preserve prior author commits and current main semantics. Recovery by GPT-6.1 Sol/high in Codex through T3 Code; independent high-effort review of the complete frozen contribution. Changed-file gates run immediately after the integration commit to select the actual contribution.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants