docs(mosaic): preserve optimized blur edge causality evidence

This commit is contained in:
fy59 2026-10-13 05:54:00 +02:00
parent e749653ccd
commit 7c22b4020e
18 changed files with 1867 additions and 0 deletions

View file

@ -4,6 +4,8 @@
### STEP20C experimental evidence
- Preserved optimized-edge gate: four completed controls reproduce the same boundary differences in pristine SceneFX, private splith and Mosaic without OPT traces. Same-binary Mosaic trace-off/on and pristine/private standard match full RGB at six states; central freshness, H2 stability and sampled clipping hold. PARTIALLY_FEASIBLE, architecture UNDECIDED: reduced-domain FBO history has source/model support but no native causal observation. Separate ABI/package audit justified; optional bounded GPU study remains unexecuted. No production change; historical graphical campaign closed.
- Preserved optimized blur cache probe: private splith/Mosaic used a real background source and output-scoped GPU cache, naturally reconstructed A/B/A and restored exact central filtered pixels with stable whole H2 RGB. Boundary equivalence still differs by up to six RGB levels in both private modes. Pristine C0 failed before captures; its duplicate initial image was charged after observation, with no retry or numerical budget overrun. PARTIALLY_FEASIBLE, architecture UNDECIDED; neutrality and whole-cache correctness unqualified. No production change; the historical campaign is closed.
- Preserved saved-target natural timeout gate: one Mosaic run used a static SHM H2 reference with four identical whole-RGB captures. The measured target saved node crossed x=0, rendered 182,214 retained-color pixels on H1, contributed no H2 pixels and was removed after a real 2500 ms timeout while the client held serial 50 alive. Two sampled pointer points found the H2 reference and visible H1 saved node. PARTIALLY_FEASIBLE because complete damage, broader input and GPU/texture lifetime remain unqualified; no production change.

View file

@ -329,3 +329,16 @@ whole-cache equivalence and intermediate frames remain unqualified. A
separate pristine/untraced boundary-sampling gate should precede ABI/package
qualification. This evidence freeze changes no production or UX contract.
STEP20C stays **NEXT**, multi-output **NOT YET SUPPORTED**.
The preserved [optimized edge causality gate](design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md)
is **PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Four completed fixed
negative-origin runs qualify pristine/private splith and same-binary Mosaic
OPT-off/on controls. Pristine/private full images and Mosaic trace-off/on
full images are exact at six homologous states; common glass across layouts
is also exact. Historical right/bottom differences persist without new traces,
with max RGB delta 6, fresh central pixels and stable whole H2. Native FBO
history causality remains unproved despite source/model support. R3 damage
extents cover stabilized differences only. No clipping regression or required
invariant violation is established. A separate SceneFX ABI/package audit is
now justified, retaining this shared rendering limitation; optional selective
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.

View file

@ -270,3 +270,16 @@ whole-cache equivalence and intermediate frames remain unqualified. A
separate pristine/untraced boundary-sampling gate should precede ABI/package
qualification. This evidence freeze changes no production or UX contract.
STEP20C stays **NEXT**, multi-output **NOT YET SUPPORTED**.
The preserved [optimized edge causality gate](design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md)
is **PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Four completed fixed
negative-origin runs qualify pristine/private splith and same-binary Mosaic
OPT-off/on controls. Pristine/private full images and Mosaic trace-off/on
full images are exact at six homologous states; common glass across layouts
is also exact. Historical right/bottom differences persist without new traces,
with max RGB delta 6, fresh central pixels and stable whole H2. Native FBO
history causality remains unproved despite source/model support. R3 damage
extents cover stabilized differences only. No clipping regression or required
invariant violation is established. A separate SceneFX ABI/package audit is
now justified, retaining this shared rendering limitation; optional selective
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.

View file

@ -179,3 +179,19 @@ whole-cache equivalence and intermediate frames remain unqualified. A
separate pristine/untraced boundary-sampling gate should precede ABI/package
qualification. This evidence freeze changes no production or UX contract.
STEP20C stays **NEXT**, multi-output **NOT YET SUPPORTED**.
The preserved [optimized edge causality gate](design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md)
is **PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Four completed fixed
negative-origin runs qualify pristine/private splith and same-binary Mosaic
OPT-off/on controls. Pristine/private full images and Mosaic trace-off/on
full images are exact at six homologous states; common glass across layouts
is also exact. Historical right/bottom differences persist without new traces,
with max RGB delta 6, fresh central pixels and stable whole H2. Native FBO
history causality remains unproved despite source/model support. R3 damage
extents cover stabilized differences only. No clipping regression or required
invariant violation is established. A separate SceneFX ABI/package audit is
now justified, retaining this shared rendering limitation; optional selective
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.
The [preserved edge source fixture](../tooling/probes/step20c-optimized-blur-edge/README.md)
records pinned reconstruction, offline checks and the exhausted four-slot ledger.

View file

@ -274,3 +274,16 @@ whole-cache equivalence and intermediate frames remain unqualified. A
separate pristine/untraced boundary-sampling gate should precede ABI/package
qualification. This evidence freeze changes no production or UX contract.
STEP20C stays **NEXT**, multi-output **NOT YET SUPPORTED**.
The preserved [optimized edge causality gate](design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md)
is **PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Four completed fixed
negative-origin runs qualify pristine/private splith and same-binary Mosaic
OPT-off/on controls. Pristine/private full images and Mosaic trace-off/on
full images are exact at six homologous states; common glass across layouts
is also exact. Historical right/bottom differences persist without new traces,
with max RGB delta 6, fresh central pixels and stable whole H2. Native FBO
history causality remains unproved despite source/model support. R3 damage
extents cover stabilized differences only. No clipping regression or required
invariant violation is established. A separate SceneFX ABI/package audit is
now justified, retaining this shared rendering limitation; optional selective
GPU observation requires a new scoped ticket. This evidence is preserved without a production change. STEP20C stays NEXT, production multi-output NOT YET SUPPORTED.

View file

