Note
🤖 Claude Opus 5.5 responding on behalf of samanyudas
The timing logs confirm the cause for the late starts: focus_with_raise blocks the engine's main loop before the slide's first frame.
The raise does not time out, though. Finder answers AXRaise in 66 to 154 ms, and no frame runs until it returns.
This records the first acceptance criterion. No behavior is changed yet.
What I ran
- Build:
mainat ffefedfa, engine a20f56e, plus localtracing::info!timing logs (engine commit f97a13e, not pushed). The logs time each call infocus_with_raise, every engine frame that runs longer than 20 ms, each pacing hold, and each window writer call. - verify-miri runs
20261106-202516(10 slides to System Settings and 10 to Zen) and20261106-202719(10 slides from T3 Code to Finder with Control-Option-6, and 10 back with Control-Option-3). Row on the 27-inch display: Spotify, Zen, T3 Code, Ghostty, System Settings, Finder. Synthetic key presses, window-server bounds sampled on every change. - Latency is measured from the engine's own
key commandlog line. The key tool's "posted" time, which the original report used, comes 50 to 143 ms after the engine has already logged the key, so it reads too low.
Slides to Finder (run 20261106-202719)
| Slide | AXRaise | focus_with_raise total | First engine frame | First window moves |
|---|---|---|---|---|
| 1 | 154 ms | 165 ms | +169 ms | 192 ms |
| 2 | 79 ms | 90 ms | +93 ms | 142 ms |
| 3 | 85 ms | 93 ms | +94 ms | 150 ms |
| 4 | 7 ms | 14 ms | 39 ms | |
| 5 | 66 ms | 80 ms | 130 ms | |
| 6 | 84 ms | 93 ms | 133 ms | |
| 7 | 69 ms | 82 ms | 118 ms | |
| 8 | 73 ms | 78 ms | 96 ms | |
| 9 | 123 ms | 131 ms | 183 ms | |
| 10 | 71 ms | 85 ms | 107 ms |
- 8 of 10 slides start later than 100 ms. The first window moves 18 to 57 ms after
focus_with_raisereturns, in every slide. - In the 3 slides I checked frame by frame, the first pacing frame of the slide runs within 3 ms after the raise returns. Before that, neither a window nor a photo moves.
- For Finder,
SetFrontProcesstakes 4 to 12 ms andmake_key_window1 to 5 ms. The raise is the cost.
Why it blocks
focus_entity (src/ecs.rs:1084) triggers FocusWindow, and focus_window_trigger (src/ecs/focus.rs:757) calls focus_with_raise (src/manager/windows.rs:1388) while the command system's commands apply.
That is before the animation and frame-write systems of the same frame.
AXRaise is served on the target app's main thread, so the engine waits for that app.
Other apps are slow too
- System Settings: in 9 of 10 slides
AXRaisetook 109 to 206 ms and returnedkAXErrorAttributeUnsupported(-25205). Those slides started 142 to 250 ms after the key. - Zen:
AXRaisetook 0.5 to 170 ms. Slides to it started 83 to 255 ms after the key. - T3 Code:
AXRaisetook 0.3 to 34 ms.
So the late start is not specific to Finder. Every app whose main thread is busy delays the slide by its raise time.
Not explained, and not reproduced
- One slide to System Settings had a 282 ms busy frame with a 12 ms raise. Something else blocked that frame, and these logs do not show what.
- The second frame of most slides runs about 25 to 95 ms, even when the raise is short. I guess it is the photo capture for the windows that enter, but I have not checked.
- The 275 ms and 490 ms starts and the 179 ms pause in Zen did not come back. The slowest Finder start was 192 ms.
- Pauses in app-window bounds are a weak signal here. An entering window slides as an engine photo and its real window jumps at the end, and the sampler did not record photo panels.
Fix options
- Raise off the main loop (recommended). Keep
SetFrontProcessandmake_key_windowinline, and send theAXRaiseto the target app's window writer thread, which already serializes AX calls per app. The first frame then runs at once.raise_without_focusinraise_window_triggerraises every visible strip window on the main loop too, so it should move the same way. The engine test for criterion 3 can use a mock window whose raise blocks, and assert that the first frame write happens without waiting for it. - Run the first frame before the focus. Defer
FocusWindowby one frame. This is smaller, but the raise then stalls the second frame for the same 66 to 206 ms, so the slide stops right after it starts. - Drop
AXRaisewhenSetFrontProcesswith the window ID already brings the window forward. This needs an experiment per app first, because upstream Paneru raises for multi-window apps.
Criterion 2 also needs a decision. Measured from the key tool's posted time, it passes more easily than a user feels it. I suggest measuring from the engine's key log line and counting engine photo panels as motion.
Artifacts on the test Mac: /tmp/m326/ (instrumentation diff, drive and analysis scripts, per-run samples and engine logs).