src/util/SchedulingPolicySpecs.md
SchedulingPolicy holds the pure timing decisions behind the AX scheduling layer, extracted so they're
testable without real clocks or queues (same pattern as SelectionResolver / AxQueryRouting):
ThrottleDecision — for one throttleOrProceed call: run on the leading edge, or (within the
window) schedule a single trailing run and coalesce the rest. Used by Throttler and ThrottlerWithKey.RetryPolicy — backoff schedule (200ms → 1s → 2s → 5s, then 5s) and the 60s give-up, for retrying
an AX call against an unresponsive app. Used by AXCallScheduler.InactiveTabScanPolicy — may we brute-force an app's AX tree for the inactive tabs its AXTabGroup named
but we hold no window for, and WHERE should that sweep start? That walk is the ONLY way to adopt an inactive tab (it appears in no CGS list) and
it is expensive, so it is gated per app on the SITUATION: the untracked titles plus the app's window count,
which is what says whether anything has changed since we last looked. Used by
Applications.discoverInactiveTabs.Mirrors SchedulingPolicyTests.swift 1:1.
runNow.runNow (window reset).scheduleTail(remaining).coalesce.runNow.scheduleTail, then coalesce for the rest.The situation used to be recorded BEFORE the scan ran and then refused forever, so one fruitless attempt — the app's AX tree not ready yet, the classic at launch — permanently gave up on it, with no retry and no later trigger. Measured over a live QA run (2026-07-30): 82 tab reads named untracked tabs and the scan adopted NOTHING, against 57 adoptions in a run whose first attempt happened to land. So the outcome is what gets recorded, and a situation gets a small budget instead of exactly one shot.
testFinderTabsAllUntracked) and no number of walks resolves them. Past the cap the tree is left alone.0..<30000 (~9.7k per 250ms) and adopted nothing, while Finder's window
elements sat at ~31000 — stopping just short, every time. A tab's window element is minted when the tab is,
so an app's windows cluster in a narrow band and a window we already track names it; anchoring a margin
below found them in a single attempt (D-01/D-03/T-15 went green, D-01 from 34s to 16s).