@ -0,0 +1,500 @@
# STEP20C optimized blur edge history and instrumentation gate
**EXPERIMENTAL / protocol recorded before new fixture development.**
The separate cache evidence freeze passed at
`e749653ccda42abad9a66a11009be6043b5b6f62`, directly verified on Forgejo
and GitHub with a clean main. The completed experimental evidence is preserved for publication. STEP20A DONE, STEP20B EXPERIMENTAL, STEP20C NEXT, STEP20D–G
PLANNED. Production multi-output Mosaic is NOT YET SUPPORTED.
No UX, production source, cumulative patch, PKGBUILD, system dependency,
shader, rendering algorithm or ABI change is authorized.
## Evidence and competing hypotheses
The [V1 cache probe](STEP20C_OPTIMIZED_BLUR_CACHE_PROBE.md) already established
actual FX/GLES 3.2, BACKGROUND cache source, optimized selection, natural
A/B/A dirty/rebuild/copy, fresh central 80,000 pixels, stable whole H2,
sampled clipping/damage/teardown. Its whole-H1 B-natural/B-explicit differences
were 36,776 private splith and 33,915 Mosaic; A-initial/A-return differences
38,308 and 35,340, maximum RGB delta 6. Common boundary ROI counts were
19,007 and 20,486. These are not excluded or dismissed as rounding.
C0 failed before captures through duplicate configure-driven initial images;
its second image was charged after observation, a reservation deviation.
The corrected client ACKs configures without drawing. Historical sources,
PNGs, logs, manifests and ledger stay intact; no V1 run is repeated.
Instrumentation neutrality and pristine graphical behavior were NOT TESTED.
| Hypothesis | Discriminating evidence | Limit |
| --- | --- | --- |
| H1 reduced-domain FBO history | Source-domain audit, spatial distributions, offline pinned-kernel predictions, identical-source repeat | Native tail content is not read; correlation cannot establish GPU causality |
| H2 new cache traces | R2/R3 same source/library/binary/client/config, OPT_TRACE alone varies | Independent timing; equality is sampled neutrality, not universal proof |
| H3 shared SceneFX property | Completed R0 pristine versus R1 private splith and R2/R3 | Private clipping and older passive traces differ from pristine |
| H4 blur command cycle state | B unchanged during one disable/enable, client events, configuration, native R3 dirty/rebuild | Cycle rearranges blur and dirties all outputs; not identical to source-only commit |
| H5 another observed rendering factor | Geometry, alpha, source colors, renderer, timing, filtering, presentation evidence | Do not invent an unobserved explanation |
## Fixed comparative matrix and binary differences
| Run | Compositor / layout | New cache traces |
| --- | --- | --- |
| R0 | pristine SwayFX 0.6 / splith + pristine SceneFX 0.5 | absent, no private patches |
| R1 | private historical compositor / splith | OPT_TRACE absent |
| R2 | same private compositor / mosaic | OPT_TRACE absent |
| R3 | identical private compositor / mosaic | OPT_TRACE=1 |
Pinned pristine SwayFX `fd71a6bdc061bd633b488ae7b83e8a6981d22f86`, Mosaic
`cd550382b311efdbfe3ed892f4e1d281d852e754`, SceneFX
`37ccd723bef49e6891156ffafce8f549f01446cc`, compatible installed wlroots 0.20.
Private builds apply unchanged inherited clip/popup/guard/timing/presentation
patches and the exact V1 cache/source trace patches. R1/R2/R3 load the same
library and binary. Their old SPIKE/TEMPORAL/PI traces are always active;
"trace-off" means the new OPT cache/source logs are disabled, not all logging.
OPT_LOG tests getenv presence, so OPT_TRACE=0 is not trace-off. Only presence
changes for R2/R3. Borrowed trace-only reads outside OPT_LOG remain in both.
No neutral instrumentation claim precedes the graphics. B does not modify
those patches or add renderer instrumentation.
## Geometry, graphical inputs and parameters
Two headless outputs only: H1 HEADLESS-1 at (-800,0), H2 HEADLESS-2 at (0,0),
both 800×600 physical/logical, scale=1, transform=normal. Record actual IPC
rect/mode/scale/transform and PNG dimensions. No rotation, scaling, hotplug,
resize, migration, popup, saved-buffer episode, animation or stress test.
At most one initial Mosaic camera correction calculated from actual applied
camera and glass geometry; no hide/return pair or P1–P3 camera change.
Reuse V1 deterministic SHM patterns exactly: BACKGROUND layer on H1, opaque
8-pixel stripes A=(245,245,245)/(15,25,45), B=(32,192,224)/(176,32,64).
Same 800×600 XRGB image for each reserved commit. Glass premultiplied ARGB
0x88442010, transparent ARGB filler, static XDG SHM reference H2. No terminal
cursor or animation. XDG configure-driven size submissions are logged separately
from the three explicitly reserved background images. No background configure
may submit pixels. Require two independent whole-H2 captures equal before FX2.
Fixed blur radius=5, passes=3, noise=0, brightness=contrast=saturation=1,
xray=off, shadows=off, corners=0, compositor opacity=1. Audit pinned command
syntax/source; save applied container/tree and startup config. Acceptance is
not effect evidence: exposed stripe colors calibrate the source; FX0/FX2
central contrast reduction demonstrates actual filtering. R3 must additionally
show FX renderer, optimized node under blur_tree, BACKGROUND path, optimized=1,
selected FBO, successful rebuild and cache copies. Other runs provide config,
source and pixel evidence without fabricated absent OPT traces.
## Pre-registered regions and comparisons
All coordinates below are output-local half-open physical rectangles:
| Region | Rectangle |
| --- | --- |
| glass center | (520,100,720,500), 80,000 pixels |
| exposed source | (20,100,160,500) |
| historical right boundary | (720,50,800,550) |
| physical right diagnostic | (750,0,800,600) |
| physical bottom diagnostic | (0,550,800,600) |
| whole H1 / whole H2 | (0,0,800,600) |
No diagnostic region is an exclusion. Every full-equality comparison uses all
800×600 RGB pixels. Intra-run full images always remain reported. Inter-layout
comparisons use only actually homologous source/common glass rectangles and
explicitly retain distinct glass extents (historically splith x=400, Mosaic
x=480); R0/R1 and R2/R3 full-image equality requires matching applied geometry.
Measure changed pixels, max channel delta, mean absolute channel delta,
per-channel amplitude histogram, bounding box, changed-pixel count per column
and per row, every ROI, H2 full differences, capture hashes and timestamps.
## One deterministic sequence per reserved run
P0: connect and map background with initial bufferless commit; wait/ACK all
layer configures, assert no image submitted, reserve then command A once.
Map filler/glass/static H2, settle actual geometry/focus and initial camera.
Capture FX0 H1/H2, later independent H2. Assert source colors and stable H2.
Enable scoped glass blur, settle, capture FX2 H1/H2; verify filtering.
P1: reserve and command B exactly once, keep blur/geometry unchanged, settle,
capture H1/H2. P2: no client image; reserve one explicit cycle, scoped blur
disable then enable with ordinary commands, settle and capture H1/H2.
P3: reserve and command A once, keep all else fixed, settle, capture H1/H2;
one later H1 confirmation must match whole RGB. Total 12 PNG per run.
Settling uses client command receipt, unchanged applied IPC geometry, at least
350 ms without intentional mutation before each measured capture; last H1
has an independent 350 ms confirmation. Native R3 logs are checked for source
commit/rebuild before capture and competing events in capture windows. Earlier
states lack duplicate H1 PNGs under the finite budget; that limits claims of
stability, especially if event/capture windows disagree. No screenshot gains
a native output-buffer identity from proximity to a commit or present.
Comparisons: FX0 A/FX2 A, P0 FX2/P3 A, P1 B/P2 B, R0/R1 homologous states,
R2/R3 homologous states, and common edge regions across layouts. Inspect
source A/B/A submission order, sizes, events and frame timing before attribution.
No deliberate focus, resize, camera, alpha, radius, passes or source mutation
outside this sequence. If a second unreserved A submission appears: abort,
retain evidence, consume reservation, no rerun, dependent results INCONCLUSIVE.
## Sampling and command audit before graphics
Inspect pinned render_blur_segments/get_main_buffer_blur, blur1.frag/blur2.frag,
texture filter/wrap, FBO allocation and written/sampled regions. For each of
six passes document full framebuffer size, freshly written domain, mapping,
radius/taps, bilinear texels beyond the valid domain, possible historical value
and expected final influence. Domain sizes for 800×600 are down 400×300,
200×150,100×75 then up 200×150,400×300,800×600, inside full-size FBOs.
Full-texture GL_CLAMP_TO_EDGE is distinct from reduced valid-domain clamp.
The offline model uses ideal bilinear sampling and 8-bit rounding, not native
mediump GLES precision, and must be reported as hypothesis only.
Audit cmd_blur: scoped container blur flag and arrange_container, then dirty
all output nodes. Natural BACKGROUND commit dirties its own output. Therefore
P1 and P2 need not traverse identical scene/rebuild sequences. Preserve native
R3 evidence of H1/H2 counts and scene/damage. No shader modification, forced
FBO clearing or GPU readback is authorized. If needed, propose only a later
bounded selective-readback protocol after results; do not execute it here.
## Budgets, isolation and preflight gate
One new LAB, one persistent campaign ledger in the home directory, exclusive
flock, atomic durable replacement, exact schema validation. Four run slots
R0–R3, each once; at most 3 background images, 1 blur cycle, 12 PNG, 150 s.
Global max 12 images,4 cycles,48 PNG; one initial calibration per Mosaic run,
zero camera pairs. Reserve before every launch/action; failed reservations
remain consumed, no refund/reset/new-LAB bypass/fifth run.
Before any compositor launch: Python AST, shell syntax if applicable, offline
ledger exhaustion/corruption/concurrency/cross-LAB tests, actual layer callback
ACK/no-draw harness, explicit-command audit, SHM widths/formats/bounds/lifetimes,
Wayland globals/listeners/output naming/protocol hashes, C17 -Werror, both
SceneFX and compositor builds, Meson mosaic-model, all pinned patch checks,
private linker identities, cleanup/timeouts. Fixtures are standalone and
corrected; C0 originals and historical LAB remain untouched.
Each run has private mode-0700 XDG_RUNTIME_DIR, private WAYLAND_DISPLAY,
explicit SWAYSOCK and swaymsg -s, WLR_BACKENDS=headless. Strip inherited daily
socket/display/library/trace variables; use private SceneFX LD_LIBRARY_PATH.
Record binary/library hashes, /proc/PID/maps, renderer logs, IPC, configs,
client sources, runner exact source, PNGs, capture windows, logs and exits.
Terminate only owned process groups; controlled IPC exit first, preserve
return codes, reap clients, remove only owned runtime/socket directory.
25 MB per raw log and watchdog bound resource use. No live desktop IPC,
killall/pkill, install, sudo, dotfiles, QuickShell or Codex changes.
Stop consumed failing controls; continue other predefined independent slots
only if safe/meaningful, never relaunch to get a favorable result. Critical
fixture/budget/isolation violation stops affected dependent execution. Compiler
or offline preflight issues may be corrected before the first reserved run.
## Result evidence and classification
Keep build/renderer-identities, private-trace-comparison, pristine-control,
source-commits, graphic-states, output-geometries, blur-settings, cache-use,
dirty-events, regeneration-events, edge-pixel-analysis, column/row-distribution,
sampling-domain-audit, edge-model-comparison, damage-analysis, capture-windows,
campaign-budget, matrix, summary and source/evidence SHA-256 manifests in
LAB/results. Generated files/builds/logs/PNG/socket/bytecode never enter Git.
The source-only fixture must rebuild from the pinned versions.
FEASIBLE requires complete comparable pristine and trace controls, coherent
cache contract, absent or natively explained compatible edges, correct allowed
Mosaic pixels and no forbidden contribution/regression. PARTIALLY_FEASIBLE
means valid majority of controls but native cause or edge property remains
open. INCONCLUSIVE means controls/fixture cannot distinguish hypotheses.
BLOCKED needs a demonstrated required invariant violation, reproducible crash,
forbidden contribution or blocking standard regression; low RGB amplitude
alone is insufficient. Architecture: OPTIMIZED_EDGE_CACHE_FEASIBLE_FOR_TESTED_MATRIX,
OPTIMIZED_EDGE_CACHE_REQUIRES_REDESIGN or UNDECIDED. Distinguish a shared SceneFX
issue from Mosaic clipping. Answer Q1–Q17 from the retained evidence after
execution; recommend minimal GPU study or ABI/package gate explicitly.
## Retained execution and measured result (2026-10-13)
`SWAYFX_MOSAIC_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE=PARTIALLY_FEASIBLE`.
Architecture: **UNDECIDED**. LAB:
`/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n/`.
All four controls completed and were comparable in their defined domains.
The old pristine failure did not recur: duplicate layer configures were ACKed
without another image. No run failed, was repeated or exceeded a budget.
The original pre-result protocol is retained as LAB/protocol-before-results.md.
| Run | Duration | A/B images | PNG | Blur cycles | Initial camera corrections | Compositor exit |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| R0 pristine splith | 4.995 s | 3 | 12 | 1 | 0 | 0 |
| R1 private splith, OPT absent | 4.293 s | 3 | 12 | 1 | 0 | 0 |
| R2 private Mosaic, OPT absent | 4.638 s | 3 | 12 | 1 | 1 | 0 |
| R3 same private Mosaic, OPT=1 | 4.652 s | 3 | 12 | 1 | 1 | 0 |
Total 4/4 runs,12/12 images,48/48 PNG,4/4 cycles,2/2 initial corrections,
zero camera pairs. The home-directory ledger is permanently exhausted for
run slots. Every mutation/capture/cycle/calibration reservation preceded its
operation; failed-reservation tests used independent offline temporary ledgers.
Clients report Wayland EPIPE=32/code 1 after controlled compositor exit; these
teardown statuses are retained, not compositor crashes. Owned children were
reaped and all four runtime/socket directories removed.
### Actual sources, geometry and rendering
Every run uses the real FX renderer, OpenGL ES 3.2, Mesa 26.2.4-arch1.1,
AMD Radeon 780M. /proc/PID/maps establishes the private pristine SceneFX in R0
and the same private patched SceneFX in R1/R2/R3, together with installed
wlroots 0.20.2. Installed package/library series are SceneFX 0.5.0, Wayland
client 1.26.0 and wayland-protocols 1.49. The build-identity JSON records
all archive revisions, exact patch hashes, library and executable digests.
Private compositor SHA-256:
`0c87ece70aa012207de951e9d558c81c5c4b3ea78548b943477bf12543264042`;
private SceneFX:
`78daf506d9aacdd0b1a05605f73b4103ee48cd0b5900a3574671b5ec11f07fe7`.
Pristine compositor:
`8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494`;
pristine SceneFX:
`7a07c1f239043f7910039670006a2f6e9bb15608bf2a840576476c5f7b1378b3`.
IPC/output records and all PNGs confirm fixed 800×600 scale-1 normal outputs
at (-800,0)/(0,0). Standard glass is 400×600, global x=-400; Mosaic source
scene glass is 640×600 at global x=-320, crossing x=0 by 320. Mosaic's initial
camera 480 receives the measured delta -320 and becomes 160. Tiled scene
translation/clip and regular blur final draw yield output-local
[480,800)×[0,600); no target regular blur draw is seen on H2 in R3. The
private scene-state traces in R2 agree. Neither P1–P3 focus, camera, geometry,
alpha nor blur parameters are deliberately changed. Stored IPC trees confirm
stable applied geometry/focus/camera during measured states.
Exposed source ROI matches every stripe pixel exactly for A/B/B/A; glass
ARGB, source image dimensions and SHM lifetime remain fixed. Container opacity
1 is source-defined (container_create alpha=1), with no opacity mutation;
ordinary IPC here does not expose an opacity/blur-parameter getter. R3 actual
regular-blur strength=1, radius=5, passes=3 and optimized=1/selected FBO=11
corroborate source/config/pixel evidence. No getter observation is fabricated.
FX0/FX2 central luminance standard deviation falls 51.165618→0.132211 and
adjacent luminance contrast 12.351341→0.015453 in every run. Actual filtering
is visible beyond alpha compositing. Source mutation changes the central
80,000 pixels and whole 480,000 H1 pixels; B's center equals explicit B exactly,
and returned A's center equals initial A exactly. Six whole-H2 captures in
each run, including two independent preflight images, are pixel-identical.
All 24 H2 PNG hashes equal the historical static-reference hash
`4471e93ccb6cf3ff4970f61fce405431a228af598d2f1f030a3b7586962ecd7a`.
### Full-image and pre-registered edge comparisons
| Comparison | R0 pristine | R1 private splith | R2 Mosaic OPT absent | R3 Mosaic OPT=1 |
| --- | ---: | ---: | ---: | ---: |
| B natural → B explicit, whole H1 | 36,776 | 36,776 | 33,915 | 33,915 |
| A initial → A returned, whole H1 | 38,308 | 38,308 | 35,340 | 35,340 |
| Maximum channel delta, both comparisons | 6 | 6 | 6 | 6 |
| Center changed pixels, both comparisons | 0 | 0 | 0 | 0 |
| H2 changed pixels, every comparison | 0 | 0 | 0 | 0 |
| Last A → independent H1 confirmation | 0 | 0 | 0 | 0 |
R0/R1 have zero whole-H1 RGB differences at all six homologous capture states.
R2/R3 likewise have zero at all six states; H2 also matches across runs.
Runner bytes, config, compiled client, private binary and loaded private library
are identical in R2/R3. New OPT logs are absent in R0/R1/R2 and present in R3;
old private logs remain active in R1/R2/R3. Thus no measured graphical effect
of activating new traces exists in this matrix. Different frame timing remains
observable, and universal instrumentation neutrality is not established.
Standard/Mosaic whole-H1 differs by 48,000 pixels at every state because glass
coverage is x=[400,800) versus [480,800). The entire common glass
[480,800)×[0,600) and exposed source are exact across layouts at every state.
This comparison does not exclude any intra-run full-image difference.
| Pre-registered edge region | B natural/explicit standard | B natural/explicit Mosaic | A initial/return standard | A initial/return Mosaic |
| --- | ---: | ---: | ---: | ---: |
| Historical boundary (720,50,800,550) | 19,007 | 19,007 | 20,486 | 20,486 |
| Physical right x=[750,800) | 23,119 | 23,119 | 22,948 | 22,948 |
| Physical bottom y=[550,600) | 15,992 | 13,139 | 15,793 | 12,825 |
Historical boundary max delta is 4 for B and 5 for A; physical bands reach 6.
Whole-H1 mean absolute channel error is 0.083278/0.083215 standard (B/A) and
0.076682/0.076858 Mosaic. Right-band means are 0.866344/0.857756; historical
boundary means 0.515933/0.524808. These small per-channel means do not erase
the tens of thousands of different pixels. Whole-image bounding boxes are
[400,800)×[0,600) standard and [480,800)×[0,600) Mosaic. Column/row arrays
and amplitude histograms retain every changed pixel. In Mosaic only 103 B
and 1,963 A differences lie outside the union of the pre-registered right
50 columns and bottom 50 rows; no pixels are discarded. Rows near the bottom
and columns near the right reach all visible glass pixels; fine quantization
also leaves sparse differences just inside the bands.
For Mosaic B, changed-pixel maximum amplitudes 1/2/3/4/5/6 occur
17,174/8,705/3,825/4,073/130/8 times. For Mosaic A return they occur
19,009/8,177/5,093/2,394/652/15 times. Channel-level histograms and standard
counts are in edge-pixel-analysis.json; this is not an assertion of ordinary
rounding as the cause. No perceptual or daily-session assessment was run.
### Natural versus explicit path, native timing and damage
R3 P1 B and P3 A each contain one mapped BACKGROUND source commit,
one H1 dirty/rebuild/result=1, two completed cache texture-copy draw calls,
one dirty clear and optimized glass use. H2 has zero reconstruction in those
windows. P2 contains no background image commit, six dirty-set observations,
two successful H1 and two successful H2 reconstructions, eight completed copy
draw calls and four dirty clears. This is consistent with scoped cmd_blur
arrangement and its unconditional all-output invalidation, not a claim that
natural and explicit updates execute the same GPU sequence. The fixed cache
objects/FBO sizes are reused; source/configuration do not change radius/passes.
Copy draw completion is not a driver fence or per-pixel intermediate readback.
All R3 relevant reconstructions finish at least 357.899 ms before H1 capture;
no source commit/reconstruction trace occurs inside the measured capture
windows. The final H1 confirmation is identical after another 350 ms.
Earlier states do not have duplicate H1 PNGs under the finite budget, and
untraced controls lack OPT quiescence evidence. Their deterministic inputs,
stable applied state, settled windows and exact cross-run pixels support the
comparisons without claiming every intervening frame.
For four stabilized R3 H1 transitions, union of available native output-state
damage extents leaves zero changed pixels uncovered. Old private scene damage
in R1/R2 is retained separately and is not promoted to equivalent output-buffer
damage proof. Pristine has no new native damage trace. Commit/present traces
keep their own IDs; no screenshot is assigned an exact native presented buffer.
No all-frame, minimum-damage, physical-presentation or input claim follows.
### Sampling domain and fixed offline model
Pinned shaders are byte-identical pristine/private. FBOs stay 800×600; the
written domains are down 400×300,200×150,100×75 and up
200×150,400×300,800×600. Down taps use 2*(p+0.5)-0.5 with ±5-pixel offsets;
up taps use (p+0.5)/2-0.5 with axial ±2.5 and diagonal ±1.25. GL_LINEAR
requires neighboring texels. First downpass sees a complete source, with
full-texture clamp; later downpasses and all upsample passes can reach past
the fresh reduced domain while remaining inside the texture. No full color
clear appears between these blur passes. Alternating full-size ping-pong FBOs
can retain earlier regions. The audited source demonstrates this possibility;
it does not identify the native value actually read there.
The exact V1 functions are reused without executing its historical writer,
with the actual eight H1 R3 rebuild timestamps and source order. No kernel,
offset or rounding fitting is performed. Model/native historical-edge mean
absolute channel error spans 0.198700–0.518242; 96.668–97.911% of channels
are within one level, maximum residual 4 in that ROI. The broader physical
right and glass-bottom domains reach model/native residual 6. For B, model
predicts 32,129 changed visible-glass pixels versus native 33,915, mask Jaccard
0.861863. For A, 37,701 versus 35,340, Jaccard 0.805086; predicted maximum
7 versus native 6. Delta residual means in visible glass are 0.049901 and
0.056941. The residual and spatial distributions are preserved, not hidden.
This is strong history compatibility, not equivalence to native mediump GLSL
or a causal measurement of intermediate FBO contents.
| Hypothesis | Result for tested matrix |
| --- | --- |
| H1 reduced-domain history | Supported by source possibility and fixed-model spatial/amplitude agreement; native causality UNDECIDED |
| H2 new trace activation causes these edge differences | Not needed to reproduce: pristine and private OPT-off show them; R2/R3 graphical effect zero; universal neutrality NOT PROVEN |
| H3 shared rendering property | Supported: pristine/private splith complete RGB identical; common glass across layouts identical |
| H4 blur cycle alters more than source cache content | Demonstrated arrangement/all-output dirty and additional reconstructions; exact RGB contribution of each side effect remains unresolved |
| H5 another observed rendering factor | Native mediump versus ideal-model residual and timing differences remain limits; geometry/source/alpha/parameters are controlled; no alternate native cause demonstrated |
### Required architectural answers
| Question | Evidence-backed answer |
| --- | --- |
| Q1 pristine edges? | Yes: R0 reproduces exact standard counts and full images of R1. |
| Q2 standard and Mosaic? | Yes: common visible glass is exact; full counts differ with coverage. |
| Q3 without new traces? | Yes: R0, R1 and R2 reproduce the edge differences. |
| Q4 measurable trace effect? | Zero RGB difference in six R2/R3 states; timing differs. Neutrality is sampled, not universal. |
| Q5 same reconstruction steps? | Same source-defined blur/copy functions, different trigger/scene/output sequence: natural 1 H1 versus explicit 2 H1+2 H2. No intermediate GPU state equivalence proved. |
| Q6 blur-command contribution? | Container blur flag/arrangement and all-output dirty, with extra reconstructions observed. Their separate pixel effects cannot be isolated here. |
| Q7 central freshness? | Yes for the predefined 80,000 pixels: changes with B, exact explicit B and exact returned A in all four runs. |
| Q8 domains localize anomalies? | Right/bottom bands contain most differences; fixed model overlaps strongly but imperfectly. Not an exact causal localization. |
| Q9 source permits stale-domain sampling? | Yes: later down and up supports cross reduced fresh bounds inside full-size textures. Actual sampled old value remains unobserved. |
| Q10 level of proof? | Source possibility plus offline prediction and native final pixels; no intermediate native causality. |
| Q11 amplitude/distribution? | Max channel 6, tens of thousands of pixels, detailed rows/columns/ROIs/histograms above and in JSON. Not dismissed as rounding. |
| Q12 Mosaic clip? | Correct for sampled final draw: H1 x=[480,800), no target draw on H2, whole H2 exact. Not a universal clipping/input proof. |
| Q13 damage? | R3 extent union covers every stabilized changed pixel in four transitions; no all-frame or screenshot/native-buffer identity claim. |
| Q14 correction justified? | Reproducible shared history-sensitive edge output warrants an upstream investigation; no Mosaic-specific correction or required invariant violation is established. No algorithm correction authorized. |
| Q15 smallest next readback? | Selected seam texels and four destination pixels per B condition at D1/D2/U2, bounded protocol below; not executed. |
| Q16 blocker or separate limitation? | Shared SceneFX edge limitation, not a demonstrated Mosaic blocker. Whole-cache/native-cause gate remains open and prevents a global FEASIBLE claim; it need not block an independent ABI audit. |
| Q17 proceed ABI/package? | Yes, an isolated read-only ABI/package feasibility audit is now justified as the next separate gate. No packaging implementation or production readiness follows. Native causal study stays a separately authorized option. |
### Proposed later minimal GPU observation — NOT EXECUTED
Only a new ticket may authorize this. Prefer one pristine fixed-geometry run
with a paired observer-off control, strict reservations, no renderer/shader
correction and no forced clearing. At H1 B natural and the last explicit B
rebuild, record source FBO identity/dimensions and actual pass ordinal.
After D1 and before D2, read only source rectangles (193,69,12,12) and
(93,143,12,12), crossing valid 200×150 right/bottom seams. After D2 and
before U2, read (96,34,7,7) and (46,71,7,7), crossing valid 100×75 seams.
These cover the ideal bilinear neighborhoods for two selected edge samples.
Read corresponding destination pixels D2 (99,37)/(49,74) and U2
(199,75)/(99,149) after their draw. Four rectangles plus four destination
pixels are 390 RGBA pixels per condition: 780 pixels / 3,120 bytes total
for the two B conditions if RGBA8 is valid. No whole-FBO read or image dump.
Observe native read format, pack state, read-FBO binding, origin and texture
transform; preserve and restore observer state. Label synchronization/timing
perturbation. Compare inside-domain versus tail texels and their change between
B rebuilds with selected destination contributions under pinned taps. This
could discriminate historical values and conversion/precision hypotheses;
readback itself still needs observer comparability and does not automatically
prove every final-pixel cause. Do not execute, create its GPU code or reserve
another run in this exhausted campaign.
### Decision and remaining scope
The missing pristine and trace controls are now qualified. The phenomenon is
shared across pristine/private rendering and layouts; no measured OPT activation
effect or forbidden Mosaic H2 contribution appears. Native edge causality is
still unresolved, so the scientific verdict is PARTIALLY_FEASIBLE and
architectural conclusion UNDECIDED. FEASIBLE would overstate the unexplained
edge property; BLOCKED would invent a required-invariant violation.
Recommend the separate SceneFX/wlroots ABI/package audit next, carrying this
explicit shared edge limitation. If selecting an upstream graphics correction
requires causal evidence, authorize the minimal observation above separately.
Neither follow-up is implemented here. No production source or policy changes.
Rotation/scaling/output changes, saved-buffer/popup/input paths, animations,
other drivers/renderers, all-frame damage and native FBO readback remain
NOT TESTED in this campaign. No saved-buffer experiment was relaunched.
STEP20C stays NEXT and production multi-output NOT YET SUPPORTED.
## Final validation and sealed inventories
PASS: shell syntax of the existing exporter (no new B shell script), AST of
all nine Python files, C17 -Wall -Wextra -Werror (unused callback parameters
explicitly exempted), duplicate-configure callback harness, final client
binary equivalence, both pristine/private SceneFX and compositor builds,
Meson mosaic-model 1/1, historical patch applicability/source equivalence,
unchanged cumulative patch forward applicability/source equivalence against
pinned pristine, loaded renderer/library identities, exact stripe calibration,
all source reservations, stable applied focus/camera/geometry, complete RGB
and edge distributions, source sampling audit, recorded R3 damage coverage,
offline/actual budgets, links/whitespace including untracked files, protected
production/V1 paths and installed /usr/bin/sway/SceneFX/wlroots hashes.
All recorded client/compositor PIDs are gone and private sockets removed.
The standalone edge-distributions.svg is an offline figure, not a 49th capture.
NOT RUN: native FBO readback/intervention, saved-buffer/popup tests, additional
geometry/driver/timing variants, ordinary headless IPC suite (no production
change; would introduce extra compositor launches), ABI/package qualification,
installation, daily-session IPC, B commit/push. The original fixture incident
is preserved only in V1; no new run or preflight build failed in this campaign.
No production behavior is claimed by compilation, screenshots or model alone.
Final source manifest: **1,020 entries**, SHA-256
`044f6ab4e0d9ced000b12daa75c7cf30f234aa32fed14dc248baa9e3e11dcdff`.
Final scientific evidence manifest: **189 entries**, SHA-256
`7554388b89fab03b5f3ad0d15c792680fcb403ee04bce8a347c55257440c73d8`.
Every entry was verified individually; sealing refuses an existing inventory.
The historical 1006/106-entry manifests remain unchanged and reverified with
the explicit pre-freeze README snapshot mapping documented by the A report.
The final result report and Git diff are captured separately from the sealed
scientific inventory to avoid self-referential digest dependencies.
Final main, origin/main and github/main remain
`e749653ccda42abad9a66a11009be6043b5b6f62`; both remotes were directly queried
again. The real index is empty; only the five synchronized tracked documents,
this new report and the source-only edge fixture are locally modified/new.
No B commit or push. The final complete Git diff, including new files, is
retained under the freeze audit directory as complete-phase-b.diff.
Production source, cumulative patch, PKGBUILD, UX, AGENTS and historical fixture
match A's published commit. Installed libraries/compositor are hash-identical.
No command targeted daily IPC/session, QuickShell, dotfiles or Codex settings.
STEP20A DONE, STEP20B EXPERIMENTAL, STEP20C NEXT, STEP20D–G PLANNED;
production multi-output NOT YET SUPPORTED.
## Independent publication freeze (2026-10-13)
The new freeze ticket reverified all 1,020 source entries and 189 evidence
entries file by file before any documentation edit; both manifest SHA-256
values above matched. Their bytes and historical evidence are preserved.
The only subsequently edited manifested source is the fixture README; its
exact historical bytes are preserved as `README.pre-freeze.md` beside it.
Use that explicit mapping when verifying the original manifest after publication.
The preceding local/uncommitted final audit describes the historical campaign,
not the publication status of this freeze. No graphical campaign, analysis
writer, model writer, seal writer or proposed GPU readback was rerun.
The verdict remains PARTIALLY_FEASIBLE and architecture UNDECIDED.

