docs(mosaic): preserve static RGB input and standard return evidence

This commit is contained in:
fy59 2026-10-13 08:24:56 +02:00
parent fb0293cd85
commit d0fa8c5c9e
17 changed files with 1569 additions and 0 deletions

View file

@ -4,6 +4,8 @@
### STEP20C experimental evidence
- 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.
- Approved private static candidate decisions (2026-10-13): separate notices, project maintenance and exact-pair qualification. Current package unchanged; private real `/usr` static build/model/staging/library identity/headless startup pass. Whole-H2 reference fixture failed after a focus change; input and splith return NOT TESTED. PARTIALLY_FEASIBLE / UNDECIDED, preserved historical evidence; the original manifests and fixture bytes remain unchanged.

View file

@ -416,3 +416,17 @@ PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.
The [static RGB/input/standard retest](design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md)
is **EXPERIMENTAL / FEASIBLE for the tested matrix**; its evidence is frozen
for publication.
The corrected shared IPC predicate/replay pass before the sole unchanged-static-
binary run. Two full H2 references and all later H2 RGB are exact; H1 retains
182,214 target pixels, H2 zero, actual visible/reference/standard button pairs
are delivered only to the right client. Targeted splith removes the native clip
and retains ordinary pixels/input. The runner exits 1 only on terminal exit IPC
after completed measurements and compositor exit 0; that raw failure is retained
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.

View file

@ -357,3 +357,17 @@ PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.
The [static RGB/input/standard retest](design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md)
is **EXPERIMENTAL / FEASIBLE for the tested matrix**; its evidence is frozen
for publication.
The corrected shared IPC predicate/replay pass before the sole unchanged-static-
binary run. Two full H2 references and all later H2 RGB are exact; H1 retains
182,214 target pixels, H2 zero, actual visible/reference/standard button pairs
are delivered only to the right client. Targeted splith removes the native clip
and retains ordinary pixels/input. The runner exits 1 only on terminal exit IPC
after completed measurements and compositor exit 0; that raw failure is retained
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.

View file

@ -275,3 +275,17 @@ PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.
The [static RGB/input/standard retest](design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md)
is **EXPERIMENTAL / FEASIBLE for the tested matrix**; its evidence is frozen
for publication.
The corrected shared IPC predicate/replay pass before the sole unchanged-static-
binary run. Two full H2 references and all later H2 RGB are exact; H1 retains
182,214 target pixels, H2 zero, actual visible/reference/standard button pairs
are delivered only to the right client. Targeted splith removes the native clip
and retains ordinary pixels/input. The runner exits 1 only on terminal exit IPC
after completed measurements and compositor exit 0; that raw failure is retained
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.

View file

@ -361,3 +361,17 @@ PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.
The [static RGB/input/standard retest](design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md)
is **EXPERIMENTAL / FEASIBLE for the tested matrix**; its evidence is frozen
for publication.
The corrected shared IPC predicate/replay pass before the sole unchanged-static-
binary run. Two full H2 references and all later H2 RGB are exact; H1 retains
182,214 target pixels, H2 zero, actual visible/reference/standard button pairs
are delivered only to the right client. Targeted splith removes the native clip
and retains ordinary pixels/input. The runner exits 1 only on terminal exit IPC
after completed measurements and compositor exit 0; that raw failure is retained
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.

View file

