src/window-tracking/AttentionModelSpecs.md
The kernel behind AttentionOrderSpecs.md, which is the one-page statement of what may move the window
order. Attention as two levels: which app has it, and which of that app's windows has it. The two come from
different places and are never compared with each other.
frontProcess <- NSWorkspace, and the click's pid
focusedWindow[process] <- that app's own answer, the click's wid, one bounded read
the visible front == focusedWindow[frontProcess]
The measurement base is twelve focus scenarios recorded with all four signal sources on one clock. Including follow-up N, Accessibility names the window in 10 of 12 scenarios and is the earliest window-naming source in 8, so it is the primary rather than the fallback. The WindowServer is never the best source in any scenario where anything else speaks and has no exclusive scenario at all, so no physical input can name attention — 808 and 815 are not part of attention. A lifecycle invalidation may erase a cached answer when its window ceases to exist; it never names or moves the front.
frontProcess is set by an app activation, and by the two namers that carry an app with them: the click
and AltTab's own switch. An app's own answer never changes which app is in front.focusedWindow is per process. Two apps holding different facts is not a conflict, it is the normal state..front and does nothing otherwise.readFocusedWindow, and
that is the only thing that emits it. A plain activation names no window from any source when the app's
focused window did not change; that is the one hole nothing else fills.AttentionEngine.axSemanticFocus). A raise burst produced four true answers inside 0.6 ms and
ended on the window it started on; a deferred burst emits in generation order with the click's outcome
last.front — the visible front moved to this windowrecorded — a fact landed and nothing visible moved: the process is not in front, or it named the window
that already held the frontreadFocusedWindow — activation with no fact; fire one kAXFocusedWindow read, off-main, 250 ms timeoutignored — stale generation, stale sequence, or an ineligible targettestAnswerFromABackgroundAppRecordsAFactAndMovesNothing — the two levels really are separate.testActivationWithNoFactAsksForOneBoundedRead — the one hole, and the one trigger.testActivationWithAFactMovesTheFrontWithoutReading — a known app costs no read.testInvalidatedWindowMakesTheNextActivationReadAgain — a dead cached window is not resurrected.testClickCarriesBothLevels — the click names the app and the window at once.testAnAppAnswerNeverChangesWhichAppIsInFront — a fact is never a command.testTheAppCorrectsTheClickBecauseItArrivesLater — the outcome beats the prediction, by arrival.testTheBoundedReadCannotOverwriteAFresherAnswerFromItsOwnApp — where per-process monotonicity bites.testAcrossProcessesNothingIsCompared — and where it deliberately does not.testAnUnknownReadNeitherMovesNorErasesTheFront — unknown is a value.testTheVisibleFrontIsNilWhileTheFrontAppHasNotAnswered — nil means nobody has said.testTheRepresentativeIsWhatLandsInTheFact — the switcher's stand-in window is the target.testAnIneligibleTargetIsRefused — a missing or cross-process representative is not guessed at.testAStaleGenerationIsRefused — a dead process's callback is not current.testARelaunchedPidForgetsThePreviousGenerationsFact — a reused pid inherits nothing.testProcessExitForgetsItsFactAndReleasesTheFront — an exit releases rather than hands over.testARaiseBurstEndsWhereItStarted — #5974's shape needs no guess made in advance and taken back later.testNamingTheWindowThatAlreadyHoldsTheFrontMovesNothing — a repeat is a fact, not a move.testAStaleAnswerFlushedByAnUnwedgeLosesToTheClicksOutcome — the measured race resolves on arrival order.