# STEP20C applied scene / input probe **Status: EXPERIMENTAL / PARTIALLY_FEASIBLE.** The protocol was recorded before the private patch and the bounded replay below. STEP20C remains **NEXT**; production multi-output Mosaic is **NOT YET SUPPORTED**. This protocol authorizes only disposable private instrumentation and fixed headless geometry. It changes no production source, clipping policy, UX, public ABI, package or installed dependency. ## Question and hypotheses Can the scene actually applied at output-state construction be observed alongside the scene used by a later real pointer hit test, without inventing a native atomic transaction generation? The previous buffer probe identified 32 committed buffers and headless presents, but its global `bi_hold` released IDs 3, 15, 21 and 27 when HEADLESS-2 repainted after HEADLESS-1 commits. That private instrumentation defect prevents claiming those four buffers were retained until present. The corrective patch is separate from the historical fixture. Hypotheses: a per-output hold survives a different output's repaint; sampled visible/hidden/returned applied scene and clip agree with selected buffer pixels; hit testing sees the sampled applied clip; actual pointer delivery may differ from the scene hit because of XDG grab semantics. A logical camera target, IPC acceptance, `wlr_scene_node_at()` result and headless `present` are different observations. ## Ownership and scene audit boundary `wlr_scene_output_build_state()` can install a swapchain buffer in the pending output state. The state owns its reference until `wlr_output_state_finish()`. A private `wlr_buffer_lock()` taken while that state is live owns a separate reference; the matching unlock must occur on present, same-output replacement, rejection, timeout or output destruction. The lock prevents swapchain slot reuse while held. No event-state or pixel pointer may outlive its callback or access scope. Use output identity plus native `commit_seq`, not an address alone or predicted sequence. Never give a no-buffer commit an earlier buffer fingerprint. Sway's logical `scroll_x` is not itself an applied scene generation. `transaction.c` applies current container/output scene positions and visibility, including tiled translation; the private Mosaic/SceneFX patches set tiled and owner-derived popup clips. XDG map/unmap and popup hierarchy changes can mutate scene trees separately. Pending geometry, current geometry, applied node position and logical camera are logged as distinct fields. SceneFX clip propagation constrains render, damage and `wlr_scene_node_at()` in the private build. The experimental epoch, if used, advances only at explicitly instrumented mutations; it is **not** a native SwayFX transaction generation. Mapping, effects, damage, direct SceneFX mutations and uninstrumented upstream paths remain outside any partial counter until proven otherwise. `sway/input/cursor.c` calls `node_at_coords()` and scene hit testing for pointer paths, while `sway/input/seat.c` relays button notifications to wlroots. XDG grabs can filter delivery and issue `popup_done`. Record the requested and resulting global coordinate, hit node/surface and current applied snapshot at the actual hit call, then separate seat notification and client-observed events. Never infer delivery solely from node-at, and never assign an input event a fictional frame number. ## Private instrument and modes Use a distinct correction patch after the frozen presentation and buffer patches. A per-output, single-slot hold is bounded to one selected HEADLESS-1 buffer in this matrix. Other-output repaint/present cannot evict it. A same-output commit with a *new* buffer may replace it; a configuration-only commit cannot. Release is idempotent by clearing the slot before unlock. A two-second post-commit timer bounds waiting for an absent present, but does not bound a stalled GPU readback. Track lock, release, timer and output identity in logs. Paths not safely triggered are **NOT TESTED**. Mode 0 records scene, native commit/present and input with no new buffer lock/read. Mode 1 reads at most three selected true buffers: visible 400, hidden 1280, restored 400. Mode 2 additionally reads those buffers a second time only while the owned lock remains live at matching present. SceneFX FX texture readback can synchronize GPU/CPU internally. Compare stage timings and event counts against mode 0; do not attribute total cost solely to `glFinish()`. ## Fixed matrix and correlation HEADLESS-1 `(-800,0)` and HEADLESS-2 `(0,0)` are each 800×600, scale 1, normal transform. The client hierarchy is TOPLEVEL → POPUP_A → POPUP_B, with real pointer-button serials for both grabs. Confirm outputs and native commit/present events. Run stable 400 with a real B hit; 1280 with the owner fully hidden; return to 400 with the same mapped A/B; then at most three hide/return pairs. In distinct runs, click visible B, clipped B on HEADLESS-2 and hidden owner; send six close alternating commands; compare unmodified SwayFX and private `splith` controls without inventing a Mosaic camera for them. Each record uses a monotonic timestamp where available. Preserve PRE_BUILD, POST_BUILD, COMMIT_ACCEPTED and PRESENT_CALLBACK applied snapshots and the native `(output, commit_seq)` link. Separately preserve the applied snapshot at hit test, the wlroots seat notification and the client delivery. A matching experimental epoch across observations is limited evidence only for mutations covered by its increments; time ordering is not an atomic identity. Preserve coalesced commands and commits without observed present as such. ## Acceptance and stop rules **FEASIBLE** requires same-output buffer lifetime through present in the tested paths, observed applied scene at build and hit, actual pointer delivery agreement, bounded cleanup and no blocking regression. **PARTIALLY_FEASIBLE** covers useful scene/render/input observations with incomplete mutation coverage, temporal identity or error-path tests. **INCONCLUSIVE** applies when safe observability is unavailable. **BLOCKED** requires a reproduced memory violation, crash, visual leak, invisible hit or blocking regression. A clean unsupported readback is not a compositor defect. Stop on a reproducible crash and retain evidence. Caps: three transition pairs, three selected readback buffers per run, one held slot per tested output, 8 MiB per copied buffer, two-second post-commit hold timeout, bounded captures/logs and explicit process cleanup. No scaling, rotation, migration or hotplug is tested. Even a successful fixed headless matrix does not authorize production STEP20C. ## Measurements The primary private laboratory is `/tmp/swayfx-mosaic-nested-grab-l2HvAkRe/`, rebuilt from the frozen buffer fixture. Its corrected output, transaction, cursor and seat sources were byte-identical to the independent rebuild in `/tmp/swayfx-mosaic-nested-grab-8CiDjoij/`. The corrective patch has SHA-256 `8a0766536fa728a6be6ef29aea3be67abbfad107f2fcd6048718097499a4d88f`. Only these disposable copies were changed. The final-patch independent one-pair run `private_mosaic-buffer_cycles-asi2-1791782683026614149` again read three buffers and matched all three native commits and headless presents, with three byte-identical second reads and one unlock per hold. Its separate visible, clipped and hidden pointer runs repeated the client-delivery results. The fixed geometry was confirmed by IPC in the run snapshots; each A/B chain used two real pointer-button serials (the three-pair run requested A=55 and B=60, each used unchanged). All named runs exited without runner or XDG protocol failure. ### Buffer ownership and pixels Mode 0 (`private_mosaic-buffer_cycles-asi0-1791781864140604493`) recorded 95 accepted native commits and 93 headless presents, with no probe lock or read. Mode 1 (`...asi1-1791781908846694616`) recorded 87/85 and read three selected buffers. Mode 2 (`...asi2-1791781917161051239`) recorded 88/86, read those three selected states and read each again at its matching present. The two unpaired accepted commits in each run were initial output sequence 2, one per output: **UNEXPLAINED / NO PRESENT OBSERVED**. The selected HEADLESS-1 buffers in mode 2 have actual native sequences 23, 27 and 30, with experimental applied epochs 13, 15 and 17. All three are 800×600 GBM XB24 source buffers, read as XRGB8888 with stride 3200 and 1,920,000 bytes per copy. The SHA-256 sequence is `9e6d2f7b094fd5124c0207a30cb294acec3a01da15b90008d7f7449d27e1ee7c`, `03e815da50d0f04b16660340db19c1a03225f31bac0824d490c2ffbab0ee165c`, then the first hash again. A/B exact-color counts are 42,200/17,940 at 400, 0/0 at 1280, then 42,200/17,940 after return. Each second read is byte-identical. Every selected buffer matched its synchronous commit callback, accepted native sequence and `presented=true` headless event; each extra lock was released **once**, on that event. No cross-output release occurred. A HEADLESS-2 `POST_BUILD` and commit intervened between the lock of buffer ID 3 and its matching HEADLESS-1 present; the buffer remained locked through that interval. This is the direct positive test against the historical scope defect. In one-pair private fault injection `private_mosaic-buffer_cycles-asi1-1791781965826777786`, ID 1 was intentionally retained after its present and released once when a later accepted HEADLESS-1 buffer commit replaced it. The other two selected buffers released on present. In separate run `private_mosaic-buffer_cycles-asi1-1791781980485708932`, ID 1 released once by the two-second timer (logged age 2,000,030,155 ns); the other two released on present. The timer and slot were cleaned in both paths. The rejected-commit and output-destroy branches compile but were **NOT TESTED** by a controlled runtime trigger. A same-output configuration commit without a selected new buffer was not a controlled lifetime test; the code condition excludes eviction, but runtime qualification is **NOT TESTED**. The historical 32-buffer readback used 61.44 MB of first copies; this selective run used 5.76 MB, a **90.6% reduction in copied bytes and first readback count**. Mode 2 adds 5.76 MB for its three second reads. Sequential runs are not a controlled latency benchmark: median HEADLESS-1 POST_BUILD→COMMIT_ACCEPTED over all frames was 0.083, 0.109 and 0.112 ms in modes 0, 1 and 2, while the three selected readback commits alone took about 4.255/1.621/1.082 ms in mode 1 and 3.287/1.004/1.025 ms in mode 2. Host scheduling, renderer work, copies and logs were not separated. The readback is therefore still materially intrusive on sampled commits; no reliable wall-clock speedup is claimed. ### Applied scene and pointer The experimental epoch increments after `arrange_popups()` handles a popup and after `transaction_progress()` completes `arrange_root()`. It is visible in PRE_BUILD, POST_BUILD and COMMIT_ACCEPTED stage logs, and at `node_at_coords()` hits and wlroots seat button notification. The final patch also records the root-popup applied clip and logical camera at each hit. The visible B hit had logical 400, applied tiled scene -400, clip x=400 and a nonempty root-popup clip. With the owner hidden at 1280, the root-popup clip was explicitly empty at the hit; the observed surface under the pointer was an ordinary surface, not a popup. The clipped HEADLESS-2 hit also found the ordinary surface while the root-popup clip remained bounded to HEADLESS-1. Nested-popup mapping remains verified in the client log, not atomically exported with the hit record. The three selected buffers carried applied tiled scene/clip `-400/400`, `-1280/1280`, `-400/400`, consistent with their decoded pixels. This is an ordered observation, not an atomic frame/scene identity. In the three-pair mode-2 run, the same owner IPC ID 8 and one A/B map persisted through all three hide/return pairs. Six further alternating IPC camera requests were accepted and returned to 400; they are not counted as six presented images. Before the final visible B button, the last observed HEADLESS-1 headless present was native sequence 48, whose COMMIT_ACCEPTED stage and subsequent scene hit both recorded epoch 37, applied scene `-400` and clip x=400. No *instrumented* mutation occurred between these observations. Buffer 48 was **not** among the three copied buffers, and the partial epoch cannot exclude uninstrumented mutations. It does not prove atomic frame-specific input agreement. Separate `run-negative-private_mosaic-{visible,b_cross,hidden_click}` controls logged actual requested and obtained global coordinates, `wlr_scene_node_at()` result, applied clip, seat notification and client events. Visible B received its button. On HEADLESS-2's clipped B portion, the hit and seat focus were the ordinary client, whose button was logged; B received no button from that point and `popup_done` occurred B then A under the XDG grab. With the owner fully hidden at 1280, the hit was an ordinary surface under the pointer, no popup button was delivered, and `popup_done` again occurred B then A. The private and unmodified `splith` controls retained the ordinary crossing B button behavior. A seat's focused-surface value is logged before wlroots notification; client logs are the separate evidence of actual delivery. No pointer event is assigned a native frame ID. The counter does **not** cover every SceneFX node mutation, client buffer commit, damage, animation, effect/saved-buffer change, direct XDG scene change, output configuration or internal grab decision. It also increments for some arrangements that may leave sampled state unchanged. Consequently matching epoch values provide only bounded negative evidence about the instrumented sites. No visual leak, invisible popup hit, memory-lifetime anomaly, crash or blocking standard-layout regression was reproduced in the tested matrix. Scaling, rotation, migration, full effects/damage and physical presentation remain **NOT TESTED**. **Verdict: PARTIALLY_FEASIBLE.** Per-output reference ownership fixes the historical cross-output release in the tested path, and applied scene, buffer, native present, scene hit and actual client delivery can be observed separately. The partial epoch and intrusive readback cannot yet establish a complete temporal scene/render/input generation. The next minimal gate is to instrument or formally bound every applied scene and hit-affecting mutation, then repeat a low-impact selected-buffer/input test against that complete boundary. This is no production decision. ## Proof manifests and validation A further fresh, bounded replay in `/tmp/swayfx-mosaic-nested-grab-OWIvboDB/` rebuilt private SceneFX and Mosaic from the preserved fixture chain. One visible/hidden/returned pair produced 65 accepted commits and 63 headless presents in trace-only mode, with no buffer lock, then 65/63 in second-read mode. All three selected HEADLESS-1 buffers matched native presents, had byte-identical second reads, and were unlocked once at their matching present. A HEADLESS-2 build/commit occurred while the first HEADLESS-1 reference was retained. The three states repeated clip x=400/1280/400 and the visible/hidden/ visible color counts. Fresh visible, clipped and hidden pointer runs reproduced the B delivery and XDG dismissal distinction; unmodified and private standard `splith` controls delivered the crossing B button. This is a targeted replay, not a new matrix qualification. The primary and independent labs retain structured `results-asi/`, raw buffers, captures, event logs and source/artifact SHA-256 manifests. The rebuild starts from published Mosaic `11c80a40f20240a2ca137d412f6e3b711c5d5f80`, inherits private Mosaic `cd550382b311efdbfe3ed892f4e1d281d852e754` and SceneFX `37ccd723bef49e6891156ffafce8f549f01446cc` through the frozen fixture chain, and uses pinned SwayFX `fd71a6bdc061bd633b488ae7b83e8a6981d22f86` with wlroots 0.20.2. The five-source manifest in each lab has SHA-256 `0ef45e3e4d7623498d20db8a290ca1dd7bc6cdd6e69a2276e96a01ad1ae475cb` and all five source checks pass. The primary lab's 729-artifact manifest has SHA-256 `f7cdefe8d90e2548c927951341b37f8c72b2ad3e64dee53fda6f43438f2b8ef9`; the final-patch independent lab's 189-artifact manifest has SHA-256 `d3f3782538646e37444fde3fd68a7cb494c50dfa190e6df60f176d5ddd1e47ce`. All listed available artifacts passed `sha256sum -c`. The older main-lab intermediate runs remain in the primary manifest as negative development evidence; final-patch reproduction is distinguished by the independent lab path and source-equivalence check.