LearnNewsExamplesServices
Frontmatter
id16435
titleMain-thread starvation carriers degrade under intensive timer throttling
stateOpen
labels
bugaiarchitecture
assignees[]
createdAtAug 3, 2026, 12:57 PM
updatedAtAug 3, 2026, 12:57 PM
githubUrlhttps://github.com/neomjs/neo/issues/16435
authorneo-fable
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Main-thread starvation carriers degrade under intensive timer throttling

Open Backlog/active-chunk-12 bugaiarchitecture
neo-fable
neo-fable commented on Aug 3, 2026, 12:57 PM

Context

Witnessed during #16425's pane leg (2026-08-03): the first DockFlip repair iteration used a 100ms main-thread timer dam to bound frame waits — sound under the synthetic rig (timers unclamped), but in the GENUINELY hidden agent pane the dam's setTimeout ticks were visibility-clamped and startTour sat wedged-in-practice for minutes (tourRunner.running: false, idle caption across 4 polls) until a document.hidden entry discriminator replaced waiting entirely for the hidden class.

The general law this exposed: main-thread timers are not starvation-proof carriers. Hidden documents clamp nested timers to >=1s/tick immediately, and intensively throttle chained timers to ~1 wake/minute once the page has been hidden >5 minutes (Chrome's budget-based intensive throttling). Workers are exempt — a dedicated/shared worker has no document and its timers never clamp.

The Problem

Every main-thread carrier that exists specifically to serve rendering-starved documents inherits the clamp it is trying to route around:

  1. ResizeObserver addon hidden poll (src/main/addon/ResizeObserver.mjspollHiddenTargets re-arming via chained main-thread timers, observed in the #16425 probe stacks at :333-347): the poll is ARMED exactly when document.hidden === true — the same condition that clamps it. At the >5min horizon the geometry cadence for a hidden pane plausibly degrades toward one delivery per minute. NOT yet measured at that horizon — the >=1s clamp tier is witnessed, the 1/min intensive tier is documented browser policy awaiting a direct receipt. Fleet-cockpit panes and agent windows live hidden for HOURS, so the horizon is the common case, not the edge.
  2. DockFlip timer dam (#nextFrame, landing via #16425): the hidden class now bypasses it via the entry discriminator; the dam remains the carrier for the occluded-but-visible class, whose timers are unclamped — correct by construction. Recorded here as the pattern precedent: dam-for-occluded + discriminator-for-hidden.
  3. Any future main-side timer cadence serving hidden documents will re-discover this law the hard way without a recorded audit trail.

The Architectural Reality

The starvation carrier hierarchy, by environment:

  • Hidden documents (document.hidden === true): rAF dead, RO deliveries dead, main timers clamped (>=1s, then ~1/min). Surviving carriers: postMessage delivery, and APP-WORKER timers (workers have no document). A hidden-document cadence must originate in a worker and ping main, or the hidden path must not wait at all (discriminator → instant/degraded-mode path).
  • Occluded-but-visible windows: rAF dead, RO dead, main timers at normal clamps. Main-side timer dams are valid here.
  • Presenting documents: everything lives; rAF is the fastest correct signal.

The Fix

  1. Instrument the RO addon hidden-poll cadence in a genuinely hidden pane past the 5-minute intensive-throttling horizon (pane route documented on #16402/#16425); record the measured inter-delivery gap.
  2. If the intensive tier bites (expected): relocate the poll's CADENCE to the app-worker side — a worker timer pings main (postMessage) to execute one poll pass; main keeps the measurement + synthetic-entry dispatch it owns today. Alternatively a visibility-keyed cadence downgrade with an explicit contract, if measurement shows the degraded cadence is acceptable for hidden-pane consumers.
  3. Encode the carrier hierarchy above at the owning seam's JSDoc (the RO addon class doc is the natural anchor, echoing the DockFlip two-layer doc) so the next starvation repair starts from the law instead of re-deriving it.

Acceptance Criteria

  • RO hidden-poll inter-delivery cadence measured in a >5min-hidden pane, receipt posted (pre-repair baseline).
  • Post-repair, geometry convergence in long-hidden panes stays within a few seconds (measured, not delayed-for).
  • Presenting + occluded behavior unchanged (existing WorkstationStarvedGeometryNL stays green; frame-fast-path untouched).
  • The carrier hierarchy is documented at the owning addon seam.

Out of Scope

  • The overflow cue's starved leg — #16434.
  • DockFlip choreography — #16425 (the discriminator+dam pattern already landing there).
  • Orchestrator-side starvation cause taxonomy — #16382 (Vega's lane; conceptually adjacent, different substrate).

Related

Related: #16402 · #16425 · #16434 · #16382

Live latest-open sweep: checked latest 20 open issues at 2026-08-03T10:57Z; no equivalent found. A2A in-flight claim sweep: latest 30 messages spanning the herd window — no overlapping claim (#16382 is orchestrator-substrate starvation detection, different seam).

Origin Session ID: f01a83b0-dab9-4fa9-a3a1-279f3e2336dd

Retrieval Hint: "main-thread timer visibility throttling starvation carrier hidden poll cadence"