Flash is one resident macOS application. Its executable also implements the CLI:
arguments invoke a verb by custom AppleEvent (Flsh / Cmd ); no arguments start
the resident. flash status and flash doctor travel the same event and read
JSON from its reply; flash config_check never leaves the CLI. Configured mappings resolve through the same command definitions
and dispatch in-process. Other mapping executables receive an argv array.
Configuration owns the surfaces: a hotkey registers only the chord it names
(ShortcutsCoordinator), the status bar, widgets and their polling exist only
with [statusbar] enabled or a [widgets.<name>] table, a popup’s PTY starts
on its first showing, the inspector listens only on demand or with
[debug] http_inspector_enabled, and CoreAudio is touched per request. A
plugin whose manifest declares only status runs while a visible surface
reads one of its segments; a plugin with only commands starts on its first
perform; a manifest without exec never spawns.
The rest starts with the process, whatever the configuration says:
| Component | Why it is eager | Cost |
|---|---|---|
AppMonitor AX observer and prepared model for the focused app |
Hints must show without a walk on the key path (see the prepared-model guide) | One AXObserver per focused app, a complete walk per focus change, maintenance walks while the user is active |
KeyboardCaptureTap |
The swallow decision must exist before the first mapped key; the no-tap fallback is slower | A session tap on main, plus the 2 s secure-input probe |
Resident plugins: every manifest with sources, query, hints or listen |
Catalogs and hint providers are kept warm so the flashlight and terminal hints are complete on first open | One child process each (about eighteen by default), three queues and a root-directory watcher per plugin, core:ax.changed fan-out to the listeners |
Window navigation catalog (WindowMover) |
Directional focus reads a prepared catalog rather than walking on the key path | kAXWindows per regular app at launch and on app launch/terminate |
| Clipboard, network, volume and power monitors | A bundled manifest listens for them | A 500 ms pasteboard poll, one SCDynamicStore session, notification observers |
ApplicationSource, frecency and command history |
Flashlight catalogs and ranking | An installed-apps scan with kqueue watchers, two files read on main |
Making the resident plugins and the window catalog configuration-driven (spawn a catalog plugin on the first flashlight open from its persisted catalog, a scoped plugin when a matching app is focused, a listener only while its host monitor has a running consumer) is the planned next step; each item trades first-use latency for idle cost and is listed with its risk in the launch inventory of the resumption notes. Until then the README claims only what the table above supports.
| Owner | Responsibility |
|---|---|
FlashCore |
Source/evaluator protocols, immutable target/candidate values, visible-region finalization |
FlashProviders |
Accessibility discovery, never target activation |
AppMonitor |
Focus observation, provider selection, complete prepared models and refresh scheduling |
ModeStore / ModeReducer |
Serialized mode transitions and ordered effects |
ActivationLifecycle |
Discovery, delayed commit, active gesture and replacement ownership |
HintSession |
Hint/search/pointer interaction state and reset; the mouse grid’s pure MouseGrid.Navigation (display, step and undo history), key shape and drag anchor; the session’s capture path and latency probe |
KeyboardLayoutMonitor |
[app] keyboard_layout’s reference table, rebuilt on input-source changes and config loads |
CandidateFinderSession |
Catalog, query, source context and selection for one finder session |
CandidateLiveQuery |
Generation-scoped live results, separate from warmed catalog rows |
ActionDispatcher |
Host mouse synthesis and completion of every owned gesture; the one held mouse button behind HOLD (mouse_button, pointer mode’s v) and smooth-scroll steps |
PluginManager / PluginProcess |
Manifest reconciliation, owned child generations, transport and RPC |
FlashStatusBarController |
Evaluation of the bar and every desktop widget, each memoized on the inputs it read; reconciled source/job records for their union and the next necessary wakeup |
WidgetController / WidgetWindow |
One click-through desktop-level window per enabled widget per display, placed in the Flash-usable frame; occlusion reported back so a covered widget stops refreshing |
An asynchronous operation captures an ownership token before dispatch. Completion must validate that token before mutating shared state, and again after additional asynchronous preparation. Cancellation invalidates ownership before cancelling resources. Resource completion and permission to apply an outcome are distinct: an obsolete gesture must still post its mouse-up even though its outcome is ignored.
Discovery captures focused-app context and a generation. Provider ordering is
priority descending, with stable identity tie-breaking. A provider may fall through
on empty output only when its policy explicitly permits it; sources are not merged.
Accessibility is the universal provider; tmux is the volatile terminal provider and
vscode is the bundle-scoped AX-enhancer provider (hints with fallback_on_empty).
Tmux keeps its window catalog and status segments warm from tmux’s own control-mode notifications, through one output-free control client per local server a user’s client is attached to, never from a timer (plugin performance).
Tmux discovers pane anchors and every identified link in the visible pane grids;
there is no per-pane link cap. Its executable is resolved once, including mise
shims, so version-manager startup never consumes each hint query’s deadline.
Client metadata and pane geometry are queried concurrently and must agree on the
server, session and window identity. Single-row, mouse-enabled status bars also
receive window-tab hints when their expanded format exposes numeric
range=window|N or range=user|N spans in a left-aligned row. These indices are
bound to live window IDs before publication. Scrolling lists, other alignments,
overflow and multi-row status layouts omit tab hints; pane and link hints remain
available. Tab selection uses the same host mouse-click path as pane selection.
Letter search in a terminal with no accessibility text reads this same client
grid and the same window rectangle, including the status line and each
pane-border title. f and cf label that grid’s panes, links, window
tabs and pane titles; they also label the same status-bar spans.
A walk resolves the window it covers once: the frame step keeps the focused
window element (AppContext.walkedWindow) and the walk starts there instead
of asking the app for its focused window again. Per node, the batched
attribute array is read in place by type id (AXBatchValues); strings are
bridged only for a node the role gate admits, and the extension-document
test is a scheme prefix check, not a URL parse. An activation reads the
window list once for the frontmost app’s top window and the dialog covering
it. Label lists are memoized per layout and target count, and the overlay
keeps one attributed string per label, prefix length and accent.
Prepared models contain a complete finalized target set and assigned labels. Reads require matching PID, dirty token, configuration revision, freshness and the window’s visible region. AX notifications, focus, Space, display and configuration changes invalidate them. Stale completed walks are discarded. Walks are never served as partial captures; an activation miss obtains a complete walk. Model refresh timers have independent ownership so a cancelled timer cannot consume a later request for the same PID.
AX event storms and slow speculative walks suspend background warming while
leaving explicit activation available. A degenerate background walk of an app
that has shown a healthy tree is owed a readiness re-walk, which neither
suspension withholds; an app whose volatile provider owns its hints and whose
own tree keeps walking empty stops being warmed at all (see
prepared hint models). Maintenance refreshes run before the
freshness ceiling and do not inherit the longer noisy-AX throttle. The active
window border has its own event-driven lifecycle and bounded recovery checks;
it does not poll continuously or retain a frame when the focused window disappears.
A move or resize names the window that changed, so its frame comes from one AX
read on that element and is applied with no settle tick — the border rides a
drag rather than trailing a WindowServer scan. Only events that change which
window is on top fall back to a window-list pass and schedule bounded recovery
checks. Those events update the border whenever they come from the active app,
judged by identity: a closed or minimized window leaves its app active with
another app’s window on top, so matching the event against the window list
dropped it and left the stroke on a window that was gone. An app focused while
still launching refuses AX registrations (kAXErrorCannotComplete); they are
retried on a bounded ladder, or the app would stay unobserved for its life.
Overlay states that exclude one another are one value, never parallel flags. The overlay owns the border’s drawn frame and derives its stroke from the badge style the status-bar pill is painted from, re-stroking when that style changes, so the border and the pill cannot show different modes. A hint session’s phase (typing a search letter, typing labels, searching, adjusting, steering the pointer) is one enum; the overlay’s key route is its projection, pushed on every session change together with the border’s visibility, which is hidden for the whole session. A toast is its own layer above everything else: it never recycles hint chips or closes the command line, and its expiry removes only itself. One resident runs per user; a second one finds the resident lock held and exits.
What genuinely cannot be driven by an event goes through PollScheduler, the
one periodic clock in the process — there is no second timer. Core watchers and
plugins register there instead of arming their own, so twenty pollers cost one
wake-up rather than twenty; deadlines snap to a multiple of each interval so
clients sharing a period also share a tick, a client whose previous run has not
returned is skipped rather than queued, and the timer stops entirely when
nothing is registered. A registration is either a fixed cadence (register) or
a one-shot deadline (scheduleOnce), which is how a client whose wake-ups are
irregular still rides the shared clock: the status controller re-registers its
next deadline — the earliest of the user’s per-source intervals, cycle
rotations, each status surface’s clock and pending output — each time one
lands, for the bar and desktop widgets alike. PollDeadline wraps one
re-armable deadline for a queue-confined owner: a trailing debounce re-arms it
per event, a backoff per attempt, and a fire already on its way is dropped once
it is re-armed or cancelled. It releases only the registration it armed, so a
successor under the same id keeps its wake-up. Plugins register over the wire with poll —
cadences and deadlines, each at its own priority — and are ticked with a
core:poll:<name> event.
Everything that re-arms itself rides the clock: prepared-model debounce,
readiness and maintenance wakes, the activation readiness ladder, the yank’s
pasteboard wait, AX observer registration retries, plugin and popup restart
backoffs, the config, plugin-file, plugin-state, application-directory and
ambient-location debounces, the network and volume change coalescers, the
catalog notify throttle, the inspector’s publish window, a covered widget’s
grace and the latched hyper key. A plain one-shot asyncAfter remains only where no clock is involved:
a timeout bounding one operation (request and perform deadlines, the kill
escalation of a stopped child, the first-paint budget, a focus hand-off), the
fixed timing of an interaction in progress (synthesized key, scroll and click
spacing, sequence and hover-dwell timeouts, recapture and caret re-arm turns,
toast and click-feedback expiry, a popup’s first frame, terminal frame pacing)
and a bounded fan-out of settle passes after one event (screen-change
recovery, window-border reconciliation, the Accessibility-grant re-check,
the held button’s catch-up after a scroll in HOLD).
Scripts/check-guardrails.sh rejects any other timer source, and blocking
sleeps outside the three single operations it names.
Nothing a poll produces can be seen while the displays sleep, the session is
switched out, the login window (a locked screen) or screen saver is in front,
or the system is going to sleep, so the scheduler holds every registration —
plugin cadences, status jobs and clocks, the clipboard watcher — while any of
those holds (PollScheduler.Gate, fed by the same workspace notifications
that hide the window border). Registrations keep changing while held; the
last release runs each one that fell due meanwhile exactly once, then
repeating ones return to their grid. The scheduler’s clock counts time spent
asleep (CLOCK_MONOTONIC), so a wake finds those deadlines overdue rather than
each waiting out its interval again. Clients never check the gate themselves.
Each registration carries a priority — there is no default — which sets how
much slack its wake-up allows: system (5 ms) for input-adjacent probes whose
lateness is visible, high (25 ms) for a value on screen that the user
watches change, normal (100 ms) for ordinary sampling, settles and
refreshes, low (1 s) for background upkeep, remote pulls, retries and
backoffs. Generous slack is what lets the kernel slide a tick onto an
interrupt it was already taking, and the tightest priority riding a wake-up
sets it, so a lax client can never loosen a demanding one. Plugins choose
among high, normal and low; system is core-only.
| Owner · registration | Cadence or deadline | Priority | Why |
|---|---|---|---|
Status controller · core:status_bar |
Next clock boundary or carousel rotation | high | The change on screen is the deadline |
Status controller · core:status_bar |
Job or source re-run, placeholder, throttled publish | normal | Output lands when the command ends; tightened to high when a clock deadline falls inside its slack |
Menu-bar reveal probe · core:menu_bar_reveal |
80 ms while the pointer is in the band | system | Lowering the bar late hides the native menu |
Activation repair · core:activation_repair:<n> |
Readiness ladder 50–750 ms, or one 150 ms retry | system | The user is waiting on the hints |
Yank · core:pasteboard_wait:<n> |
15 ms probes, at most 20 | system | The yank waits on each probe |
Popup restart · core:popup_restart:<id> |
0.1 s first step, then 1–30 s | high, then low | A quit popup is on screen; a crash loop is not |
Prepared model · core:prepared_model_refresh:<pid>, …_readiness:<pid> |
80 ms debounce; readiness ladder | normal | Warms a model ahead of an activation nobody asked for yet |
Prepared model · core:prepared_model_maintenance |
Before the 1.5–30 s freshness ceiling | normal | Slack stays inside the 250 ms lead |
Clipboard watcher · core:clipboard |
0.5 s while a plugin subscribes | normal | No pasteboard notification; a late copy reaches history late |
Secure input · core:secure-input |
2 s for the life of the resident | low | The tap receives no keys while the flag is on, so this poll is what sees a stuck hold |
Secure input release · core:secure-input-fix |
0.2–0.7 s steps of one release | normal | Spacing an activate, blur or restore already in progress |
AX observer retry · core:ax_observer_retry:<pid> |
60 ms–3 s ladder | normal | Events go unobserved until it lands |
Debounces · core:config_reload, core:plugin_state_refresh, core:app_directories, core:ambient_location, core:plugin_reload:<id>:<n> |
100–750 ms after the last event | normal | Someone is about to look, nobody is watching the instant |
Change coalescers · core:network_changed, core:volumes_changed |
500 ms window | normal | Plugins re-read what changed |
Catalog notify · core:catalog_notify:<id> |
At most once a second | normal | An open flashlight re-reads the store on it |
Inspector publish · core:inspector_publish:<id> |
100 ms window while a stream is open | normal | A diagnostic page |
Plugin restart · core:plugin_restart:<id>:<n> |
1–30 s backoff | low | Catalogs survive a restart |
Plugin status grace · core:plugin_status_grace:<id>:<n> |
10 s after a planned reload | low | A stale label blanking late is not watched |
Liveness sweep · core:plugin_liveness |
30 s while a plugin runs | low | Background upkeep |
Covered widget · core:widget_hidden:<name> |
30 s grace | low | Nobody can see a covered widget |
Plugin cadences and deadlines · plugin:<id>:<name> |
Plugin-chosen | Plugin-chosen: high, normal or low | See the status plugins table |
Registrations are also scoped to when they can observe anything at all: the
pasteboard watcher runs only while a plugin subscribes to clipboard.changed,
and the menu-bar reveal probe only while the pointer is in the band. Secure
input is the exception that stays registered: the tap receives nothing while
the flag is on, so core:secure-input is how a stuck hold is noticed at all.
Main-thread
stalls need no poll at all: the run loop reports each busy stretch itself, and
the HTTP inspector pushes its state on the changes it shows.
Visible regions subtract every higher window from the active one, except fully
transparent windows. The frontmost app’s window can sit under another app’s
normal-level window only while the window list lags an activation, so when that
leaves it fully covered the regions are recomputed without those windows
([discover] frontmost_window_covered); floating layers still cover it. A
system dialog that never becomes the frontmost app — Gatekeeper’s card
(CoreServicesUIAgent), an authentication panel — is still the surface f
and EasyMotion walk: an elevated dialog-sized window, or a normal-level
window of a non-regular process, in front of the focused app. Picture in
Picture, Stage Manager, menu-bar status strips, and another regular app’s
ordinary window are not that surface. The walk hints a window’s close button
when it has one, and a dialog’s default, cancel, and help buttons. A click on
an agent that cannot become frontmost is posted immediately. A walk
that leaves the window no visible region is never cached as a prepared model
(see prepared models). An
activation result that is empty, or collapsed below a tenth of the app’s last
trusted count, is degenerate, and a cached model judged the same way is a miss.
A degenerate activation walk is walked once more and the fuller result is
served. An app that builds its tree on demand is re-walked after 150 ms. A
runtime that builds it asynchronously (Chromium, Flutter, Gecko) climbs a
readiness ladder instead: after 50, 100, 200 and 400 ms a bounded probe
(AccessibilityReadiness: a web area with content, or more than a
decorated window) decides whether the tree is there, and the one extra walk
runs as soon as it is, or after a final 750 ms. The waits are system
deadlines on the shared clock, never sleeps, and the activation going away
ends the climb.
When a volatile provider (tmux) declined and the app’s own tree has never
produced targets, the empty walk is the answer and is not repeated. Empty
endings stay silent in the UI and are logged ([latency] hints_empty).
The core reasons about an app’s traits, read once from its bundle
(AppTraits), never about its name: a web browser declares the http and
https URL schemes; Gecko ships Contents/MacOS/XUL; Chromium (browsers,
Electron and CEF apps) ships renderer helper apps, and a browser’s installed
web app (PWA) is a shim whose executable is app_mode_loader or whose
Info.plist carries CrAppModeShortcutID; Flutter ships
FlutterMacOS.framework. App-specific knowledge — shortcuts, which apps are
terminal emulators — is plugin data. Browser pages keep a Vimium-style
semantic allowlist. Inside any other app’s web view, a control-sized AXGroup
or AXListItem with a press action is a target too — the cards and rows those
apps build from clickable divs. Chromium and Flutter build their accessibility
tree only after an assistive client sets the enhanced-UI flags, so Flash sets
them at launch, on each focus change (the focus walk then waits for the
readiness probe) and on each walk; every other app builds its tree on demand
and never gets them.
Gecko turns its accessibility mode on when its tree is read; while the mode is
on, programmatic window moves become a slow, often incomplete animation, and
switching it off discards the tree, which the next read finds empty while it
is rebuilt. GeckoAccessibility owns the mode for every Flash AX client, under
one lock per process, and records which processes have it on because Flash
turned it on. That mode stays on after a walk, probe or commit while the app
is focused, so the next one reads a built tree. It is switched off, away from
the main thread, when another app is activated, on a Space change, and when
the monitor stops (at quit, which waits for it, bounded); it is forgotten when
the app quits; and withWindowManagement switches it off immediately before
every window geometry write, lingering or not, until the next tree operation
turns it on again. Without focus reports nothing lingers. A mode another assistive
client turned on (VoiceOver) is never switched off. A third-party window
manager moving a focused Gecko window meets the same flag Flash already leaves
on in every Chromium app, and VoiceOver in every app.
Inside UIKit content (the iOSContentGroup a Mac Catalyst or iPad app’s
window hosts, as in Messages and WhatsApp), each conversation row or message
is one leaf AXStaticText, and every element there carries a press action; a
control-sized leaf is a target, ranked like a generic container. A text-input
role there enters INSERT only when its focus is settable, which excludes
read-only message bubbles.
Finalization rejects invalid geometry, filters visible regions and deduplicates overlap with smaller frames winning, except that a semantic control always beats a generic pressable container or UIKit text cell over the same area, and a typing surface beats a same-size control laid over it (WhatsApp’s search field). Visual rows anchor their vertical tolerance to the row’s topmost target, then sort horizontally with stable tie-breakers. A pairwise “within eight pixels” comparator is not transitive and must not be used as a sorting predicate.
See prepared hint models for scheduling priorities, maintenance deadlines and suppression thresholds.
Activation progresses through idle, discovering, pendingCommit, and
committing. Discovery and a not-yet-started commit may be replaced immediately.
An active gesture retains ownership until release; the latest replacement waits
behind it. Old completions neither clear a newer activation nor apply stale mode
or navigation effects. A commit whose target is not frontmost raises it and
waits for that app’s activation before posting; the commit carries that pid as
its handoff, so the activation it waits for, Flash resigning key and the Space
change the raise can cause are the handoff, not a cancellation, before and
during the gesture alike. Activating any other app still cancels, and the
handoff timeout still posts. Pointer-session reset consumes a held-button release once.
Shutdown lets already-started finite mouse synthesis finish.
Hints retain the target captured by discovery. Before a commit, AX targets
re-read the retained element’s identity, properties and geometry; scrolling can
move the click with that element, while a replaced, hidden or changed control
cancels the selection. The final hit must still belong to that element. Dock
items and scroll containers retain the same AX identity; native menu-bar items
retain their owner PID and WindowServer window ID as their positions change.
Plugin targets use a bounded fresh hints request and require one matching role,
label, URL and optional source context_id in the same captured application
window. Tmux supplies backend, client, server-lifetime, session, window and pane
identity, so another server’s %1 cannot replace the selected pane. Missing or
ambiguous matches cancel instead of falling back to the old coordinates.
Resolution runs off the main loop and its result belongs to the activation
generation. Configuration changes cancel existing hints before changing layout
or actions.
Every click sends a mouse-move event with the same modifiers before mouse-down and mouse-up, even when the pointer is already at the target. Terminals use this hover event to prepare their link action; Shift only on the button events can arrive too late for Alacritty’s cached link highlight.
Flash holds status-bar publications while status hints are visible and until a selected host click finishes. A popup-only hint retains its captured content and anchor until the pointer leaves it. The latest queued status model is then applied. Live mode labels still bind to the current mode; a running terminal’s own output remains live. External targets still receive a real host mouse event: Flash cannot freeze another application’s state atomically with its click, or distinguish objects that expose identical reused AX/plugin data.
Hint labels hide the pointer with NSCursor.setHiddenUntilMouseMoves, which
AppKit ignores from a background app, and Flash stays in the background while
the session tap reads keys. At launch BackgroundCursor sets the window
server’s private SetsCursorInBackground connection property, resolved at
run time, so a macOS without it only leaves the pointer visible.
TrackpadGesture emulates trackpad gestures the same way, through private
interfaces resolved at run time. Zoom, rotate and smart zoom post gesture
CGEvents with the undocumented gesture fields set (event type 29, the gesture
subtype, magnification, rotation and phase). Mission Control (the gesture
and system_key --key=mission_control), App Exposé, show desktop and, before
macOS 26, Launchpad send Dock notifications through
CoreDockSendNotification. A macOS
that drops either only makes those gestures do nothing.
Targets use global NSScreen coordinates: primary-screen bottom-left origin,
positive Y upward. Accessibility and CGEvent use the primary-screen top-left
origin, positive Y downward. Convert with the primary screen’s height, never the
maximum screen extent. Screen unions start at CGRect.null, preserving negative
origins on displays to the left or below the primary display.
All overlay layer mutations disable implicit Core Animation actions. Add new
animated properties to OverlayPanel.noActions as well as using disabled-action
transactions. An empty discovery result stays silent.
The persistent status bar draws in its own click-through StatusBarWindow,
which shares the overlay panel’s union-of-screens frame and hosts only the bar
layers. It is ordered above the native menu bar and its extras, so a reveal
Flash did not ask for (a menu key equivalent flashing its title, Flash becoming
active, a wake) slides those windows in behind the bar instead of painting over
it for a second. The pointer is the exception: while the reveal probe sees the
native menu bar actually revealed under the pointer, the bar window drops below
it and the click windows turn click-through, so reaching for the top edge still
gets the real menu bar and its clicks. Hover goes with it: StatusBarHoverState
silences the wash, previews and pointing hand for as long as the reveal lasts,
and only then; the top point row, where a pointer thrown at the bar rests,
hovers like the rest of the band. Enabling the bar also asks macOS to
auto-hide the native menu bar, and disabling it restores only a menu bar Flash
itself hid. The overlay panel keeps the focus border at .floating and
transient surfaces at the screen-saver level, so status hints still render
above the bar and transient teardown never detaches it.
On each display the bar is as tall as that display’s own native menu bar, read
by level and bounds from WindowServer’s main-menu window, and window_move
slots reserve the same band: both take it from one screen snapshot
(ScreenSnapshot.statusBarHeight), never from a live visible frame, which
moves as soon as Flash auto-hides the native menu bar. The click windows cover
exactly the painted bands, and the focus border outlines a window only below
any band it runs under, so its stroke is never drawn beneath the bar.
AppKit’s app-wide menuBarHeight follows whichever
display last hosted the active menu bar, so it is only the last resort. The
heights are re-read on display changes and before every recovery pass after
one, never on a Space switch or wake, so a restored window lands exactly where
the next window_move would put it. Restores and moves accept a placement only
within a point of its slot; the two-point tolerance recognises slots, it does
not accept placements. Accessibility cannot read or move windows while the
session is locked or asleep, which is when a wake reports new displays, so
restores wait for the session: a window counts as restored only once its frame
was read back in its slot, and one whose frame was unreadable stays pending
until the session resumes or the window is focused.
The overlay’s AppKit-owned content view contains a layer-hosting drawing view
and the native command editor as siblings. Flash replaces only the drawing
view’s contentLayer.sublayers; replacing the native container’s sublayers
detaches AppKit’s editor backing layers during transient cleanup. The drawing
view has no native subviews and resizes with the panel. This follows AppKit’s
layer-hosting ownership contract.
Mode projection describes render/input state without changing mode as a drawing side effect. Reentrant effects enqueue events behind the current transition. Transient surfaces preserve their return state and obey advanced-mode eligibility. See normal mode for the input latency and transition contracts.
The finder warms catalog snapshots independently of query-specific work. A live search result belongs to its session, query generation and source; a timeout or late preparation from an older query cannot replace current rows. Rendering a new query starts from the catalog plus only the current live result set.
Waiting for a child process always ends on its kernel exit event
(ProcessExit, a kqueue NOTE_EXIT or a process dispatch source), never a
sleep loop: plugin stops, status-job shutdown and deferred terminal reaping
return the moment the process is gone, and deadlines only bound one that is
not. Plugins, status jobs and persistent popups start once the login
environment resolves, since a child keeps the environment it spawned with; a
resolution that fails keeps the seeded one. Status jobs retain their running process and value when effective definitions
are unchanged. Replacements invalidate tokens before stopping children in one
bounded batch. The scheduler arms only evaluated jobs, active cycles, the
next boundary of the finest time unit a visible surface shows, and pending
output publication. Inactive shell jobs and their output
are discarded; inactive named sources retain last-good values without wakeups.
See status formats and popup ownership.
Plugin manifests are immutable runtime definitions. A changed manifest replaces its process/adapter registration; a settings-only update reconciles the existing definition. Host RPC replies are tied to the child generation that issued them. Transport queues, encoded payloads and request admission are bounded. Details, limits and the conformance suite live in the protocol and Rust SDK documents.