From 899c007c37beac03a337ba58ead4326ea8db8878 Mon Sep 17 00:00:00 2001 From: fy59 Date: Tue, 13 Oct 2026 12:53:12 +0200 Subject: [PATCH] docs(mosaic): preserve static nested grab geometry fixture failure evidence --- CHANGELOG.md | 2 + docs/ARCHITECTURE.md | 13 + docs/KNOWN_LIMITATIONS.md | 13 + docs/README.md | 13 + docs/ROADMAP.md | 13 + .../STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md | 389 ++++++++++++++++++ .../step20c-static-nested-grab/README.md | 87 ++++ .../step20c-static-nested-grab/check.py | 124 ++++++ .../step20c-static-nested-grab/collect.py | 92 +++++ .../geometry_audit.py | 80 ++++ .../ipc_predicates.py | 139 +++++++ .../placement.patch | 16 + .../step20c-static-nested-grab/predicates.py | 146 +++++++ .../step20c-static-nested-grab/prepare.py | 77 ++++ .../probes/step20c-static-nested-grab/run.py | 319 ++++++++++++++ .../step20c-static-nested-grab/support.py | 100 +++++ 16 files changed, 1623 insertions(+) create mode 100644 docs/design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md create mode 100644 tooling/probes/step20c-static-nested-grab/README.md create mode 100644 tooling/probes/step20c-static-nested-grab/check.py create mode 100644 tooling/probes/step20c-static-nested-grab/collect.py create mode 100644 tooling/probes/step20c-static-nested-grab/geometry_audit.py create mode 100644 tooling/probes/step20c-static-nested-grab/ipc_predicates.py create mode 100644 tooling/probes/step20c-static-nested-grab/placement.patch create mode 100644 tooling/probes/step20c-static-nested-grab/predicates.py create mode 100644 tooling/probes/step20c-static-nested-grab/prepare.py create mode 100644 tooling/probes/step20c-static-nested-grab/run.py create mode 100644 tooling/probes/step20c-static-nested-grab/support.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b5e81..ca9cb2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### STEP20C experimental evidence +- Preserved static nested-grab fixture failure: sole unchanged-binary run confirms identity/output/camera/tiled clip and exact whole-H2 pre-popup references, then fails before any click/grab on a native-SHM versus painted-height assertion. INCONCLUSIVE / UNDECIDED; additional IPC-y/titlebar assumption invalid, offline assertion audit insufficient. Raw runner exit1 and compositor exit0 retained; popup/standard gates NOT TESTED, no retry/build/production change. Historical runner and sealed artifacts preserved. + - Preserved independent static RGB/input/standard retest: corrected shared predicate and real replay pass; exact whole-H2 RGB, H1 retained/H2 forbidden pixels, real target/reference clicks and targeted splith clip removal/input pass. FEASIBLE for this matrix. Terminal exit IPC makes runner exit 1 after measured gates and compositor exit 0; raw failure retained and offline diagnosis passes. No new compositor/library build or production change. - Preserved static focus/input/standard fixture failure: exact static binary and loaded pair verified; one run stopped before captures/input on an incorrect border-width fixture assertion. INCONCLUSIVE / UNDECIDED; camera and clip traces retained, RGB/clicks/splith NOT TESTED, no retry or production change. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 5854560..3ec2d68 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -430,3 +430,16 @@ and separately diagnosed/revalidated offline. No algorithm, build, production, package or UX change follows. Wider static popup/effect/lifecycle, complete damage/generation, ABI/maintenance/notices gates remain open. D1–D4 persist; STEP20C stays NEXT and production multi-output remains NOT YET SUPPORTED. + +The preserved [static nested valid-grab probe](design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md) +is **EXPERIMENTAL / INCONCLUSIVE**, architecture UNDECIDED. The sole unchanged- +static-binary run verified actual identity, outputs, applied camera/tiled clip +and two exact whole-H2 references, then stopped before any click or popup on +an invalid painted-height assertion (573 vs native SHM320). An additional +IPC-y/titlebar assertion was also invalid. The recorded offline test PASS had +an insufficient source-assertion audit; raw runner exit1 and all evidence remain +immutable. Root/nested grabs, geometry, popup pixels/input/dismissal and splith +are NOT TESTED in this campaign. No algorithm/production/UX/package change or +retry; budget exhausted. Correct/replay geometry predicates offline before a +separately authorized fresh static popup gate. Historical failure evidence is frozen; +STEP20C NEXT, production multi-output NOT YET SUPPORTED. diff --git a/docs/KNOWN_LIMITATIONS.md b/docs/KNOWN_LIMITATIONS.md index e3e7131..dc1f47d 100644 --- a/docs/KNOWN_LIMITATIONS.md +++ b/docs/KNOWN_LIMITATIONS.md @@ -371,3 +371,16 @@ and separately diagnosed/revalidated offline. No algorithm, build, production, package or UX change follows. Wider static popup/effect/lifecycle, complete damage/generation, ABI/maintenance/notices gates remain open. D1–D4 persist; STEP20C stays NEXT and production multi-output remains NOT YET SUPPORTED. + +The preserved [static nested valid-grab probe](design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md) +is **EXPERIMENTAL / INCONCLUSIVE**, architecture UNDECIDED. The sole unchanged- +static-binary run verified actual identity, outputs, applied camera/tiled clip +and two exact whole-H2 references, then stopped before any click or popup on +an invalid painted-height assertion (573 vs native SHM320). An additional +IPC-y/titlebar assertion was also invalid. The recorded offline test PASS had +an insufficient source-assertion audit; raw runner exit1 and all evidence remain +immutable. Root/nested grabs, geometry, popup pixels/input/dismissal and splith +are NOT TESTED in this campaign. No algorithm/production/UX/package change or +retry; budget exhausted. Correct/replay geometry predicates offline before a +separately authorized fresh static popup gate. Historical failure evidence is frozen; +STEP20C NEXT, production multi-output NOT YET SUPPORTED. diff --git a/docs/README.md b/docs/README.md index 1c04225..b9d9173 100644 --- a/docs/README.md +++ b/docs/README.md @@ -289,3 +289,16 @@ and separately diagnosed/revalidated offline. No algorithm, build, production, package or UX change follows. Wider static popup/effect/lifecycle, complete damage/generation, ABI/maintenance/notices gates remain open. D1–D4 persist; STEP20C stays NEXT and production multi-output remains NOT YET SUPPORTED. + +The preserved [static nested valid-grab probe](design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md) +is **EXPERIMENTAL / INCONCLUSIVE**, architecture UNDECIDED. The sole unchanged- +static-binary run verified actual identity, outputs, applied camera/tiled clip +and two exact whole-H2 references, then stopped before any click or popup on +an invalid painted-height assertion (573 vs native SHM320). An additional +IPC-y/titlebar assertion was also invalid. The recorded offline test PASS had +an insufficient source-assertion audit; raw runner exit1 and all evidence remain +immutable. Root/nested grabs, geometry, popup pixels/input/dismissal and splith +are NOT TESTED in this campaign. No algorithm/production/UX/package change or +retry; budget exhausted. Correct/replay geometry predicates offline before a +separately authorized fresh static popup gate. Historical failure evidence is frozen; +STEP20C NEXT, production multi-output NOT YET SUPPORTED. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 634b0c9..17a5eb8 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -375,3 +375,16 @@ and separately diagnosed/revalidated offline. No algorithm, build, production, package or UX change follows. Wider static popup/effect/lifecycle, complete damage/generation, ABI/maintenance/notices gates remain open. D1–D4 persist; STEP20C stays NEXT and production multi-output remains NOT YET SUPPORTED. + +The preserved [static nested valid-grab probe](design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md) +is **EXPERIMENTAL / INCONCLUSIVE**, architecture UNDECIDED. The sole unchanged- +static-binary run verified actual identity, outputs, applied camera/tiled clip +and two exact whole-H2 references, then stopped before any click or popup on +an invalid painted-height assertion (573 vs native SHM320). An additional +IPC-y/titlebar assertion was also invalid. The recorded offline test PASS had +an insufficient source-assertion audit; raw runner exit1 and all evidence remain +immutable. Root/nested grabs, geometry, popup pixels/input/dismissal and splith +are NOT TESTED in this campaign. No algorithm/production/UX/package change or +retry; budget exhausted. Correct/replay geometry predicates offline before a +separately authorized fresh static popup gate. Historical failure evidence is frozen; +STEP20C NEXT, production multi-output NOT YET SUPPORTED. diff --git a/docs/design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md b/docs/design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md new file mode 100644 index 0000000..47db27a --- /dev/null +++ b/docs/design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md @@ -0,0 +1,389 @@ +# STEP20C static root/nested valid-grab continuity probe + +**EXPERIMENTAL / INCONCLUSIVE; architecture UNDECIDED. Frozen failure evidence.** The protocol below was written before fixture implementation +and graphical launch; completed observations are appended separately. +Canonical [architecture](../ARCHITECTURE.md), [UX](../UX.md), +[limitations](../KNOWN_LIMITATIONS.md) and [roadmap](../ROADMAP.md) govern. +STEP20A DONE, STEP20B EXPERIMENTAL, STEP20C NEXT, STEP20D–G PLANNED; +production multi-output NOT YET SUPPORTED. No algorithm or UX change. + +## Prior evidence and exact question + +Phase A froze the [static tiled RGB/input/standard evidence](STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md) +in commit d0fa8c5c9e6f878a0cf30c16c5ce006392838791, verified directly on +Forgejo and GitHub with clean worktree. Its scientific verdict is FEASIBLE +only for its tested tiled matrix; its runner exit 1 / runtime INCONCLUSIVE +and compositor exit 0 remain immutable. No historical run is repeated. + +The [popup ownership](STEP20C_POPUP_OWNERSHIP_PROBE.md), +[valid-grab hidden-owner](STEP20C_VALID_GRAB_HIDDEN_OWNER_PROBE.md), +[nested-grab](STEP20C_NESTED_VALID_GRAB_PROBE.md), +[vertical](STEP20C_VERTICAL_OUTPUT_NESTED_GRAB_PROBE.md) and +[R90 partial-popup](STEP20C_ROTATED_PARTIAL_POPUP_CLIP_PROBE.md) reports +establish sampled shared-private behavior and controls, not static continuity. +They distinguish invisible hits from legitimate outside-grab dismissal. +No new pristine control run is authorized; comparison uses those retained +controls. Static/shared exhaustive equivalence is NOT PROVEN. + +Reuse only static candidate +`/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt/stage-static/usr/bin/swayfx-mosaic`, +SHA-256 `f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`, +build ID `942db64269640778ee433e5970ba1100bc60f94c`. Verify private identical +0755 copy, ELF64 x86-64, ordinary clip definitions, no SceneFX DT_NEEDED, +no missing relocation, and actual loaded system wlroots 0.20.2 SHA-256 +`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`. +No compositor/library builds, binary modifications, system install or daily IPC. + +## Client and XDG contract + +Historical `step20c-nested-grab/nested-client.c` must remain byte-identical, +SHA-256 `2e40c98af72a0df8b316810589a0be8f4425a763db1de448b49a7813956b6f7d`. +Retain it separately from the adapted copy and private placement patch. +Reuse the exact valid-grab pointer daemon and static-reference/saved-client +sources. Reuse manifest-verified generated protocol files; retain XML hashes +and verify regeneration byte-for-byte. Compile only clients as C17 with +-Werror (-Wno-unused-parameter for existing listener signatures). + +The [XDG protocol](https://raw.githubusercontent.com/wayland-mirror/wayland-protocols/main/stable/xdg-shell/xdg-shell.xml) +and installed XML require a user-action serial, mapped parent, grabbing popup +parent for B, pre-map grab, bufferless initial commit, configure/ACK before +buffer attachment, and reverse destruction. Client binds wm_base v3. A uses +the exact TOPLEVEL left press serial; B uses a new POPUP_A left press serial. +Both logs and WAYLAND_DEBUG requests must agree; nonzero serial alone fails. +A/B have distinct wl_surface, xdg_surface and popup roles, with B.parent=A.xdg. +No arbitrary serial, forced grab or protocol workaround is permitted. + +The only private C delta adds placement `static_cross`: A anchor (100,80), +size 350×170; B anchor (100,60), size 300×130. Both 1×1 anchors are inside +parent effective SHM geometry (TOPLEVEL 420×320; A 350×170). Anchor top-left, +gravity bottom-right, default constraint adjustment none. No draw/grab/serial/ +ACK/map/dismissal/destruction change. The toplevel keeps historical 420×320 +buffer; assigned container content is measured separately. Source constants +and actual wl_surface.attach/protocol sizes qualify drawn dimensions. + +For applied top content near (-318,25), predicted A=(-218,105,350,170), +B=(-118,165,300,130). Predictions are not evidence. Parse actual role configure, +measure owner scene and content offset, then independently correlate solid +paint and pointer-local coordinates. Require A.left<0layers.popup, +positions it from the applied view content tree, and derives its local clip +from current workspace's already-applied tiling clip and output intersection. +B's scene wrapper descends from A's XDG tree, retaining the same owner. +Source trace PI_OWNER distinguishes logical/current rectangle from scene; +PI_SCENE reports applied tiling clip; PI_POPUP reports root wrapper clip; +PROBE_POPUP_CLIP records owner pointer/local clip. Root wrapper origin differs +from A's configured painted origin. Correlate root local clip + applied content +origin with global [-800,0,800,600]. Child effective clip is inherited, backed +by exact source hierarchy and measured B pixels/input; no new child-node or +atomic-generation instrumentation may be claimed. Standard root clip removal +must appear in PI_POPUP, and B standard rendering/input must exercise inheritance +removal. Missing direct child trace is documented, not manufactured. + +## Offline preflight and resource ledger + +Before run reservation: source/protocol manifests, C17 syntax/build, binary/ +loader identity, AST/shell/links, source audit (XDG hierarchy, input mapping, +configure/ACK/map, child-first destruction, clipping and shutdown), deterministic +color/coordinate/geometry/button predicates and negative cases, dismissal +classification and exit-classification tests all PASS. Live predicates are the +same functions used offline. Record `STATIC_NESTED_GRAB_FIXTURE_PREFLIGHT=PASS`. +No graphical launch to discover an offline-detectable error. + +One exclusive persistent campaign owner file and one LAB outside Git. Stable +flock serializes fsync/atomic ledger updates; reservations persist before run, +chain, grab, click, pointer positioning, PNG, camera and transition. Failures +consume reservations; no refunds/reset/second LAB. Limits: run 1, pristine 0, +SceneFX/Mosaic builds 0, chains 2, grabs 4, camera 2 (at most one per chain), +Mosaic→splith 1, complete clicks 9, PNG 16, positioning/injection actions 24, +run duration 150 seconds. Planned 8 clicks and 15 PNGs; every click includes +and consumes one positioning action. No hide/return, scale/rotation/resize/ +migration or additional campaign. Watchdog bounds owned process groups/logs; +all command timeouts and cleanup fit inside 150 seconds. + +## Ordered runtime protocol + +One headless compositor, private short runtime mode 0700, private Wayland and +explicit swaymsg -s socket. Clear inherited display/IPC/library/session overrides. +H1=(-800,0,800,600); H2=(0,0,800,600); scale1/normal. focus_follows_mouse no. +Create static H2 reference, discover PID/con_id, apply only its `[con_id=ID] +border none. Validate empty deco_rect, 800×600 content, visible XDG identity, +output/workspace; stored border width need not be zero. H1 filler may share +App ID, but never identity/PID/ancestor. Establish Mosaic, owner and H1 focus +before two H2 baselines separated by >=500 ms. Park private pointer and require +all 480,000 RGB pixels #303038 and exact equality. Stop dependents if unstable. + +E1: reserve a fresh chain, map owner; compute signed relative camera delta from +actual PI_OWNER/content rectangle to center assigned content at x=0. Reserve +at most one correction; validate applied camera, same owner/workspace and clip. +Capture actual blue pixels, choose interior point, reserve grab and full left +click, open A using received press. Require configure/ACK/map; capture A, select +actual yellow interior, reserve second grab/left click to open B. Capture H1/H2; +require both crossings, positive A/B paint H1, zero A/B H2 and whole-H2 equality. +Choose B visible point from actual green pixels and inject right; require B pair, +no reference/filler event, no prior popup_done. Choose H2 point within measured +B continuation and actual reference, inject right; no invisible A/B button may +arrive. Observe reference pair and topmost-first popup_done separately; legitimate +dismissal is not an invisible hit. Verify clean H2, close E1 owner normally. + +E2: new process, owner IDs and serials in the same compositor. Reserve chain, +verify Mosaic workspace, recompute sole possible camera correction, open A/B +with new actual left presses. Capture clipped state and whole H2, then send +`[con_id=E2] layout splith` without exterior click. Verify same workspace, +actual applied splith, both still mapped, no prior popup_done, tile/root clip +absent. Capture both outputs: require actual A/B crossings and positive A/B +colors H2, not empty-reference equality during intentional popup spill. Right +click actual green H2 interior must go to B. Send historical `close_b` command; +require B destruction, A survives, capture H2 and right click actual yellow +interior to A. Close owner normally; final H2 must fully equal baseline with +zero A/B residue. No stale IDs or points are reused across stages. + +## Closure, classification and preservation + +Record completion of all scientific gates before terminal exit. Audit +sway/commands/exit.c, ipc-server.c and main.c: termination may close socket before +queued writable reply. Keep swaymsg stdout, stderr and real code separately; +wait for compositor code and shutdown log, clients and residual groups/sockets. +Nonzero exit IPC is nonblocking transport only when all prior measures pass, +server handled exit, compositor=0, no crash/protocol error or rescue cleanup. +Never force code0 or claim IPC success when transport failed. Earlier functional +failure remains fatal to scientific qualification, regardless of shutdown. + +FEASIBLE requires all listed identity/reference/XDG/grab/crossing/render/input/ +dismissal/standard/cleanup gates in this matrix. PARTIALLY_FEASIBLE retains a +working main chain with missing secondary measurements; INCONCLUSIVE means +fixture attribution, geometry or protocol sequence not exercised. BLOCKED needs +actual visual/input leak, grab violation, runtime ABI/crash or standard regression. +Architecture is STATIC_NESTED_VALID_GRAB_FEASIBLE_FOR_TESTED_MATRIX, +STATIC_NESTED_VALID_GRAB_REQUIRES_REDESIGN, or UNDECIDED respectively. + +Retain raw exact source/patch/runner/config, WAYLAND_DEBUG, timestamps/injections, +IPC/scene/client logs, actual codes, PNGs, dependency maps and source/evidence +SHA-256 inventories. Seal only after collection; verify every entry read-only. +Available damage extents and stabilized capture changes are partial evidence: +no intermediate-frame/minimal-damage/native-buffer/GPU-texture guarantee. +Hidden owner, scaling/rotation, migration/resize, saved buffers, effects, +complete damage/transactions and exhaustive ABI are NOT TESTED in this campaign. +Synchronize this report, fixture README, architecture, limitations, roadmap, +document index and changelog. B stays local and uncommitted, never pushed. + +## Offline preparation completed before run reservation + +LAB `/home/fy59/swayfx-mosaic-static-nested-grab-m0a5e2qh`, exclusive owner +`/home/fy59/swayfx-mosaic-static-nested-grab-v1.owner`. Exact candidate/0755/ +ELF/build ID/ordinary symbols/ldd -r/system wlroots 0.20.2 pass. Three client-only +C17 syntax and executable builds pass with -Werror; both protocol regenerations +are byte-identical to manifest-verified inputs. Original nested SHA matches. +Private placement patch SHA-256 +`e0e87fb0cb3d6e7dafb1cd59ccb50f323f2934e65247526199da1695487d3e4a`; +adapted C SHA-256 +`6eafb8ad63b0b06b8297e7e72711056a6d42bdf33ccf795793eeda77f98221c7`. +Exact frozen live runner SHA-256 +`e2bde767036096e4633f9dd4fe3deb1b147f6415dc550a46802a6a90c4bd6486`. + +First offline check stopped on a negative with the original button deliberately +removed: the shared predicate correctly rejected it via ValueError, but the +test harness initially counted only assertion/runtime errors. Correct only +that harness in `LAB/preflight-correction/check.py`; retain immutable original +`LAB/fixture/check.py` and the incident JSON. No live predicate/runner change, +no budget reservation or graphical launch occurred. The corrected checker +then passes retained actual standard-control protocol, actual historical +border-none/stored-width2 reference, labelled synthetic geometry/paint/input, +16 negatives, ledger concurrency/exhaustion/corruption and terminal outcome +positives/five negatives. AST/shell/links and exact frozen inventory pass. +All live predicates are the same frozen functions exercised offline. + +`STATIC_NESTED_GRAB_FIXTURE_PREFLIGHT=PASS`. +Runtime remains NOT TESTED at this point; reserve the sole run only afterward. + + +## Completed single runtime — 2026-10-13 + +`SWAYFX_MOSAIC_STATIC_NESTED_VALID_GRAB_PROBE=INCONCLUSIVE`. +Architecture: **UNDECIDED**. This is a fixture geometry failure before the first +popup, not a demonstrated clipping, grab, ABI or standard-layout defect. +The sole compositor run is consumed; no retry or second laboratory. The +intended two-chain experiment was **not completed** and must not be described +as popup evidence. No new Mosaic/SceneFX build, binary/algorithm/shader change, +production/UX/package change or installation occurred. + +### Actual identity, output, focus and reference + +Original and private executable retain SHA-256 +`f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`, +build ID `942db64269640778ee433e5970ba1100bc60f94c`, mode0755, ELF64 x86-64. +Ordinary clip definitions and ldd -r pass; no SceneFX DT_NEEDED/RPATH/RUNPATH +or loaded shared SceneFX. /proc/exe resolves to the private copy; actual /proc +maps show system wlroots 0.20.2 SHA-256 +`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`. +All protected production/system/package/UX/AGENTS/binary hashes pass afterward. + +Actual outputs match H1=(-800,0,800,600), H2=(0,0,800,600), scale1/normal. +Reference ID7/PID1737643 belongs to workspace2 ID5/H2; only its discovered +con_id receives border none. Stored width2 persists, deco_rect empty, +rect/window_rect 800×600, visible xdg_shell, correct app_id/PID/output. +Filler PID1737649 is distinct despite the same app_id. E1 owner ID9/PID1737655 +belongs to workspace1 ID6/H1, focused and mapped. IDs were discovered, not +hard-coded. Only one chain instance was reserved/mapped. + +After E1 map, applied camera was 480. PI_OWNER measured scene(-640,0,640,600), +content x=-638, assigned content636×573. Desired content x=-318 yields the +sole relative correction **-320**, resulting in camera160, applied owner +scene(-320,0,640,600), content x=-318. PI_SCENE reports local tile(-160,0), +clip(160,0,800,600); with H1 origin this gives [-800,0)×[0,600). +H2 tiling clip is absent. No popup root or child clip was measured. + +Two independent 800×600 reference PNGs were captured after setup/H1 focus and +pointer parking, >=500ms apart (requested extra delay600ms). Every RGB pixel +is #303038; zero differences, no mask or tolerance. Both PNG SHA-256: +`4471e93ccb6cf3ff4970f61fce405431a228af598d2f1f030a3b7586962ecd7a`. +This establishes pre-popup reference stability only. No popup-phase whole-H2 +capture exists. The third PNG, E1-top, has 182,214 actual blue pixels, box +[482,25,800,598) in H1 local coordinates, giving global visible x[-318,0), +y[25,598). It does not establish popup clipping or popup geometry. + +### Runtime fixture failure and source-based diagnosis + +The frozen runner stops at `E1-open-A`, **before any click/grab reservation**. +Line182 asserts painted height320 from the native SHM constants. The actual +client creates a 420×320 SHM buffer, then receives and ACKs configure636×573 +without redrawing (historical client behavior). SwayFX +`output_configure_scene` in the exact candidate's sway/desktop/output.c:331 +sets the tiled scene buffer destination from applied content width/height. +The captured blue height is573, consistent with this source path, not320. +The failed assertion is therefore an invalid fixture assumption. + +An additional assertion at line183 was not reached: it requires painted y += owner scene y + IPC window_rect.y. Exact sway/ipc-json.c intentionally +sets window_rect.y=0 when a titlebar is enabled; actual IPC has that0, +deco height25 and painted y25. The prior static retest already distinguished +native buffer y from window_rect.y. This source-visible error should also +have been caught before the run. No rendered-buffer/IPC equivalence contract +is invented to make the fixture pass. + +The recorded preflight marker remains PASS for its actually executed tests. +Its assertion/source audit was **INSUFFICIENT / retrospective FAIL** for these +two live assertions; it did not satisfy the full intended offline gate. +No retrospective rewrite of offline-gate.json, runtime.json, frozen runner, +protocol or budget is made. Preparation's missing-origin negative-case harness +incident and this runtime geometry incident are separate. The full client +protocol buffer/configure/ACK/map, IPC, PI_OWNER/PI_SCENE and PNG are retained. +`geometry_audit.py` reproduces both invalid assumptions read-only, verifies +actual reference/camera/clip/paint/codes and confirms zero popup creation, +zero pointer button and zero grab. This offline diagnosis is not graphical +qualification. The original runner remains byte-identical and is preserved +as failed experimental evidence; it is not a ready replay fixture. + +### Unexercised protocol, geometry and input + +The historical nested C source remains byte-identical; the sole private delta +is the legal static_cross placement. Original C and adapted C/patch are retained +with distinct hashes. The source hierarchy and historical control/replay show +correct XDG parentage, real button serial use and child-first destruction. +In this new runtime, however, **A and B were never created**. No A/B configure, +ACK, MAP, grab request, serial, enter/button, popup_done or destruction exists. +Neither predicted crossing is a measurement. E2 was never reserved or spawned; +no standard transition, standard popup pixels/input or popup residue cleanup +sample was exercised. All those matrix cells are NOT TESTED. + +### Terminal exit and owned cleanup + +Runtime duration5.485246415s; actual runner exit1, runtime INCONCLUSIVE, +measurements_completed=false, watchdog=false. The finally path explicitly sends +exit on /tmp/msng-jfnc_v3o's private IPC socket; stdout empty, stderr +`Unable to receive IPC response`, actual swaymsg code1. Server log records +Handling command exit and Shutting down sway; compositor exits0. E1, filler, +reference and pointer all exit0, with normal owned cleanup and no rescue signal. +No wl_display.error/protocol-error event appears. Ordinary startup backend +errors/noise are retained; no compositor crash is observed. + +The command's queued response may have been lost at socket closure, compatible +with audited source. It is **not accepted as a nonblocking scientific exception**: +the earlier geometry failure occurred before measurements completed. The +terminal classifier correctly retains scientific_cleanup_pass=false. Composite +shutdown/cleanup success does not turn this campaign into FEASIBLE. +All owned PIDs/process groups are gone; short runtime removed, no sockets left. +No daily compositor/socket/process was touched. + +### Budget, damage and retained inventories + +| Resource | Consumed / ceiling | +| --- | --- | +| Static compositor runs | 1/1 | +| Pristine / SceneFX build / Mosaic build | 0/0 each | +| Chain instances | 1/2; E1 toplevel only | +| Valid-grab reservations | 0/4 | +| Camera corrections | 1/2; E1 only | +| Mosaic→splith | 0/1 | +| Complete clicks | 0/9 | +| Pointer positioning | 1/24 | +| PNGs | 3/16 | +| Runtime seconds | 5.485246415/150 | + +Unused chain/click/PNG counters cannot authorize another compositor run. +The durable ledger remains consumed, with no refund/reset/retry. Available +TEMPORAL_DAMAGE extents cover only setup, camera, captures and shutdown; +A/B appearance, partial popup clipping, outside dismissal/destruction, standard +return and disappearance damage are NOT TESTED. Only stabilized reference +RGB is compared. No all-frame/minimal-damage/native-PNG/GPU guarantee. + +Frozen pre-run fixture and source inventories remain immutable. Collector and +read-only diagnostic are preserved separately in LAB/postrun-fixture; corrected +offline harness in LAB/preflight-correction. Both are included in source sealing. +Source:30 entries, SHA-256 +`9b76aae162faa631bf1930677d484359dfc3e29ace237b9aee2daa3d5cd68dc4`. +Evidence:96 entries, SHA-256 +`5750d1e74636c2d276800f795ed0214c3e12999908149855af0b382a2da9bfb4`. +Every entry is verified read-only; reports and closeout receipts live outside +the sealed inventories to avoid circular hashes. No generated binary/PNG/JSON/ +protocol/build/socket/log is added to Git. + +### Architectural answers for this campaign + +| Question | Evidence-backed answer | +| --- | --- | +| Q1 A/B real button serial? | NOT TESTED; zero runtime buttons or grabs. Historical source/control passed offline. | +| Q2 distinct compliant grabs? | NOT TESTED; no A/B objects in the new run. | +| Q3 hierarchy correct? | Correct in exact source and retained historical control; runtime A→B NOT TESTED. | +| Q4 both really cross x=0? | NOT EXERCISED. Requested anchors and predicted boxes are not configured geometry. | +| Q5 clip from applied owner? | Tiled applied viewport measured; popup root/child NOT TESTED. | +| Q6 permitted A/B pixels? | NOT TESTED; only toplevel blue182214 measured. | +| Q7 forbidden popup pixels absent H2? | NOT TESTED; two pre-popup H2 references stable. | +| Q8 visible B input? | NOT TESTED. | +| Q9 invisible B events? | NOT TESTED; no popup/input exists, not proof of exclusion. | +| Q10 outside grab behavior? | NOT TESTED; historical controls distinguish dismissal from hit. | +| Q11 popup_done distinct from invisible button? | Yes as protocol/measurement categories; no new dismissal or button sample. | +| Q12 A/B survive splith E2? | NOT TESTED; E2 and transition not executed. | +| Q13 clips removed? | NOT TESTED for popup or standard transition. | +| Q14 standard popup colors on H2? | NOT TESTED. | +| Q15 standard A/B buttons? | NOT TESTED. | +| Q16 popup residues after closure? | NOT TESTED because none opened; process/socket cleanup PASS separately. | +| Q17 static regression versus private shared controls? | UNDECIDED for popups; no valid static comparison was obtained. | +| Q18 algorithm change justified? | No. Confirmed errors are fixture assertions; no clipping defect demonstrated. | +| Q19 matrices open? | Static hidden-owner, scale, rotation, resize/migration, saved-buffer/effects, full damage/transactions and exhaustive ABI. Historical sampled evidence is not replayed or promoted. | +| Q20 smallest next gate? | First correct and replay all geometry predicates offline against actual native/configured/painted/IPC observations, including titlebar and standard states; then a separately authorized fresh one-run static two-chain gate. Current run budget is exhausted. | + +Validation PASS: original nested hash, placement-only delta, C17/client builds, +protocol byte identity, binary/loader/maps, outputs/focus/camera/tiled clip, +reference whole RGB, actual paint diagnosis, owned codes/cleanup, protected +hashes, manifests, AST/shell/links and diff. FAIL: full preflight assertion audit +and the exact runtime geometry assertion; runner code1. NOT RUN/NOT TESTED: +new pristine/compositor/library builds and all new popup/input/standard gates. +No subagent review is claimed; final local delta/source/evidence audit is by the +parent. This historical report and source fixture are now frozen for publication +in the failure-freeze phase. A separate offline geometry gate remains local +and cannot change this historical verdict or authorize another run. +STEP20C remains NEXT, production multi-output NOT YET SUPPORTED. + +## Failure evidence freeze — 2026-10-13 + +Read-only revalidation verifies all24/30/96 entries of the historical inventories +and reproduces both invalid assertions without launching a compositor. AST, +Shell blocks, Markdown links, source-only Git content and exact distinctions +between executed, corrected-checker and postrun sources pass. The executed +runner SHA and raw runtime/terminal codes remain unchanged. The single commit +for this freeze preserves INCONCLUSIVE / UNDECIDED and all NOT TESTED cells. +No historical run, ledger, manifest, client source or binary is modified. diff --git a/tooling/probes/step20c-static-nested-grab/README.md b/tooling/probes/step20c-static-nested-grab/README.md new file mode 100644 index 0000000..7502962 --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/README.md @@ -0,0 +1,87 @@ +# Static nested valid-grab fixture + +EXPERIMENTAL / FROZEN FAILURE EVIDENCE. No production integration. +Read the [protocol](../../../docs/design/STEP20C_STATIC_NESTED_VALID_GRAB_PROBE.md). +One exclusive laboratory, one existing immutable static binary, client-only +C17 builds; no SceneFX/Mosaic build or pristine run. Original nested source is +retained separately; `placement.patch` adds only legal static_cross anchors. + +`prepare.py` verifies identity/provenance and compiles clients outside Git. +`check.py` exercises actual historical observations and labelled synthetic +negative fixtures, ledger concurrency/exhaustion/corruption, lifecycle and exit +classification before run reservation. `run.py` shares those predicates, +consumes reservations before every action, and preserves real terminal codes. +`collect.py` records only measured states and seals inventories once. + +```sh +PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-nested-grab/prepare.py +# Use only the LAB printed and registered by prepare; no second LAB or reset. +PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-nested-grab/check.py "$LAB" +# Only after STATIC_NESTED_GRAB_FIXTURE_PREFLIGHT=PASS: +PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/run.py" "$LAB" +PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/collect.py" "$LAB" +``` + +Budgets: 1 run, 2 chains, 4 grabs, 2 calculated camera corrections (one per +chain), 1 splith transition, 9 complete clicks, 16 PNGs, 24 positions, 150 s. +Failed actions consume budget. Private sockets/groups only, no desktop IPC. +Generated protocol/client binaries/logs/JSON/PNG/build files stay outside Git. +Fixture preflight success is not graphical evidence. Results appended after +collection; every unexecuted matrix cell remains NOT TESTED. + + +## Completed campaign: INCONCLUSIVE, do not rerun + +LAB `/home/fy59/swayfx-mosaic-static-nested-grab-m0a5e2qh`; run exhausted1/1. +Only E1 toplevel mapped: two stable full #303038 H2 images, computed camera +480→160 by -320, measured applied tiled clip, H1 blue182214 pixels. No popup, +click or grab occurred. Runner exits1 on its painted-height320 assertion; +actual height573. A further IPC window_rect.y=0/titlebar y25 assertion was +invalid but unreached. The original run.py here and LAB/fixture remain exact +failed evidence, **not a qualified replay runner**. Before any fresh separately +authorized campaign, repair/replay all geometric assertions offline. No runtime +retry is permitted by this ticket; unused chain/click/capture capacity is not +a new run authorization. The recipes above describe the preparation/run that +was attempted, not permission to repeat it. + +`check.py` now counts ValueError from a deliberately removed-button negative +as rejection; the initial immutable checker is retained in LAB/fixture and the +corrected preflight checker in LAB/preflight-correction. Recorded preflight PASS +covers its tests, but the full geometric source/assertion audit was insufficient. +No historical gate/runtime status or manifest is rewritten. `geometry_audit.py` +rechecks actual SHM/configure/ACK/map, scene/IPC/paint, reference RGB, zero +popup/button/grab and terminal codes without launching a graphical process. +Post-run collector/diagnostic copies are in LAB/postrun-fixture, separate from +the executed fixture. Source/evidence inventories include these distinctions. + +Terminal swaymsg actual1 (response receive failure), compositor0, all four +clients/injector0, no rescue or residual process/socket. Earlier measurement +failure is preserved; shutdown success cannot qualify scientific success. +Budget: run1, chain1, grab0, camera1, transition0, click0, move1, PNG3, +5.485246415 seconds. New Mosaic/SceneFX builds0, pristine runs0. +Source30 entries SHA256 +`9b76aae162faa631bf1930677d484359dfc3e29ace237b9aee2daa3d5cd68dc4`; +evidence96 entries SHA256 +`5750d1e74636c2d276800f795ed0214c3e12999908149855af0b382a2da9bfb4`. +Both are sealed once and verified. Root/nested input, crossings, dismissal, +standard-return/input/residues and related damage remain NOT TESTED. + +Read-only completed-lab checks (never rerun collect or regenerate manifests): + +```sh +PYTHONDONTWRITEBYTECODE=1 python "$LAB/postrun-fixture/geometry_audit.py" "$LAB" +PYTHONDONTWRITEBYTECODE=1 python - "$LAB" <<'PYCODE' +import sys +from pathlib import Path +lab = Path(sys.argv[1]) +sys.path.insert(0, str(lab / 'postrun-fixture')) +from support import verify +for name in ['fixture-freeze', 'source', 'evidence']: + print(name, verify(lab / 'results' / (name + '-sha256.txt'))) +PYCODE +``` + +Verdict SWAYFX_MOSAIC_STATIC_NESTED_VALID_GRAB_PROBE=INCONCLUSIVE. +Architecture UNDECIDED. STEP20C NEXT; multi-output NOT YET SUPPORTED. +This historical failure fixture is frozen for publication. The separate new +offline geometry-review fixture remains local, uncommitted and unpublished. diff --git a/tooling/probes/step20c-static-nested-grab/check.py b/tooling/probes/step20c-static-nested-grab/check.py new file mode 100644 index 0000000..2a2d755 --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/check.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Offline protocol/pixel/ledger/terminal tests; never starts a compositor. + +Historical observations and synthetic negative cases are explicitly labelled. +""" +import ast +import concurrent.futures +import copy +import json +from pathlib import Path +import re +import subprocess +import sys +import tempfile +from PIL import Image +from support import initial,reserve,LIMITS,save,sha,verify,OWNER,REPO,NESTED_SHA +from predicates import protocol,geometry,pair,dismissal,terminal,point,reference,root_clip,popup_pixels,Defect +from ipc_predicates import validate_reference,validate_outputs + +def attempt(args): + lab,k=args + try:reserve(lab,k);return True + except RuntimeError:return False + +def rejected(name,func): + try:func() + except (AssertionError,Defect,RuntimeError,ValueError):return name + raise AssertionError('negative accepted: '+name) + +if __name__=='__main__': + lab=Path(sys.argv[1]);assert OWNER.read_text().strip()==str(lab) + verify(lab/'results/fixture-freeze-sha256.txt') + assert json.loads((lab/'results/budget.json').read_text())['used']==dict.fromkeys(LIMITS,0) + for p in (lab/'fixture').glob('*.py'):ast.parse(p.read_text(),filename=str(p)) + for p in [lab/'protocol-before-run.md',lab/'fixture/README.md']: + for s in re.findall(r'```(?:sh|bash)\n(.*?)```',p.read_text(),re.S):subprocess.run(['sh','-n'],input=s,text=True,check=True) + with tempfile.TemporaryDirectory(prefix='static-nested-ledger-offline-') as tmp: + test=Path(tmp);(test/'results').mkdir();initial(test) + with concurrent.futures.ProcessPoolExecutor(max_workers=4) as pool: + assert sum(pool.map(attempt,[(test,'capture')]*24))==16 + for k,n in LIMITS.items(): + if k=='capture':continue + for _ in range(n):reserve(test,k) + assert not attempt((test,k)) + before=(test/'results/budget.json').read_bytes() + assert not attempt((test,'run')) and (test/'results/budget.json').read_bytes()==before + state=json.loads(before);state['used']['run']=-1 + (test/'results/budget.json').write_text(json.dumps(state));assert not attempt((test,'run')) + # Use the retained actual unmodified nested control, stopping while mapped. + old=Path('/tmp/swayfx-mosaic-nested-grab-If8ofeUu/run-control-open-visible/client.log') + text=old.read_text().split('TOPLEVEL DESTROY')[0] + cuts=[text.find(s) for s in ['POPUP_B DESTROY','POPUP_B POPUP_DONE'] if s in text] + if cuts:text=text[:min(cuts)] + good=protocol(text);negative=[] + for name,wrong in [('serial',text.replace('used=61','used=62')), + ('parent',text.replace('parent_xdg=12','parent_xdg=9')), + ('button origin',text.replace('target=TOPLEVEL serial=61 button=272 state=1','target=OTHER serial=61 button=272 state=1')), + ('protocol error',text+'\nwl_display#1.error(1, 1, "bad")'), + ('premature dismissal',text+'\nPOPUP_B POPUP_DONE role=17')]: + negative.append(rejected(name,lambda t=wrong:protocol(t))) + snap=json.loads((Path('/home/fy59/swayfx-mosaic-static-rgb-input-retest-hn67lnhq/results/preflight-snapshot.json')).read_text()) + ref=validate_reference(snap,7,1700453);validate_outputs(snap['outputs']);assert ref['stored_border_width']==2 + negative.append(rejected('wrong reference identity',lambda:validate_reference(snap,8,1700459))) + synthetic='POPUP_A ROLE_CONFIGURE x=100 y=80 w=350 h=170\nPOPUP_B ROLE_CONFIGURE x=100 y=60 w=300 h=130' + g=geometry(synthetic,[-318,25]);assert g['A']==[-218,105,350,170] and g['B']==[-118,165,300,130] + negative.append(rejected('no crossing',lambda:geometry(synthetic,[-700,25]))) + # Labelled synthetic color regions challenge exact rectangle/occlusion predicates. + from PIL import ImageDraw + baseline=Image.new('RGB',(800,600),(48,48,56)) + def synthetic_pixels(mosaic): + images=[] + for ox in [-800,0]: + im=baseline.copy();draw=ImageDraw.Draw(im) + if not mosaic or ox==-800: + for key,color in [('A',(224,192,32)),('B',(32,208,160))]: + x,y,w,h=g[key];draw.rectangle((x-ox,y,x+w-ox-1,y+h-1),fill=color) + images.append(im) + return images + left,right=synthetic_pixels(True);popup_pixels(left,right,g,True,baseline) + badright=right.copy();badright.putpixel((1,200),(32,208,160)) + negative.append(rejected('actual forbidden color',lambda:popup_pixels(left,badright,g,True,baseline))) + lstd,rstd=synthetic_pixels(False);popup_pixels(lstd,rstd,g,False,baseline) + negative.append(rejected('standard B missing',lambda:popup_pixels(lstd,right,g,False,baseline))) + assert point(left,'POPUP_B',-800)[0]<0 + # Initial synthetic protocol must reject missing/early ACK and buffer attachment. + cfg=good['grabs'][0]['ack'];role=good['grabs'][0]['ids'][1] + negative.append(rejected('missing real ACK',lambda:protocol(text.replace(f'xdg_surface#{role}.ack_configure({cfg})',f'xdg_surface#{role}.ack_configure(999)')))) + assert 100+1<=420 and 80+1<=320 and 100+1<=350 and 60+1<=170 + assert -800+800==0 and 799+800==1599 # Signed global -> verified output-layout fraction. + im=Image.new('RGB',(800,600),(48,48,56));reference(im,im.copy()) + bad=im.copy();bad.putpixel((799,599),(48,48,57)) + negative.append(rejected('one changed RGB',lambda:reference(im,bad))) + negative.append(rejected('sparse paint point',lambda:point(im,'POPUP_B',0))) + lines=['POINTER_BUTTON target=POPUP_B serial=70 button=273 state=1','POINTER_BUTTON target=POPUP_B serial=71 button=273 state=0'] + pair(lines,'POPUP_B') + negative.append(rejected('wrong delivered target',lambda:pair(lines,'POPUP_A'))) + d='POPUP_B POPUP_DONE role=17\nPOPUP_A POPUP_DONE role=14\nPOPUP_B DESTROY role=17\nPOPUP_A DESTROY role=14' + dismissal(d,[]) + negative.append(rejected('invisible delivery',lambda:dismissal(d,lines))) + negative.append(rejected('wrong dismissal order',lambda:dismissal(d.replace('POPUP_B POPUP_DONE','POPUP_A POPUP_DONE'),[]))) + sc='PI_POPUP output=HEADLESS-1 ns=1 local_index=1 tree=0x123 enabled=1 clip_valid=1 clip=-482,-25,800,600' + root_clip(sc,[-318,25],True) + negative.append(rejected('wrong applied clip',lambda:root_clip(sc,[-317,25],True))) + log="Handling command 'exit'\nShutting down sway" + assert terminal(True,1,0,log,False,False)['transport_incident'] + for args in [(False,1,0,log,False,False),(True,1,1,log,False,False),(True,1,0,'',False,False),(True,1,0,log,True,False),(True,1,0,log,False,True)]: + assert not terminal(*args)['scientific_cleanup_pass'] + src=(lab/'historical-source/nested-client.c').read_text();assert sha(lab/'historical-source/nested-client.c')==NESTED_SHA + patched=(lab/'client-source/nested-client.c').read_text() + # CONTRACT: remove precisely the sole new placement branch, recover original bytes. + expected=src.replace(' if (!strcmp(placement,"visible")) {',' /* EXPERIMENTAL: only placement changes; serial/grab/lifecycle stay historical.\n * Both 1x1 anchors lie inside their parent SHM window geometry. */\n if (!strcmp(placement,"static_cross")) {\n a.anchor_x=100; a.anchor_y=80; a.width=350;\n b.anchor_x=100; b.anchor_y=60; b.width=300;\n } else if (!strcmp(placement,"visible")) {') + assert patched==expected,'unauthorized client behavior change' + for tokens in ['xdg_popup_grab(s->popup,seat,serial)','parent->xdg','if (!s->mapped && !s->done) draw(s)','destroy_popup(&b); destroy_popup(&a);']: + assert tokens in src + candidate=Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt/mosaic-source') + token_files={'sway/desktop/transaction.c':['con->current.workspace','wlr_scene_tree_get_clip(ws->layers.tiling','wlr_scene_tree_set_clip(popup_tree, NULL)'], + 'sway/desktop/xdg_shell.c':['popup->xdg_surface_tree, popup->image_capture_tree','popup->desc.relative = &view->content_tree->node'], + 'sway/commands/exit.c':['sway_terminate(0)'], + 'sway/main.c':['wl_display_terminate(server.wl_display)','Shutting down sway','return exit_value'], + 'sway/ipc-server.c':['WL_EVENT_WRITABLE','ipc_send_reply']} + for file,tokens in token_files.items():assert all(t in (candidate/file).read_text() for t in tokens),(file,tokens) + save(lab,'offline-gate',dict(marker='STATIC_NESTED_GRAB_FIXTURE_PREFLIGHT=PASS',status='PASS',fixture_sha256=sha(lab/'results/fixture-freeze-sha256.txt'),historical_control=str(old),historical_protocol=good,synthetic_geometry=g,negative_cases=negative,ledger='PASS concurrency/exhaustion/corruption',terminal='PASS synthetic success and five failures',client_delta='placement only',AST='PASS',shell='PASS',source_audit='PASS',C17='PASS from preparation',runtime='NOT TESTED')) + print('STATIC_NESTED_GRAB_FIXTURE_PREFLIGHT=PASS') + print('PASS historical control, reference width2, labelled synthetic predicates,',len(negative),'negative cases, ledger and terminal classification') diff --git a/tooling/probes/step20c-static-nested-grab/collect.py b/tooling/probes/step20c-static-nested-grab/collect.py new file mode 100644 index 0000000..54fe59e --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/collect.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Seal retained one-run observations without manufacturing missing evidence. + +CONTRACT: runtime failures and real IPC codes stay immutable. No graphical replay. +INVARIANT: source/evidence manifests are created once and checked read-only. +""" +import argparse +import json +from pathlib import Path +import re +from PIL import Image +from support import OWNER,sha,save,verify,inventory +from predicates import protocol,mapped,geometry,popup_pixels,paint,pair,dismissal,root_clip,equal +from ipc_predicates import nodes,workspace,validate_outputs,validate_reference,scene_from_log +from geometry_audit import diagnose +p=argparse.ArgumentParser();p.add_argument('lab',type=Path);lab=p.parse_args().lab.resolve() +assert OWNER.read_text().strip()==str(lab) +assert not (lab/'results/evidence-sha256.txt').exists(),'already sealed' +verify(lab/'results/fixture-freeze-sha256.txt') +def read(n):return json.loads((lab/'results'/(n+'.json')).read_text()) +run=lab/'run';runtime=read('runtime');terminal=read('terminal-exit');cleanup=read('cleanup') +protected=read('protected-before');assert all(sha(p)==h for p,h in protected.items()),'protected source/system change' +save(lab,'protected-after',dict(status='PASS',hashes=protected)) +assert cleanup['runtime_removed'] and not Path(cleanup['runtime']).exists() +assert all(not Path('/proc',str(p['pid'])).exists() for p in cleanup['owned_processes']) +text=(run/'compositor.log').read_text();events=[json.loads(s) for s in (run/'events.jsonl').read_text().splitlines()] +full=runtime['status']=='PASS' and runtime['measurements_completed'] and terminal['scientific_cleanup_pass'] +independent={} +if not full and runtime['stage']=='E1-open-A': + diagnostic=diagnose(lab);save(lab,'geometry-fixture-diagnostic',diagnostic) + save(lab,'applied-owner-geometry',dict(status='PARTIAL',E1=diagnostic['applied_owner'],painted_content_origin=diagnostic['painted_content_origin'],SHM_size=diagnostic['SHM_size'],assigned_content=diagnostic['assigned_content'],painted=diagnostic['painted_TOPLEVEL'],popups='NOT TESTED')) + save(lab,'applied-clips',dict(status='PARTIAL',tiled=diagnostic['tiled_clip'],root='NOT TESTED',child='NOT TESTED')) + save(lab,'mosaic-pixels',dict(status='PARTIAL',toplevel_H1=diagnostic['painted_TOPLEVEL'],H2=diagnostic['H2'],root='NOT TESTED',nested='NOT TESTED')) +if full: + baseline=Image.open(run/'H2-reference-1.png').convert('RGB') + owners=read('applied-owner-geometry');clips=read('applied-clips');grab_serials=[] + for name in ['E1','E2']: + client=(run/(name+'.log')).read_text();cut=client.index('POPUP_B MAP ');prefix=client[:client.index('\n',cut)+1] + pr=protocol(prefix);grab_serials += [g['serial'] for g in pr['grabs']] + g=geometry(prefix,owners[name]['content_origin']) + independent[name]=dict(protocol=pr,geometry=g,pixels=popup_pixels(Image.open(run/(name+'-mosaic-H1.png')).convert('RGB'),Image.open(run/(name+'-mosaic-H2.png')).convert('RGB'),g,True,baseline)) + assert clips[name]['root']['raw'] in text + root_clip(clips[name]['root']['raw'],owners[name]['content_origin'],True) + assert len(set(grab_serials))==4 and grab_serials==sorted(grab_serials) + for file in ['H2-reference-2.png','E1-mosaic-H2.png','E1-dismissed-H2.png','E2-mosaic-H2.png','final-H2.png']: + assert equal(baseline,Image.open(run/file).convert('RGB'))['equal'],('full H2',file) + returns=read('standard-return');assert returns['root_clip']['raw'] in text + root_clip(returns['root_clip']['raw'],returns['geometry']['origin'],False) + stdsnap=read('standard-snapshot');ws=workspace(stdsnap['tree'],'1');assert ws['id']==owners['E2']['workspace'] and ws['layout']=='splith' + validate_reference(stdsnap,read('reference-stability')['reference']['con_id'],read('reference-stability')['reference']['pid']) + std=popup_pixels(Image.open(run/'standard-H1.png').convert('RGB'),Image.open(run/'standard-H2.png').convert('RGB'),returns['geometry'],False,baseline) + client=(run/'E2.log').read_text();cut=next(e['ns'] for e in events if e['kind']=='measurements-completed') + # Delivery must exist in actual retained recipient logs, not just summary JSON. + for row in read('clicks'): + for name,lines in row['events'].items(): + actual=(run/(name+'.log')).read_text() + assert all(line in actual for line in lines) + for name,target in [('E1-visible-B','POPUP_B'),('E2-standard-B','POPUP_B'),('E2-standard-A','POPUP_A')]: + row=next(r for r in read('clicks') if r['tag']==name);who='E1' if name.startswith('E1') else 'E2' + pair(row['events'][who],target) + assert not row['events']['reference'] and not row['events']['filler'] + forbidden=read('forbidden-input');dismissal((run/'E1.log').read_text(),forbidden['popup_events']) + assert client.index('POPUP_B DESTROY ')=500000000 + exact=Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt/mosaic-source') + output=(exact/'sway/desktop/output.c').read_text();ipc=(exact/'sway/ipc-json.c').read_text();trans=(exact/'sway/desktop/transaction.c').read_text() + assert 'wlr_scene_buffer_set_dest_size(buffer, MAX(content_width, 0), MAX(content_height, 0))' in output + assert 'has_titlebar ? 0 : c->pending.content_y - c->pending.y' in ipc + assert 'border_left, border_top' in trans + runner=(lab/'fixture/run.py').read_text();assert 'topbox[3]-topbox[1]==320' in runner + assert "origin[1]==after['scene'][1]+owner['window_rect']['y']" in runner + terminal=json.loads((lab/'results/terminal-exit.json').read_text()) + assert terminal['ipc_exit']==1 and terminal['compositor_exit']==0 and not terminal['scientific_cleanup_pass'] + assert 'Unable to receive IPC response' in terminal['stderr'] + text=(run/'compositor.log').read_text();assert "Handling command 'exit'" in text and 'Shutting down sway' in text + assert all(row['exit']==0 and not Path('/proc',str(row['pid'])).exists() for row in terminal['owned_process_codes']) + assert not re.search(r'wl_display[^\n]*\.error\(|protocol error',client,re.I) + return dict(status='PASS offline diagnosis; runtime remains INCONCLUSIVE',runner_sha256=sha(lab/'fixture/run.py'), + executed_assertion_line=182,first_failure='rendered blue height 573 != immutable SHM height 320', + additional_unreached_assertion_line=183,additional_failure='titlebar-enabled IPC window_rect.y=0 does not give painted/content y=25', + applied_owner=applied,painted_TOPLEVEL=value,SHM_size=[420,320],assigned_content=[636,573],painted_content_origin=[-318,25], + applied_camera=dict(before=480,relative_delta=-320,after=160),tiled_clip=scene, + H2=dict(whole_RGB='PASS two references',color='#303038',changed_pixels=0,PNG_sha256=sha(run/'H2-reference-1.png')), + source_locations={'destination':'sway/desktop/output.c:331','window_rect_y':'sway/ipc-json.c:712','content_origin':'sway/desktop/transaction.c:705'}, + origin_diagnosis='actual paint supplies y; IPC window_rect excludes enabled titlebar; rendered destination is separate from native SHM', + offline_preflight_limit='recorded PASS did not audit these two live assertions against the source/available prior IPC; it was insufficient', + popup_geometry='NOT EXERCISED',grabs='NOT TESTED',input='NOT TESTED',standard='NOT TESTED',new_runs=0, + correction_scope='offline fixture geometry revalidation required; no clipping algorithm change justified') + +if __name__=='__main__': + p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('--record',action='store_true');a=p.parse_args() + verify(a.lab/'results/fixture-freeze-sha256.txt');result=diagnose(a.lab) + if a.record:save(a.lab,'geometry-fixture-diagnostic',result) + print('PASS reproduce failed height assertion and additional invalid IPC-y assumption; no popup evidence manufactured') diff --git a/tooling/probes/step20c-static-nested-grab/ipc_predicates.py b/tooling/probes/step20c-static-nested-grab/ipc_predicates.py new file mode 100644 index 0000000..df5a800 --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/ipc_predicates.py @@ -0,0 +1,139 @@ +"""Pure validators for replay and live observations, never desktop IPC. + +WHY: stored border thickness and container IPC geometry are not rendered pixels. +CONTRACT: callers supply measured identities; validate ancestors and applied scene. +INVARIANT: no width-zero predicate, hard-coded runtime IDs, or native buffer y. +""" +import re + +OUTPUTS = {'HEADLESS-1': (-800, 0, 800, 600, 1.0, 'normal'), + 'HEADLESS-2': (0, 0, 800, 600, 1.0, 'normal')} +ZERO = dict(x=0, y=0, width=0, height=0) +FULL = dict(x=0, y=0, width=800, height=600) + + +def nodes(node): + yield node + for child in node.get('nodes', []) + node.get('floating_nodes', []): + yield from nodes(child) + + +def workspace(tree, name): + rows = [n for n in nodes(tree) if n.get('type') == 'workspace' and n.get('name') == name] + assert len(rows) == 1, 'workspace identity' + return rows[0] + + +def ancestry(tree, con_id, path=()): + if tree.get('id') == con_id: + return path + (tree,) + for child in tree.get('nodes', []) + tree.get('floating_nodes', []): + found = ancestry(child, con_id, path + (tree,)) + if found: + return found + return () + + +def validate_outputs(outputs): + actual = {o['name']: (*[o['rect'][k] for k in ('x', 'y', 'width', 'height')], + o['scale'], o['transform']) for o in outputs if o['active']} + assert actual == OUTPUTS, ('output geometry', actual) + assert len([o for o in outputs if o['active']]) == 2, 'duplicate output' + return actual + + +def validate_reference(snapshot, con_id, pid, workspace_name='2', output_name='HEADLESS-2'): + """B_NONE may retain width 2; require actual empty decoration/full content. + + IPC visibility means view_is_visible, not proof of SHM coverage. RGB is + independently required by the runner. Width is returned only as metadata. + """ + validate_outputs(snapshot['outputs']) + path = ancestry(snapshot['tree'], con_id) + assert path, 'reference surface absent' + ref = path[-1] + ws = next((n for n in path if n.get('type') == 'workspace'), None) + out = next((n for n in path if n.get('type') == 'output'), None) + assert ref.get('type') == 'con' and ref.get('shell') == 'xdg_shell', 'reference view absent' + assert ref.get('app_id') == 'org.mosaic.StaticReference', 'reference App ID' + assert ref.get('pid') == pid and pid > 0 and ref['id'] == con_id, 'reference PID/ID' + assert ws and ws['name'] == workspace_name and ws.get('output') == output_name, 'reference workspace' + assert out and out['name'] == output_name and out['rect'] == FULL, 'reference output' + assert ref.get('visible') is True, 'reference visibility' + assert ref.get('border') == 'none', 'reference border mode' + assert ref.get('rect') == FULL and ref.get('window_rect') == FULL, 'reference full content' + assert ref.get('deco_rect') == ZERO, 'effective reference decoration' + return {'status': 'PASS', 'con_id': con_id, 'pid': pid, 'workspace': ws['id'], + 'output': output_name, 'stored_border_width': ref.get('current_border_width'), + 'reference': ref} + + +def scene_from_log(text, output): + rows = [s for s in text.splitlines() if 'PI_SCENE output=' + output + ' ' in s + and 'workspace=none' not in s] + assert rows, 'applied scene trace absent' + line = rows[-1] + m = re.search(r'workspace=(\d+) layout=(\d+) scroll=(-?[\d.]+) tile_scene=(-?\d+),(-?\d+) clip_valid=(\d+) clip=(-?\d+),(-?\d+),(\d+),(\d+)', line) + assert m, 'scene trace format' + v = list(map(float, m.groups())) + origin = OUTPUTS[output][:2] + return {'workspace': int(v[0]), 'layout_enum': int(v[1]), 'scroll': v[2], + 'tile_local': v[3:5], 'clip_valid': bool(v[5]), 'clip_local': v[6:10], + 'global_clip': [origin[0] + v[3] + v[6], origin[1] + v[4] + v[7], v[8], v[9]], + 'raw': line} + + +def measure_owner(text, owner): + rows = [x for x in text.splitlines() if 'PI_OWNER output=HEADLESS-1 ' in x + and f' id={owner["id"]} ' in x] + assert rows, 'applied owner trace absent' + line = rows[-1] + m = re.search(r'scene_valid=1 scene=(-?\d+),(-?\d+) applied_size=(\d+),(\d+)', line) + assert m, 'owner trace format' + x, y, w, h = map(int, m.groups()) + win = owner['window_rect'] + return {'id': owner['id'], 'scene': [x, y, w, h], 'window_rect': win, + 'horizontal_content': [x + win['x'], win['width']], 'raw': line, 'IPC': owner} + + +def client_commit(text): + rows = [s for s in text.splitlines() if s.startswith('COMMIT ')] + assert rows, 'no real client commit' + m = re.fullmatch(r'COMMIT ns=(\d+) serial=(\d+) size=(\d+)x(\d+) color=([0-9a-f]{8})', rows[-1]) + assert m, 'client commit format' + ns, serial, w, h = map(int, m.groups()[:4]) + assert ns > 0 and 0 < serial <= 0xffffffff and 0 < w <= 4096 and 0 < h <= 4096 + return {'ns': ns, 'serial': serial, 'width': w, 'height': h, 'color': m[5], 'raw': rows[-1]} + + +def validate_preflight(snapshot, con_id, pid, target_id, target_pid, scene, h2_scene, measured, commit): + reference = validate_reference(snapshot, con_id, pid) + ws = workspace(snapshot['tree'], '1') + path = ancestry(snapshot['tree'], target_id) + assert path and ws in path, 'target workspace ownership' + owner = path[-1] + assert owner.get('app_id') == 'org.mosaic.SavedBufferProbe' and owner.get('pid') == target_pid + assert owner.get('shell') == 'xdg_shell' and owner.get('visible') is True, 'target mapped' + assert ws['output'] == 'HEADLESS-1' and ws['layout'] == 'mosaic', 'H1 Mosaic' + assert owner.get('focused') is True and reference['reference']['focused'] is False, 'initial focus' + assert [z['name'] for z in snapshot['workspaces'] if z['focused']] == ['1'], 'workspace focus' + scroll = ws['mosaic']['rows'][0]['scroll_x'] + assert scene['workspace'] == ws['id'] and scene['layout_enum'] == 5, 'applied Mosaic owner' + assert scene['scroll'] == scroll and scene['tile_local'] == [-scroll, 0], 'applied camera' + assert scene['clip_valid'] and scene['global_clip'] == [-800, 0, 800, 600], 'viewport clip' + assert scene['clip_local'] == [scroll, 0, 800, 600], 'local clip' + h2ws = workspace(snapshot['tree'], '2') + assert h2_scene['workspace'] == h2ws['id'] and not h2_scene['clip_valid'], 'H2 ordinary clip' + assert measured['id'] == target_id and measured['IPC'] == owner, 'measured owner identity' + x, w = measured['horizontal_content'] + assert -800 < x < 0 < x + w, 'target continuation crosses boundary' + assert w == commit['width'] == owner['window_rect']['width'], 'configured content width' + assert commit['height'] == owner['window_rect']['height'] and commit['color'] in ('002344cc', '0020d0a0') + return {'status': 'PASS', 'reference': reference, 'workspace': ws['id'], + 'target': target_id, 'scroll': scroll, 'scene': scene, 'H2_scene': h2_scene, + 'measured': measured, 'commit': commit} + + +def validate_camera(before_scroll, after_scroll, delta, scene): + assert type(delta) is int and -2147483647 <= delta <= 2147483647 + assert after_scroll == before_scroll + delta and scene['scroll'] == after_scroll, 'camera correction applied' diff --git a/tooling/probes/step20c-static-nested-grab/placement.patch b/tooling/probes/step20c-static-nested-grab/placement.patch new file mode 100644 index 0000000..404ddd1 --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/placement.patch @@ -0,0 +1,16 @@ +--- a/nested-client.c ++++ b/nested-client.c +@@ -205,7 +205,12 @@ + int main(int argc,char **argv) { + signal(SIGTERM,stop); + const char *placement=argc>1?argv[1]:"b_cross"; +- if (!strcmp(placement,"visible")) { ++ /* EXPERIMENTAL: only placement changes; serial/grab/lifecycle stay historical. ++ * Both 1x1 anchors lie inside their parent SHM window geometry. */ ++ if (!strcmp(placement,"static_cross")) { ++ a.anchor_x=100; a.anchor_y=80; a.width=350; ++ b.anchor_x=100; b.anchor_y=60; b.width=300; ++ } else if (!strcmp(placement,"visible")) { + a.anchor_x=50; a.anchor_y=80; a.width=250; + b.anchor_x=20; b.anchor_y=60; b.width=170; + } else if (!strcmp(placement,"both_cross")) { diff --git a/tooling/probes/step20c-static-nested-grab/predicates.py b/tooling/probes/step20c-static-nested-grab/predicates.py new file mode 100644 index 0000000..fa44a6a --- /dev/null +++ b/tooling/probes/step20c-static-nested-grab/predicates.py @@ -0,0 +1,146 @@ +"""Shared offline/live predicates: facts precede scientific classification. + +CONTRACT: full RGB comparisons have no mask/tolerance; grabs require real logs. +INVARIANT: dismissal, client destruction, clipping and layout remain distinct. +""" +import re +from PIL import Image,ImageChops +COLORS=dict(TOPLEVEL=(35,68,204),POPUP_A=(224,192,32),POPUP_B=(32,208,160)) + +class Defect(RuntimeError): + """A directly measured scientific contract failure, distinct from fixture ambiguity.""" + +def paint(im,target): + im=im.convert('RGB');assert im.size==(800,600) + mask=Image.new('L',im.size);mask.putdata([255 if p==COLORS[target] else 0 for p in im.get_flattened_data()]) + return dict(count=sum(mask.histogram()[1:]),bbox=mask.getbbox()) + +def point(im,target,origin): + box=paint(im,target)['bbox'];assert box,('no measured paint',target) + # WHY: A may be occluded by B; bbox center alone is not a valid hit point. + x1,y1,x2,y2=box + for y in range(y1+10,y2-10,5): + for x in range(x1+10,x2-10,5): + if all(im.getpixel((xx,yy))==COLORS[target] for yy in range(y-8,y+9) for xx in range(x-8,x+9)): + return [x+origin,y] + raise AssertionError('no solid measured interior '+target) + +def equal(a,b): + assert a.size==b.size==(800,600) + rows=[(i%800,i//800,x,y) for i,(x,y) in enumerate(zip(a.get_flattened_data(),b.get_flattened_data())) if x!=y] + return dict(equal=not rows,changed=len(rows),bbox=ImageChops.difference(a,b).getbbox(),pixels=rows) + +def reference(a,b): + result=equal(a,b) + assert result['equal'] and all(c==(48,48,56) for c in a.get_flattened_data()),'unstable reference' + return result + +def configured(text,name): + rows=re.findall(rf'{name} ROLE_CONFIGURE x=(-?\d+) y=(-?\d+) w=(\d+) h=(\d+)',text) + assert rows,(name,'no configure') + return list(map(int,rows[-1])) + +def geometry(text,origin): + a=configured(text,'POPUP_A');b=configured(text,'POPUP_B') + A=[origin[0]+a[0],origin[1]+a[1],a[2],a[3]] + B=[A[0]+b[0],A[1]+b[1],b[2],b[3]] + assert all(r[0]<0=2 + assert request.start() POPUP_A -> POPUP_B',status='PASS') + +def pair(lines,target): + events=[] + for line in lines: + m=re.fullmatch(r'POINTER_BUTTON target=(\w+) serial=(\d+) button=(\d+) state=(\d+)',line);assert m + events.append([m[1],*map(int,m.groups()[1:])]) + if len(events)!=2 or any(e[0]!=target for e in events) or [e[2:] for e in events]!=[[273,1],[273,0]]: + raise Defect('wrong actual button recipient/pair: '+str(events)) + assert 00,'permitted popup color absent' + if mosaic and result['H2'][name]['count']: + raise Defect('forbidden popup pixels '+name) + if not mosaic and not result['H2'][name]['count']: + raise AssertionError('standard popup continuation absent '+name) + # CONTRACT: compare the exact color region against configured geometry, + # subtracting B's occlusion of A, not against a predicted painted bbox. + for side,im,ox in [('H1',left,-800),('H2',right,0)]: + for y in range(600): + for x in range(800): + gx=x+ox + inside=rect[0]<=gx150 or any(p.stat().st_size>32*1024*1024 for p in run.glob('*.log')): + expired.set();stop_owned();return + +thread=threading.Thread(target=watchdog,daemon=True);thread.start() + +def log(name):return (run/(name+'.log')).read_text(errors='replace') + +def wait(fn,seconds=6): + end=min(time.monotonic()+seconds,started+143) + while time.monotonic()before);time.sleep(.18) + event('injection',action=action,global_point=xy,sent=[x+800,y],extent=[1600,600],grab=grab) + +def buttons(name): + return [s for s in log(name).splitlines() if s.startswith(('POINTER_BUTTON ','BUTTON '))] + +def click(tag,name,xy,grab=False,action='right'): + before={n:len(buttons(n)) for n in [name,'reference','filler']} + t0=time.monotonic_ns();inject(action,xy,grab) + row=dict(tag=tag,global_point=xy,action=action,start_ns=t0,end_ns=time.monotonic_ns(),events={n:buttons(n)[before[n]:] for n in before}) + clicks.append(row);save(lab,'clicks',clicks);return row + +def delivery(row,name,target): + assert not row['events']['reference'] and not row['events']['filler'],'other client received visible popup button' + return pair(row['events'][name],target) + +def correlate_pointer(name,xy,rect,target): + # WHY: an injection request alone does not identify applied popup origin. + rows=re.findall(rf'POINTER_(?:ENTER|MOTION) target={target} .*?x=([\d.-]+) y=([\d.-]+)',log(name)) + assert rows,'missing pointer local coordinates' + local=list(map(float,rows[-1]));expected=[xy[0]-rect[0],xy[1]-rect[1]] + assert local==expected,('pointer/global geometry mismatch',target,local,expected) + return dict(global_point=xy,local=local,rectangle=rect) + +def close_client(proc,name,value): + proc.stdin.write(value+'\n');proc.stdin.flush() + code=proc.wait(timeout=3);assert code==0,(name,code) + assert 'TOPLEVEL DESTROY' in log(name) + wait(lambda:find('org.mosaic.NestedGrabProbe',proc.pid) is None) + +# CONTRACT: applied owner, paint and XDG surface size are distinct measurements. +def owner_state(name,proc,tag): + snap=snapshot(tag);ws=workspace(snap['tree'],'1');owner=next(n for n in nodes(ws) if n.get('pid')==proc.pid) + assert owner['app_id']=='org.mosaic.NestedGrabProbe' and owner['shell']=='xdg_shell' and owner['visible'] + assert ws['output']=='HEADLESS-1' and owner['focused'] and snap['focused']==[owner['id']] + measured=measure_owner(log('compositor'),owner);scene=scene_from_log(log('compositor'),'HEADLESS-1') + assert scene['workspace']==ws['id'] + return snap,ws,owner,measured,scene + +def open_chain(name): + global stage + reserve(lab,'chain');cmd('workspace 1');proc=spawn(name,[lab/'nested-client','static_cross'],debug=True) + wait(lambda:find('org.mosaic.NestedGrabProbe',proc.pid) and 'TOPLEVEL MAP ' in log(name));time.sleep(.6) + snap,ws,owner,before,sc=owner_state(name,proc,name+'-before-camera') + assert ws['layout']=='mosaic' + current=ws['mosaic']['rows'][0]['scroll_x'];x,w=before['horizontal_content'];desired=-w//2;delta=int(x-desired) + if delta: + reserve(lab,'camera');cmd(f'[con_id={owner["id"]}] layout mosaic scroll {delta}');time.sleep(.6) + snap,ws,owner,after,scene=owner_state(name,proc,name+'-applied') + validate_camera(current,ws['mosaic']['rows'][0]['scroll_x'],delta,scene) + assert scene['clip_valid'] and scene['global_clip']==[-800,0,800,600] + assert not scene_from_log(log('compositor'),'HEADLESS-2')['clip_valid'] + refstate=validate_reference(snap,refid,ref.pid) + camera_rows.append(dict(chain=name,before=current,delta=delta,after=scene['scroll'],before_owner=before,after_owner=after,scene=scene,reference=refstate)) + save(lab,'camera-calibration',camera_rows) + if name=='E1': + stage='reference';inject('move',[-790,590]);time.sleep(.5) + first=capture('H2-reference-1','HEADLESS-2');time.sleep(.6) + second=capture('H2-reference-2','HEADLESS-2');cmp=reference(first,second) + save(lab,'reference-stability',dict(status='PASS',comparison=cmp,reference=refstate,interval='>=600ms',focus=snap['focused'])) + global baseline + baseline=first + stage=name+'-open-A';topim=capture(name+'-top','HEADLESS-1') + topbox=paint(topim,'TOPLEVEL')['bbox'];assert topbox + # Tiled scene+window offset is checked against actual painted blue origin. + origin=[after['horizontal_content'][0],topbox[1]] + assert topbox[0]-800==origin[0] and topbox[3]-topbox[1]==320 + assert origin[1]==after['scene'][1]+owner['window_rect']['y'] + owners[name]=dict(id=owner['id'],pid=proc.pid,workspace=ws['id'],logical_rect=owner['rect'],applied=after,drawn_size=[420,320],paint=paint(topim,'TOPLEVEL'),content_origin=origin) + save(lab,'applied-owner-geometry',owners) + cp=click(name+'-open-A',name,point(topim,'TOPLEVEL',-800),True,'left') + wait(lambda:'POPUP_A MAP ' in log(name));time.sleep(.25) + aim=capture(name+'-A','HEADLESS-1');ap=point(aim,'POPUP_A',-800) + stage=name+'-open-B';click(name+'-open-B',name,ap,True,'left') + wait(lambda:'POPUP_B MAP ' in log(name));time.sleep(.3) + prot=protocol(log(name));g=geometry(log(name),origin) + assert g['configured_A']==[100,80,350,170] and g['configured_B']==[100,60,300,130] + correlate_pointer(name,ap,g['A'],'POPUP_A') + left=capture(name+'-mosaic-H1','HEADLESS-1');right=capture(name+'-mosaic-H2','HEADLESS-2') + rgb=popup_pixels(left,right,g,True,baseline) + clip=root_clip(log('compositor'),origin,True) + chains[name]=dict(protocol=prot,geometry=g,pixels=rgb,owner=owners[name]) + clips[name]=dict(tile=scene,root=clip,child=clip['child']) + save(lab,'grab-serials',{n:v['protocol']['grabs'] for n,v in chains.items()}) + save(lab,'xdg-protocol-order',{n:v['protocol'] for n,v in chains.items()}) + save(lab,'popup-root-geometry',{n:v['geometry']['A'] for n,v in chains.items()}) + save(lab,'popup-nested-geometry',{n:v['geometry']['B'] for n,v in chains.items()}) + save(lab,'applied-clips',clips);save(lab,'mosaic-pixels',{n:v['pixels'] for n,v in chains.items()}) + return proc,owner,g,left + +try: + comp=spawn('compositor',[lab/'swayfx-mosaic','-d','-c',run/'config']) + sock=wait(lambda:next(iter(runtime.glob('sway-ipc.*.sock')),None)) + display=wait(lambda:next((x for x in runtime.glob('wayland-*') if x.is_socket()),None)) + env.update(WAYLAND_DISPLAY=display.name,SWAYSOCK=str(sock)) + assert Path(f'/proc/{comp.pid}/exe').resolve()==lab/'swayfx-mosaic' + maps=Path(f'/proc/{comp.pid}/maps').read_text();(run/'proc-maps.txt').write_text(maps) + paths=sorted({s.split()[-1] for s in maps.splitlines() if len(s.split())>=6 and s.split()[-1].startswith('/')}) + wl=Path('/usr/lib/libwlroots-0.20.so').resolve() + assert [Path(x).resolve() for x in paths if 'libwlroots' in x]==[wl] and sha(wl)==WLROOTS_SHA + assert not any('libscenefx' in x for x in paths) + save(lab,'dependency-mappings',dict(status='PASS',paths=paths,wlroots=str(wl),wlroots_sha256=sha(wl),SceneFX_shared=False)) + outputs=ipc('-t','get_outputs');validate_outputs(outputs);save(lab,'output-geometry',outputs) + pointer=spawn('pointer',[lab/'pointer-daemon']);wait(lambda:'POINTER_READY' in log('pointer')) + cmd('workspace 2');ref=spawn('reference',[lab/'static-reference'],debug=True) + rn=wait(lambda:find('org.mosaic.StaticReference',ref.pid));refid=rn['id'];cmd(f'[con_id={refid}] border none') + cmd('workspace 1');filler=spawn('filler',[lab/'static-reference'],debug=True) + wait(lambda:find('org.mosaic.StaticReference',filler.pid));cmd('layout mosaic') + E1,owner1,g1,im1=open_chain('E1') + stage='E1-visible-B';xy=point(im1,'POPUP_B',-800);row=click(stage,'E1',xy) + result=delivery(row,'E1','POPUP_B');coords=correlate_pointer('E1',xy,g1['B'],'POPUP_B');mapped(log('E1')) + save(lab,'mosaic-input',dict(status='PASS',pair=result,coordinates=coords,click=row)) + stage='E1-forbidden-B';B=g1['B'];xy=[int((max(0,B[0])+min(800,B[0]+B[2]))//2),B[1]+30] + assert B[0]<=xy[0]LIMITS[k] for k in kinds): + raise RuntimeError('budget exhausted') + for k in kinds: + state['used'][k]+=1 + state['events'].append(dict(seq=len(state['events']),kind=k,ns=time.monotonic_ns())) + durable(path,state) + return state + +def environment(): + # WHY: inherited desktop/loader settings must not select a daily socket. + excluded={'DISPLAY','WAYLAND_DISPLAY','SWAYSOCK','I3SOCK','XDG_RUNTIME_DIR', + 'WAYLAND_DEBUG','DBUS_SESSION_BUS_ADDRESS','XDG_SESSION_ID','XDG_SEAT', + 'XDG_VTNR','XDG_SESSION_TYPE','XDG_CURRENT_DESKTOP','XDG_SESSION_DESKTOP', + 'LD_LIBRARY_PATH','LD_PRELOAD','LD_AUDIT','PKG_CONFIG_PATH','WLR_BACKENDS', + 'WLR_RENDERER','WLR_HEADLESS_OUTPUTS','WLR_DRM_DEVICES','WLR_LIBINPUT_NO_DEVICES'} + env={k:v for k,v in os.environ.items() if k not in excluded and not k.startswith('WLR_')} + env.update(LC_ALL='C',PYTHONDONTWRITEBYTECODE='1') + return env + +def command(lab,label,argv,cwd=None): + result=subprocess.run(list(map(str,argv)),cwd=cwd,env=environment(),capture_output=True,text=True,timeout=30) + (lab/'raw'/(label+'.stdout')).write_text(result.stdout) + (lab/'raw'/(label+'.stderr')).write_text(result.stderr) + with (lab/'raw/commands.jsonl').open('a') as out: + out.write(json.dumps(dict(label=label,argv=list(map(str,argv)),exit=result.returncode,cwd=str(cwd)))+'\n') + assert result.returncode==0,(label,result.returncode,result.stderr) + return result.stdout + +def verify(manifest): + seen=set() + for row in Path(manifest).read_text().splitlines(): + digest,p=row.split(' ',1) + assert p not in seen and sha(p)==digest,(manifest,p) + seen.add(p) + assert seen + return dict(entries=len(seen),sha256=sha(manifest)) + +def inventory(path, files): + with Path(path).open('x') as out: + for p in sorted(files):out.write(f'{sha(p)} {p}\n') + return verify(path)