View file

@ -0,0 +1,77 @@
# Optimized blur edge causality fixture
**EXPERIMENTAL / PRESERVED EVIDENCE; historical campaign closed.** Read the
[pre-registered protocol](../../../docs/design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md).
The historical V1 cache campaign is closed; never launch or analyze into it.
This fixture is a new four-slot comparative campaign, not a renderer fix.
From a full project clone containing the pinned project commits, with Meson,
Ninja, C17, Wayland scanner, compatible wlroots 0.20 build dependencies,
Python numpy/Pillow, grim and swaymsg, use:
```sh
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-optimized-blur-edge/test-budget.py
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-optimized-blur-edge/prepare.py
```
Preparation builds only, creates a new LAB outside Git and retrieves pinned
SceneFX. It reads the published V1 XML/patches and older preserved patch chain
without modifying them. Compiler logs and all generated files stay in LAB.
Offline callback, source, AST, ledger, patch, build and linker preflight must
pass before any run. The client ACKs background configures without drawing;
only explicit A/B/A commands can submit its three 800×600 images.
Execute `run.py LAB R0`, then R1, R2 and R3 exactly once each, with
PYTHONDONTWRITEBYTECODE=1. The persistent locked
`~/swayfx-mosaic-optimized-edge-v1-budget.json` binds all four slots to one LAB.
Failed launch/action reservations count; no refund, reset or alternate LAB.
Each run has 3 images, 1 scoped blur cycle, 12 PNG maximum and a 150 s watchdog.
All sockets/IPC are explicitly private; cleanup targets recorded children only.
R2/R3 share binary/client/library/config; OPT_TRACE absent versus 1 toggles new
cache traces. Old private traces remain enabled in both. No GPU readback,
forced clearing, shader, algorithm, production, package or desktop changes.
Keep exact executed sources, configurations, binary/library identities,
client events, logs, PNGs, exit codes and manifests outside Git. Analyze all
RGB pixels without excluding edge bands. Model/source sampling possibilities
are not native causality. Results and final offline validation are recorded
in the protocol after the four attempts. Fixture preservation is authorized; no new graphical run is authorized.
## Closed V1 campaign
LAB `/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n` consumed all four runs,
12 images,48 captures,4 cycles and two initial Mosaic corrections; no rerun
is allowed. All controls completed. R0/R1 full RGB and R2/R3 full RGB match
in six states, while the edge anomaly persists in pristine and without OPT.
Result PARTIALLY_FEASIBLE, architecture UNDECIDED; native FBO cause unresolved.
The next justified distinct gate is ABI/package audit with this limitation.
A proposed selective GPU observation is documented but not executed.
Offline analysis in this new LAB is `analyze.py LAB` then `model.py LAB`.
`model.py` reuses exact V1 functions through AST selection without running the
historical writer. Native rebuild schedule exists only for R3. `preflight.py`
affects offline files only and must precede all graphical runs; no re-execution
of the exhausted run slots is authorized. Source/evidence manifest creation
is separate from analysis to preserve final inventories. Generated outputs
remain outside Git. All runtime/socket directories were removed and owned
children reaped; compositor exit=0, clients retain controlled-shutdown EPIPE.
`seal.py LAB` creates each final manifest once and refuses replacement; verify
its entries rather than regenerating after sealing. Optional `plot.py LAB`
requires matplotlib and produces a standalone SVG from complete column/row
arrays; it is an offline figure, not another captured PNG. Its cache stays
in LAB. Run all derived analysis before sealing.
## Freeze verification (2026-10-13)
All 1,020 source and 189 evidence entries were independently reverified before
publication. Their original manifest bytes remain unchanged. The historical
manifest entry for this README (SHA-256
`e9b4ea06512f1cbcef6107eecaa356d02767f01bb66b0a6cb3b797d8094b7d4c`)
must now be checked against [README.pre-freeze.md](README.pre-freeze.md),
the exact pre-publication snapshot; every other entry uses its recorded path.
Source manifest SHA-256:
`044f6ab4e0d9ced000b12daa75c7cf30f234aa32fed14dc248baa9e3e11dcdff`.
Evidence manifest SHA-256:
`7554388b89fab03b5f3ad0d15c792680fcb403ee04bce8a347c55257440c73d8`.
Verification must never reseal, rewrite results or rerun the exhausted campaign.

View file

@ -0,0 +1,63 @@
# Optimized blur edge causality fixture
**EXPERIMENTAL / LOCAL / UNCOMMITTED.** Read the
[pre-registered protocol](../../../docs/design/STEP20C_OPTIMIZED_BLUR_EDGE_CAUSALITY_PROBE.md).
The historical V1 cache campaign is closed; never launch or analyze into it.
This fixture is a new four-slot comparative campaign, not a renderer fix.
From a full project clone containing the pinned project commits, with Meson,
Ninja, C17, Wayland scanner, compatible wlroots 0.20 build dependencies,
Python numpy/Pillow, grim and swaymsg, use:
```sh
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-optimized-blur-edge/test-budget.py
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-optimized-blur-edge/prepare.py
```
Preparation builds only, creates a new LAB outside Git and retrieves pinned
SceneFX. It reads the published V1 XML/patches and older preserved patch chain
without modifying them. Compiler logs and all generated files stay in LAB.
Offline callback, source, AST, ledger, patch, build and linker preflight must
pass before any run. The client ACKs background configures without drawing;
only explicit A/B/A commands can submit its three 800×600 images.
Execute `run.py LAB R0`, then R1, R2 and R3 exactly once each, with
PYTHONDONTWRITEBYTECODE=1. The persistent locked
`~/swayfx-mosaic-optimized-edge-v1-budget.json` binds all four slots to one LAB.
Failed launch/action reservations count; no refund, reset or alternate LAB.
Each run has 3 images, 1 scoped blur cycle, 12 PNG maximum and a 150 s watchdog.
All sockets/IPC are explicitly private; cleanup targets recorded children only.
R2/R3 share binary/client/library/config; OPT_TRACE absent versus 1 toggles new
cache traces. Old private traces remain enabled in both. No GPU readback,
forced clearing, shader, algorithm, production, package or desktop changes.
Keep exact executed sources, configurations, binary/library identities,
client events, logs, PNGs, exit codes and manifests outside Git. Analyze all
RGB pixels without excluding edge bands. Model/source sampling possibilities
are not native causality. Results and final offline validation are recorded
in the protocol after the four attempts. No commit or push for this fixture.
## Closed V1 campaign
LAB `/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n` consumed all four runs,
12 images,48 captures,4 cycles and two initial Mosaic corrections; no rerun
is allowed. All controls completed. R0/R1 full RGB and R2/R3 full RGB match
in six states, while the edge anomaly persists in pristine and without OPT.
Result PARTIALLY_FEASIBLE, architecture UNDECIDED; native FBO cause unresolved.
The next justified distinct gate is ABI/package audit with this limitation.
A proposed selective GPU observation is documented but not executed.
Offline analysis in this new LAB is `analyze.py LAB` then `model.py LAB`.
`model.py` reuses exact V1 functions through AST selection without running the
historical writer. Native rebuild schedule exists only for R3. `preflight.py`
affects offline files only and must precede all graphical runs; no re-execution
of the exhausted run slots is authorized. Source/evidence manifest creation
is separate from analysis to preserve final inventories. Generated outputs
remain outside Git. All runtime/socket directories were removed and owned
children reaped; compositor exit=0, clients retain controlled-shutdown EPIPE.
`seal.py LAB` creates each final manifest once and refuses replacement; verify
its entries rather than regenerating after sealing. Optional `plot.py LAB`
requires matplotlib and produces a standalone SVG from complete column/row
arrays; it is an offline figure, not another captured PNG. Its cache stays
in LAB. Run all derived analysis before sealing.

View file