@ -0,0 +1,377 @@
# STEP20C static RGB, input and standard-return retest
**EXPERIMENTAL / LOCAL / UNCOMMITTED. Protocol before fixture development.**
Phase A froze the historical failure in
`fb0293cd855a2ec917c14df3ecf3504c064499fc`, published to canonical Forgejo
and GitHub with a clean worktree. This independent phase B is never committed
or published. [Architecture](../ARCHITECTURE.md), [UX](../UX.md),
[Roadmap](../ROADMAP.md) and [Packaging](../PACKAGING.md) remain authoritative.
D1–D4 persist. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G
PLANNED. Production multi-output remains NOT YET SUPPORTED.
## Historical error and mandatory offline gates
The [frozen follow-up](STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md)
stopped on an invalid `current_border_width == 0` assertion, before any PNG,
click or standard return. Its verdict remains INCONCLUSIVE / UNDECIDED and
its run/camera budget is exhausted. Neither its runner nor manifests change.
Pinned SwayFX `fd71a6bdc061bd633b488ae7b83e8a6981d22f86` border.c sets
B_NONE and only changes thickness with an optional second argument. The exact
candidate's ipc-json.c reports stored current.border_thickness; transaction.c
sets effective B_NONE border width/top to zero. Therefore width 2 is accepted
as recorded metadata under border none. A shared pure predicate validates the
actual IPC object, identity, ancestors, visibility, border mode, empty deco_rect,
full rect/window_rect and exact H2 geometry. The runner imports the same function
as the offline tests. No parallel implementation is permitted.
Read-only replay uses the sealed
`/home/fy59/swayfx-mosaic-static-followup-nhwejz6u/results/preflight-snapshot.json`,
scene traces, client logs and earlier real IPC observations. Historical IDs
7/8/9 and PIDs are used only in replay. Negative tests alter explicit copies:
wrong App ID, PID, ID, workspace, output, visibility, border, deco, absent view,
window/content/output geometry, focus, camera and clip must be rejected.
An actual earlier border-normal reference must also fail.
`STATIC_REFERENCE_IPC_REPLAY=PASS` and `STATIC_FIXTURE_PREFLIGHT_REPLAY=PASS`
are mandatory before any run reservation. Historical PNGs from the older static
runtime may validate pixel/coordinate code, clearly labelled HISTORICAL; the
failed follow-up contains no PNG. Synthetic button fixtures are explicitly
SYNTHETIC, never new input evidence. Tests check exact BTN_RIGHT=273 press/release,
positive real-format serials, timestamps/order, event count, malformed lines,
wrong recipient and absence in both other clients. No events may be silently
discarded by the button parser.
## Assertion authority and remaining measurement
| Field / assertion | Source and meaning | Offline evidence / live requirement |
| --- | --- | --- |
| outputs rect/scale/transform | ipc-json output description, logical coordinates | actual snapshots, wrong-output negatives; reread live |
| App ID/PID/con_id/ancestors | real view/node and tree ownership | actual reference/target/filler; live IDs discovered by PID |
| visible/focused/workspaces | view_is_visible and default-seat focus; not pixel coverage | actual tree plus get_workspaces; sample live |
| border/width | current border enum and stored thickness | B_NONE width 2 accepted; normal rejected |
| rect/deco_rect/window_rect | node box with title removed; decoration and content-relative box | actual full H2, empty deco; pixels still require capture |
| scroll_x | Mosaic row state, may precede applied scene | compare IPC with native PI_SCENE after settling |
| PI_OWNER scene/applied_size | wlr_scene_node_coords, animation current size | parse actual target trace; never use IPC rect as buffer y |
| PI_SCENE local/global clip | applied tiling tree, private get_clip | output origin + tiling translation + local clip equals viewport |
| client COMMIT size/color | exact C17 SHM draw, configure/ACK loop | actual logs; target horizontal width compared with IPC content |
| SHM colors | XRGB8888 target 0x2344cc/0x20d0a0; reference 0x303038 | source and historical PNG; live captures required |
| capture coordinates | scale 1/normal, local x=global x-output origin | signed-coordinate tests and measured pixels; no approximate native y |
| click point | 41×41 actual-color interior, margin 20; H2 continuation | historical image and negative sparse fixtures; recompute live |
| BUTTON log | real pointer callback, ns/serial/button/state | positive/negative parser tests; live delivery mandatory |
| splith criteria owner | layout.c uses matched container workspace; Mosaic clears parent target | exact candidate source audited; standard trace/pixels/input live |
The machine-readable assertion audit records each source location, historical
input, replay result and uncertainty. Assertions depending on new RGB/input or
standard-return observations remain explicitly live requirements; historical
or synthetic success cannot qualify them. No presentation hypothesis is an
invariant. True output/ownership/input/clip invariants remain required.
## Exact candidate, isolation and immutable runner
Reuse only
`/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt/stage-static/usr/bin/swayfx-mosaic`:
SHA-256 `f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`,
build ID `942db64269640778ee433e5970ba1100bc60f94c`.
Check regular executable, ELF64 x86-64, private clip get/set ordinary ELF definitions (not necessarily .dynsym exports), DT_NEEDED,
no SceneFX shared dependency, ldd -r and the exact system wlroots SHA-256
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
Copy unchanged into one new campaign-owned laboratory outside Git. Compile
only the three historical C17 clients from manifest-verified exact C/protocol
sources; retain sources, commands and binary hashes. No SceneFX/Mosaic build.
The prepared laboratory has one exclusive owner file and persistent flock
ledger. Offline gates, source audit, AST, C17, library identity, ledger tests,
documentation and the exact runner inventory must all pass before reservation.
Freeze hashes of the executed fixture and verify them immediately before run.
Do not edit that copy afterward. Reports/collector receipts are separate.
Private runtime mode 0700, headless backend, private WAYLAND_DISPLAY and
explicit private swaymsg -s only. Strip inherited desktop sockets/library
overrides. Own process groups, bounded logs and 150-second watchdog. Retain
logs/exit codes before removing only owned sockets/runtime. No daily IPC.
## One-run ordered protocol
HEADLESS-1=(-800,0,800,600), HEADLESS-2=(0,0,800,600), scale 1, normal.
No popup, rotation, fractional scaling, resize, migration, saved-buffer scenario
or added effect. Set focus_follows_mouse no. Create H2 static reference, discover
its real PID/con_id, and apply only `[con_id=ID] border none`. H1 filler shares
its App ID; never apply a global App ID rule. Create H1 target and establish
Mosaic with target/workspace 1 focused before any H2 reference PNG.
Measure applied scene and client content width. Compute desired left=-width/2
and signed camera delta from that actual state, reserve once and apply. Do not
hard-code +160. Confirm IPC/applied scroll, owner crossing x=0, workspace identity
and global clip [-800,0,800,600]. No second adjustment if later focus moves it.
Park pointer on H1 (-790,590), stabilize and capture H2 twice at least 500 ms
apart. Require 800×600, fixed #303038 throughout and exact full RGB equality;
retain PNG hashes. No tolerance, crop or excluded ROI. Difference stops all
dependent tests and records every differing pixel.
Capture H1/H2 Mosaic. Require positive measured target paint on H1, continuation
across x=0, zero target colors on H2 and full H2 RGB equality. Client COMMIT,
painted bbox and scene-derived horizontal continuation are separate observations.
Select visible click from actual target-colored H1 pixels with 20-pixel margins.
Select H2 point in geometric continuation, within the actual full reference
surface, at least 20 pixels from borders. One right press/release to visible
target; exact client traces, no delivery to other clients. One right click to H2;
zero new target/filler events, actual reference pair, record focus and camera.
Park pointer on H1, recapture whole H2 and require exact equality after focus.
Send `[con_id=targetID] layout splith`, even when global focus is H2. Require
same H1 workspace, actual splith, mapped target, absent H1/H2 tiling clips,
ordinary target pixels and zero forbidden target pixels on H2. Capture both
outputs and compare whole H2. Recompute standard target interior; third right
click must deliver real target pair only. Park pointer, capture final H2 and
check focus-transition RGB. No reuse of Mosaic point without measurement.
## Budget, classification and preservation
Limits: static run 1, pristine 0, Mosaic build 0, SceneFX build 0, camera 1,
right clicks 3 (one of each named kind), movements 8, PNG 12, duration 150 s.
Every click also moves the pointer and consumes a movement reservation. Reserve
before run/capture/click/camera/move; failed work consumes reservations, no reset,
refund or second laboratory. Expected PNG 8, movement 6 including click motions.
FEASIBLE requires all exact candidate/replay/reference/Mosaic/input/standard
gates and no crash/protocol blocker in this precise matrix. PARTIALLY_FEASIBLE
retains demonstrated properties with open paths. INCONCLUSIVE denotes fixture
measurement failure. BLOCKED requires measured clipping/input/ABI/standard
defect, not an invalid fixture assertion. Architecture is respectively
STATIC_MOSAIC_RGB_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX,
STATIC_MOSAIC_RGB_INPUT_STANDARD_REQUIRES_REDESIGN, or UNDECIDED.
Retain exact fixture/client/config, raw PNG/IPC/protocol/client/scene logs,
timestamps, serials, library mappings and exit codes outside Git. Produce all
requested result JSON and seal source/evidence inventories once, excluding
manifests themselves. Verify every entry read-only. Damage extents, client
damage requests and final PNGs do not prove intermediate frames or native
output-buffer identity. Wider geometry, popup/grabs, lifecycle/effects/buffers,
applied-generation consistency, complete ABI/maintenance/notices remain open.
No algorithm change or STEP20C integration follows automatically.
The [source-only fixture](../../tooling/probes/step20c-static-rgb-input-retest/README.md)
contains replay, predicates, ledger, runner, pixel analysis and collector.
## Preparation incident before run reservation
The first preparation in the single new LAB stopped because the checker used
`nm -D` for private static clip functions. The exact historical symbol receipt
qualifies ordinary executable T definitions, and `nm` confirms both. This is
a checker error, not a missing function or ABI failure. Preserve the original
checker and dynamic-symbol log; continue only that same LAB's pre-run preparation,
with no budget reservation/reset and no compositor launch. The corrected gate
uses ordinary ELF symbols. The initial checker and incident are evidence files;
the subsequently frozen/executed runner is explicitly distinct.
## Completed single campaign — 2026-10-13
`SWAYFX_MOSAIC_STATIC_RGB_INPUT_STANDARD_PROBE=FEASIBLE`
Architecture:
**STATIC_MOSAIC_RGB_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX**.
This is a scientific verdict on the requested static, tiled, two-output matrix;
it does not mean the runner command exited zero, nor close STEP20C production.
LAB `/home/fy59/swayfx-mosaic-static-rgb-input-retest-hn67lnhq`.
Run duration 9.142914 seconds, one run, no retry. No pristine run, no new
SceneFX/Mosaic build, no binary/shader/algorithm/production/package/UX change.
### Candidate, clients and offline gates
The original and copied executable both retain SHA-256
`f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`
and build ID `942db64269640778ee433e5970ba1100bc60f94c`, regular executable
mode 0755, ELF64 x86-64. Ordinary ELF clip get/set T definitions pass; these
private static definitions need not appear in .dynsym. No SceneFX DT_NEEDED,
RPATH/RUNPATH, unresolved relocation or loaded shared SceneFX exists. Actual
process maps identify the unchanged system wlroots with SHA-256
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
The historical full source/evidence inventories and follow-up 24/46-entry
inventories were verified read-only. No historical manifest was rewritten.
Three C17 client-only syntax checks and builds passed from exact verified
historical C/generated-protocol inputs; commands are in raw/commands.jsonl:
| Client | Result executable SHA-256 |
| --- | --- |
| saved-client | `3d5d0c5e446b995c210006a5bafe0d86dd5f32a228e1b7137d12af6ec9e217da` |
| static-reference | `11166ea1bb7cb3bb08f10ab93e091c5c5ea8687649b5fb0705288c7ca91680e8` |
| pointer-daemon | `76ad0f093f2b7a97359920b7bd456cfa93a1581c73875547a86eb911f2ed2d7a` |
The corrected shared predicate accepted the actual historical H2 node with
border none / stored width 2, full window_rect and zero deco_rect. Thirty-nine
negative cases rejected wrong identities/ownership/geometry/focus/clip, the
actual filler, an actual earlier normal-border reference, sparse/inaccessible
pixel fixtures and invalid synthetic button sequences/recipients. Historical
PNG and synthetic input observations remain explicitly labelled. Both required
replay markers passed, with AST, shell recipes, links and durable concurrent/
exhausted/corrupt-ledger tests before reservation.
The exact runner SHA-256 is
`07ce1f98c9cb35ddb5219aff3991f19321759ac4edb26ab6d354096a853979d6`.
The pre-run fixture inventory (10 source files plus protocol) has 11 entries,
SHA-256 `294c001b5e88d7759255afb59646a386a59e2273f21e19c5a29512a4230f56a2`.
The executed copy remains immutable. The live runner imports the same validators
as replay. No width-zero assertion was relaxed after launching the compositor.
### Applied ownership, border, camera and clip
Both real outputs match (-800,0,800,600) / (0,0,800,600), scale 1, normal.
Reference con_id 7/PID 1700453 belonged to workspace 2 (ID 5), H2. Filler ID 8/
PID 1700459 kept normal border; target ID 9/PID 1700462 belonged to workspace 1
(ID 6), H1. These IDs were discovered at runtime, never hard-coded in the runner.
Target/workspace 1 initially focused; reference unfocused. Only `[con_id=7]
border none` affected H2. IPC retained width 2, rect/window_rect 800×600 and
zero deco_rect; full RGB then independently established absent rendered decoration.
Applied scroll initially 0. Actual target horizontal content left -158, width
636, desired left -318 gave the sole signed correction +160. This was computed
again from actual state, not borrowed as a constant. IPC and scene both read
160. Owner scene (-320,0,640,600), content continuation [-318,318), actual client
COMMIT 636×573. Tile-local translation (-160,0), local clip (160,0,800,600), H1
origin (-800,0) yield global clip (-800,0,800,600). H2 tiling clip is absent.
Native buffer y was never inferred from window_rect.y.
### Whole RGB and measured input
The two 800×600 H2 references, captured 600 ms apart after H1 focus/setup and
pointer parking, are entirely #303038 and exact RGB equals. Every subsequent
H2 PNG is also entirely equal: Mosaic, after H2 click/focus, standard layout,
and after standard target click/focus. Six H2 PNGs share SHA-256
`4471e93ccb6cf3ff4970f61fce405431a228af598d2f1f030a3b7586962ecd7a`.
Five independent whole-frame comparisons each have zero changed pixels; no
crop, excluded ROI, RGB tolerance or focus compensation.
H1 Mosaic contains 182,214 target-colored pixels, solid painted box local
[482,25,800,598), global x [-318,0), against continuation [-318,318).
H2 has zero target-colored pixels plus independently exact whole RGB.
H1 Mosaic PNG SHA-256:
`1ca64ee4aac80dd8377609528598608a412c1169d75bd4bce93b394292919ab8`.
| Actual injection | Recipient and real serials | Other client buttons | Focus afterward |
| --- | --- | --- | --- |
| Mosaic visible (-159,311) | target, BTN_RIGHT 273 press 35/release 36 | reference/filler none | target 9, workspace 1 |
| Mosaic continuation on H2 (100,311) | reference, press 48/release 49 | target/filler none | reference 7, workspace 2 |
| Standard recomputed (-200,311) | target, press 67/release 68 | reference/filler none | target 9, workspace 1 |
Each point uses measured captured pixels and a 41×41 interior neighborhood;
H2 also lies within the un-clipped target's measured horizontal continuation.
Client logs retain ns timestamps, real serials, enter/motion and protocol traces.
All target logs contain exactly four button lines (visible + standard pairs),
reference exactly two, filler zero. Camera remains 160 after both Mosaic clicks.
Reference focus changes without any H2 RGB difference.
After reference focus, `[con_id=9] layout splith` keeps H1 workspace ID 6,
changes its actual layout to splith and leaves the target mapped. commands.c
binds handler context to matched container.pending.workspace; layout.c clears
container targeting when that workspace is Mosaic. This source audit preceded
execution. Native PI_SCENE reports layout enum 1, tile translation (0,0),
clip_valid=0 on both H1 and H2. H1 standard has 226,908 target pixels, local
[402,25,798,598), configured 396×573, with zero target pixels on H2. H1 standard
PNG SHA-256 `195c4faea3020af31819214346772339f62a34c8a82386afb3176ddb9391ee1a`.
The standard click point was recalculated from this image. Actual standard input,
clip removal, layout, ownership and rendered pixels pass together.
### Terminal fixture incident, preserved raw status
The executed runner's final `swaymsg -s PRIVATE exit` returned 1 after all
measurements and `standard-return.json` had been written. It therefore exited
1 and wrote runtime.status=INCONCLUSIVE. Those bytes/statuses are preserved,
not changed to PASS. The compositor log shows Handling command exit, orderly
shutdown and compositor code 0. All three clients exited 0, logging errno 32
(EPIPE) only after the final capture; pointer was owned and terminated -15.
No wl_display.error protocol event or pre-measurement transport failure occurs.
Exact source is compatible with the terminal observation: cmd_exit invokes
sway_terminate(0), which terminates the display event loop; ipc_send_reply queues
its write for a later writable callback. Closing the compositor may remove the
socket before swaymsg reads that reply. This is an observed terminal fixture
incident, not a clipping/input/standard-layout failure. Loss of the queued IPC
response is a compatible explanation, not an independently observed packet
trace or a universal assertion about every exit command. Its exact response error
text was not retained separately because subprocess.check=True raised; the
CalledProcessError, command/code and compositor/client logs remain available.
A separate post-run validator rechecks actual PNGs, IPC/scene, recipient event
lists, focus, clip absence, budget, library identity and cleanup without any
runtime invocation. Negative post-run cases reject abnormal compositor exit,
retained standard clip, invisible-target delivery and even one changed H2 pixel.
Only all measured gates plus this narrow terminal-source/log diagnosis permit
scientific FEASIBLE. The collector and validator are retained under postrun-fixture,
separate from immutable fixture. No new run, workaround or budget refund.
### Budget, damage, integrity and validation
| Resource | Consumed / maximum |
| --- | --- |
| Static runs | 1/1 |
| Pristine runs | 0/0 |
| SceneFX / Mosaic builds | 0/0 each |
| Camera correction | 1/1 |
| Right clicks | 3/3, one per named kind |
| Pointer motions, including click positioning | 6/8 |
| PNGs | 8/12 |
| Duration | 9.142914 / 150 seconds |
Persistent ledger remains consumed. Unused moves/PNGs never authorize another
run. The single runtime `/tmp/msr-26spytfz` is removed; all owned process groups
are reaped and PIDs absent. No private socket remains. Protected system binary,
wlroots/SceneFX libraries, AGENTS/UX/package/patch hashes remain unchanged.
No daily IPC/session/QuickShell/dotfile/Codex change or STEP20D work.
271 TEMPORAL_DAMAGE lines cover setup, transitions and cleanup. Actual clients
request full SHM damage on commits, and stabilized PNGs are consistent. This
is PARTIAL damage evidence: no complete intermediate-frame coverage, native
output-buffer/PNG identity or atomic transaction/input generation is proved.
Popups/grabs, scaling/rotation, resize/migration, extra effects and saved-buffer
scenarios are NOT TESTED in this new campaign. Exhaustive semantic ABI, other
library pairs, notice/legal closure and named release/security roles remain open.
Final source inventory: 31 entries, SHA-256
`cb812ae3a27c9715efce2e1b2cfd36f315ea897d003c77944f688bc931861f1e`.
Final evidence inventory: 93 entries, SHA-256
`209196b2857381db7a856347dcce45cd3ee33c48c2db0c73777fc982d01ad568`.
Both sealed once and every entry verified. Final report/documentation/Git diff
receipts are outside those inventories to avoid circular digests. Repository
README result additions are later documentation, not the executed source copy.
Validation: AST, shell, Markdown links, historical manifests, IPC reference/
preflight replay, 39 negatives, ledger concurrency/exhaustion/corruption, C17
sources/builds, exact binary/loader/maps, actual outputs/camera/clip/pixels/buttons/
focus/splith, protected hashes and residual-process/socket checks PASS. Run
command FAIL at terminal exit IPC only; scientific gates PASS after independent
artifact revalidation. Meson compile/test, ordinary headless suite and pristine
run NOT RUN (outside this ticket and prohibited new builds/runs). Parent source,
contract and final-delta audit performed; no independent subagent review claimed.
### Architectural answers and next bounded gate
| Question | Evidence-backed answer |
| --- | --- |
| Q1 corrected none/width 2? | Yes, accepted in actual historical and live snapshots; mode/deco/content still checked. |
| Q2 other assertions replayed? | Yes for available IPC/scene/COMMIT; pixel selector uses historical PNG; buttons synthetic negatives; live-only states then actually measured. |
| Q3 two whole references identical? | Yes, 480,000 RGB pixels, zero differences and same PNG SHA. |
| Q4 H2 unchanged during Mosaic? | Yes, whole RGB exact. |
| Q5 permitted H1 pixels retained? | Yes, 182,214 actual target pixels. |
| Q6 forbidden H2 pixels absent? | Yes, zero target colors and full reference equality. |
| Q7 visible click correct? | Yes, actual target serials 35/36 only. |
| Q8 invisible portion excluded? | Yes, no new target/filler event on the H2 click. |
| Q9 H2 receives click? | Yes, reference serials 48/49. |
| Q10 focus graphical change? | No H2 change across both actual focus transitions; no claim about all compositor decorations. |
| Q11 splith removes clip? | Yes, actual H1 workspace splith and native H1/H2 clip_valid=0. |
| Q12 standard input? | Yes, recalculated target point, actual serials 67/68 only. |
| Q13 static retains shared-private behavior? | Tested tiled/render/input/standard continuity is supported; exhaustive static/shared equivalence and broader effects/popups are not proved. |
| Q14 algorithm change justified? | No reproduced clipping defect; both incidents belong to fixture checks/terminal handling. |
| Q15 remaining integration limits? | Static popup/grab/effects/buffer continuity, wider geometry/lifecycle, complete damage/generation, exhaustive ABI/maintenance/notices/release ownership. |
Smallest next justified experiment: a separately authorized same-static-binary
root/nested valid-grab popup RGB/input/standard-return continuity gate. Do not
extend this exhausted campaign or integrate clipping in production. STEP20C
remains NEXT and production multi-output NOT YET SUPPORTED. At the start of the evidence-freeze ticket, local main, Forgejo and GitHub
all pointed to fb0293cd855a2ec917c14df3ecf3504c064499fc. This report and the
source-only fixture are frozen for a single evidence-publication commit. The
separate new static nested-grab campaign may start only after that commit is
verified on both remotes with a clean worktree, and remains uncommitted and
unpublished. The historical runtime and three sealed inventories are immutable.

