Skip to content

Console bell and approvals badge keep polling every 60 s for the first 10 minutes of a hidden tab #28084

Closed

Description

@objectstack-fleet

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

Filing gate: ③ ruling-directed. Ruling B on #26676 (6520722066) fixes a new card under #26676 as the remedy for a finding after a landing. Found by the post-merge proxy preview review of PR #27907 (#27626): comment 6522108365, on main 18318361, examples/app-crm, 1440×900, evidence branch claude/issue-27626-proxy-evidence @ 4c9afa65. ⛔ Not a claim.

Business reason

Who: every console reader who leaves tabs open in the background. What happens: a hidden tab polls as often as a visible one for its first ten minutes, so the background load #27626 meant to cut stays.

Observed

Wanted

A hidden tab stops the bell's and the approvals badge's polling, or polls clearly less often than a visible one. It reads once when the tab becomes visible again. Measure first why objectui#12439 kept a 10-minute hidden window. If that was a ruling, this card is needs_decision.

Acceptance

  • With the tab hidden, the feeds make no poll, or polls at the agreed hidden cadence, from the moment it hides. Pinned with fake timers and visibilitychange.
  • On becoming visible, one read per feed.

Parent: #26676 · Refs #27626 · PR #27907


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    and removed on Oct 30, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    Claim: PM loop round 1
    Session: session_01Utp6CbDAucNyQ4UtzCJKyh
    Account: os-bill
    Branch: claude/project-thread-rrumoz (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-28084
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/packages/app-shell/src/hooks/hiddenTabPollStop.ts, ui/packages/app-shell/src/hooks/sharedUserFeeds.ts, ui/packages/app-shell/src/hooks/useInboxArrivalNotifier.ts, the hooks/components reading their constants, their tests, .changeset/*.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on fec60e00: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: a hidden tab stops (or clearly slows) the bell and approvals polling from the moment it hides and reads once per feed on becoming visible; first measure whether objectui#12439's 10-minute hidden window was ruled (then needs_decision)
    Thread-read: none
    Premise: holds
    Serial constraints cleared: #27907 merged 11:04Z; no open PR touches the surface

    Dispatched 2026-10-30T12:06Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CXAofd4m4ymS3HqKt5

    os-dev-report

    {
      "issue": 28084,
      "status": "needs_decision",
      "branch": "claude/project-thread-rrumoz",
      "pr": null,
      "session": "session_01Utp6CXAofd4m4ymS3HqKt5 (card thread; seat session_01Utp6CbDAucNyQ4UtzCJKyh, Claim 6522358408)",
      "premise_still_valid": true,
      "summary": "Stopped before any code, as the ruling section orders: the 10-minute hidden window was chosen by the maintainer and kept by triage, and its stated purpose is the desktop notification for a background tab. Reading of objectui#12439: the card was filed by hotlong (a GOVERNED_APPROVERS account) with a section headed 'The trade-off (needs an explicit yes)'. It proposes 'While hidden but under N minutes, keep today's 60 s cadence, so a tab switched away briefly still behaves exactly as now', names the cost ('a message that arrives after the tab has been hidden for more than N minutes gets no desktop notification until the user returns to the tab'), and says 'The maintainer asked for this card knowing the trade-off. Pick N, or make it configurable, with that cost in mind.' Triage 6186333502 set scope to 'the card's proposal. Stop arming the timer after N minutes (10 suggested)'; Claim 6186836402 and review 6187549213 fixed 10 minutes as a constant ('HIDDEN_POLL_STOP_MS (10 minutes, a constant, as the claim ruled)'); PR objectui#12445 merged as objectui main 27411ea. On objectstack origin/main 5448585d the hiddenTabPollStop.ts docblock still says the cost 'was accepted for this value when it was chosen'. What changed since: the window was designed when the foreground polled at 10 s / 30 s, so 60 s hidden was the slower cadence (the HIDDEN_POLL_MS docblock: 'a shared feed that polled a hidden tab at its foreground rate would have been a regression'). #27907 moved both foreground cadences to 60 s and left HIDDEN_POLL_MS at 60 s, so that invariant no longer holds for the first 10 minutes. The scale-to-zero goal of objectui#12439 is unaffected by any option below: every option still stops all polling after 10 minutes. Card premise holds as written (60 s hidden = 60 s visible for 10 minutes on main).",
      "tests": "none: reading only, no diff. Code read on origin/main 5448585d: sharedUserFeeds.ts (APPROVALS_POLL_MS = INBOX_POLL_MS = HIDDEN_POLL_MS = 60_000; schedule() uses Math.max(HIDDEN_POLL_MS, backoffMs) when hidden; onVisibilityChange refetches once per feed on return), hiddenTabPollStop.ts (HIDDEN_POLL_STOP_MS = 10 * 60_000), useInboxArrivalNotifier.ts (hidden tab raises showDesktopNotification only when preferences.desktop and desktopPermission granted; the module header carries the objectui#12439 trade-off). The desktop toggle is user-facing in layout/NotificationPreferencesMenu.tsx. git grep over ui/ for hiddenTabPollStop / HIDDEN_POLL_STOP_MS / isPollingSuspended / HIDDEN_POLL_MS: the three hooks plus three tests (hiddenTabPollStop-12439, sharedUserFeeds.hiddenStop-12439, sharedInboxFeed.cadence-7249); no other poller reads the clock.",
      "gates": "none run: no diff (needs_decision). check-sdui-manifest not applicable without a change.",
      "dogfood_battery": "n/a - no PR",
      "line_budget": "0 changed lines",
      "files_changed": [],
      "deviations": [
        "The order says read objectui#12439 through REST; the objectui repository had to be attached to this session first (read via gh api after attach). No write to objectui."
      ],
      "mcp_calls": "0 - no GitHub MCP tool used.",
      "api_writes": "1 - this os-dev-report comment via post-stamped (POST issues/28084/comments). No push, no PR.",
      "open_questions": [
        {
          "question": "In the first 10 minutes after a console tab goes to the background, how often should the bell (inbox) and the approvals badge still check for new items?",
          "governing_text": "objectui#12439 body, section 'The trade-off (needs an explicit yes)', filed by hotlong; triage 6186333502 ('the card's proposal ... 10 suggested'); Claim 6186836402 (10 minutes, a constant); #26676 ruling 6520722066 and this card's ruling section (a hidden tab stops, or polls clearly less often than a visible one; one read per feed on return).",
          "options": {
            "A": "Stop at the moment the tab hides (no hidden cadence at all); one read per feed on return. Background load from a hidden tab drops to zero at once. Cost: a hidden tab never reads, so the desktop notification of objectui#7011 can never fire while no push channel exists, and the user-facing desktop toggle in the notification menu would switch on a feature that does nothing (Prime Directive #10, declared is not enforced). Honest version needs a second change outside this card's file surface: retire or hide that toggle and its permission prompt until a push channel lands. ui lane, 1 PR here plus 1 follow-up card.",
            "B": "Keep the 10-minute window, slow the hidden cadence to clearly below the visible one: HIDDEN_POLL_MS 60 s to 180 s (three times the visible 60 s). A hidden tab reads at 3, 6 and 9 minutes, then stops at 10 as today; one read per feed on return. Desktop notification stays, up to about 3 minutes late instead of 1. ui lane, 1 small PR inside the claimed file surface (one constant, three docblocks, the hidden-stop pin plus an ablation), no new knob.",
            "C": "Keep 60 s for 10 minutes as the maintainer accepted it; close this card as not planned and correct only the inaccurate 'still stop in a hidden tab' sentence in the unreleased #27907 changeset. No code change; the 10 hidden reads per feed in the first 10 minutes stay."
          },
          "four_axis": {
            "long_term": "The lasting answer is the push channel #27626 already wired (setUserFeedPushChannelOpen): with it open, neither feed polls, hidden or not. Until then the poll is a fallback, and B restores the rule the hidden cadence has always had (slower than visible, #4225) with one constant. A goes further than the fallback needs and leaves a dead preference unless a second change retires it. C leaves the hidden and visible cadences equal, which no docblock in the tree intends.",
            "business_need": "Measured need on the card: idle background tabs add request load (proxy preview evidence on app-crm). objectui#12439's own driver, staging kept awake, is already solved by the 10-minute stop under every option, because the staging idle window (15 / 30 minutes per that card) is longer than 10 minutes. So the gain on the table is load only: per hidden tab and feed, 10 reads in the first 10 minutes today, 3 under B, 0 under A. The desktop notification has a real user toggle (NotificationPreferencesMenu); no usage reading was taken.",
            "ai_error_prevention": "No metadata is involved. A without the toggle retirement leaves a declared capability the runtime cannot deliver, which is the shape AGENTS.md forbids; B and C keep declaration and behaviour in step.",
            "startup_scope": "B is the smallest change that meets this card's ruling (one constant, no new knob, no second lane). A costs two changes and removes a shipped feature. C costs nothing but leaves the card's finding standing."
          },
          "recommendation": "B. Long-term axis leads: it restores the slower-when-hidden rule with one constant and keeps the maintainer's accepted trade-off (desktop notification within the first 10 minutes) instead of deleting it; the other three axes do not flip it. If the maintainer prefers A, the toggle retirement should be carded in the same ruling. Confidence gap: 180 s is a proposed figure, not a measured one; any value clearly above 60 s fits the same PR."
        }
      ],
      "out_of_scope_findings": [
        "carrier: seat 4 (PR text) · noted, not filed. #27907's PR body and changeset say the feeds 'still stop in a hidden tab'; on main they stop only after 10 continuous hidden minutes. That changeset (.changeset/27626-record-open-requests.md, 'still stop in a hidden tab') is still unreleased on origin/main 5448585d, so the PR that lands the ruled option can correct its sentence in place and state what is then true."
      ]
    }
  4. os-bill commented on Oct 30, 2026

    @os-bill
    Collaborator

    Ruling: #28084 hidden-tab cadence · letter B · maintainer 「B」 2026-10-30T12:30Z · decision thread 「决定后台标签页铃铛轮询频率」(presented 12:17Z, Bill ruled in person; not a proxy ruling)

    Marker: maintainer-ruled, recorded by the decision thread.

    Re-verified readings (on origin/main ec7e15a, by the decision thread, beside the card thread's report 6522509433):

    • ui/packages/app-shell/src/hooks/sharedUserFeeds.ts L112–136: APPROVALS_POLL_MS = INBOX_POLL_MS = HIDDEN_POLL_MS = 60 s since perf(console): opening a record re-reads no list, asks its field access once, and the bell polls once a minute #27907 (main 40be9b5); the HIDDEN_POLL_MS docblock still states the rule it no longer meets ("a shared feed that polled a hidden tab at its foreground rate would have been a regression", Three datasource-admin routes answer 503 naming the wrong service (carried out of #3973 so it is not buried) #4225).
    • schedule() L475–510 reads document.hidden only when it arms, so the foreground tick already armed at the moment of hiding fires once more; hidden ticks are then spaced at Math.max(HIDDEN_POLL_MS, backoffMs), and any tick due after HIDDEN_POLL_STOP_MS (10 min, hiddenTabPollStop.ts) sends nothing. onVisibilityChange L516–535 reads once per feed on return.
    • An inbox tick is two requests (messages + receipts, L1443–1450); an approvals tick is one. Today a hidden tab makes up to 10 ticks per feed in its first 10 minutes (≈30 requests); at 180 s it makes at most 4 (≤1 / ~4 / ~7 / ~10 min, ≈12 requests).
    • useInboxArrivalNotifier.ts L9–48, L202: the desktop notification (objectui#7011) fires only in a hidden tab, behind the user's preference and a granted permission; its header still says "up to about a minute late".
    • .changeset/27626-record-open-requests.md L15 (unreleased): "still stop in a hidden tab" — on main they stop only after 10 continuous hidden minutes.

    Governing text: objectui#12439 (hotlong), section "The trade-off (needs an explicit yes)": keep polling while hidden for N minutes so a desktop notification can still reach a background tab; triage 6186333502 and Claim 6186836402 fixed N = 10 as a constant. #26676 ruling 6520722066 (findings after a landing become cards under #26676). This card's Wanted: "stops ... or polls clearly less often than a visible one", one read per feed on return.

    Ruling (B, 一次裁清): the 10-minute hidden window and the full stop after it stay exactly as objectui#12439 chose them. The hidden cadence goes back to being clearly slower than the foreground: HIDDEN_POLL_MS 60_000 → 180_000. No new knob, no per-feed rule, the desktop notification and its toggle stay (now up to about three minutes late instead of one). Execution parameters:

    1. sharedUserFeeds.ts: HIDDEN_POLL_MS = 180_000; rewrite its docblock (hidden 3 min against foreground 1 min, slower-when-hidden is the rule since Three datasource-admin routes answer 503 naming the wrong service (carried out of #3973 so it is not buried) #4225, full stop after HIDDEN_POLL_STOP_MS per objectui#12439).
    2. useInboxArrivalNotifier.ts header: "up to about a minute late" → "up to about three minutes late".
    3. .changeset/27626-record-open-requests.md L15: "still stop in a hidden tab" → polls every three minutes while the tab is hidden and stops after ten hidden minutes (the changeset is unreleased, so the sentence is corrected in place).
    4. Pin (fake timers + visibilitychange, in sharedUserFeeds.hiddenStop-12439.test.tsx or a sibling): after hiding, each feed reads at most 4 times in 10 minutes, consecutive hidden reads 180 s apart after the first, nothing after the 10th minute, exactly one read per feed on return. Ablation: the constant set back to 60_000 must turn the pin red.
    5. Acceptance line 1's "agreed hidden cadence" = 180 s. One PR on the card thread's branch claude/project-thread-rrumoz, Fixes Console bell and approvals badge keep polling every 60 s for the first 10 minutes of a hidden tab #28084, inside the claimed file surface (Claim 6522358408).

    Four axes: Long term: the lasting answer is the push channel seam #27626 already wired (setUserFeedPushChannelOpen); until a host opens one the poll is a fallback, and the fallback's standing rule is hidden-slower-than-visible, which one constant restores. Business need: the card's evidence is request load from idle background tabs (6522108365); the scale-to-zero driver of objectui#12439 is already covered by the 10-minute stop under every option. Error prevention: no metadata; B keeps declaration and behaviour in step, whereas stopping at hide (A) would leave the desktop toggle declared but inert until a second card retired it. Scope: one constant, three docblocks, one pin; no new lane, no Clause-②.

    Options not taken: A (stop at hide, plus a card retiring the desktop toggle), C (keep 60 s, correct only the changeset sentence), D (approvals stop at hide, inbox 180 s — two hidden rules for a small saving). Cadence values 120 s / 300 s were offered; the maintainer took the recommended 180 s by answering 「B」.

    Execution after the ruling: the card thread (seat domain:ui#4, Claim 6522358408) lands the one PR above with Ruling-ref: <this comment>; no re-opening of the 10-minute window, the full stop, the immediate read on return, or the desktop toggle. Ledger row added to the project's rulings file; the seat 4 thread and the card thread are told with the maintainer's words.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CXAofd4m4ymS3HqKt5

    os-dev-report

    {
      "issue": 28084,
      "status": "done",
      "branch": "claude/project-thread-rrumoz",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28133",
      "session": "session_01Utp6CXAofd4m4ymS3HqKt5 (card thread; seat session_01Utp6CbDAucNyQ4UtzCJKyh, Claim 6522358408)",
      "premise_still_valid": true,
      "summary": "Executes ruling B (6522899149). sharedUserFeeds.ts HIDDEN_POLL_MS 60_000 to 180_000 with its docblock rewritten (hidden 3 min against foreground 1 min, slower-when-hidden since #4225, the already-armed foreground tick fires once after hiding, objectui#12439 full stop after HIDDEN_POLL_STOP_MS unchanged); module header names the cadence. useInboxArrivalNotifier.ts header now says a desktop notification can be up to about three minutes late. The unreleased .changeset/27626-record-open-requests.md sentence 'still stop in a hidden tab' is corrected in place (execution parameter 3). New changeset 28084-hidden-tab-bell-cadence.md (app-shell patch). The 10-minute window, the full stop, the read on return and the desktop toggle are untouched. Supersedes report 6522509433 (needs_decision).",
      "tests": "Head e802b06a. Closure build --filter '@object-ui/app-shell^...' lock VERDICT command-exit 0. New case in sharedUserFeeds.hiddenStop-12439.test.tsx (fake timers + visibilitychange; visible mount, hidden 30 s in): 1 read at the already-armed foreground tick, then one per full 180 s, 4 reads per feed in the 10 hidden minutes, none after, 0 timers armed, exactly 1 read per feed on return. Three hidden-tab files: Test Files 3 passed (3), Tests 15 passed (15), VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs (implementation committed first): anchor 'const HIDDEN_POLL_MS = 180_000;' to 60_000, anchor 1 to 0, blob 3ebb502c to 74dfdfe3; Tests 3 failed / 7 passed of the two feed files, VERDICT command-exit 1 (new case 'expected { approvals: 3, inbox: 3 } to deeply equal { approvals: 1, inbox: 1 }', cut-off case '{ approvals: 10, inbox: 10 }' vs 4, 7249 hidden throttle 'expected 4 to be 2'); restored blob == HEAD 3ebb502c, git diff HEAD empty; tests import src, no dist rebuild. packages/app-shell/src/hooks/: Test Files 84 passed (84), Tests 715 passed (715). pnpm --filter @object-ui/app-shell run type-check (tsc --noEmit + tsconfig.test.json): exit 0.",
      "gates": "dispatch-gates --commands at e802b06a: 34 derived; 34 run, 33 exit 0 (check-sdui-manifest, check-sdui-manifest --self-test, vitest run scripts/, check:type-check-coverage, check:type-check-debt, check:nul-bytes and the rest); --ran reconciliation 34 derived / 34 run / 0 NOT-MEASURED. One red by design: check-empty-changeset --base origin/main exit 1 on .changeset/27626-record-open-requests.md, the DELIBERATE CORRECTION class, ordered by ruling 6522899149 parameter 3 and stated in the PR body. check:new-line-citations: 0 new. CI check-runs: in_progress at report time.",
      "dogfood_battery": "n/a - no '!' in the PR title and no (narrowing) arm",
      "line_budget": "+93 / -25 across 6 files (one constant, docblocks, one test case, two changesets)",
      "files_changed": [
        ".changeset/27626-record-open-requests.md",
        ".changeset/28084-hidden-tab-bell-cadence.md",
        "ui/packages/app-shell/src/hooks/sharedUserFeeds.ts",
        "ui/packages/app-shell/src/hooks/useInboxArrivalNotifier.ts",
        "ui/packages/app-shell/src/hooks/__tests__/sharedUserFeeds.hiddenStop-12439.test.tsx",
        "ui/packages/app-shell/src/hooks/__tests__/sharedInboxFeed.cadence-7249.test.tsx"
      ],
      "deviations": [
        "Harness after-PR assign/review step skipped, as the order requires (no assignees, no review request).",
        "check-empty-changeset is red on purpose (pending note corrected in place per the ruling); the gate's own text says it stays red and a person confirms."
      ],
      "mcp_calls": "0 - no GitHub MCP tool used.",
      "api_writes": "3 - report 6522509433 (POST issues/28084/comments), pr_create via fleet-write (POST pulls, #28133, draft, no assignees), this report (POST issues/28084/comments). Plus one git push of the branch.",
      "open_questions": [],
      "out_of_scope_findings": []
    }
  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    ACCEPT → PR #28133 comment 6524977384 · 2026-10-30T14:16Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uipm:dispatchedpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions