Fail closed on unknown AX window observations#335
Merged
xrf9268-hue merged 2 commits intoJul 18, 2026
Conversation
xrf9268-hue
approved these changes
Jul 18, 2026
xrf9268-hue
left a comment
Owner
There was a problem hiding this comment.
Approved against exact head 2636a03 after Standards + Spec review and exact-head macOS runtime validation.
7 tasks
xrf9268-hue
added a commit
that referenced
this pull request
Jul 21, 2026
* perf: instrument and bound main-actor AX observation cost Measurement-first work for the remaining synchronous AX observation on the main actor. Every window observation now runs through a phase-labeled wrapper (preAction, activationConfirmation, deactivationConfirmation, launchContinuation, launchConfirmation, snapshotFallback) that emits an os_signpost interval under com.wink.app/AXObservation and an AX_OBSERVATION_SLOW diagnostic when a capture exceeds the documented 50ms observation latency budget. The observation result and all toggle decision logic are unchanged. Measured baselines via scripts/profile-ax-observation.swift (the exact live AX sequence, Apple silicon, macOS 15, 30 iterations): 1 window p50 0.12ms/max 0.17ms; 20 windows p50 0.50ms/p95 8.8ms; 100 windows p50 4.8ms/p95 12.5ms - the healthy path stays far inside the budget, so no structural optimization is applied. The one measured violation is an unresponsive target (SIGSTOP fixture): each AX roundtrip blocks until the messaging timeout, ~18s per observation at the 6s global default. The live capture therefore sets a bounded 1s AXUIElementSetMessagingTimeout on the app and window elements (~3s worst case per observation); a timeout surfaces as a failed read that the #335 fail-closed handling already treats correctly. The instrumentation seam adds defaulted now/onSlowObservation closures to ApplicationObservation.Client, so existing fakes and call sites are source-compatible; tests cover pass-through, over/under-budget reporting, and the snapshot-fallback phase. Fixes #321 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: scope the bounded AX messaging timeout to the app element Review follow-up: stamping the 1s timeout on window elements would let a timed-out per-window kAXMinimized read count a stalled app's minimized window as visible (fabricated stable-activation evidence) and drop it from minimizedWindows (lost unminimize), and the sticky per-element stamp would silently bound the later unminimize write through the same stored refs. Per-window reads keep their pre-existing global-timeout semantics; the app-element bound alone still caps the hung-target observation at the measured ~3s (the measurement harness already stamped only the app element). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: yvan <yvan.yang@outlook.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #320
Summary
isHidden == trueremains an independent affirmative signal.Acceptance mapping
frontmost=false+isHidden=false:failedWindowObservationDoesNotConfirmDeactivationOrClearSessionproves no confirmation and preserves the same stable/pending/deactivating ownership.failedWindowObservationCanConfirmWhenTargetIsHiddenprovesisHidden=truemay confirm even when the AX read fails.successfulZeroWindowObservationStillConfirmsDeactivationpreserves supported windowless behavior.persistentFailedWindowObservationTimesOutAndRestoresStableSessionproves the existing timeout restoresactiveStablerather than clearing ownership.windowObservationSucceeded=false,targetHidden=false, andconfirmed=false, then confirms only after the real hidden-state transition.Exact-head verification
118deead760eeb847cdc0d4b2b1697a88fdb630b2636a036fc6fd9deca8281dcc630d03dba49c833WINK_AX_WINDOW_OBSERVATION_FAULT_INJECTION— PASS.swift test— 472 tests / 44 suites PASS.swift build -c release --skip-update— PASS.bash scripts/package-app.shin injected and clean modes — PASS.shellcheck scripts/package-app.shplus invalid-profile, mutual-exclusion, and production-cleanup cases — PASS.Exact-head packaged runtime evidence
/Users/yvan/developer/Wink/.worktrees/issue-320-ax-window-observation/build/validation/issue-320-2636a036fc6fd9deca8281dcc630d03dba49c833-20260718T071504Z/injected/Wink.app.abc2b72baf3db0f6f343adda0113ce717edd413f18a461b0b816ecc3cafa4378./Users/yvan/developer/Wink/.worktrees/issue-320-ax-window-observation/build/validation/issue-320-2636a036fc6fd9deca8281dcc630d03dba49c833-20260718T071504Z/clean/Wink.app.d079a97346440c0715b208474ce25f0815427cdda14de592d6d05fab2accda6c.codesign --verify --deep --strict. The clean bundle has no validation profile/revision keys or fault-injection marker.4E0F5B4F-0091-437B-BC83-AC46FEEEADC0, generation 1: exactly one failed read stayedphase=deactivating confirmed=false; three following successful reads still saw one visible window and remained unconfirmed; a real hide then emittedTOGGLE_HIDE_CONFIRMEDat 252 ms./Users/yvan/developer/Wink/.worktrees/issue-320-ax-window-observation/build/validation/issue-320-2636a036fc6fd9deca8281dcc630d03dba49c833-20260718T071504Z/logs/injected-runtime.log./Users/yvan/developer/Wink/.worktrees/issue-320-ax-window-observation/build/validation/issue-320-2636a036fc6fd9deca8281dcc630d03dba49c833-20260718T071504Z/RESULTS.md.State restoration
821b3cfd866445435ace749f4b711c63e7f41a8c096d8b162134bb689e764113./Users/yvan/developer/Wink/build/Wink.apprelaunched.Validation Status
Docs sync check