View file

@ -0,0 +1,95 @@
# Static RGB, input and standard-return retest fixture
EXPERIMENTAL / FROZEN EVIDENCE. No production integration.
Read the [protocol](../../../docs/design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md).
The historical failed campaign is frozen and must never be rerun.
`ipc_predicates.py` supplies pure validators shared by replay and the real runner.
`replay.py` uses actual sealed historical IPC/scene/client observations and
clearly labelled historical PNGs or synthetic input fixtures. Negative tests
must reject wrong identities/ownership/geometry/focus/clip and malformed input.
`pixels.py` compares every RGB pixel, preserves differences, measures actual
paint and selects points with a 20-pixel interior margin. `support.py` and
`test-budget.py` implement/test durable exclusive no-refund reservations.
Offline preparation, client-only C17 checks and gates:
```sh
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-rgb-input-retest/test-budget.py
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-rgb-input-retest/prepare.py
# LAB is the one newly printed path, registered in the exclusive owner file.
PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/replay.py" --lab "$LAB"
```
Preparation copies the exact qualified static binary, verifies the unchanged
historical manifests, builds only exact historical clients and freezes the
fixture inventory. No Mosaic or SceneFX build is allowed. `replay.py` verifies
source authority, syntax and ledger tests and records the two mandatory PASS
markers. `run.py` verifies the immutable inventory and all gates before its
first run reservation. Never replace the owner file, reset the ledger, modify
the executed fixture or create a second lab to retry.
Only after every gate passes, execute the single retained runner:
```sh
PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/run.py" "$LAB"
PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/collect.py" "$LAB"
PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/verify.py" "$LAB"
```
One static run, zero pristine runs/builds, one camera correction, three named
right clicks, eight movements including click positioning, twelve PNGs and
150 seconds. Reservations precede work and remain consumed on failure.
Headless/private mode-0700 runtime only, explicit swaymsg -s, owned process
groups, bounded logs and cleanup. No inherited daily IPC or library override.
The collector seals source/evidence once, verification never rewrites them.
Generated C protocols, clients/binaries/builds/logs/PNG/sockets stay outside Git.
Actual observations and final verdict are recorded below after collection.
## Post-run collection distinction
The exact executed fixture remains immutable in LAB/fixture. The measurements
completed, but its final `swaymsg exit` returned 1 after normal compositor exit.
Raw runtime.json and runner code/status are preserved. `validate_results.py`
independently rechecks actual PNG, IPC/scene, real recipient events, focus,
layout/clip and cleanup, and rejects changed RGB, invisible delivery, retained
clip or abnormal compositor exit. It accepts terminal transport only after all
scientific measurements, explicit exit, compositor code 0 and terminal EPIPE.
The post-run collector is retained separately in LAB/postrun-fixture, with its
own source inventory. Run that collector/validator for this completed campaign;
do not substitute it for the historical executed fixture or rerun the compositor.
## Measured result (one run, no retry)
LAB `/home/fy59/swayfx-mosaic-static-rgb-input-retest-hn67lnhq`.
Scientific FEASIBLE for the exact matrix, architecture
STATIC_MOSAIC_RGB_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX. The executed runner
exited 1 only on final exit IPC; compositor exited 0 after every measured gate.
Raw failure is retained, never rewritten as a successful runner execution.
271 damage traces are partial evidence, not every-frame qualification.
H2 six PNGs are whole-RGB identical; 182,214 target pixels on H1, zero H2.
Actual target visible serials 35/36, reference 48/49, standard target 67/68;
wrong clients have no button delivery. Real H1 splith/clip removal measured,
standard H1 paint 226,908 pixels. Budget run 1, camera 1, PNG 8, clicks 3,
movements 6 including click positioning, duration 9.142914 seconds. No second run.
Source manifest 31 entries SHA-256
`cb812ae3a27c9715efce2e1b2cfd36f315ea897d003c77944f688bc931861f1e`;
evidence 93 entries SHA-256
`209196b2857381db7a856347dcce45cd3ee33c48c2db0c73777fc982d01ad568`.
Every entry verifies; runner/pre-run inventory and post-run collector are
separate retained sources. Result additions here are later documentation.
Read-only final validation (no recording into sealed results):
```sh
PYTHONDONTWRITEBYTECODE=1 python "$LAB/postrun-fixture/validate_results.py" "$LAB"
PYTHONDONTWRITEBYTECODE=1 python "$LAB/postrun-fixture/verify.py" "$LAB"
```
No production readiness, broad static/shared equivalence, popup/effect/buffer
qualification, full damage/generation or exhaustive ABI approval. STEP20C NEXT,
production multi-output NOT YET SUPPORTED. The completed retest sources and report are preserved for publication.
Any later static nested-grab campaign is separate and remains local, uncommitted
and unpublished. No installation or system/session change.

View file