@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""Offline comparisons from retained pixels/events; no native causality invented.
CONTRACT: full RGB and pre-registered regions; traces absent remain absent.
INVARIANT: raw runs and original source manifests are not rewritten here.
"""
import hashlib
import json
import re
import sys
from pathlib import Path
import numpy as np
from PIL import Image
lab = Path(sys.argv[1]).resolve()
out = lab / 'results'
fixture = Path(__file__).resolve().parent
ROIS = {'center': (520,100,720,500), 'source': (20,100,160,500),
'boundary': (720,50,800,550), 'physical_right': (750,0,800,600),
'physical_bottom': (0,550,800,600)}
PAIRS = [('P0-FX0','P0-FX2'), ('P0-FX2','P1-B-natural'),
('P1-B-natural','P2-B-explicit'), ('P0-FX2','P3-A-natural'),
('P3-A-natural','P3-confirm')]
NAMES = ['OPT_NODE_CREATE','OPT_NODE_RENDER','OPT_BACKGROUND_COMMIT','OPT_DIRTY_SET',
'OPT_DIRTY_CLEAR','OPT_REGEN_ATTEMPT','OPT_REGEN_RESULT','OPT_GPU_REBUILD',
'OPT_GPU_RESULT','OPT_GPU_COPY','OPT_GPU_COPY_DRAWN','OPT_BLUR_USE',
'OPT_CACHE_STATE','OPT_REGULAR_NODE','OPT_OUTPUT_BUFFER','PI_SCENE',
'PI_OWNER','PI_OUTPUT_COMMIT','PI_OUTPUT_PRESENT','TEMPORAL_DAMAGE']
def write(name, value):
(out / (name + '.json')).write_text(json.dumps(value, indent=2, sort_keys=True) + '\n')
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def crop(image, box):
left, top, right, bottom = box
return image[top:bottom,left:right]
def metric(first, second):
delta = np.abs(first.astype(np.int16) - second.astype(np.int16))
mask = np.any(delta, axis=2)
ys, xs = np.where(mask)
return {'pixels': int(mask.sum()), 'total_pixels': int(mask.size),
'max_channel': int(delta.max()), 'mean_abs_channel': float(delta.mean()),
'bbox': [int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] if mask.any() else None,
'channel_amplitude_histogram': {str(i):int(n) for i,n in enumerate(np.bincount(delta.ravel(),minlength=256)) if n},
'changed_pixel_max_amplitude_histogram': {str(i):int(n) for i,n in enumerate(np.bincount(delta.max(axis=2).ravel(),minlength=256)) if n}}
def compare(first, second):
return {'whole': metric(first,second),
'ROIs': {name: metric(crop(first,box),crop(second,box)) for name,box in ROIS.items()}}
def parsed(lines, name):
return [{'raw': line, **dict(re.findall(r'([A-Za-z_]+)=([^ ]+)',line))}
for line in lines if re.search(r'\b' + name + r'\b',line)]
runs = {}
pixels = {}
columns = {}
rows = {}
renderers = {}
commits = {}
states = {}
geometries = {}
settings = {}
windows = {}
phase_traces = {}
damage = {}
contrast = {}
ledger = json.loads((out/'campaign-budget.json').read_text())
for mode in ('R0','R1','R2','R3'):
run = lab/'runs'/mode
status = json.loads((run/'exit-status.json').read_text())
events = [json.loads(line) for line in (run/'events.jsonl').read_text().splitlines()]
lines = (run/'compositor.log').read_text().splitlines()
traces = {name: parsed(lines,name) for name in NAMES}
images = {event['tag']+'-'+event['output']: np.array(Image.open(run/event['path']).convert('RGB'))
for event in events if event['kind']=='capture'}
runs[mode] = {'status':status,'events':events,'traces':traces,'images':images}
renderer_event = next(e for e in events if e['kind']=='renderer_start')
loaded = sorted(set(line.split()[-1] for line in (run/'library-maps.txt').read_text().splitlines()
if 'libscenefx' in line or 'libwlroots' in line))
renderers[mode] = {'lines':[line for line in lines if any(text in line for text in
('Using OpenGL ES','GL renderer:','GL vendor:','EGL vendor:','FX RENDERER:'))],
'loaded_libraries':{path:sha(Path(path)) for path in loaded},
'binary':renderer_event['binary'],'binary_sha256':sha(Path(renderer_event['binary'])),
'OPT_TRACE':renderer_event['OPT_TRACE'],'native_OPT_evidence':mode=='R3'}
client = parsed((run/'background.log').read_text().splitlines(),'CLIENT_COMMIT')
reserves = [e for e in events if e['kind']=='mutation_reserved']
configures = parsed((run/'background.log').read_text().splitlines(),'BACKGROUND_CONFIGURED')
assert len(client)==ledger['modes'][mode]['mutation']==len(reserves)
assert [c['pattern'] for c in client]==[r['pattern'] for r in reserves]
assert all(int(c['ns'])>r['ns'] and c['size']=='800x600' for c,r in zip(client,reserves))
assert len(status['captures'])==ledger['modes'][mode]['capture']
assert status['duration']<150 and status['runtime_removed'] and not (run/'runtime').exists()
commits[mode] = {'client':client,'configures':configures,'reservations':reserves,
'server':traces['OPT_BACKGROUND_COMMIT'],
'reservation_protocol':'PASS before every observed A/B image',
'no_configure_draw':'PASS duplicate configure ACK without autonomous image',
'identity_limit':'surface/buffer IDs may be reused; sequence/lifetime required'}
states[mode] = [e for e in events if e['kind']=='graphic_state']
geometries[mode] = {p.stem:json.loads(p.read_text()) for p in run.glob('*-outputs.json')}
settings[mode] = {'config':(run/'config').read_text(),
'commands':[e for e in events if e['kind']=='ipc' and e['args'] and e['args'][0].startswith('[app_id=')],
'pixel_effect_gate':[e for e in events if e['kind']=='P0_gate'],
'setting_limit':'accepted config + source audit; filtering proves effect; no OPT parameters fabricated for untraced controls'}
windows[mode] = status['captures']
if status['failure']:
pixels[mode] = {'failure':status['failure']}
continue
assert [c['pattern'] for c in client]==['A','B','A']
baseline = images['P0-FX0-HEADLESS-2']
p = {'comparisons':{},'H2_full':{},'capture_sha256':{q.name:sha(q) for q in run.glob('*.png')}}
columns[mode] = {}; rows[mode] = {}; contrast[mode] = {}
for first,second in PAIRS:
key=first+' -> '+second
a=images[first+'-HEADLESS-1']; b=images[second+'-HEADLESS-1']
p['comparisons'][key]=compare(a,b)
mask=np.any(a!=b,axis=2)
columns[mode][key]=mask.sum(axis=0).astype(int).tolist()
rows[mode][key]=mask.sum(axis=1).astype(int).tolist()
for name,image in images.items():
if name.endswith('HEADLESS-2'):
p['H2_full'][name]=metric(baseline,image)
else:
center=crop(image,ROIS['center']).astype(float)
luminance=center@[.2126,.7152,.0722]
contrast[mode][name]={'luminance_std':float(luminance.std()),
'adjacent_luminance':float(np.abs(np.diff(luminance,axis=1)).mean()),
'mean_RGB':center.mean(axis=(0,1)).tolist()}
# Full exposed-source calibration in every measured state, not just membership.
x=np.arange(20,160)
for tag,pattern in [('P0-FX0','A'),('P0-FX2','A'),('P1-B-natural','B'),('P2-B-explicit','B'),('P3-A-natural','A')]:
colors=np.array([[245,245,245],[15,25,45]] if pattern=='A' else [[32,192,224],[176,32,64]],dtype=np.uint8)
expected=np.broadcast_to(colors[(x//8)%2],(400,140,3))
assert np.array_equal(expected,crop(images[tag+'-HEADLESS-1'],ROIS['source']))
p['source_calibration']='PASS exact stripes A/B/B/A in exposed ROI'
pixels[mode]=p
phase_traces[mode]={}
for tag in ('P0-FX2','P1-B-natural','P2-B-explicit','P3-A-natural'):
capture=next(e for e in status['captures'] if e['tag']==tag and e['output']=='HEADLESS-1')
if tag=='P0-FX2':
start=next(e['after_ns'] for e in status['captures'] if e['tag']=='P0-reference')
elif tag=='P2-B-explicit':
start=next(e['ns'] for e in events if e['kind']=='explicit_cycle_start')
else:
start=reserves[1 if tag=='P1-B-natural' else 2]['ns']
phase_traces[mode][tag]={'start_ns':start,'capture_before_ns':capture['before_ns'],
'end_ns':capture['after_ns'],
'trace':{name:[t for t in values if 'ns' in t and start<=int(t['ns'])<=capture['after_ns']]
for name,values in traces.items()}}
if mode!='R3':
damage[mode]={'output_state_OPT_damage':'NOT AVAILABLE',
'old_scene_damage':traces['TEMPORAL_DAMAGE'],
'limit':'older private scene damage (if present) is not OPT output-state buffer damage; no equivalent coverage claim'}
else:
damage[mode]={}
for first,second in [('P0-FX0','P0-FX2'),('P0-FX2','P1-B-natural'),('P1-B-natural','P2-B-explicit'),('P2-B-explicit','P3-A-natural')]:
ca=next(e for e in status['captures'] if e['tag']==first and e['output']=='HEADLESS-1')
cb=next(e for e in status['captures'] if e['tag']==second and e['output']=='HEADLESS-1')
mask=np.any(images[first+'-HEADLESS-1']!=images[second+'-HEADLESS-1'],axis=2)
cover=np.zeros((600,800),bool); records=[]
for t in traces['OPT_OUTPUT_BUFFER']:
if t['output']!='HEADLESS-1' or not ca['before_ns']<=int(t['ns'])<=cb['after_ns']:continue
left,top,right,bottom=map(int,t['damage'].split(','));cover[max(0,top):min(600,bottom),max(0,left):min(800,right)]=True;records.append(t)
damage[mode][first+' -> '+second]={'changed_pixels':int(mask.sum()),
'uncovered_by_output_damage_extents':int((mask&~cover).sum()),'records':records,
'limit':'extent union, stabilized captures only; no all-frame coverage/minimal damage/native PNG identity'}
cross={}
for first,second in [('R0','R1'),('R2','R3')]:
key=first+' -> '+second
a=runs[first];b=runs[second]
comparable=not a['status']['failure'] and not b['status']['failure']
cross[key]={'geometry_equal':states[first]==states[second], 'comparable':comparable}
# State records contain timestamps; compare applied geometry and tags only.
cross[key]['geometry_equal']=[(e['tag'],e['geometry']) for e in states[first]]==[(e['tag'],e['geometry']) for e in states[second]]
if comparable and cross[key]['geometry_equal']:
cross[key]['states']={tag:compare(a['images'][tag+'-HEADLESS-1'],b['images'][tag+'-HEADLESS-1'])
for tag in ('P0-FX0','P0-FX2','P1-B-natural','P2-B-explicit','P3-A-natural','P3-confirm')}
cross[key]['H2']=metric(a['images']['P0-FX0-HEADLESS-2'],b['images']['P0-FX0-HEADLESS-2'])
else:cross[key]['limit']='control geometry/fixture not comparable'
write('renderer-identities',renderers);write('source-commits',commits)
write('graphic-states',states);write('output-geometries',geometries);write('blur-settings',settings)
write('edge-pixel-analysis',pixels);write('column-distribution',columns);write('row-distribution',rows)
write('capture-windows',windows);write('damage-analysis',damage);write('phase-events',phase_traces)
write('filtering-analysis',contrast);write('private-trace-comparison',cross['R2 -> R3']);write('pristine-control',cross['R0 -> R1'])
for category,names in [('cache-use',['OPT_BLUR_USE','OPT_CACHE_STATE','OPT_REGULAR_NODE']),
('dirty-events',['OPT_DIRTY_SET','OPT_DIRTY_CLEAR']),
('regeneration-events',['OPT_REGEN_ATTEMPT','OPT_REGEN_RESULT','OPT_GPU_REBUILD','OPT_GPU_RESULT','OPT_GPU_COPY','OPT_GPU_COPY_DRAWN'])]:
write(category,{mode:{name:data['traces'][name] for name in names} for mode,data in runs.items()})
write('matrix',{mode:{'failure':data['status']['failure'],'duration':data['status']['duration'],
'compositor_exit':data['status']['compositor'],'captures':len(data['status']['captures']),
'images':len(commits[mode]['client']),'runtime_removed':data['status']['runtime_removed']}
for mode,data in runs.items()})
# Never infer the final scientific verdict merely from process success.
print(json.dumps({'edge_counts':{mode:{pair:value['whole'] for pair,value in data.get('comparisons',{}).items()
if pair in ('P1-B-natural -> P2-B-explicit','P0-FX2 -> P3-A-natural')}
for mode,data in pixels.items()},
'cross':{key:{'geometry_equal':data['geometry_equal'],
'state_changed_pixels':{tag:value['whole']['pixels'] for tag,value in data.get('states',{}).items()}}
for key,data in cross.items()}},indent=2))

View file

@ -0,0 +1,69 @@
"""Durable fail-closed reservations, bound to one LAB for this ticket.
WHY: a failed graphical attempt must never create capacity for a rerun.
CONTRACT: exclusive flock; fsync file then atomic replace and directory fsync.
INVARIANT: exact schema, bounded counters, no reset/refund or alternate LAB.
"""
import fcntl
import json
import os
import tempfile
from pathlib import Path
MODES = ('R0', 'R1', 'R2', 'R3')
KEYS = ('run', 'mutation', 'capture', 'cycle', 'calibration')
CAMPAIGN = 'SWAYFX_MOSAIC_STEP20C_OPTIMIZED_CACHE_FREEZE_AND_EDGE_CAUSALITY_GATE_V1'
DEFAULT = Path.home() / 'swayfx-mosaic-optimized-edge-v1-budget.json'
def limits(mode):
return dict(run=1, mutation=3, capture=12, cycle=1,
calibration=1 if mode in ('R2', 'R3') else 0)
def validate(state, lab):
if set(state) != {'campaign', 'lab', 'modes'} or state['campaign'] != CAMPAIGN or state['lab'] != lab:
raise RuntimeError('wrong campaign/LAB or corrupt schema')
if set(state['modes']) != set(MODES):
raise RuntimeError('corrupt modes')
for mode, counts in state['modes'].items():
if set(counts) != set(KEYS):
raise RuntimeError('corrupt resource schema')
for key, value in counts.items():
if type(value) is not int or not 0 <= value <= limits(mode)[key]:
raise RuntimeError('corrupt/out-of-bounds counter')
if any(counts[k] for k in KEYS if k != 'run') and counts['run'] != 1:
raise RuntimeError('action before run')
def reserve(lab, mode, resource, path=DEFAULT):
if mode not in MODES or resource not in KEYS:
raise ValueError('unknown reservation')
lab = str(Path(lab).resolve())
path = Path(path)
with path.with_suffix('.lock').open('a+') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
state = json.loads(path.read_text()) if path.exists() else {
'campaign': CAMPAIGN, 'lab': lab,
'modes': {m: {k: 0 for k in KEYS} for m in MODES}}
validate(state, lab)
counts = state['modes'][mode]
if resource != 'run' and counts['run'] != 1:
raise RuntimeError('reserve run first')
if counts[resource] >= limits(mode)[resource]:
raise RuntimeError('budget exhausted: ' + resource)
counts[resource] += 1
fd, tmp = tempfile.mkstemp(prefix=path.name + '.', dir=path.parent)
try:
with os.fdopen(fd, 'w') as out:
json.dump(state, out, sort_keys=True)
out.write('\n')
out.flush()
os.fsync(out.fileno())
os.replace(tmp, path)
directory = os.open(path.parent, os.O_DIRECTORY)
try:
os.fsync(directory)
finally:
os.close(directory)
finally:
if os.path.exists(tmp):
os.unlink(tmp)
return state

View file

@ -0,0 +1,303 @@
#define _POSIX_C_SOURCE 200809L
#include "layer-shell-client.h"
#include "xdg-shell-client.h"
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
/* WHY: one persistent background supplies real A/B commits below blur_tree.
* CONTRACT: fixed opaque background, premultiplied ARGB glass and static H2.
* INVARIANT: no animation, synthetic configure, popup or held transaction. */
static struct wl_display *display;
static struct wl_compositor *compositor;
static struct wl_shm *shm;
static struct xdg_wm_base *wm;
static struct zwlr_layer_shell_v1 *shell;
static struct wl_output *h1;
static struct wl_surface *surface;
static struct xdg_surface *xdg;
static struct xdg_toplevel *top;
static struct zwlr_layer_surface_v1 *layer;
static const char *mode;
static int width = 420, height = 320;
static unsigned commits, live_buffers;
static char pattern = 'A';
static volatile sig_atomic_t running = 1;
struct pixels {
struct wl_buffer *buffer;
void *map;
size_t size;
struct pixels *next;
};
static struct pixels *buffers;
static long long stamp(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (long long)t.tv_sec * 1000000000LL + t.tv_nsec;
}
static void free_pixels(struct pixels *p) {
struct pixels **it = &buffers;
while (*it && *it != p)
it = &(*it)->next;
if (*it)
*it = p->next;
wl_buffer_destroy(p->buffer);
munmap(p->map, p->size);
free(p);
--live_buffers;
}
static void release(void *data, struct wl_buffer *b) {
fprintf(stderr, "RELEASE ns=%lld buffer=%u\n", stamp(),
wl_proxy_get_id((struct wl_proxy *)b));
free_pixels(data);
}
static const struct wl_buffer_listener buffer_listener = {.release = release};
static void draw(void) {
/* CONTRACT: exactly one runner command per reserved A/B image.
* INVARIANT: configures cannot draw, and the sequence is A/B/A only. */
if (!strcmp(mode, "background") &&
(width != 800 || height != 600 || commits >= 3 ||
pattern != (commits == 1 ? 'B' : 'A')))
exit(2);
if (width <= 0 || height <= 0 || width > 4096 || height > 4096 ||
live_buffers >= 8)
exit(2);
size_t size = (size_t)width * (size_t)height * 4;
/* CONTRACT: each mapping stays immutable until native wl_buffer.release.
* Bounds keep pool/stride integers valid, with at most eight outstanding. */
char name[512];
const char *runtime = getenv("XDG_RUNTIME_DIR");
if (!runtime || snprintf(name, sizeof(name), "%s/blur-shm-XXXXXX", runtime) >=
(int)sizeof(name))
exit(2);
int fd = mkstemp(name);
if (fd < 0)
exit(2);
unlink(name);
if (ftruncate(fd, (off_t)size) < 0)
exit(2);
struct pixels *p = calloc(1, sizeof(*p));
if (!p)
exit(2);
p->map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
p->size = size;
if (p->map == MAP_FAILED)
exit(2);
uint32_t *pixels = p->map;
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x) {
uint32_t color = 0x00303038;
if (!strcmp(mode, "background"))
color = pattern == 'A' ? (x / 8 % 2 ? 0x000f192d : 0x00f5f5f5)
: (x / 8 % 2 ? 0x00b02040 : 0x0020c0e0);
else if (!strcmp(mode, "glass"))
color = 0x88442010;
else if (!strcmp(mode, "filler"))
color = 0x00000000;
pixels[(size_t)y * (size_t)width + (size_t)x] = color;
}
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, (int)size);
p->buffer = wl_shm_pool_create_buffer(
pool, 0, width, height, width * 4,
(!strcmp(mode, "glass") || !strcmp(mode, "filler"))
? WL_SHM_FORMAT_ARGB8888
: WL_SHM_FORMAT_XRGB8888);
p->next = buffers;
buffers = p;
++live_buffers;
wl_buffer_add_listener(p->buffer, &buffer_listener, p);
wl_surface_attach(surface, p->buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, width, height);
wl_surface_commit(surface);
fprintf(stderr,
"CLIENT_COMMIT ns=%lld mode=%s surface=%u buffer=%u index=%u "
"pattern=%c size=%dx%d damage=0,0,%d,%d\n",
stamp(), mode, wl_proxy_get_id((struct wl_proxy *)surface),
wl_proxy_get_id((struct wl_proxy *)p->buffer), ++commits, pattern,
width, height, width, height);
wl_shm_pool_destroy(pool);
close(fd);
}
static void ping(void *d, struct xdg_wm_base *b, uint32_t s) {
xdg_wm_base_pong(b, s);
}
static const struct xdg_wm_base_listener wm_listener = {.ping = ping};
static void top_configure(void *d, struct xdg_toplevel *t, int32_t w, int32_t h,
struct wl_array *a) {
if (w > 0)
width = w;
if (h > 0)
height = h;
}
static void top_close(void *d, struct xdg_toplevel *t) { running = 0; }
static const struct xdg_toplevel_listener top_listener = {
.configure = top_configure, .close = top_close};
static void configure(void *d, struct xdg_surface *s, uint32_t serial) {
xdg_surface_ack_configure(s, serial);
draw();
}
static const struct xdg_surface_listener xdg_listener = {.configure =
configure};
/* CONTRACT: configures ACK geometry, never submit an unreserved image.
* WHY: duplicate layer configures must not spend autonomous mutations.
* INVARIANT: the runner reserves then sends every A/B draw command. */
static void layer_configure(void *d, struct zwlr_layer_surface_v1 *s,
uint32_t serial, uint32_t w, uint32_t h) {
if (w == 0 || h == 0 || w > 4096 || h > 4096)
exit(2);
if (commits && ((int)w != width || (int)h != height))
exit(2);
width = (int)w;
height = (int)h;
zwlr_layer_surface_v1_ack_configure(s, serial);
fprintf(stderr, "BACKGROUND_CONFIGURED ns=%lld serial=%u size=%dx%d\n",
stamp(), serial, width, height);
}
static void layer_close(void *d, struct zwlr_layer_surface_v1 *s) {
running = 0;
}
static const struct zwlr_layer_surface_v1_listener layer_listener = {
.configure = layer_configure, .closed = layer_close};
static void output_name(void *d, struct wl_output *o, const char *name) {
if (!strcmp(name, "HEADLESS-1"))
h1 = o;
}
static void output_geometry(void *d, struct wl_output *o, int32_t x, int32_t y,
int32_t w, int32_t h, int32_t sp, const char *m,
const char *n, int32_t tr) {}
static void output_mode(void *d, struct wl_output *o, uint32_t flags, int32_t w,
int32_t h, int32_t refresh) {}
static void output_done(void *d, struct wl_output *o) {}
static void output_scale(void *d, struct wl_output *o, int32_t scale) {}
static void output_description(void *d, struct wl_output *o, const char *desc) {
}
static const struct wl_output_listener output_listener = {
.geometry = output_geometry,
.mode = output_mode,
.done = output_done,
.scale = output_scale,
.name = output_name,
.description = output_description};
static void global(void *d, struct wl_registry *r, uint32_t id,
const char *name, uint32_t v) {
if (!strcmp(name, "wl_compositor"))
compositor =
wl_registry_bind(r, id, &wl_compositor_interface, v < 4 ? v : 4);
else if (!strcmp(name, "wl_shm"))
shm = wl_registry_bind(r, id, &wl_shm_interface, 1);
else if (!strcmp(name, "xdg_wm_base"))
wm = wl_registry_bind(r, id, &xdg_wm_base_interface, v < 3 ? v : 3);
else if (!strcmp(name, "zwlr_layer_shell_v1"))
shell =
wl_registry_bind(r, id, &zwlr_layer_shell_v1_interface, v < 2 ? v : 2);
else if (!strcmp(name, "wl_output") && v >= 4) {
struct wl_output *o = wl_registry_bind(r, id, &wl_output_interface, 4);
wl_output_add_listener(o, &output_listener, NULL);
}
}
static void removed(void *d, struct wl_registry *r, uint32_t id) {}
static const struct wl_registry_listener registry_listener = {
.global = global, .global_remove = removed};
static void stop(int s) { running = 0; }
int main(int argc, char **argv) {
if (argc != 2)
return 2;
mode = argv[1];
if (strcmp(mode, "background") && strcmp(mode, "glass") &&
strcmp(mode, "filler") && strcmp(mode, "reference"))
return 2;
signal(SIGTERM, stop);
display = wl_display_connect(NULL);
if (!display)
return 1;
struct wl_registry *r = wl_display_get_registry(display);
wl_registry_add_listener(r, &registry_listener, NULL);
if (wl_display_roundtrip(display) < 0 || wl_display_roundtrip(display) < 0)
return 1;
if (!compositor || !shm || !wm)
return 2;
xdg_wm_base_add_listener(wm, &wm_listener, NULL);
surface = wl_compositor_create_surface(compositor);
if (!strcmp(mode, "background")) {
if (!shell || !h1)
return 2;
layer = zwlr_layer_shell_v1_get_layer_surface(
shell, surface, h1, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
"mosaic-dynamic-background");
zwlr_layer_surface_v1_add_listener(layer, &layer_listener, NULL);
zwlr_layer_surface_v1_set_size(layer, 0, 0);
zwlr_layer_surface_v1_set_anchor(layer,
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
zwlr_layer_surface_v1_set_exclusive_zone(layer, -1);
zwlr_layer_surface_v1_set_keyboard_interactivity(layer, 0);
} else {
xdg = xdg_wm_base_get_xdg_surface(wm, surface);
xdg_surface_add_listener(xdg, &xdg_listener, NULL);
top = xdg_surface_get_toplevel(xdg);
xdg_toplevel_add_listener(top, &top_listener, NULL);
const char *app = !strcmp(mode, "reference") ? "org.mosaic.StaticReference"
: !strcmp(mode, "glass") ? "org.mosaic.BlurGlass"
: "org.mosaic.BlurFiller";
xdg_toplevel_set_app_id(top, app);
xdg_toplevel_set_title(top, app);
}
wl_surface_commit(surface);
while (running) {
if (wl_display_dispatch_pending(display) < 0)
break;
if (wl_display_flush(display) < 0 && errno != EAGAIN)
break;
struct pollfd fds[2] = {
{.fd = wl_display_get_fd(display), .events = POLLIN},
{.fd = STDIN_FILENO, .events = POLLIN}};
int n = poll(fds, 2, 100);
if (n < 0) {
if (errno == EINTR)
continue;
break;
}
if (fds[0].revents & (POLLIN | POLLERR | POLLHUP))
if (wl_display_dispatch(display) < 0)
break;
if (fds[1].revents & POLLIN) {
char cmd[64];
if (!fgets(cmd, sizeof(cmd), stdin))
break;
if (!strcmp(mode, "background") && (cmd[0] == 'A' || cmd[0] == 'B') &&
cmd[1] == '\n') {
pattern = cmd[0];
draw();
fprintf(stderr, "COMMAND %c DONE ns=%lld\n", pattern, stamp());
} else if (!strncmp(cmd, "close", 5))
running = 0;
}
}
int error = wl_display_get_error(display);
fprintf(stderr, "EXIT ns=%lld mode=%s error=%d commits=%u\n", stamp(), mode,
error, commits);
if (layer)
zwlr_layer_surface_v1_destroy(layer);
if (top)
xdg_toplevel_destroy(top);
if (xdg)
xdg_surface_destroy(xdg);
wl_surface_destroy(surface);
wl_display_flush(display);
while (buffers)
free_pixels(buffers);
wl_registry_destroy(r);
wl_display_disconnect(display);
return error ? 1 : 0;
}

View file

@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Reuse V1 ideal kernels; compare hypothesis with R3 native reconstruction order.
CONTRACT: no GLSL/native precision equivalence, FBO contents or causal proof.
INVARIANT: model prediction is fixed before fitting; no kernel/offset fitting.
"""
import ast
import hashlib
import json
import re
import sys
from pathlib import Path
import numpy as np
from PIL import Image
lab=Path(sys.argv[1]).resolve()
fixture=Path(__file__).resolve().parent
historical=fixture.parent/'step20c-optimized-blur-cache/edge-model.py'
source=historical.read_text()
# WHY: reuse the exact reviewed functions without executing the historical writer.
# CONTRACT: retain only function definitions/imports and W,H constants from its AST.
module=ast.parse(source)
selected=[node for node in module.body if isinstance(node,(ast.Import,ast.ImportFrom,ast.FunctionDef))
or (isinstance(node,ast.Assign) and any(isinstance(t,ast.Tuple) for t in node.targets))]
context={}
exec(compile(ast.Module(body=selected,type_ignores=[]),str(historical),'exec'),context)
rebuild=context['rebuild'];pattern=context['pattern']
run=lab/'runs/R3'
lines=(run/'compositor.log').read_text().splitlines()
commits=[]
for line in (run/'background.log').read_text().splitlines():
if line.startswith('CLIENT_COMMIT '):
commits.append((int(re.search(r'ns=(\d+)',line)[1]),re.search(r'pattern=([AB])',line)[1]))
rebuilds=[int(re.search(r'ns=(\d+)',line)[1]) for line in lines
if line.startswith('OPT_GPU_REBUILD ') and 'output=HEADLESS-1' in line]
ping=np.zeros((600,800,3));pong=ping.copy();states=[]
for ns in rebuilds:
current=next((c for t,c in reversed(commits) if t<ns),None)
image=pattern(current) if current else np.zeros((600,800,3))
cache=rebuild(image,ping,pong)
states.append((ns,current,cache))
events=[json.loads(line) for line in (run/'events.jsonl').read_text().splitlines()]
metrics={};predictions={};actuals={}
for event in events:
if event['kind']!='capture' or event['output']!='HEADLESS-1' or event['tag']=='P0-FX0':continue
ns,current,cache=next(s for s in reversed(states) if s[0]<event['before_ns'])
predicted=np.round(cache*(119/255)+[68,32,16])
actual=np.array(Image.open(run/event['path']).convert('RGB')).astype(float)
predictions[event['tag']]=predicted;actuals[event['tag']]=actual
rois={'center':(520,100,720,500),'boundary':(720,50,800,550),
'physical_right':(750,0,800,600),'glass_bottom':(480,550,800,600)}
measurements={}
for name,(l,t,r,b) in rois.items():
d=np.abs(predicted[t:b,l:r]-actual[t:b,l:r])
measurements[name]={'mean_abs_channel_error':float(d.mean()),
'max_channel_error':float(d.max()),'within_one_channel':float((d<=1).mean())}
metrics[event['tag']]={'source':current,'model_rebuild_ns':ns,'ROIs':measurements}
distribution={}
for first,second in [('P1-B-natural','P2-B-explicit'),('P0-FX2','P3-A-natural')]:
pd=predictions[second]-predictions[first];nd=actuals[second]-actuals[first]
# Model applies only to the measured glass, not the entire composited source.
pd[:,:480]=0;pm=np.any(pd!=0,axis=2);nm=np.any(nd!=0,axis=2)
union=(pm|nm).sum();intersection=(pm&nm).sum()
disagreement=np.abs(pd-nd)
distribution[first+' -> '+second]={
'predicted_changed_pixels_in_visible_glass':int(pm.sum()),'native_changed_pixels':int(nm.sum()),
'mask_intersection':int(intersection),'mask_union':int(union),
'mask_jaccard':float(intersection/union) if union else 1,
'predicted_max_channel':float(np.abs(pd).max()),'native_max_channel':float(np.abs(nd).max()),
'delta_residual_mean_abs_channel':float(disagreement[:,480:].mean()),
'predicted_columns':pm.sum(axis=0).astype(int).tolist(),
'predicted_rows':pm.sum(axis=1).astype(int).tolist(),
'native_columns':nm.sum(axis=0).astype(int).tolist(),'native_rows':nm.sum(axis=1).astype(int).tolist(),
'interpretation':'fixed-kernel history compatibility only; native FBO tail and mediump behavior unobserved'}
(lab/'results/edge-model-comparison.json').write_text(json.dumps({
'model_source':str(historical),'model_sha256':hashlib.sha256(historical.read_bytes()).hexdigest(),
'native_schedule':'R3 OPT_GPU_REBUILD timestamps only; no invented timestamps for untraced runs',
'rebuild_count':len(rebuilds),'metrics':metrics,'distributions':distribution,
'causality':'NOT DEMONSTRATED; no GPU readback or intervention'},indent=2)+'\n')
print(json.dumps({'rebuild_count':len(rebuilds),'metrics':metrics,
'distributions':{k:{n:v for n,v in d.items() if not n.endswith(('columns','rows'))}
for k,d in distribution.items()}},indent=2))

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Optional standalone scientific SVG from complete offline distributions."""
import json
import sys
import os
from pathlib import Path
lab=Path(sys.argv[1]).resolve()
os.environ['MPLCONFIGDIR']=str(lab/'plot-cache')
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
out=lab/'results'
columns=json.loads((out/'column-distribution.json').read_text())
rows=json.loads((out/'row-distribution.json').read_text())
model=json.loads((out/'edge-model-comparison.json').read_text())['distributions']
pairs=['P1-B-natural -> P2-B-explicit','P0-FX2 -> P3-A-natural']
fig,axes=plt.subplots(2,2,figsize=(12,7),constrained_layout=True)
for index,key in enumerate(pairs):
for dim,data,name in [(0,columns,'columns'),(1,rows,'rows')]:
ax=axes[dim,index]
ax.plot(data['R0'][key],label='R0/R1 standard native',color='#376ca0',linewidth=1.1)
ax.plot(data['R2'][key],label='R2/R3 Mosaic native',color='#19816b',linewidth=1.1)
ax.plot(model[key]['predicted_'+name],label='Fixed offline model, Mosaic domain',
color='#ba6d20',linewidth=1,linestyle='--',alpha=.8)
ax.axvspan(750 if dim==0 else 550,800 if dim==0 else 600,color='#888888',alpha=.12)
ax.set_title(('B natural / explicit' if index==0 else 'A initial / return')+' — '+name)
ax.set_xlabel('Output-local '+('x' if dim==0 else 'y')+' (px)')
ax.set_ylabel('Changed pixels per '+('column' if dim==0 else 'row'))
ax.set_xlim(0,800 if dim==0 else 600);ax.grid(alpha=.2)
axes[0,0].legend(fontsize=8)
fig.suptitle('Complete edge distributions — shared native behavior; offline model is not causal proof',fontsize=12)
fig.savefig(out/'edge-distributions.svg',metadata={'Date':None})
print('PASS standalone SVG; not an additional compositor capture')

View file

@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""Offline source, callback, protocol and pinned sampling audit; no graphics."""
import ast
import hashlib
import json
import subprocess
import sys
import xml.etree.ElementTree as ET
from pathlib import Path
lab = Path(sys.argv[1]).resolve()
fixture = Path(__file__).resolve().parent
repo = fixture.parents[2]
results = lab / 'results'
for path in fixture.glob('*.py'):
ast.parse(path.read_text(), filename=str(path))
for path in lab.glob('*.xml'):
ET.parse(path)
client = (fixture / 'clients.c').read_text()
assert client == (lab / 'client-source.c').read_text()
callback = client[client.index('static void layer_configure('):client.index('static void layer_close(')]
assert 'draw(' not in callback and 'ack_configure(s, serial)' in callback
# WHY: exercise the real callback text under duplicate configures without Wayland.
# CONTRACT: stub only ACK and clock; no native graphics or mutation reservation.
harness = '''#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
struct zwlr_layer_surface_v1 {int unused;};
static int width=420,height=320;
static unsigned commits=0,acks=0;
static long long stamp(void){return 0;}
static void zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *s,uint32_t serial){(void)s;(void)serial;++acks;}
''' + callback + '''
int main(void){struct zwlr_layer_surface_v1 s={0};layer_configure(NULL,&s,1,800,600);layer_configure(NULL,&s,2,800,600);assert(acks==2 && commits==0 && width==800 && height==600);commits=1;layer_configure(NULL,&s,3,800,600);assert(acks==3 && commits==1);return 0;}
'''
(lab / 'callback-test.c').write_text(harness)
subprocess.run(['cc', '-std=c17', '-Wall', '-Wextra', '-Werror', '-Wno-unused-parameter',
str(lab / 'callback-test.c'), '-o', str(lab / 'callback-test')], check=True)
subprocess.run([str(lab / 'callback-test')], check=True)
assert 'commits >= 3' in client and "pattern != (commits == 1 ? 'B' : 'A')" in client
assert 'WL_SHM_FORMAT_ARGB8888' in client and 'WL_SHM_FORMAT_XRGB8888' in client
assert 'live_buffers >= 8' in client and 'wl_buffer_listener' in client
assert 'wl_display_roundtrip(display)' in client and 'wl_output_interface, 4' in client
run = (fixture / 'run.py').read_text()
assert "reserve(lab,mode,'mutation')" in run and "reserve(lab,mode,'capture')" in run
assert "['swaymsg','-s',str(sock)" in run and "signal.setitimer(signal.ITIMER_REAL,150)" in run
assert "if instrumented:env['OPT_TRACE']='1'" in run
# Both private controls have every same unconditional borrowed read and old trace.
trace_header = (lab / 'scenefx/include/render/opt_probe.h').read_text()
assert 'if (getenv("OPT_TRACE"))' in trace_header
for shader in ('blur1.frag', 'blur2.frag'):
rel = Path('render/fx_renderer/shaders') / shader
assert (lab / 'scene0' / rel).read_bytes() == (lab / 'scenefx' / rel).read_bytes()
command = (lab / 'pristine/sway/commands/blur.c').read_text()
assert 'arrange_container(con)' in command and 'root->all_outputs' in command
passes = []
# Source-derived coordinate support, ideal bilinear positions, no driver claims.
for direction, level, valid in [('down', 0, (800,600)), ('down', 1, (400,300)),
('down', 2, (200,150)), ('up', 2, (100,75)),
('up', 1, (200,150)), ('up', 0, (400,300))]:
written = (800 // (2 ** (level + 1 if direction == 'down' else level)),
600 // (2 ** (level + 1 if direction == 'down' else level)))
mapping = '2*(p+0.5)-0.5 plus offsets +/-5' if direction == 'down' else '(p+0.5)/2-0.5 plus axial +/-2.5 and diagonal +/-1.25'
maximum = [(2 * (v - .5) - .5 + 5) if direction == 'down'
else ((v - .5) / 2 - .5 + 2.5) for v in written]
passes.append({'direction': direction, 'level': level, 'framebuffer': [800,600],
'fresh_input_domain': list(valid), 'written_domain': list(written),
'sampler_mapping': mapping, 'max_unclamped_sample_xy': maximum,
'can_read_beyond_fresh_domain': (direction == 'up') or (direction == 'down' and level > 0),
'possible_value': 'earlier write/allocation content, native content NOT OBSERVED',
'final_influence': 'propagated right/bottom boundary bands; strength predicted by model only'})
source_files = [lab / 'scene0/render/fx_renderer' / p for p in
('fx_pass.c','fx_texture.c','fx_framebuffer.c','fx_offscreen_buffers.c','shaders/blur1.frag','shaders/blur2.frag')]
(results / 'sampling-domain-audit.json').write_text(json.dumps({
'status': 'SOURCE POSSIBILITY; native intermediate content NOT OBSERVED',
'source_sha256': {str(p.relative_to(lab)): hashlib.sha256(p.read_bytes()).hexdigest() for p in source_files},
'passes': passes, 'filter': 'GL_LINEAR', 'wrap': 'GL_CLAMP_TO_EDGE full texture, not reduced domain',
'precision': 'native mediump GLES versus ideal offline bilinear/8-bit rounding',
'clear': 'no full color clear between blur passes in audited render_blur_segments',
'blur_cycle': 'scoped blur flag + arrange_container; dirty every output; natural background commit dirties its output',
'causality': 'not established by source audit'}, indent=2) + '\n')
(results / 'preflight.json').write_text(json.dumps({
'AST': 'PASS', 'XML': 'PASS', 'C17_callback_duplicate_ACK_no_draw': 'PASS',
'SHM_bounds_formats_lifetimes': 'PASS source audit, live proof pending',
'Wayland_connections_protocols': 'PASS source audit and C17 build, live proof pending',
'explicit_command_reservations': 'PASS source audit',
'timeouts_owned_cleanup': 'PASS source audit',
'shader_bytes_pristine_private': 'IDENTICAL',
'trace_toggle': 'presence only; same private code, old traces remain',
'graphical_runs': 0}, indent=2) + '\n')
print('PASS AST/XML/C17 callback, SHM/Wayland/reservation/cleanup audit, immutable shaders, sampling audit')

View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""Build-only reconstruction from fixed archives and immutable source patches.
CONTRACT: no compositor launch, system install or write to older fixtures.
INVARIANT: consumed LABs cannot be rebuilt, and prepared identities are hashed.
"""
import hashlib
import io
import json
import os
import subprocess
import sys
import tarfile
import tempfile
from pathlib import Path
fixture = Path(__file__).resolve().parent
repo = fixture.parents[2]
lab = Path(sys.argv[1]).resolve() if len(sys.argv) == 2 else Path(
tempfile.mkdtemp(prefix='swayfx-mosaic-optimized-edge-', dir=Path.home()))
if (lab / 'runs').exists() and any((lab / 'runs').iterdir()):
raise RuntimeError('never rebuild a consumed LAB')
(lab / 'results').mkdir(parents=True, exist_ok=True)
(lab / 'runs').mkdir(exist_ok=True)
print('LAB=' + str(lab), flush=True)
def run(args, **kwargs):
with (lab / 'build.log').open('ab') as out:
subprocess.run(args, check=True, stdout=out, stderr=subprocess.STDOUT, **kwargs)
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def archive(git, revision, name):
target = lab / name
if target.exists():
raise RuntimeError('existing source tree; continue Meson explicitly, never overwrite')
target.mkdir()
data = subprocess.check_output(['git', '-C', str(git), 'archive', revision])
tarfile.open(fileobj=io.BytesIO(data)).extractall(target, filter='data')
scene_revision = '37ccd723bef49e6891156ffafce8f549f01446cc'
mosaic_revision = 'cd550382b311efdbfe3ed892f4e1d281d852e754'
pristine_revision = 'fd71a6bdc061bd633b488ae7b83e8a6981d22f86'
run(['git', 'clone', '--quiet', '--no-checkout', 'https://github.com/wlrfx/scenefx.git', str(lab / 'scenefx-git')])
for name in ('scene0', 'scenefx'):
archive(lab / 'scenefx-git', scene_revision, name)
archive(repo, pristine_revision, 'pristine')
archive(repo, mosaic_revision, 'mosaic')
chains = {
'scenefx': ['step20c-valid-grab/scenefx-popup-plus-base.patch',
'step20c-temporal-damage/scenefx-timing.patch',
'step20c-optimized-blur-cache/scenefx-cache-trace.patch'],
'mosaic': ['step20c-valid-grab/mosaic-popup-plus-base.patch',
'step20c-valid-grab/hidden-guard.patch',
'step20c-temporal-damage/mosaic-output-timing.patch',
'step20c-presentation-identity/presentation-events.patch',
'step20c-optimized-blur-cache/sway-source-trace.patch']}
patches = []
(lab / 'patches').mkdir()
for tree, chain in chains.items():
for name in chain:
source = repo / 'tooling/probes' / name
target = lab / 'patches' / source.name
target.write_bytes(source.read_bytes())
patches.append({'path': name, 'sha256': sha(target), 'target': tree})
run(['git', 'apply', '--check', str(target)], cwd=lab / tree)
run(['git', 'apply', str(target)], cwd=lab / tree)
for source, build in [('scene0', 'scene0-build'), ('scenefx', 'scenefx-build')]:
run(['meson', 'setup', str(lab / build), str(lab / source), '-Dexamples=false', '-Dwerror=false'])
run(['meson', 'compile', '-C', str(lab / build), '-j', '8'])
for source, build, scene in [('pristine', 'pristine-build', 'scene0-build'),
('mosaic', 'mosaic-build', 'scenefx-build')]:
env = {**os.environ, 'PKG_CONFIG_PATH': str(lab / scene / 'meson-uninstalled')}
args = ['meson', 'setup', str(lab / build), str(lab / source),
'-Ddefault-wallpaper=false', '-Dswaybar=false', '-Dswaynag=false',
'-Dman-pages=disabled', '-Dsd-bus-provider=libsystemd', '-Dwerror=false']
if source == 'mosaic':
args.append('-Dmosaic-package=true')
run(args, env=env)
run(['meson', 'compile', '-C', str(lab / build), '-j', '8'], env=env)
protocols = repo / 'tooling/probes/step20c-optimized-blur-cache'
for stem, name, digest in [
('layer-shell', 'wlr-layer-shell-unstable-v1.xml', '996b8c66e1c2276c443336d4cb174634e8e5948823779042f244930525b79cc2'),
('xdg-shell', 'xdg-shell.xml', '7ba7f9c8473deee674cb1f154a18abd0bb0cc072604fc055b0c15e459fc4c7df')]:
xml = protocols / name
assert sha(xml) == digest
target = lab / name
target.write_bytes(xml.read_bytes())
run(['wayland-scanner', 'client-header', str(target), str(lab / (stem + '-client.h'))])
run(['wayland-scanner', 'private-code', str(target), str(lab / (stem + '-protocol.c'))])
(lab / 'client-source.c').write_bytes((fixture / 'clients.c').read_bytes())
flags = subprocess.check_output(['pkg-config', '--cflags', '--libs', 'wayland-client'], text=True).split()
run(['cc', '-std=c17', '-Wall', '-Wextra', '-Werror', '-Wno-unused-parameter',
'-I' + str(lab), str(lab / 'client-source.c'), str(lab / 'xdg-shell-protocol.c'),
str(lab / 'layer-shell-protocol.c'), '-o', str(lab / 'clients'), *flags])
run(['meson', 'test', '-C', str(lab / 'mosaic-build'), 'mosaic-model', '--print-errorlogs'])
versions = {package: subprocess.check_output(['pkg-config', '--modversion', package], text=True).strip()
for package in ('wlroots-0.20', 'scenefx-0.5', 'wayland-client', 'wayland-protocols')}
binaries = {str(p.relative_to(lab)): sha(p) for p in [
lab / 'clients', lab / 'mosaic-build/sway/swayfx-mosaic', lab / 'pristine-build/sway/sway',
lab / 'scenefx-build/libscenefx-0.5.so', lab / 'scene0-build/libscenefx-0.5.so']}
for rel in ('mosaic-build/sway/swayfx-mosaic', 'pristine-build/sway/sway'):
run(['ldd', str(lab / rel)])
(lab / 'results/build-identities.json').write_text(json.dumps({
'revisions': {'SceneFX': scene_revision, 'Mosaic': mosaic_revision, 'pristine': pristine_revision},
'packages': versions, 'patches': patches, 'binaries': binaries,
'R2_R3': 'identical binary/library/client/config; only OPT_TRACE presence differs'}, indent=2) + '\n')
print('BUILD_PASS LAB=' + str(lab), flush=True)

View file

@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""One reserved, deadline-limited run per mode; only owned headless processes."""
import argparse,json,os,re,shutil,signal,subprocess,time
from pathlib import Path
import numpy as np
from PIL import Image
from budget import reserve,DEFAULT
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('mode',choices=['R0','R1','R2','R3']);a=p.parse_args()
lab=a.lab.resolve();mode=a.mode
if not (lab/'clients').is_file():p.error('prepared LAB required')
if not (lab/'results/preflight.json').is_file():p.error('offline preflight required')
import hashlib
identities=json.loads((lab/'results/build-identities.json').read_text())
for rel,digest in identities['binaries'].items():
if hashlib.sha256((lab/rel).read_bytes()).hexdigest()!=digest:p.error('changed prepared binary/library')
if (lab/'client-source.c').read_bytes()!=Path(__file__).with_name('clients.c').read_bytes():p.error('changed compiled client source')
reserve(lab,mode,'run')
run=lab/'runs'/mode;run.mkdir(mode=0o700)
runtime=run/'runtime';runtime.mkdir(mode=0o700)
(run/'fixture-source.py').write_text(Path(__file__).read_text())
shutil.copy2(Path(__file__).with_name('clients.c'),run/'client-source.c')
(run/'config').write_text('''output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0
output HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0
workspace 1 output HEADLESS-1
workspace 2 output HEADLESS-2
focus_follows_mouse no
mouse_warping none
default_border none
font monospace 8
shadows disable
blur disable
blur_xray disable
blur_radius 5
blur_passes 3
blur_noise 0
blur_brightness 1
blur_contrast 1
blur_saturation 1
corner_radius 0
animation_duration_ms 0
''')
env={k:v for k,v in os.environ.items() if k not in ['SWAYSOCK','I3SOCK','WAYLAND_DISPLAY','LD_LIBRARY_PATH','OPT_TRACE','SAVED_TRACE','SAVED_HIT_TRACE','FX_TRACE']}
env.update(XDG_RUNTIME_DIR=str(runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2',WLR_RENDERER='gles2')
private=mode!='R0'
mosaic=mode in ('R2','R3')
instrumented=mode=='R3'
binary=lab/('mosaic-build/sway/swayfx-mosaic' if private else 'pristine-build/sway/sway')
env['LD_LIBRARY_PATH']=str(lab/('scenefx-build' if private else 'scene0-build'))
if instrumented:env['OPT_TRACE']='1'
geometry_baseline={}
children=[];files=[];comp=None;sock=None;failure=None;started=time.monotonic();captures=[]
def event(kind,**data):
with (run/'events.jsonl').open('a') as out:out.write(json.dumps({'kind':kind,'ns':time.monotonic_ns(),**data})+'\n')
def guard():
if time.monotonic()-started>150:raise TimeoutError('run deadline')
for q in run.glob('*.log'):
if q.stat().st_size>25_000_000:raise RuntimeError('bounded log exceeded')
def spawn(argv,name,stdin=False):
guard();f=(run/name).open('w');files.append(f)
child=subprocess.Popen(argv,env=env,stdin=subprocess.PIPE if stdin else subprocess.DEVNULL,stdout=f,stderr=subprocess.STDOUT,text=stdin,start_new_session=True);children.append(child)
event('spawn',argv=argv,pid=child.pid);return child
def wait(fn,seconds=8):
end=time.monotonic()+seconds
while time.monotonic()<end:
guard();result=fn()
if result:return result
if comp and comp.poll() is not None:raise RuntimeError('compositor exited')
time.sleep(.025)
raise TimeoutError('fixture wait')
def ipc(*args):
guard();r=subprocess.run(['swaymsg','-s',str(sock),*args],env=env,capture_output=True,text=True,timeout=5,check=True)
data=json.loads(r.stdout);event('ipc',args=args,data=data);return data
def command(s):
r=ipc(s)
if not r or not all(v.get('success') for v in r):raise RuntimeError('IPC rejected '+s)
def nodes(n):
yield n
for c in n.get('nodes',[])+n.get('floating_nodes',[]):yield from nodes(c)
def find(app):return next((n for n in nodes(ipc('-t','get_tree')) if n.get('app_id')==app),None)
def log(name):return (run/name).read_text()
def snapshot(tag,only_h2=False,only_h1=False):
outs=ipc('-t','get_outputs');expected={'HEADLESS-1':(-800,0,800,600),'HEADLESS-2':(0,0,800,600)}
if log('background.log').count('CLIENT_COMMIT ')!=json.loads(DEFAULT.read_text())['modes'][mode]['mutation']:raise RuntimeError('unreserved background image')
actual={o['name']:tuple(o['rect'][k] for k in ['x','y','width','height']) for o in outs if o['active']}
if actual!=expected or any(o['scale']!=1 or o['transform']!='normal' for o in outs if o['active']):raise RuntimeError('output geometry gate')
(run/(tag+'-outputs.json')).write_text(json.dumps(outs,indent=2)+'\n')
tree=ipc('-t','get_tree')
(run/(tag+'-tree.json')).write_text(json.dumps(tree,indent=2)+'\n')
geometry={n['app_id']:{k:n.get(k) for k in ('rect','window_rect','deco_rect','geometry','opacity')} for n in nodes(tree) if n.get('app_id')}
if tag=='P0-FX0':geometry_baseline.update(geometry)
if geometry!=geometry_baseline:raise RuntimeError('applied client geometry/opacity changed')
event('graphic_state',tag=tag,geometry=geometry)
for name in (['HEADLESS-2'] if only_h2 else ['HEADLESS-1'] if only_h1 else ['HEADLESS-1','HEADLESS-2']):
reserve(lab,mode,'capture');q=run/(tag+'-'+name+'.png');before=time.monotonic_ns()
subprocess.run(['grim','-s','1','-o',name,str(q)],env=env,timeout=5,check=True,stdout=subprocess.DEVNULL)
if Image.open(q).size!=(800,600):raise RuntimeError('capture size gate')
record={'tag':tag,'output':name,'path':q.name,'before_ns':before,'after_ns':time.monotonic_ns()};captures.append(record);event('capture',**record)
def image(tag,out='HEADLESS-1'):return np.array(Image.open(run/(tag+'-'+out+'.png')).convert('RGB'))
def native_gate():
text=log('compositor.log')
return 'OPT_BLUR_USE' in text and 'OPT_REGEN_RESULT' in text and any('OPT_REGEN_RESULT' in x and 'output=HEADLESS-1' in x and 'result=1' in x for x in text.splitlines()) and any('OPT_BLUR_USE' in x and 'output=HEADLESS-1' in x and 'optimized=1' in x and 'chosen=(nil)' not in x for x in text.splitlines())
def mutate(pattern):
reserve(lab,mode,'mutation');before=log('background.log').count('CLIENT_COMMIT ');event('mutation_reserved',pattern=pattern)
background.stdin.write(pattern+'\n');background.stdin.flush()
wait(lambda:log('background.log').count('CLIENT_COMMIT ')>before)
count=log('background.log').count('CLIENT_COMMIT ')
if count!=before+1:raise RuntimeError('unexpected background commits')
event('mutation_received_client',pattern=pattern,count=count);time.sleep(.35)
def alarm(s,f):raise TimeoutError('150 second watchdog')
signal.signal(signal.SIGALRM,alarm);signal.setitimer(signal.ITIMER_REAL,150)
try:
f=(run/'compositor.log').open('w');files.append(f)
comp=subprocess.Popen([str(binary),'-d','-c',str(run/'config')],env=env,stdout=f,stderr=subprocess.STDOUT,start_new_session=True)
def sockets():
s=next(iter(runtime.glob('sway-ipc.*.sock')),None);w=next((q for q in runtime.glob('wayland-*') if q.is_socket()),None)
return (s,w) if s and w else None
sock,way=wait(sockets);env.update(SWAYSOCK=str(sock),WAYLAND_DISPLAY=way.name)
(run/'library-maps.txt').write_text(Path(f'/proc/{comp.pid}/maps').read_text())
event('renderer_start',pid=comp.pid,binary=str(binary),library=env['LD_LIBRARY_PATH'],OPT_TRACE=env.get('OPT_TRACE'),layout='mosaic' if mosaic else 'splith')
background=spawn([str(lab/'clients'),'background'],'background.log',True)
wait(lambda:'BACKGROUND_CONFIGURED ' in log('background.log'))
if log('background.log').count('CLIENT_COMMIT ')!=0:raise RuntimeError('unreserved configure image')
mutate('A')
command('workspace 1');command('layout mosaic' if mosaic else 'layout splith')
filler=spawn([str(lab/'clients'),'filler'],'filler.log',True);wait(lambda:find('org.mosaic.BlurFiller'))
glass=spawn([str(lab/'clients'),'glass'],'glass.log',True);wait(lambda:find('org.mosaic.BlurGlass'))
command('workspace 2');reference=spawn([str(lab/'clients'),'reference'],'reference.log',True);wait(lambda:find('org.mosaic.StaticReference'))
command('[app_id="org.mosaic.BlurGlass"] focus');time.sleep(.5)
if mosaic:
tree=ipc('-t','get_tree');ws=next(n for n in nodes(tree) if n.get('type')=='workspace' and n.get('name')=='1');owner=next(n for n in nodes(ws) if n.get('app_id')=='org.mosaic.BlurGlass')
current=ws['mosaic']['rows'][0]['scroll_x'];left=owner['rect']['x']+owner['window_rect']['x']-current
delta=int(left+owner['window_rect']['width']//2)
reserve(lab,mode,'calibration');event('camera_calibration',current=current,logical=owner['rect'],window=owner['window_rect'],delta=delta)
command('layout mosaic scroll '+str(delta));time.sleep(.35)
if log('background.log').count('CLIENT_COMMIT ')!=1:raise RuntimeError('unexpected initial background image count')
snapshot('P0-FX0');time.sleep(.75);snapshot('P0-reference',True)
if not np.array_equal(image('P0-FX0','HEADLESS-2'),image('P0-reference','HEADLESS-2')):raise RuntimeError('H2 preflight instability')
# Source A must be visible outside glass before interpretation of any cache.
source=image('P0-FX0')[100:500,20:160]
colors={tuple(x) for x in source.reshape(-1,3)}
if not {(245,245,245),(15,25,45)}<=colors:raise RuntimeError('unblurred source gate')
command('[app_id="org.mosaic.BlurGlass"] blur enable');time.sleep(.35);snapshot('P0-FX2')
roi0=image('P0-FX0')[100:500,520:720].astype(float);roi2=image('P0-FX2')[100:500,520:720].astype(float)
adjacent0=np.abs(np.diff(roi0,axis=1)).mean();adjacent2=np.abs(np.diff(roi2,axis=1)).mean()
if adjacent0<=5 or adjacent2>=adjacent0*.8 or not np.any(roi0!=roi2):raise RuntimeError('visible filtering gate')
if instrumented and not native_gate():raise RuntimeError('optimized native use gate')
event('P0_gate',source=True,filtering=True,native_optimized=bool(instrumented),adjacent_fx0=adjacent0,adjacent_fx2=adjacent2)
mutate('B');snapshot('P1-B-natural')
reserve(lab,mode,'cycle');event('explicit_cycle_start')
command('[app_id="org.mosaic.BlurGlass"] blur disable');time.sleep(.15)
command('[app_id="org.mosaic.BlurGlass"] blur enable');time.sleep(.35);snapshot('P2-B-explicit')
mutate('A');snapshot('P3-A-natural')
time.sleep(.35);snapshot('P3-confirm',only_h1=True)
if not np.array_equal(image('P3-A-natural'),image('P3-confirm')):raise RuntimeError('final whole-H1 stabilization gate')
event('complete',camera_pair='NOT EXERCISED: forbidden by this campaign')
except Exception as exc:
failure=repr(exc);event('failure',error=failure)
finally:
signal.setitimer(signal.ITIMER_REAL,0)
# WHY: request normal compositor teardown first so cache destructors can log.
# CONTRACT: explicit private socket, then only recorded owned process groups.
if comp and comp.poll() is None and sock:
try:command('exit');comp.wait(timeout=5)
except (subprocess.SubprocessError,RuntimeError,OSError):pass
for c in children+([comp] if comp else []):
if c.poll() is None:
try:os.killpg(c.pid,signal.SIGTERM)
except ProcessLookupError:pass
for c in children+([comp] if comp else []):
try:c.wait(timeout=3)
except subprocess.TimeoutExpired:
os.killpg(c.pid,signal.SIGKILL);c.wait(timeout=3)
for f in files:f.close()
shutil.rmtree(runtime)
(run/'exit-status.json').write_text(json.dumps({'failure':failure,'duration':time.monotonic()-started,'compositor':comp.returncode if comp else None,'children':[{'args':c.args,'pid':c.pid,'code':c.returncode} for c in children],'runtime_removed':not runtime.exists(),'captures':captures},indent=2)+'\n')
shutil.copy2(DEFAULT,lab/'results/campaign-budget.json')
event('cleanup',runtime_removed=not runtime.exists())
print(mode,'FAILURE='+str(failure),'RUN='+str(run),flush=True)
if failure:raise SystemExit(1)

View file

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""Seal retained source and evidence inventories once, without overwriting.
CONTRACT: raw observations and completed source fixtures precede this seal.
INVARIANT: existing manifests are refused; verification never regenerates them.
"""
import hashlib
import sys
from pathlib import Path
lab=Path(sys.argv[1]).resolve()
fixture=Path(__file__).resolve().parent
out=lab/'results'
if any((out/name).exists() for name in ('source-sha256.txt','evidence-sha256.txt')):
raise RuntimeError('sealed inventories already exist; verify, never overwrite')
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
sources=set(p for p in fixture.iterdir() if p.is_file())
sources.add(fixture.parent/'step20c-optimized-blur-cache/edge-model.py')
for name in ('scene0','scenefx','mosaic','pristine'):
sources.update(p for p in (lab/name).rglob('*')
if p.is_file() and 'subprojects' not in p.relative_to(lab/name).parts)
sources.update(p for p in (lab/'patches').iterdir() if p.is_file())
sources.update(p for p in lab.iterdir() if p.is_file() and p.suffix in ('.c','.h','.xml'))
sources.add(lab/'protocol-before-results.md')
for run in (lab/'runs').iterdir():
sources.update(run/name for name in ('fixture-source.py','client-source.c','config'))
source_manifest=out/'source-sha256.txt'
source_manifest.write_text(''.join(f'{sha(p)} {p}\n' for p in sorted(sources)))
# Evidence includes all run files, result categories and build/analysis/model/final
# validation logs. Build binaries are identified by digests in build-identities.
evidence=set(p for p in (lab/'runs').rglob('*') if p.is_file())
evidence.update(p for p in out.iterdir() if p.is_file() and not p.name.endswith('-sha256.txt'))
evidence.update(p for p in lab.glob('*.log') if p.is_file())
evidence_manifest=out/'evidence-sha256.txt'
evidence_manifest.write_text(''.join(f'{sha(p)} {p}\n' for p in sorted(evidence)))
for manifest in (source_manifest,evidence_manifest):
for line in manifest.read_text().splitlines():
digest,path=line.split(' ',1)
assert sha(Path(path))==digest,path
print(manifest.name,len(manifest.read_text().splitlines()),sha(manifest))

View file

@ -0,0 +1,55 @@
"""Offline tests use disposable ledgers; never touch campaign reservations."""
import json
import multiprocessing
import tempfile
from pathlib import Path
from budget import reserve, MODES
def denied(call):
try:
call()
except (ValueError, RuntimeError, KeyError, TypeError):
return
raise AssertionError('accepted forbidden reservation')
def contender(root, queue):
try:
reserve(root, 'R0', 'run', Path(root) / 'contention.json')
queue.put('accepted')
except RuntimeError:
queue.put('denied')
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / 'budget.json'
denied(lambda: reserve(tmp, 'R0', 'capture', path))
for mode in MODES:
reserve(tmp, mode, 'run', path)
denied(lambda: reserve(tmp, mode, 'run', path))
for key, count in [('mutation', 3), ('capture', 12), ('cycle', 1),
('calibration', 1 if mode in ('R2', 'R3') else 0)]:
for _ in range(count):
reserve(tmp, mode, key, path)
denied(lambda: reserve(tmp, mode, key, path))
denied(lambda: reserve(tmp + '-another', 'R0', 'run', path))
denied(lambda: reserve(tmp, 'R4', 'run', path))
state = json.loads(path.read_text())
assert sum(c['capture'] for c in state['modes'].values()) == 48
assert sum(c['mutation'] for c in state['modes'].values()) == 12
for bad in ['', '{}', '{"campaign":"wrong"}']:
path.write_text(bad)
denied(lambda: reserve(tmp, 'R0', 'run', path))
for corrupt in [True, -1, 2, 1.0]:
state['modes']['R0']['run'] = corrupt
path.write_text(json.dumps(state))
denied(lambda: reserve(tmp, 'R1', 'capture', path))
with tempfile.TemporaryDirectory() as tmp:
ctx = multiprocessing.get_context('fork')
queue = ctx.Queue()
children = [ctx.Process(target=contender, args=(tmp, queue)) for _ in range(2)]
for child in children:
child.start()
for child in children:
child.join(5)
assert child.exitcode == 0
assert sorted(queue.get(timeout=1) for _ in children) == ['accepted', 'denied']
print('PASS exact exhaustion, no fifth run, cross-LAB, corrupt schema/counters, exclusive lock')