@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Collect actual one-run evidence and seal inventories once.
WHY: unexecuted cells remain NOT TESTED, never manufactured by collection.
CONTRACT: verdict uses real gate observations; a fixture failure is not a leak.
INVARIANT: source fixture and historical inventories remain untouched.
"""
import argparse
import json
from pathlib import Path
from support import save,sha
from verify import verify_inventory
from validate_results import check as check_actual_observations
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
assert (Path.home()/'swayfx-mosaic-static-rgb-input-retest-v1.owner').read_text().strip()==str(lab)
assert not (lab/'results/evidence-sha256.txt').exists(), 'already sealed'
verify_inventory(lab/'results/fixture-freeze-sha256.txt')
runtime=json.loads((lab/'results/runtime.json').read_text());budget=json.loads((lab/'results/budget.json').read_text())
for row in runtime['process_exit_codes']:assert not Path('/proc',str(row['pid'])).exists(), 'owned PID residual'
assert not Path(runtime['runtime_removed']).exists(), 'private runtime residual'
protected=json.loads((lab/'results/protected-before.json').read_text())
assert all(sha(p)==digest for p,digest in protected.items()), 'protected file changed'
save(lab,'protected-after',{'status':'PASS','hashes':protected})
required=['binary-identity','source-provenance','ipc-replay','preflight-replay','assertion-audit',
'output-geometry','focus-states','reference-state','reference-stability','camera-calibration',
'scene-clips','painted-geometry','pixel-analysis','visible-input','forbidden-input','reference-input',
'standard-return','damage-analysis']
for name in required:
if not (lab/'results'/(name+'.json')).exists():
save(lab,name,{'status':'NOT TESTED','reason':runtime['failure'],'stopping_stage':runtime['stopping_stage']})
def read(name):return json.loads((lab/'results'/(name+'.json')).read_text())
qualification=check_actual_observations(lab)
full=qualification['scientific_gates']=='ALL PASS'
save(lab,'scientific-qualification',qualification)
verdict='FEASIBLE' if full else 'INCONCLUSIVE'
# Classification does not turn a metadata/parser failure into a compositor defect.
for name in ['forbidden-pixels']:
if (lab/'results'/(name+'.json')).exists() and read(name).get('count',0)>0:verdict='BLOCKED'
if not full and verdict!='BLOCKED' and read('pixel-analysis').get('STATIC_MOSAIC_VISIBLE_PIXELS')=='PASS':
verdict='PARTIALLY_FEASIBLE'
architecture=('STATIC_MOSAIC_RGB_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX' if full else
'STATIC_MOSAIC_RGB_INPUT_STANDARD_REQUIRES_REDESIGN' if verdict=='BLOCKED' else 'UNDECIDED')
matrix={
'binary_identity':'PASS','source_provenance':'PASS exact historical C17 clients',
'IPC_replay':'PASS','preflight_replay':'PASS','initial_focus':'PASS target/workspace 1',
'effective_border':'PASS border none, stored width 2, empty deco and uniform full RGB',
'outputs':'PASS negative-origin scale-1 normal', 'camera':'PASS calculated once and applied',
'Mosaic_clip':'PASS applied local/global relationship plus RGB/input',
'H2_reference':'PASS two full RGB images','H1_pixels':'PASS measured target paint',
'H2_forbidden_pixels':0,'whole_H2_RGB':'PASS five complete comparisons',
'visible_click':'PASS target real pair only','invisible_target':'PASS no event',
'reference_click':'PASS reference real pair only','focus':'PASS H1 to H2 to H1',
'standard_return':'PASS splith, clip absent, rendered pixels, real target pair',
'damage':'PARTIAL client requests and output extents; intermediate frames NOT TESTED',
'runner_exit':'FAIL exit 1 on terminal exit IPC; raw status preserved',
'compositor_exit':'PASS code 0','terminal_transport':'observed EPIPE after completed measurements',
'cleanup':'PASS','native_PNG_buffer_identity':'NOT PROVEN','popups':'NOT TESTED'}
save(lab,'matrix',matrix)
save(lab,'summary',{'verdict':verdict,'architecture':architecture,'runtime':runtime,'budget':budget,
'scientific_qualification':qualification,'executed_runner_status_unchanged':True,
'new_Mosaic_builds':0,'new_SceneFX_builds':0,'STEP20C':'NEXT','production_multi_output':'NOT YET SUPPORTED',
'marker':'SWAYFX_MOSAIC_STATIC_RGB_INPUT_STANDARD_PROBE='+verdict})
source=sorted(p for root in ['fixture','postrun-fixture','client-source'] for p in (lab/root).rglob('*') if p.is_file())
source.append(lab/'protocol-before-run.md')
evidence=sorted(p for root in ['raw','run','results'] for p in (lab/root).rglob('*') if p.is_file())
evidence += [lab/n for n in ['swayfx-mosaic','saved-client','static-reference','pointer-daemon','campaign-owner']]
# Capture both lists before writing either inventory, avoiding circular digests.
for name,files in [('source',source),('evidence',evidence)]:
with (lab/'results'/(name+'-sha256.txt')).open('x') as out:
for path in files:out.write(f'{sha(path)} {path}\n')
print(name,verify_inventory(lab/'results'/(name+'-sha256.txt')))
print('SWAYFX_MOSAIC_STATIC_RGB_INPUT_STANDARD_PROBE='+verdict)

View file

@ -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'

View file

@ -0,0 +1,71 @@
"""Exact whole-image comparisons and measured interior click selection.
CONTRACT: no mask or tolerance may turn a whole-output difference into PASS.
INVARIANT: coordinates of every differing pixel are retained on failure.
"""
from PIL import Image, ImageChops
import re
COLORS = {(35,68,204),(32,208,160)}
def analyze(im):
mask=Image.new('L',im.size)
values=[255 if c in COLORS else 0 for c in im.get_flattened_data()]
mask.putdata(values)
return {'count':sum(v!=0 for v in values),'bbox':mask.getbbox()}
def compare(a,b):
assert a.size==b.size
points=[{'x':i%a.width,'y':i//a.width,'before':x,'after':y}
for i,(x,y) in enumerate(zip(a.get_flattened_data(),b.get_flattened_data())) if x!=y]
return {'equal':not points,'changed':len(points),'bbox':ImageChops.difference(a,b).getbbox(),'pixels':points}
def interior(im):
box=analyze(im)['bbox'];assert box
x1,y1,x2,y2=box;assert x2-x1>40 and y2-y1>40
x,y=(x1+x2)//2,(y1+y2)//2
assert all(im.getpixel((xx,yy)) in COLORS for xx in range(x-20,x+21) for yy in range(y-20,y+21))
return x,y
def button_pair(lines):
# CONTRACT: malformed or extra client button lines must never be filtered out.
events=[]
for line in lines:
m=re.fullmatch(r'BUTTON ns=(\d+) serial=(\d+) button=(\d+) state=(\d+)',line)
assert m, ('malformed button',line)
events.append(tuple(map(int,m.groups())))
assert len(events)==2 and [x[2:] for x in events]==[(273,1),(273,0)],events
assert all(x[0]>0 and 0<x[1]<=0xffffffff for x in events), 'timestamp/serial bounds'
assert events[0][0]<events[1][0] and events[0][1]!=events[1][1], 'press/release order'
return [{'ns':x[0],'serial':x[1],'button':x[2],'state':x[3]} for x in events]
def painted_geometry(im, measured, commit):
"""Paint supplies y; applied scene and actual COMMIT supply horizontal continuation.
WHY: window_rect.y deliberately excludes an enabled server titlebar.
CONTRACT: require solid target paint to the viewport boundary, no guessed y.
"""
value = analyze(im);box = value['bbox'];assert box, 'no permitted target pixels'
x1,y1,x2,y2 = box;x,w = measured['horizontal_content']
assert x1-800 == x and x2 == 800, 'paint/scene horizontal correlation'
assert value['count'] == (x2-x1)*(y2-y1), 'nonrectangular target paint'
assert y2-y1 == commit['height'] and commit['width'] == w, 'painted configured dimensions'
assert 20 <= y1+20 < y2-20 <= 600
return value
def click_points(left, right, measured):
"""Select from captured target pixels; H2 must be inside continuation/reference."""
px,py = interior(left);x,w = measured['horizontal_content']
hx = min(100,int(x+w)-30)
assert 20 <= hx < 780 and 20 <= py < 580, 'H2 point margin'
assert x+20 <= hx < x+w-20, 'inside continued content'
assert all(right.getpixel((xx,yy)) == (48,48,56)
for xx in range(hx-20,hx+21) for yy in range(py-20,py+21)), 'H2 accessible reference interior'
return {'permitted': (px-800,py), 'forbidden': (hx,py),
'paint_bbox': analyze(left)['bbox'], 'horizontal_content': [x,w]}
def delivery(events, recipient):
"""No fabricated recipient: only a real pair in its named client log counts."""
assert set(events) == {'target','reference','filler'}, 'client log set'
pair=button_pair(events[recipient])
assert all(not rows for name,rows in events.items() if name != recipient), 'wrong recipient delivery'
return pair

View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Copy exact candidate and compile verified historical C17 clients only.
CONTRACT: exclusive campaign registration prevents another LAB/reset/retry.
WHY: historical client executable hashes were not sealed; source provenance was.
"""
import json, os, shutil, stat, subprocess, tempfile, ast, argparse
from pathlib import Path
from support import sha, command, save
OLD=Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt')
OWNER=Path.home()/'swayfx-mosaic-static-rgb-input-retest-v1.owner'
p=argparse.ArgumentParser();p.add_argument('--resume-preparation',action='store_true');a=p.parse_args()
if a.resume_preparation:
# CONTRACT: only recover pre-run preparation in the same exclusive LAB.
# No ledger/reset, second LAB, completed preparation or executed run is allowed.
lab=Path(OWNER.read_text().strip()).resolve()
assert lab.is_dir() and (lab/'campaign-owner').read_text().strip()==str(OWNER)
assert not (lab/'run').exists() and not (lab/'results/budget.json').exists()
assert not (lab/'results/fixture-freeze-sha256.txt').exists()
else:
fd=os.open(OWNER,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
with os.fdopen(fd,'w') as f:
lab=Path(tempfile.mkdtemp(prefix='swayfx-mosaic-static-rgb-input-retest-',dir=Path.home()))
f.write(str(lab)+'\n');f.flush();os.fsync(f.fileno())
for name in ['results','raw']:(lab/name).mkdir()
(lab/'campaign-owner').write_text(str(OWNER)+'\n')
for name,digest in [('source','ed55bd418595ca977c022fa98917a899072cf3b6f0fb15457db823081046e797'),('evidence','e260ce5793ffc9a29ee4bfa2189663feeb6dc025bd46d3f5ddebccd225f3eef1')]:
manifest=OLD/'results'/(name+'-sha256.txt');assert sha(manifest)==digest
for line in manifest.read_text().splitlines():
h,p=line.split(' ',1);assert sha(p)==h,p
binary=OLD/'stage-static/usr/bin/swayfx-mosaic'
expected='f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b'
assert binary.is_file() and not binary.is_symlink() and os.access(binary,os.X_OK)
assert sha(binary)==expected and stat.S_IMODE(binary.stat().st_mode)==0o755
if not (lab/'swayfx-mosaic').exists():shutil.copy2(binary,lab/'swayfx-mosaic')
assert sha(lab/'swayfx-mosaic')==expected
elf=((lab/'raw/binary-elf.log').read_text() if a.resume_preparation else
command(lab,'binary-elf',['readelf','-hW','-nW','-dW',lab/'swayfx-mosaic']))
assert 'libscenefx' not in elf and '942db64269640778ee433e5970ba1100bc60f94c' in elf
assert 'ELF64' in elf and 'Advanced Micro Devices X86-64' in elf
# WHY: private static clip definitions are ordinary ELF symbols, not exports.
symbols=command(lab,'clip-symbols-static',['nm',lab/'swayfx-mosaic'])
assert all(' T '+s in symbols for s in ['wlr_scene_tree_set_clip','wlr_scene_tree_get_clip'])
assert '(RPATH)' not in elf and '(RUNPATH)' not in elf
loader=command(lab,'dependencies',['ldd','-r',lab/'swayfx-mosaic'])
assert 'not found' not in loader and 'undefined symbol' not in loader and 'libscenefx' not in loader
wl=Path('/usr/lib/libwlroots-0.20.so').resolve()
assert sha(wl)=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
assert str(wl) in loader
save(lab,'binary-identity',{'status':'PASS','source':str(binary),'copy':str(lab/'swayfx-mosaic'),'sha256':expected,'build_id':'942db64269640778ee433e5970ba1100bc60f94c','mode':'0755','wlroots':str(wl),'wlroots_sha256':sha(wl),'new_compositor_builds':0})
shutil.copytree(OLD/'client-source',lab/'client-source')
flags=subprocess.check_output(['pkg-config','--cflags','--libs','wayland-client'],text=True).split()
clients=[]
for name,proto in [('saved-client','xdg-shell'),('static-reference','xdg-shell'),('pointer-daemon','virtual-pointer')]:
src=lab/'client-source';argv=['cc','-std=c17','-Wall','-Wextra','-Werror','-Wno-unused-parameter','-I'+str(src),src/(name+'.c'),src/(proto+'-protocol.c'),*flags]
command(lab,name+'-syntax',[*argv,'-fsyntax-only'])
command(lab,name+'-compile',[*argv,'-o',lab/name])
clients.append({'name':name,'source_sha256':sha(src/(name+'.c')),'binary_sha256':sha(lab/name),'historical_binary_sha256_unsealed':sha(OLD/name)})
save(lab,'client-provenance',{'historical':json.loads((OLD/'results/client-provenance.json').read_text()),'source_manifest_verified':True,'clients':clients})
save(lab,'source-provenance',{'clients':clients,'historical_manifests_verified':True,
'source_manifest_sha256':sha(OLD/'results/source-sha256.txt'),
'evidence_manifest_sha256':sha(OLD/'results/evidence-sha256.txt'),
'new_Mosaic_builds':0,'new_SceneFX_builds':0})
repo=Path(__file__).resolve().parents[3]
shutil.copytree(Path(__file__).parent,lab/'fixture')
(lab/'protocol-before-run.md').write_bytes((repo/'docs/design/STEP20C_STATIC_RGB_INPUT_STANDARD_RETEST.md').read_bytes())
for p in (lab/'fixture').glob('*.py'):ast.parse(p.read_text(),filename=str(p))
with (lab/'results/fixture-freeze-sha256.txt').open('x') as f:
for p in sorted((lab/'fixture').rglob('*')):
if p.is_file():f.write(f'{sha(p)} {p}\n')
f.write(f'{sha(lab/"protocol-before-run.md")} {lab/"protocol-before-run.md"}\n')
save(lab,'protected-before', {str(p):sha(p) for p in [Path('/usr/bin/sway'),
Path('/usr/lib/libwlroots-0.20.so').resolve(),Path('/usr/lib/libscenefx-0.5.so').resolve(),
repo/'PKGBUILD',repo/'patches/0001-mosaic-layout-viewport.patch',repo/'AGENTS.md',repo/'docs/UX.md']})
print('LAB='+str(lab))

View file

@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""Read-only historical replay and explicitly labelled synthetic negative tests.
CONTRACT: exactly the live pure functions are exercised, without compositor launch.
INVARIANT: old manifests/snapshots are never written; historical IDs stay here.
"""
import argparse
import ast
import copy
import json
from pathlib import Path
import subprocess
from PIL import Image
from support import save, sha
from ipc_predicates import (nodes, validate_reference, validate_outputs, workspace,
measure_owner, scene_from_log, client_commit, validate_preflight, validate_camera)
from pixels import analyze, compare, interior, painted_geometry, click_points, delivery, button_pair
HIST = Path('/home/fy59/swayfx-mosaic-static-followup-nhwejz6u')
OLD = Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt')
def check():
from verify import verify_inventory
for base,pairs in [(HIST,[('source','5f9ab2b6d1ca23ef7743623d1e544237c8b70b62fe6de8164a46f1342e5a8ee2'),('evidence','41cc5e3d305d89e83314723e67d7116c36d1503dd35647894bc898c49343e1a5')]),
(OLD,[('source','ed55bd418595ca977c022fa98917a899072cf3b6f0fb15457db823081046e797'),('evidence','e260ce5793ffc9a29ee4bfa2189663feeb6dc025bd46d3f5ddebccd225f3eef1')])]:
for name,digest in pairs:
manifest=base/'results'/(name+'-sha256.txt');assert sha(manifest)==digest
verify_inventory(manifest)
snap=json.loads((HIST/'results/preflight-snapshot.json').read_text())
text=(HIST/'run/compositor.log').read_text()
ref=next(n for n in nodes(snap['tree']) if n.get('id')==7)
target=next(n for n in nodes(snap['tree']) if n.get('id')==9)
results=[]
def rejected(name,fun):
try:fun()
except (AssertionError,KeyError,StopIteration,TypeError):results.append({'negative':name,'status':'REJECTED'})
else:raise AssertionError('invalid fixture accepted: '+name)
validate_reference(snap,7,ref['pid'])
assert ref['border']=='none' and ref['current_border_width']==2
for field,value in [('app_id','wrong'),('pid',1),('visible',False),('shell',None),
('border','normal'),('rect',dict(x=1,y=0,width=800,height=600)),
('window_rect',dict(x=0,y=0,width=799,height=600)),
('deco_rect',dict(x=0,y=0,width=800,height=25))]:
s=copy.deepcopy(snap);r=next(n for n in nodes(s['tree']) if n.get('id')==7);r[field]=value
rejected(field,lambda s=s:validate_reference(s,7,ref['pid']))
rejected('wrong con_id',lambda:validate_reference(snap,99,ref['pid']))
rejected('wrong workspace',lambda:validate_reference(snap,7,ref['pid'],'1'))
rejected('wrong output',lambda:validate_reference(snap,7,ref['pid'],'2','HEADLESS-1'))
rejected('actual H1 filler',lambda:validate_reference(snap,8,next(n for n in nodes(snap['tree']) if n.get('id')==8)['pid']))
s=copy.deepcopy(snap);workspace(s['tree'],'2')['nodes']=[]
rejected('absent surface',lambda:validate_reference(s,7,ref['pid']))
for field,value in [('rect',dict(x=0,y=0,width=801,height=600)),('scale',1.25),('transform','90')]:
s=copy.deepcopy(snap);next(o for o in s['outputs'] if o['name']=='HEADLESS-2')[field]=value
rejected('output '+field,lambda s=s:validate_reference(s,7,ref['pid']))
# Earlier border-normal state is an actual IPC reply, not a manufactured state.
early=None
for line in (HIST/'run/events.jsonl').read_text().splitlines():
e=json.loads(line)
if e.get('kind')=='ipc' and list(e.get('args',[]))==['-t','get_tree']:
r=next((n for n in nodes(e['result']) if n.get('id')==7),None)
if r and r.get('border')=='normal':early=e['result'];break
assert early is not None, 'real earlier border-normal observation missing'
rejected('actual earlier normal reference',lambda:validate_reference({**snap,'tree':early},7,ref['pid']))
scene=scene_from_log(text,'HEADLESS-1');h2=scene_from_log(text,'HEADLESS-2')
measured=measure_owner(text,target);commit=client_commit((HIST/'run/target.log').read_text())
good=validate_preflight(snap,7,ref['pid'],9,target['pid'],scene,h2,measured,commit)
calibration=json.loads((HIST/'results/camera-calibration.json').read_text())
validate_camera(calibration['before'],calibration['after'],calibration['delta'],scene)
args=[snap,7,ref['pid'],9,target['pid'],scene,h2,measured,commit]
for name,index,field,value in [('clip global',5,'global_clip',[0,0,800,600]),
('clip absent',5,'clip_valid',False),('camera',5,'scroll',0),
('scene owner',5,'workspace',999),('H2 clip',6,'clip_valid',True),
('content width',8,'width',635)]:
bad=copy.deepcopy(args);bad[index][field]=value
rejected(name,lambda bad=bad:validate_preflight(*bad))
for name,mutate in [('focus',lambda s:next(n for n in nodes(s['tree']) if n.get('id')==9).update(focused=False)),
('layout',lambda s:workspace(s['tree'],'1').update(layout='splith'))]:
bad=copy.deepcopy(args);mutate(bad[0]);rejected(name,lambda bad=bad:validate_preflight(*bad))
rejected('camera correction',lambda:validate_camera(0,160,159,scene))
historical=Image.open(OLD/'run/h1-mosaic.png').convert('RGB')
# The older static campaign used exactly this measured content continuation.
paint=painted_geometry(historical,measured,commit)
fixed=Image.new('RGB',(800,600),(48,48,56))
points=click_points(historical,fixed,measured)
assert points['permitted']==(-159,311) and points['forbidden']==(100,311)
assert analyze(fixed)['count']==0 and compare(fixed,fixed.copy())['equal']
edge=fixed.copy();edge.putpixel((799,599),(48,48,57))
diff=compare(fixed,edge);assert not diff['equal'] and diff['changed']==1
rejected('no target paint',lambda:interior(fixed))
sparse=historical.copy();sparse.putpixel((640,311),(0,0,0))
rejected('sparse paint',lambda:painted_geometry(sparse,measured,commit))
blocked=fixed.copy();blocked.putpixel((100,311),(0,0,0))
rejected('H2 point inaccessible',lambda:click_points(historical,blocked,measured))
# Positive and negatives below are SYNTHETIC, not new or historical delivery.
pair=['BUTTON ns=1 serial=3 button=273 state=1','BUTTON ns=2 serial=4 button=273 state=0']
assert delivery(dict(target=pair,reference=[],filler=[]),'target')
for name,lines in [('none',[]),('missing release',pair[:1]),('extra',pair+pair[:1]),
('malformed',pair+['BUTTON garbage']),('wrong code',[s.replace('273','272') for s in pair]),
('wrong order',pair[::-1]),('zero serial',[pair[0].replace('serial=3','serial=0'),pair[1]]),
('same serial',[pair[0],pair[1].replace('serial=4','serial=3')]),
('backwards time',[pair[0].replace('ns=1','ns=3'),pair[1]])]:
rejected('SYNTHETIC button '+name,lambda lines=lines:button_pair(lines))
rejected('SYNTHETIC wrong recipient',lambda:delivery(dict(target=pair,reference=pair,filler=[]),'target'))
src=OLD/'mosaic-source'
# Source tokens are supporting semantic audit, not behavioral tests.
checks=[('sway/commands/border.c',['set_border(container, B_NONE)','if (argc == 2)']),
('sway/ipc-json.c',['json_object_new_int(c->current.border_thickness)','c->pending.content_width','view_is_visible']),
('sway/commands/layout.c',['workspace && workspace->layout == L_MOSAIC','container = NULL;','workspace->layout = new_layout']),
('sway/desktop/transaction.c',['border_width = 0;','wlr_scene_tree_set_clip(child->layers.tiling, NULL)']),
('sway/desktop/output.c',['wlr_scene_node_coords','PI_SCENE output=','PI_OWNER output='])]
sources=[]
for name,tokens in checks:
p=src/name;contents=p.read_text();assert all(t in contents for t in tokens),(p,tokens)
sources.append({'file':str(p),'sha256':sha(p),'semantics_checked':tokens})
fields=[('outputs rect/scale/transform','sway/ipc-json.c','actual IPC','PASS','live reread'),
('view App ID/PID/con_id/ancestors/visible','sway/ipc-json.c','actual IPC','PASS','visibility is not pixels'),
('workspace/focus/layout','sway/ipc-json.c; sway/commands/layout.c','actual IPC','PASS','later focus live'),
('border/stored width/deco/window_rect','border.c; ipc-json.c; transaction.c','actual IPC plus earlier normal','PASS','effective RGB live'),
('camera scroll_x/scene tree/clip local/global','transaction.c; output.c; ipc-json.c','actual traces + calibration','PASS','no atomic generation asserted'),
('content dimensions/SHM colors','client-source/saved-client.c','actual COMMIT + historical PNG','PASS','new pixels live'),
('capture coordinates/painted click selection','pixels.py; virtual pointer source','HISTORICAL H1 + SYNTHETIC H2','PASS','new accessible surface verified live'),
('buttons/recipient/count/order/serial','saved-client.c:pointer_button; pointer-daemon.c','SYNTHETIC labelled cases','PASS','new actual client events required'),
('targeted splith return','sway/commands/layout.c','exact candidate source','PASS source audit','layout/clip/render/input live; NOT TESTED offline')]
audit=[dict(field=f,source=s,historical_data=h,replay=r,remaining_uncertainty=u,
invariant=f+' remains required live') for f,s,h,r,u in fields]
runtime_assertions=[]
runner=Path(__file__).with_name('run.py').read_text()
for node in ast.walk(ast.parse(runner)):
if isinstance(node,ast.Assert):
runtime_assertions.append({'line':node.lineno,'expression':ast.get_source_segment(runner,node.test),
'authority':'see corresponding field/source table; operational assertions use owned files/processes',
'offline':'shared predicates and historical/synthetic tests above; environment identity checked by prepare',
'uncertainty':'live-only states require new observations; no retrospective PASS'})
audit={'fields':audit,'runner_assertions':sorted(runtime_assertions,key=lambda x:x['line'])}
return {'reference':{'marker':'STATIC_REFERENCE_IPC_REPLAY=PASS','snapshot_sha256':sha(HIST/'results/preflight-snapshot.json'),
'accepted_width':2,'reference':good['reference'],'negatives':results},
'preflight':{'marker':'STATIC_FIXTURE_PREFLIGHT_REPLAY=PASS','actual':good,'paint_HISTORICAL':paint,
'points_HISTORICAL_SYNTHETIC':points,'synthetic_input_tests':'PASS'},
'audit':audit,'source_audit':sources}
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--lab',type=Path);a=p.parse_args()
result=check()
for path in Path(__file__).parent.glob('*.py'):ast.parse(path.read_text(),filename=str(path))
subprocess.run(['python',str(Path(__file__).with_name('test-budget.py'))],check=True)
if a.lab:
from verify import verify_inventory
verify_inventory(a.lab/'results/fixture-freeze-sha256.txt')
save(a.lab,'ipc-replay',result['reference']);save(a.lab,'preflight-replay',result['preflight'])
save(a.lab,'assertion-audit',result['audit']);save(a.lab,'source-audit',result['source_audit'])
save(a.lab,'offline-gate',{'reference':'PASS','preflight':'PASS','AST':'PASS','ledger':'PASS','source_audit':'PASS'})
print(result['reference']['marker']);print(result['preflight']['marker'])
print('PASS',len(result['reference']['negatives']),'negative cases; historical/synthetic evidence labelled')

View file

@ -0,0 +1,258 @@
#!/usr/bin/env python3
"""One independent budgeted RGB/input/standard retest of the unchanged verified static compositor.
WHY: resolved relocations do not demonstrate real compositor execution.
CONTRACT: verify /proc executable/library identity before mapping any client.
INVARIANT: owned sockets/groups only, one camera correction, durable captures.
"""
import argparse
import json
import os
from pathlib import Path
import re
import shutil
import signal
import subprocess
import tempfile
import threading
import time
from PIL import Image,ImageChops
from support import environment,reserve,save,sha
from pixels import analyze, compare, interior, painted_geometry, click_points, delivery
from ipc_predicates import (nodes, workspace, validate_outputs, validate_reference,
validate_preflight, measure_owner, scene_from_log, client_commit, validate_camera)
from verify import verify_inventory
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
assert not __import__('sys').flags.optimize, 'Python assertions must be enabled'
assert json.loads((lab/'results/binary-identity.json').read_text())['status']=='PASS'
assert sha(lab/'swayfx-mosaic')=='f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b'
assert (Path.home()/'swayfx-mosaic-static-rgb-input-retest-v1.owner').read_text().strip()==str(lab)
# CONTRACT: run reservation is impossible until the exact frozen fixture passes.
assert not __import__('sys').flags.optimize, 'Python assertions must be enabled'
verify_inventory(lab/'results/fixture-freeze-sha256.txt')
gate=json.loads((lab/'results/offline-gate.json').read_text())
assert gate=={'reference':'PASS','preflight':'PASS','AST':'PASS','ledger':'PASS','source_audit':'PASS'}
assert json.loads((lab/'results/ipc-replay.json').read_text())['marker']=='STATIC_REFERENCE_IPC_REPLAY=PASS'
assert json.loads((lab/'results/preflight-replay.json').read_text())['marker']=='STATIC_FIXTURE_PREFLIGHT_REPLAY=PASS'
reserve(lab,'run');run=lab/'run';run.mkdir();runtime=Path(tempfile.mkdtemp(prefix='msr-',dir='/tmp'))
runtime.chmod(0o700);(run/'runtime-path').write_text(str(runtime)+'\n')
(run/'config').write_text('output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0\noutput HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0\nworkspace 1 output HEADLESS-1\nworkspace 2 output HEADLESS-2\nfocus_follows_mouse no\nseat seat0 hide_cursor 100\n')
env=environment();env.update(XDG_RUNTIME_DIR=str(runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2',WLR_RENDERER='pixman')
binary=lab/'swayfx-mosaic';processes=[];files=[];failure=None;compositor=None;events=[];stage='startup'
started=time.monotonic();finished=threading.Event();expired=threading.Event()
def event(kind,**data):
row={'ns':time.monotonic_ns(),'kind':kind,**data}
path=run/'events.jsonl'
assert not path.exists() or path.stat().st_size<64*1024*1024, 'event output bound'
with path.open('a') as out:out.write(json.dumps(row)+'\n')
def stop_owned():
for proc in list(processes):
try:os.killpg(proc.pid,signal.SIGTERM)
except ProcessLookupError:pass
def watchdog():
while not finished.wait(.1):
if time.monotonic()-started>150 or any(x.stat().st_size>64*1024*1024 for x in run.glob('*.log')):
expired.set();stop_owned();return
thread=threading.Thread(target=watchdog,daemon=True);thread.start()
def spawn(name,argv,stdin=False,extra=None):
assert not expired.is_set()
out=(run/(name+'.log')).open('w');files.append(out)
proc=subprocess.Popen(list(map(str,argv)),env={**env,**(extra or {})},stdin=subprocess.PIPE if stdin else subprocess.DEVNULL,stdout=out,stderr=subprocess.STDOUT,text=True,start_new_session=True)
processes.append(proc);event('spawn',name=name,pid=proc.pid,argv=list(map(str,argv)));return proc
def wait(predicate,seconds=8):
end=time.monotonic()+seconds
while time.monotonic()<end:
if expired.is_set():raise RuntimeError('deadline/log bound exceeded')
if compositor and compositor.poll() is not None:raise RuntimeError('compositor exited '+str(compositor.returncode))
value=predicate()
if value:return value
time.sleep(.025)
raise RuntimeError('fixture wait timed out')
def log(name):return (run/(name+'.log')).read_text(errors='replace')
def ipc(*args):
assert sock.is_relative_to(runtime) and sock.is_socket()
result=subprocess.run(['swaymsg','-s',str(sock),*args],env=env,capture_output=True,text=True,timeout=5,check=True)
data=json.loads(result.stdout);event('ipc',args=args,result=data);return data
def cmd(value):
data=ipc(value);assert all(x.get('success') for x in data),(value,data)
def find(app):return next((n for n in nodes(ipc('-t','get_tree')) if n.get('app_id')==app),None)
def inject(action,x,y,kind='move'):
reserve(lab,kind)
if kind!='move':reserve(lab,'move') # Clicks also contain one actual pointer motion.
assert -800<=x<800 and 0<=y<600
marker=f'INJECT_DONE {action} {x+800},{y}';before=log('pointer').count(marker)
pointer.stdin.write(f'{action} {x+800} {y}\n');pointer.stdin.flush()
wait(lambda:log('pointer').count(marker)>before)
time.sleep(.15);event('pointer',action=action,global_point=[x,y],target_tail=log('target').splitlines()[-6:] if (run/'target.log').exists() else [])
def capture(tag,output):
reserve(lab,'capture');path=run/(tag+'.png')
subprocess.run(['grim','-s','1','-o',output,str(path)],env=env,check=True,timeout=5,stdout=subprocess.DEVNULL)
im=Image.open(path).convert('RGB');assert im.size==(800,600)
event('capture',file=path.name,output=output,sha256=sha(path));return im
def measure(owner):return measure_owner(log('compositor'),owner)
# WHY: global focus becomes H2 after the forbidden-region click. Workspace and
# owner IDs, rather than global command context, bind the standard-return test.
focus_states=[]
def snapshot(tag):
tree=ipc('-t','get_tree');outputs=ipc('-t','get_outputs');workspaces=ipc('-t','get_workspaces')
row={'tag':tag,'ns':time.monotonic_ns(),'tree':tree,'outputs':outputs,'workspaces':workspaces,
'focused':[n['id'] for n in nodes(tree) if n.get('focused')]}
focus_states.append(row);save(lab,'focus-states',focus_states)
save(lab,tag+'-snapshot',row)
return tree
def scene(output):return scene_from_log(log('compositor'),output)
def camera(tree):return workspace(tree,'1')['mosaic']['rows'][0]['scroll_x']
def buttons(name):return [x for x in log(name).splitlines() if x.startswith('BUTTON ')]
def measured_click(kind,point):
before={n:len(buttons(n)) for n in ['target','reference','filler']}
inject('right',*point,kind=kind)
row={'global_point':point,'events':{n:buttons(n)[before[n]:] for n in before},
'tails':{n:log(n).splitlines()[-15:] for n in before},'ns':time.monotonic_ns()}
save(lab,kind,row);return row
def whole(tag,baseline,im):
value=compare(baseline,im);save(lab,tag,value);assert value['equal'],'whole H2 differs: '+tag
return value
try:
compositor=spawn('compositor',[binary,'-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(SWAYSOCK=str(sock),WAYLAND_DISPLAY=display.name)
maps=Path(f'/proc/{compositor.pid}/maps').read_text();(run/'proc-maps.txt').write_text(maps)
exe=Path(f'/proc/{compositor.pid}/exe').resolve();assert exe==binary and sha(exe)==sha(binary)
paths=sorted({line.split()[-1] for line in maps.splitlines() if len(line.split())>=6 and line.split()[-1].startswith('/')})
wl=Path('/usr/lib/libwlroots-0.20.so').resolve()
assert not any('libscenefx' in x for x in paths)
assert [Path(x).resolve() for x in paths if 'libwlroots' in x]==[wl]
assert sha(wl)=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
assert not any('/swayfx-mosaic-' in x and Path(x)!=binary for x in paths)
save(lab,'dependency-mappings',{'status':'PASS','exe':str(exe),'paths':paths,'wlroots_sha256':sha(wl),'SceneFX_shared':False})
outputs=ipc('-t','get_outputs');validate_outputs(outputs);actual={o['name']:(o['rect']['x'],o['rect']['y'],o['rect']['width'],o['rect']['height'],o['scale'],o['transform']) for o in outputs if o['active']}
assert actual=={'HEADLESS-1':(-800,0,800,600,1.0,'normal'),'HEADLESS-2':(0,0,800,600,1.0,'normal')},actual
save(lab,'output-geometry',outputs)
pointer=spawn('pointer',[lab/'pointer-daemon','1600','600'],stdin=True);wait(lambda:'POINTER_READY' in log('pointer'))
stage='clients'
cmd('workspace 2');ref=spawn('reference',[lab/'static-reference'],stdin=True,extra={'WAYLAND_DEBUG':'1'})
reference=wait(lambda:find('org.mosaic.StaticReference'));refid=reference['id']
cmd(f'[con_id={refid}] border none')
cmd('workspace 1');filler=spawn('filler',[lab/'static-reference'],stdin=True)
wait(lambda:len([n for n in nodes(ipc('-t','get_tree')) if n.get('app_id')=='org.mosaic.StaticReference'])==2)
target=spawn('target',[lab/'saved-client'],stdin=True,extra={'WAYLAND_DEBUG':'1'})
wait(lambda:find('org.mosaic.SavedBufferProbe'))
stage='preflight'
cmd('layout mosaic');time.sleep(.6)
tree=snapshot('before-camera');owner=find('org.mosaic.SavedBufferProbe');targetid=owner['id'];wsid=workspace(tree,'1')['id']
before=measure(owner);sc_before=scene('HEADLESS-1');scroll_before=camera(tree)
assert sc_before['scroll']==scroll_before
x,w=before['horizontal_content'];desired=-w//2;delta=x-desired
reserve(lab,'camera');cmd(f'layout mosaic scroll {delta}')
time.sleep(.6);owner=find('org.mosaic.SavedBufferProbe');after=measure(owner)
tree=snapshot('preflight');scroll_after=camera(tree);sc_after=scene('HEADLESS-1')
save(lab,'camera-calibration',{'before':scroll_before,'delta':delta,'desired_left':desired,'after':scroll_after,'before_scene':sc_before,'after_scene':sc_after})
validate_camera(scroll_before,scroll_after,delta,sc_after)
x,w=after['horizontal_content'];assert x<0<x+w and -800<x
assert sc_after['clip_valid'] and sc_after['global_clip']==[-800,0,800,600]
assert sc_after['workspace']==wsid
reference=next(n for n in nodes(tree) if n['id']==refid)
commit=client_commit(log('target'))
preflight=validate_preflight(focus_states[-1],refid,ref.pid,targetid,target.pid,
sc_after,scene('HEADLESS-2'),after,commit)
save(lab,'reference-state',preflight['reference'])
save(lab,'live-preflight',preflight)
save(lab,'owner-geometry',{'before':before,'after':after,'reference':reference,'last_client_commit':[s for s in log('target').splitlines() if s.startswith('COMMIT ')][-1]})
save(lab,'scene-clips',{'mosaic':sc_after,'H2':scene('HEADLESS-2')})
stage='reference'
inject('move',-790,590);time.sleep(.6)
baseline=capture('h2-reference-1','HEADLESS-2');time.sleep(.6);second=capture('h2-reference-2','HEADLESS-2')
comparison=compare(baseline,second)
save(lab,'reference-stability',{'H2_REFERENCE_STABLE':comparison['equal'],'H2_WHOLE_RGB_EQUAL':comparison['equal'],'reference':reference,'focus':focus_states[-1]['focused'],'hashes':{n:sha(run/n) for n in ['h2-reference-1.png','h2-reference-2.png']}})
save(lab,'reference-pixel-analysis',comparison)
assert comparison['equal'] and all(c==(48,48,56) for c in baseline.get_flattened_data()),'reference preflight failed'
stage='mosaic'
left=capture('h1-mosaic','HEADLESS-1');right=capture('h2-mosaic','HEADLESS-2')
visible=analyze(left);forbidden=analyze(right)
save(lab,'permitted-pixels',visible);save(lab,'forbidden-pixels',forbidden)
assert visible['count']>0 and forbidden['count']==0
whole('v1-H2-comparison',baseline,right)
paint=painted_geometry(left,after,commit)
points=click_points(left,right,after)
permitted=points['permitted'];forbidden_point=points['forbidden']
save(lab,'painted-geometry',{'paint':paint,'measured':after,'commit':commit,'points':points})
save(lab,'pixel-analysis',{'STATIC_MOSAIC_VISIBLE_PIXELS':'PASS',
'STATIC_MOSAIC_FORBIDDEN_PIXELS':forbidden['count'],'STATIC_MOSAIC_H2_WHOLE_RGB':'PASS',
'H1':visible,'H2':forbidden})
save(lab,'click-points',{'permitted':permitted,'forbidden':forbidden_point,'paint_bbox':visible['bbox'],'horizontal_content':[x,w]})
stage='visible-input'
click=measured_click('visible_click',permitted);events_visible=delivery(click['events'],'target')
assert not click['events']['reference'] and not click['events']['filler']
save(lab,'visible-input',{'status':'PASS','point':permitted,'events':events_visible})
tree=snapshot('after-visible');assert camera(tree)==scroll_after
assert measure(find('org.mosaic.SavedBufferProbe'))['horizontal_content']==[x,w]
stage='forbidden-input'
click=measured_click('forbidden_click',forbidden_point);events_reference=delivery(click['events'],'reference')
assert not click['events']['target'] and not click['events']['filler'],'invisible target or wrong client input'
save(lab,'forbidden-input',{'status':'PASS','target_received':False})
save(lab,'reference-input',{'status':'PASS','point':forbidden_point,'events':events_reference,'invisible_target_events':click['events']['target']})
tree=snapshot('after-reference');assert camera(tree)==scroll_after
assert next(n for n in nodes(tree) if n['id']==refid)['focused']
save(lab,'reference-after-focus',validate_reference(focus_states[-1],refid,ref.pid))
inject('move',-790,590);time.sleep(.6)
whole('post-click-H2-comparison',baseline,capture('h2-after-clicks','HEADLESS-2'))
stage='standard-return'
cmd(f'[con_id={targetid}] layout splith');time.sleep(.6)
tree=snapshot('standard');standard_ws=workspace(tree,'1');assert standard_ws['id']==wsid and standard_ws['layout']=='splith'
standard_owner=next(n for n in nodes(standard_ws) if n['id']==targetid);assert standard_owner['visible']
standard_left=capture('h1-standard','HEADLESS-1');standard_right=capture('h2-standard','HEADLESS-2')
save(lab,'standard-pixels',{'H1':analyze(standard_left),'H2':analyze(standard_right)})
assert analyze(standard_right)['count']==0, 'standard target leaked to H2'
whole('standard-H2-comparison',baseline,standard_right)
standard_scene=scene('HEADLESS-1');standard_h2=scene('HEADLESS-2')
assert standard_scene['workspace']==wsid and not standard_scene['clip_valid'] and not standard_h2['clip_valid']
sx,sy=interior(standard_left);point=(sx-800,sy)
click=measured_click('standard_click',point);events_standard=delivery(click['events'],'target')
assert not click['events']['reference'] and not click['events']['filler']
final_tree=snapshot('after-standard-click')
assert next(n for n in nodes(final_tree) if n['id']==targetid)['focused']
assert [z['name'] for z in focus_states[-1]['workspaces'] if z['focused']]==['1']
save(lab,'reference-after-standard',validate_reference(focus_states[-1],refid,ref.pid))
inject('move',-790,590);time.sleep(.6)
whole('final-H2-comparison',baseline,capture('h2-standard-after-click','HEADLESS-2'))
save(lab,'standard-return',{'status':'STATIC_STANDARD_RETURN=PASS','workspace':standard_ws,'owner':measure(standard_owner),'scene':standard_scene,'H2_scene':standard_h2,'point':point,'events':events_standard,'pixels':analyze(standard_left),'H2_equal':True})
assert all(p.poll() is None for p in [target,ref,filler,pointer])
stage='completed'
cmd('exit');compositor.wait(timeout=5);assert compositor.returncode==0
except Exception as exc:
failure=f'{type(exc).__name__}: {exc}';event('failure',message=failure)
finally:
finished.set();thread.join(timeout=1);stop_owned()
for proc in processes:
try:proc.wait(timeout=3)
except subprocess.TimeoutExpired:
os.killpg(proc.pid,signal.SIGKILL);proc.wait(timeout=3)
try:os.killpg(proc.pid,signal.SIGKILL)
except ProcessLookupError:pass
for out in files:out.close()
shutil.rmtree(runtime)
save(lab,'runtime',{'status':'PASS' if failure is None else 'INCONCLUSIVE','failure':failure,'stopping_stage':stage,'duration_seconds':time.monotonic()-started,'process_exit_codes':[{'pid':x.pid,'argv':x.args,'exit':x.returncode} for x in processes],'runtime_removed':str(runtime),'cleanup':'owned groups reaped, runtime removed'})
damage=[s for s in log('compositor').splitlines() if 'TEMPORAL_DAMAGE ' in s] if (run/'compositor.log').exists() else []
save(lab,'damage-analysis',{'extents':damage,'all_frames':'NOT TESTED','PNG_native_buffer_identity':'NOT PROVEN','client_damage':'wl_surface_damage_buffer full SHM at actual commits; see client source/protocol logs'})
print('STATIC_RGB_INPUT_STANDARD',failure or 'PASS')
if failure:raise SystemExit(1)

View file

@ -0,0 +1,90 @@
"""Private probe support; no desktop environment or global process ownership.
WHY: failures must consume the same persistent experiment budget as successes.
CONTRACT: a stable flock file serializes durable atomic ledger updates.
INVARIANT: no reset/refund; only argv execution and owned process groups.
"""
import fcntl
import hashlib
import json
import os
from pathlib import Path
import signal
import subprocess
import tempfile
import time
REPO = Path(__file__).resolve().parents[3]
LIMITS = {'run': 1, 'capture': 12, 'camera': 1, 'visible_click': 1,
'forbidden_click': 1, 'standard_click': 1, 'move': 8}
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def save(lab, name, value):
(Path(lab)/'results'/(name+'.json')).write_text(json.dumps(value, indent=2)+'\n')
def reserve(lab, kind):
lab = Path(lab)
if kind not in LIMITS:
raise ValueError(kind)
with (lab/'budget.lock').open('a+') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
path = lab/'results/budget.json'
state = json.loads(path.read_text()) if path.exists() else {
'limits': LIMITS, 'used': dict.fromkeys(LIMITS, 0), 'events': []}
# CONTRACT: corruption cannot create negative/extra capacity or erase history.
valid = (state.get('limits') == LIMITS and set(state.get('used', {})) == set(LIMITS)
and all(type(state['used'][k]) is int and 0 <= state['used'][k] <= LIMITS[k] for k in LIMITS)
and len(state.get('events', [])) == sum(state['used'].values())
and all(e.get('sequence') == i and e.get('kind') in LIMITS for i,e in enumerate(state.get('events', [])))
and all(sum(e['kind'] == k for e in state['events']) == state['used'][k] for k in LIMITS))
if not valid or state['used'][kind] >= LIMITS[kind]:
raise RuntimeError('budget exhausted or changed: '+kind)
state['used'][kind] += 1
state['events'].append({'sequence': len(state['events']), 'kind': kind})
fd, temporary = tempfile.mkstemp(prefix='.budget-', dir=path.parent)
with os.fdopen(fd, 'w') as out:
json.dump(state, out, indent=2); out.write('\n'); out.flush(); os.fsync(out.fileno())
os.replace(temporary, path)
fd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(fd)
finally:
os.close(fd)
return state
def environment():
env = {k:v for k,v in os.environ.items() if k not in (
'SWAYSOCK','I3SOCK','WAYLAND_DISPLAY','DISPLAY','XDG_RUNTIME_DIR',
'LD_LIBRARY_PATH','LD_PRELOAD','PKG_CONFIG_PATH')}
env.update(LC_ALL='C', PYTHONDONTWRITEBYTECODE='1', MESON_ROOT_CMD='/bin/false')
return env
def command(lab, label, argv, cwd=None, extra=None, timeout=600, allow_failure=False):
lab=Path(lab); path=lab/'raw'/(label+'.log')
assert not path.exists(), 'diagnostic already recorded: '+label
with path.open('wb') as out:
proc=subprocess.Popen(list(map(str,argv)),cwd=cwd,env={**environment(),**(extra or {})},
stdout=out,stderr=subprocess.STDOUT,start_new_session=True)
try:
deadline=time.monotonic()+timeout
while proc.poll() is None:
if time.monotonic()>=deadline or path.stat().st_size>64*1024*1024:
raise subprocess.TimeoutExpired(argv,timeout)
time.sleep(.1)
except subprocess.TimeoutExpired:
os.killpg(proc.pid,signal.SIGTERM)
try: proc.wait(timeout=3)
except subprocess.TimeoutExpired:
os.killpg(proc.pid,signal.SIGKILL);proc.wait()
try: os.killpg(proc.pid,signal.SIGKILL)
except ProcessLookupError: pass
raise
with (lab/'raw/commands.jsonl').open('a') as out:
out.write(json.dumps({'label':label,'argv':list(map(str,argv)), 'cwd':str(cwd),
'env':extra or {},'exit':proc.returncode})+'\n')
assert path.stat().st_size < 64*1024*1024, 'output bound exceeded'
if not allow_failure and proc.returncode:
raise RuntimeError(f'{label} exit={proc.returncode}; inspect {path}')
return path.read_text(errors='replace')

View file

@ -0,0 +1,31 @@
"""Offline exhaustion and cross-process reservation tests, outside campaign LAB."""
import concurrent.futures
import json
from pathlib import Path
import tempfile
from support import LIMITS, reserve
def attempt(args):
lab, kind = args
try: reserve(lab,kind); return True
except RuntimeError: return False
if __name__ == '__main__':
with tempfile.TemporaryDirectory(prefix='static-budget-test-') as tmp:
lab=Path(tmp);(lab/'results').mkdir()
with concurrent.futures.ProcessPoolExecutor(max_workers=4) as pool:
assert sum(pool.map(attempt,[(lab,'capture')]*20))==12
for kind,limit in LIMITS.items():
if kind=='capture':continue
for _ in range(limit):assert attempt((lab,kind))
assert not attempt((lab,kind))
state=json.loads((lab/'results/budget.json').read_text())
assert state['used']==LIMITS
assert not attempt((lab,'run'))
before=(lab/'results/budget.json').read_bytes()
assert not attempt((lab,'capture'))
assert (lab/'results/budget.json').read_bytes()==before, 'failed exhaustion rewrote budget'
corrupted=json.loads(before);corrupted['used']['run']=-1
(lab/'results/budget.json').write_text(json.dumps(corrupted))
assert not attempt((lab,'run')), 'negative capacity accepted'
print('PASS offline ledger: concurrent reservations, exhaustion, durable failures, no refund')

View file

@ -0,0 +1,129 @@
#!/usr/bin/env python3
"""Offline requalification of actual RGB/input/return artifacts after the sole run.
WHY: shutdown IPC may lose its reply after cmd_exit terminates the compositor.
CONTRACT: never rewrite runtime.json or claim the executed runner exited zero.
INVARIANT: all scientific gates must independently pass; terminal transport is
accepted only with completed measurements, explicit exit and compositor code 0.
"""
import argparse
import copy
import json
from pathlib import Path
import re
from PIL import Image
from support import sha,save
from ipc_predicates import (nodes,workspace,validate_preflight,measure_owner,
scene_from_log,client_commit,validate_reference,validate_outputs,validate_camera)
from pixels import compare,analyze,painted_geometry,click_points,delivery,button_pair,interior
def check(lab,overrides=None):
lab=Path(lab);overrides=overrides or {}
def read(name):return overrides.get(name,json.loads((lab/'results'/(name+'.json')).read_text()))
def image(name):return overrides.get(name,Image.open(lab/'run'/name).convert('RGB'))
text=(lab/'run/compositor.log').read_text()
snap=read('preflight-snapshot');cal=read('camera-calibration');measured=read('owner-geometry')['after']
ref=read('reference-state');tid=measured['id'];rid=ref['con_id']
owner=next(n for n in nodes(snap['tree']) if n['id']==tid)
assert measured['raw'] in text and cal['after_scene']['raw'] in text
measured=measure_owner(measured['raw'],owner)
scene=scene_from_log(cal['after_scene']['raw'],'HEADLESS-1')
clips=read('scene-clips');assert clips['H2']['raw'] in text
h2=scene_from_log(clips['H2']['raw'],'HEADLESS-2')
commit=read('live-preflight')['commit'];assert commit['raw'] in (lab/'run/target.log').read_text()
commit=client_commit(commit['raw'])
validate_preflight(snap,rid,ref['pid'],tid,owner['pid'],scene,h2,measured,commit)
validate_camera(cal['before'],cal['after'],cal['delta'],scene)
baseline=image('h2-reference-1.png');assert baseline.size==(800,600)
assert all(p==(48,48,56) for p in baseline.get_flattened_data())
rgb={}
for name in ['h2-reference-2.png','h2-mosaic.png','h2-after-clicks.png','h2-standard.png','h2-standard-after-click.png']:
im=image(name);assert im.size==(800,600)
value=compare(baseline,im);assert value['equal'], ('whole H2 RGB',name)
assert analyze(im)['count']==0
rgb[name]={'changed':0,'PNG_sha256':sha(lab/'run'/name)}
paint=painted_geometry(image('h1-mosaic.png'),measured,commit)
points=click_points(image('h1-mosaic.png'),image('h2-mosaic.png'),measured)
logs={n:(lab/'run'/(n+'.log')).read_text() for n in ['target','reference','filler']}
button_lines={n:[s for s in t.splitlines() if s.startswith('BUTTON ')] for n,t in logs.items()}
pairs={}
for name,recipient,point in [('visible_click','target',points['permitted']),
('forbidden_click','reference',points['forbidden']),
('standard_click','target',None)]:
click=read(name);pairs[name]=delivery(click['events'],recipient)
assert all(row in button_lines[recipient] for row in click['events'][recipient]), 'event not in actual log'
if point:assert tuple(click['global_point'])==point
assert button_lines['target']==read('visible_click')['events']['target']+read('standard_click')['events']['target']
assert button_lines['reference']==read('forbidden_click')['events']['reference'] and not button_lines['filler']
focus={}
for name,expected in [('after-visible',tid),('after-reference',rid),('standard',rid),('after-standard-click',tid)]:
s=read(name+'-snapshot');assert s['focused']==[expected]
validate_reference(s,rid,ref['pid']);validate_outputs(s['outputs'])
if name in ['after-visible','after-reference']:
assert workspace(s['tree'],'1')['mosaic']['rows'][0]['scroll_x']==cal['after']
focus[name]=expected
standard=read('standard-return');assert standard['status']=='STATIC_STANDARD_RETURN=PASS'
stdsnap=read('standard-snapshot');ws=workspace(stdsnap['tree'],'1')
assert ws['id']==workspace(snap['tree'],'1')['id'] and ws['layout']=='splith'
stdowner=next(n for n in nodes(ws) if n['id']==tid)
assert stdowner['visible'] and stdowner['pid']==owner['pid']
for output,key in [('HEADLESS-1','scene'),('HEADLESS-2','H2_scene')]:
sc=standard[key];assert sc['raw'] in text
parsed=scene_from_log(sc['raw'],output);assert not parsed['clip_valid'] and parsed['layout_enum']==1
std=image('h1-standard.png');paint_std=analyze(std);assert paint_std['count']>0
x1,y1,x2,y2=paint_std['bbox'];assert paint_std['count']==(x2-x1)*(y2-y1)
stdpoint=interior(std);assert read('standard_click')['global_point']==[stdpoint[0]-800,stdpoint[1]]
assert analyze(image('h2-standard.png'))['count']==0
runtime=read('runtime');assert runtime['duration_seconds']<150
assert [r['exit'] for r in runtime['process_exit_codes']]==[0,-15,0,0,0], 'unexpected owned process exit'
assert not Path(runtime['runtime_removed']).exists()
for row in runtime['process_exit_codes']:
assert not Path('/proc',str(row['pid'])).exists(), 'owned process remains'
budget=read('budget');assert budget['used']==dict(run=1,capture=8,camera=1,visible_click=1,forbidden_click=1,standard_click=1,move=6)
assert len(list((lab/'run').glob('*.png')))==8
injections=[s for s in (lab/'run/pointer.log').read_text().splitlines() if s.startswith('INJECT ')]
assert len(injections)==6 and sum('INJECT right ' in s for s in injections)==3
events=[json.loads(s) for s in (lab/'run/events.jsonl').read_text().splitlines()]
adjustments=[e for e in events if e['kind']=='ipc' and e['args']==['layout mosaic scroll '+str(cal['delta'])]]
assert len(adjustments)==1
assert 'wl_display' not in '\n'.join(s for t in logs.values() for s in t.splitlines() if '.error(' in s)
assert not re.search(r'protocol error|INVALID_|MISSING_GLOBAL', '\n'.join(logs.values()), re.I)
shutdown_ns=min(int(re.search(r'EXIT ns=(\d+)',t)[1]) for t in logs.values())
last_capture=max(e['ns'] for e in events if e['kind']=='capture')
assert shutdown_ns>last_capture, 'client transport failure before final measurements'
terminal=False
if runtime['status']!='PASS':
assert runtime['stopping_stage']=='completed'
assert runtime['failure'].startswith('CalledProcessError: Command') and "'exit']' returned non-zero exit status 1." in runtime['failure']
assert "Handling command 'exit'" in text and 'Shutting down sway' in text
assert all('error=32' in t.splitlines()[-1] for t in logs.values()), 'different terminal transport error'
terminal=True
return {'status':'PASS','scientific_gates':'ALL PASS','verdict':'FEASIBLE',
'architecture':'STATIC_MOSAIC_RGB_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX',
'RGB':rgb,'H1':paint,'standard_H1':paint_std,'pairs':pairs,'focus':focus,
'terminal_transport_incident':terminal,'runner_exit':1 if terminal else 0,
'original_runtime_status_preserved':runtime['status'],
'terminal_diagnosis':'completed measurements precede explicit compositor exit 0; client errno 32 is terminal EPIPE, not a wl_display protocol error'}
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('--record',action='store_true');a=p.parse_args()
result=check(a.lab)
# Negative post-run classification tests do not write any original observation.
negatives=[]
for name in ['runtime','standard-return','forbidden_click']:
value=json.loads((a.lab/'results'/(name+'.json')).read_text());bad=copy.deepcopy(value)
if name=='runtime':bad['process_exit_codes'][0]['exit']=1
elif name=='standard-return':bad['scene']['clip_valid']=True;bad['scene']['raw']=bad['scene']['raw'].replace('clip_valid=0','clip_valid=1')
else:bad['events']['target']=['BUTTON ns=1 serial=3 button=273 state=1']
try:check(a.lab,{name:bad})
except AssertionError:negatives.append(name+' REJECTED')
else:raise AssertionError('postrun bad state accepted: '+name)
changed=Image.open(a.lab/'run/h2-mosaic.png').convert('RGB');changed.putpixel((799,599),(48,48,57))
try:check(a.lab,{'h2-mosaic.png':changed})
except AssertionError:negatives.append('whole H2 RGB REJECTED')
else:raise AssertionError('postrun changed H2 accepted')
result['negative_tests']=negatives
if a.record:save(a.lab,'postrun-validation',result)
print('PASS actual RGB, scene, all real buttons/focus, standard return, budget and terminal diagnosis')
print('PASS negative postrun classification',negatives)

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python3
"""Read-only integrity checks; no remapping, rewriting, or budget refund."""
import argparse
from pathlib import Path
from support import sha
def verify_inventory(manifest):
rows=Path(manifest).read_text().splitlines();assert rows
seen=set()
for row in rows:
digest,p=row.split(' ',1);assert p not in seen;seen.add(p)
assert sha(p)==digest,('manifest mismatch',p)
return {'entries':len(rows),'sha256':sha(manifest)}
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args()
for name in ['fixture-freeze','source','evidence']:
print(name,verify_inventory(a.lab/'results'/(name+'-sha256.txt')),'PASS')