docs(mosaic): preserve static focus input fixture failure evidence

This commit is contained in:
fy59 2026-10-13 07:47:38 +02:00
parent 5e2961d05b
commit fb0293cd85
14 changed files with 957 additions and 0 deletions

View file

@ -4,6 +4,8 @@
### STEP20C experimental evidence
- Preserved static focus/input/standard fixture failure: exact static binary and loaded pair verified; one run stopped before captures/input on an incorrect border-width fixture assertion. INCONCLUSIVE / UNDECIDED; camera and clip traces retained, RGB/clicks/splith NOT TESTED, no retry or production change.
- Approved private static candidate decisions (2026-10-13): separate notices, project maintenance and exact-pair qualification. Current package unchanged; private real `/usr` static build/model/staging/library identity/headless startup pass. Whole-H2 reference fixture failed after a focus change; input and splith return NOT TESTED. PARTIALLY_FEASIBLE / UNDECIDED, preserved historical evidence; the original manifests and fixture bytes remain unchanged.
- Preserved static distribution contract: one hash-qualified static executable staged through private Meson metadata at /usr/bin/swayfx-mosaic, exact-one-file shape PASS without recompilation. Existing scene interposition is characterized, version/security/notice obligations and an unsubmitted upstream API dossier are explicit. PARTIALLY_FEASIBLE, contract decision required; no recipe, packaging contract, production or system change. Historical verdict retained; later user decisions authorize a separate private gate only.

View file

@ -405,3 +405,14 @@ reference equality fails through a focus/decor mismatch; input and splith return
are NOT TESTED after the single run stopped. No rerun or algorithm modification
occurred. Distribution notice closure, named release/security roles and broader
ABI/graphics gates remain open. STEP20C NEXT, multi-output NOT YET SUPPORTED.
The [static focus/input/standard follow-up](design/STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md)
is **EXPERIMENTAL / INCONCLUSIVE**, architecture **UNDECIDED**. One run reused
the exact static binary, verified loaded wlroots/output geometry, calculated
camera and observed the applied clip. A fixture incorrectly required stored
current_border_width=0 under border none; actual IPC retained thickness 2 with
empty decoration and full-size client rectangles. The run stopped before any
PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.

View file

@ -346,3 +346,14 @@ reference equality fails through a focus/decor mismatch; input and splith return
are NOT TESTED after the single run stopped. No rerun or algorithm modification
occurred. Distribution notice closure, named release/security roles and broader
ABI/graphics gates remain open. STEP20C NEXT, multi-output NOT YET SUPPORTED.
The [static focus/input/standard follow-up](design/STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md)
is **EXPERIMENTAL / INCONCLUSIVE**, architecture **UNDECIDED**. One run reused
the exact static binary, verified loaded wlroots/output geometry, calculated
camera and observed the applied clip. A fixture incorrectly required stored
current_border_width=0 under border none; actual IPC retained thickness 2 with
empty decoration and full-size client rectangles. The run stopped before any
PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.

View file

@ -264,3 +264,14 @@ reference equality fails through a focus/decor mismatch; input and splith return
are NOT TESTED after the single run stopped. No rerun or algorithm modification
occurred. Distribution notice closure, named release/security roles and broader
ABI/graphics gates remain open. STEP20C NEXT, multi-output NOT YET SUPPORTED.
The [static focus/input/standard follow-up](design/STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md)
is **EXPERIMENTAL / INCONCLUSIVE**, architecture **UNDECIDED**. One run reused
the exact static binary, verified loaded wlroots/output geometry, calculated
camera and observed the applied clip. A fixture incorrectly required stored
current_border_width=0 under border none; actual IPC retained thickness 2 with
empty decoration and full-size client rectangles. The run stopped before any
PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.

View file

@ -350,3 +350,14 @@ reference equality fails through a focus/decor mismatch; input and splith return
are NOT TESTED after the single run stopped. No rerun or algorithm modification
occurred. Distribution notice closure, named release/security roles and broader
ABI/graphics gates remain open. STEP20C NEXT, multi-output NOT YET SUPPORTED.
The [static focus/input/standard follow-up](design/STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md)
is **EXPERIMENTAL / INCONCLUSIVE**, architecture **UNDECIDED**. One run reused
the exact static binary, verified loaded wlroots/output geometry, calculated
camera and observed the applied clip. A fixture incorrectly required stored
current_border_width=0 under border none; actual IPC retained thickness 2 with
empty decoration and full-size client rectangles. The run stopped before any
PNG, click or splith transition. Those gates are NOT TESTED; no clipping defect
or need for an algorithm change is demonstrated. No retry/build/production
change occurred. A separately authorized corrected-fixture gate is required.
STEP20C stays NEXT; production multi-output remains NOT YET SUPPORTED.

View file

@ -0,0 +1,320 @@
# STEP20C static runtime focus, input and standard-return probe
**EXPERIMENTAL / INCONCLUSIVE. Historical campaign frozen as evidence.**
Phase A published `5e2961d05b7b33252718aabc4dbf787724f79ef0` to both remotes,
with a clean worktree. The original phase B was local and unpublished; the later evidence freeze publishes its sources and findings without replay.
[Architecture](../ARCHITECTURE.md), [UX](../UX.md), [Packaging](../PACKAGING.md)
and [Roadmap](../ROADMAP.md) remain authoritative. STEP20A DONE, STEP20B
EXPERIMENTAL, STEP20C NEXT, STEP20D–G PLANNED; production multi-output
NOT YET SUPPORTED. No production, algorithm, shader, package or UX change.
## Historical evidence and exact candidate
The [previous runtime](STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md) remains
PARTIALLY_FEASIBLE / UNDECIDED. It captured h2-reference.png while workspace 2
was focused, then h2-mosaic.png after workspace 1/client setup and Mosaic.
The full RGB comparison failed by 23,807 pixels, bounding box (0,0,800,600).
Changes outside the unchanged reference interior support focus/title/border
variation. No target colors appeared on H2; 182,214 survived on H1. Neither
cropping borders nor a new successful run can retroactively pass that comparison.
Reuse only the historical staged binary from
`/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt/stage-static/usr/bin/swayfx-mosaic`:
SHA-256 `f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`,
ELF build ID `942db64269640778ee433e5970ba1100bc60f94c`.
Verify original/copy hash, executable regular-file mode, ELF dependencies and
system wlroots SHA-256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
No SceneFX DT_NEEDED or loaded SceneFX shared mapping is allowed. Stop before
launch if identity is unavailable. No SceneFX/Mosaic build or binary alteration.
Historical C17 saved-client and static-reference plus the extent-adapted virtual
pointer are reused from manifest-verified source. Their historical executable
files are not entries of those manifests: rebuild only these clients from the
verified C/generated-protocol sources, record commands and resulting hashes.
No client algorithm changes. Target alternates two opaque colors on configure;
reference remains RGB (48,48,56) across focus/configure changes. Pointer uses
1600×600 logical extent and global-x + 800 normalization.
## Single-run protocol and stop gates
One new private LAB, mode-0700 runtime, explicit private Wayland display and IPC
socket, WLR_BACKENDS=headless. Every swaymsg uses -s. Only owned process groups
are stopped; preserve exit codes and verify no residual process/socket.
A 150-second deadline and bounded logs apply. No pristine run, no popup.
HEADLESS-1=(-800,0,800,600), HEADLESS-2=(0,0,800,600), scale 1, normal.
Create reference on workspace 2, then filler and target on workspace 1. Both
reference and filler have org.mosaic.StaticReference App ID. Therefore do not
use the broad for_window App ID rule: obtain reference con_id while it is the
only reference and apply `[con_id=ID] border none` in private IPC. Check reference
PID, workspace/output, border none and zero border width; filler keeps its normal
border. Keep focus_follows_mouse no. No daily configuration changes.
Activate Mosaic, read real applied owner/tiling traces and IPC scroll. Compute
one signed camera delta from horizontal content offset and configured width,
then reread scene/scroll. The historical +160 is not a constant. No further
camera command or hide/return. Focus must initially remain target/workspace 1.
If later normal focus changes move the camera, retain that observation and stop
any assertion needing unchanged Mosaic geometry; do not correct it.
PI_OWNER gives an applied scene owner, not a buffer. IPC rect/window_rect/deco_rect
and the client's last real COMMIT size are separate evidence. Use the actual H1
painted color rectangle to identify vertical content and pick an interior point.
Correlate measured paint with horizontal scene offset and client width; never
label the old source_box y estimate a native buffer coordinate. PI_SCENE local
clip plus output-parent origin and tiled translation gives global viewport.
After complete Mosaic setup and sole calibration, park pointer at (-790,590),
wait for stabilization, capture H2 twice at least 500 ms apart. Check 800×600,
full-frame reference color, border, focus/workspace and exact whole RGB equality.
Store PNG hashes and snapshots. No ROI, no tolerance. Failure stops before input.
V1: capture H1/H2, retain positive target-color count on H1, zero on H2, and
whole-H2 equality to preflight. Require content crossing x=0 and clip ending
there. Save every differing pixel coordinate/color on failure; stop dependent tests.
V2: choose a point at least 20 pixels inside the actually painted H1 rectangle,
outside decorations and borders. One right press/release; require actual target
BUTTON 273 states 1 then 0 with recorded serials, no reference delivery. A move
alone is never input proof. Preserve available passive scene hit and Wayland logs.
V3: choose an H2 point within the geometrical continuation of the target content
and the full-frame reference interior. One right press/release; require zero
new target BUTTON events and two real reference events. Record focus after click,
park pointer, stabilize and recapture whole H2 with exact equality. Do not infer
PASS from neither client receiving input.
V4: address the target con_id with layout splith (after V3 global focus is H2;
a bare layout command could affect the wrong workspace). Verify workspace identity,
applied splith, target still mapped, applied owner and PI_SCENE clip_valid=0 on
H1 and ordinary H2. Capture H1/H2. Choose a fresh interior target-colored point
from the standard image, inject one click and require actual target press/release.
Park pointer and recapture H2 after focus transition. Require stable H2 and no
residual clip. Do not reuse the Mosaic click point blindly.
## Budget, analysis and classification
Persistent ledger, exclusive flock, atomic durable reservation before run,
each PNG, each click, camera and bounded pointer movement. Failed reservations
remain consumed; no reset/refund or alternate LAB. Offline tests must precede run.
Limits: run 1, pristine 0, SceneFX/Mosaic builds 0, camera adjustment 1, each of
three named clicks 1, PNG 12, pointer moves 8, duration 150 seconds. Expected
PNG count 8. All generated artifacts stay outside Git. Seal source and evidence
manifests once after collecting results, then verify every entry without rewriting.
Keep client damage requests, scene/output damage extents and stabilized PNGs
separate. No claim of complete intermediate frames or native output-buffer/PNG
identity. Root/nested grabs, other geometry, migration, saved buffers/effects,
full semantic ABI, other wlroots pairs, legal notice closure and named release/
security owners are NOT TESTED or remain open. D1–D4 stay approved privately.
FEASIBLE requires every V1–V4 assertion and conformant binary/dependencies,
no crash or blocking defect in this matrix. PARTIALLY_FEASIBLE retains demonstrated
principal properties with unqualified input/return/pixels. INCONCLUSIVE means an
unreliable fixture. BLOCKED requires demonstrated pixel/input leakage, runtime
ABI defect or standard regression. Focus/calibration error alone is not a clipping
failure. Architecture: STATIC_MOSAIC_FOCUS_INPUT_STANDARD_FEASIBLE_FOR_TESTED_MATRIX,
STATIC_MOSAIC_FOCUS_INPUT_STANDARD_REQUIRES_REDESIGN, or UNDECIDED respectively.
## Independent future reconstruction (not authorized in this campaign)
A later campaign can archive pinned Mosaic cd550382b311efdbfe3ed892f4e1d281d852e754,
SceneFX 37ccd723bef49e6891156ffafce8f549f01446cc and wlroots
d783533489e1f75d6886c2ab5c5960090ef268f8, without the old LAB. Apply the ordered
three SceneFX and five Mosaic patches listed in the
[static distribution report](STEP20C_SCENEFX_STATIC_DISTRIBUTION_CONTRACT.md).
Use SceneFX default_library=static/examples=false, its uninstalled pkg-config
provider and Mosaic --prefix=/usr --bindir=bin with mosaic-package=true,
swaybar/swaynag=false, man-pages=disabled, default-wallpaper=false,
sd-bus-provider=libsystemd, werror=false. Build in independent directories,
qualify exact shared wlroots, model, ELF, notices/stage and new authorized
runtime budgets. Preserve toolchain/generated protocols/patch hashes. This is
source reconstruction, not a promise of byte-reproducibility or permission to
substitute another binary in this ticket.
The [fixture](../../tooling/probes/step20c-static-runtime-followup/README.md)
provides the bounded runner, offline tests and evidence collector.
## Completed campaign — retained fixture failure
`SWAYFX_MOSAIC_STATIC_FOCUS_INPUT_STANDARD_PROBE=INCONCLUSIVE`
Architecture: **UNDECIDED**. LAB:
`/home/fy59/swayfx-mosaic-static-followup-nhwejz6u`. One run, 2.661722 seconds.
The unique runtime stopped before the first screenshot, movement or button.
No restart, second LAB, binary/algorithm repair or budget refund occurred.
### Identity, setup and measured state
The copied executable retains SHA-256
`f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`,
ELF build ID `942db64269640778ee433e5970ba1100bc60f94c`, mode 0755.
Readelf and ldd -r pass; process maps resolve the qualified system wlroots
0.20.2 binary with SHA-256
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
No shared SceneFX or historical LAB dependency appears in the runtime mappings.
Meson/model evidence is reused from A; no new compositor/library build.
Three C17 client-only builds from manifest-verified sources passed syntax and
compilation and produced the same bytes as the historical executables:
| Client | SHA-256 |
| --- | --- |
| saved-client | `3d5d0c5e446b995c210006a5bafe0d86dd5f32a228e1b7137d12af6ec9e217da` |
| static-reference | `11166ea1bb7cb3bb08f10ab93e091c5c5ea8687649b5fb0705288c7ca91680e8` |
| pointer-daemon | `76ad0f093f2b7a97359920b7bd456cfa93a1581c73875547a86eb911f2ed2d7a` |
These hashes are newly recorded; the historical executable files themselves
were not sealed. Historical C/generated protocol inputs were sealed and verified.
The new retained source/command/binary chain supplies explicit provenance.
Both outputs matched the requested negative-origin 800×600/scale-1/normal
matrix. Private configuration retained focus_follows_mouse no. The command
`[con_id=7] border none` succeeded for reference PID 1674352 on workspace 2
(ID 5), H2. Filler ID 8, with the same App ID, kept normal border and width 2.
Target ID 9/PID 1674361 on workspace 1 (ID 6), H1 remained focused; reference
was unfocused. No broad App ID rule affected the filler.
Applied camera read 0 before calibration. The target's horizontal content offset
was -158 with configured width 636; desired left -318 gave signed delta +160.
IPC and scene both read 160 afterward. Applied owner scene became (-320,0),
size 640×600. IPC window offset x=2 and actual client COMMIT size 636×573
support horizontal content continuation [-318,318), crossing x=0. They do not
prove native buffer y or actually visible pixels without a capture.
PI_SCENE records tile-local (-160,0), clip (160,0,800,600), translated through
H1 origin (-800,0) to global clip (-800,0,800,600). H2 records clip_valid=0.
This proves the observed applied clip state, not the unperformed pixel/input gates.
### Exact stopping assertion and source diagnosis
The executed runner required both border none and current_border_width==0.
Reference IPC instead reports:
```json
{"border":"none","current_border_width":2,
"rect":{"x":0,"y":0,"width":800,"height":600},
"deco_rect":{"x":0,"y":0,"width":0,"height":0},
"window_rect":{"x":0,"y":0,"width":800,"height":600}}
```
This failed the stored-width predicate before writing owner-geometry.json or
reference-preflight.json and before any capture reservation. Offline collection
reconstructs the actual owner/reference observations from retained IPC/traces
and explicitly labels all unexecuted results NOT TESTED.
Source diagnosis: `sway/commands/border.c:cmd_border` calls set_border(B_NONE);
it changes border_thickness only with a second argument. `sway/ipc-json.c`
publishes current.border_thickness unconditionally. `sway/desktop/transaction.c`
sets the effective border width/top to zero for B_NONE. Therefore stored width 2
is compatible with no border. The runner confused stored configuration with
effective decoration geometry. No rendered border or Mosaic leak is demonstrated.
The initial protocol's zero-width expectation above is retained as the failed
hypothesis; it is not a new product contract. The exact executed runner remains
unchanged, and no corrected runner is represented as executed.
### Matrix, damage and cleanup
| Gate | Result |
| --- | --- |
| Binary, loader, exact wlroots | PASS |
| Output geometry, client mapping, focus, targeted border mode | PASS IPC/source evidence |
| Calculated camera and applied clip | OBSERVED; no pixel qualification |
| Two H2 reference captures / whole RGB equality | NOT TESTED; zero PNG |
| H1 retained pixels / H2 forbidden colors | NOT TESTED in B |
| Visible Mosaic button | NOT TESTED |
| Invisible Mosaic non-delivery / reference H2 delivery | NOT TESTED |
| H2 focus-transition RGB stability | NOT TESTED |
| splith return / clip removal / standard button | NOT TESTED |
| Root/nested popup or grab | NOT TESTED, deliberately outside scope |
| Damage | Setup/client/scene/output extents retained; no PNG correlation or intermediate-frame coverage |
| Crash/protocol error | No crash; each ordinary client exited 0 with error=0 |
| Cleanup | PASS; compositor exit 0, three clients exit 0, owned pointer terminated -15; PIDs and private runtime absent |
The owned runtime was `/tmp/msr-330t08c8`, removed after all owned process groups
were reaped. No daily IPC, installation, system dependency replacement, package,
production patch/source, UX, dotfile, QuickShell, Codex or STEP20D change occurred.
Budget: SceneFX build 0, Mosaic build 0, binary modification 0, run 1/1, pristine
0, camera 1/1, captures 0/12, each named click 0/1, moves 0/8. The consumed run
prevents further execution despite unused PNG/click capacity. The persistent
owner registration and ledger are retained and never reset.
The future smallest justified gate is a separately authorized single run after
correcting the fixture's border-semantic predicate and testing it offline against
this retained border-none/width-2 snapshot. Keep the same focus/RGB/input/splith
objectives and exact candidate identity if still available. No SceneFX or clip
algorithm modification is justified by this fixture failure. This ticket does
not close the static runtime continuity gate or authorize STEP20C integration.
Q1–Q2 reference stability and full RGB: NOT TESTED. Q3 forbidden pixels: NOT
TESTED in B; A's zero target colors remain historical only. Q4–Q9 button delivery,
invisible input, focus RGB, clip removal and standard input: NOT TESTED. Q10
static/shared behavioral equivalence: not established by this run. Q11 algorithm
change justified: no demonstrated runtime anomaly. Q12 wider geometry, lifecycle,
full damage/effects/saved buffers, applied-generation and exhaustive ABI remain
unqualified, as do this run's intended principal gates. Q13 preparation for
integration: the static build/load candidate is established, but the requested
continuity evidence remains open. Q14 next technical gate: correct the fixture
and obtain a newly authorized bounded RGB/input/standard-return observation.
D1–D4 persist; STEP20C NEXT, production multi-output NOT YET SUPPORTED.
## Validation and integrity receipt
Python AST, shell/recipe syntax, concurrent/exhausted persistent-ledger tests,
exact-RGB and actual-button offline tests, retained border snapshot/source
semantics, local Markdown links, source-only fixture, protected source/system
hashes, unchanged executed runner and owned-process/socket cleanup: PASS.
Runtime scenario: FAIL at fixture assertion, scientific verdict INCONCLUSIVE.
Meson/model: historical qualified results reused, NOT RUN anew. Standard test
suite/headless campaign: NOT RUN, would exceed the authorized runtime scope.
No subagent review was requested; the parent performed the bounded source,
contract, provenance and final evidence audit. No independent review is claimed.
188 TEMPORAL_DAMAGE trace lines are retained from setup. They establish no
post-click, standard-return or every-frame damage result.
B source manifest: 24 entries, SHA-256
`5f9ab2b6d1ca23ef7743623d1e544237c8b70b62fe6de8164a46f1342e5a8ee2`.
B evidence manifest: 46 entries, SHA-256
`41cc5e3d305d89e83314723e67d7116c36d1503dd35647894bc898c49343e1a5`.
Both were created once and every entry verified. Executed fixture and later
collector/final fixture are distinct source directories. The report and final
Git diff are outside these inventories to avoid circular digests. A's original
manifests still verify without rewriting or snapshot remapping.
Final tracked B delta: this report plus Architecture, Known Limitations,
Roadmap, documentation index and Changelog; source-only follow-up fixture added.
No AGENTS, UX, Packaging, PKGBUILD, cumulative patch or production source change
in B. Local main and both remotes remain the A publication commit
`5e2961d05b7b33252718aabc4dbf787724f79ef0`; B remains unstaged and uncommitted.
## Static focus fixture evidence freeze — 2026-10-13
This new freeze task starts at main
`5e2961d05b7b33252718aabc4dbf787724f79ef0`. Direct `git ls-remote`
queries to canonical Forgejo and GitHub both returned that exact commit.
The expected local documentation and source-only fixture were audited in full.
No foreign change was found. This preservation commit does not alter the
historical executed runner, either historical manifest, or any raw observation.
The earlier LOCAL/UNCOMMITTED statements describe the original campaign's state,
not the publication state of this frozen evidence.
All 24 source and 46 evidence entries pass their original SHA-256 inventories;
the manifest digests above are unchanged. The Python files match the sealed
final-fixture byte for byte, and run.py matches the actually executed fixture.
Python AST, offline RGB/button tests, concurrent/exhausted ledger tests,
read-only collector verification, shell recipe syntax, local Markdown links
and git diff --check pass. Production source, package recipe and cumulative
patch are unchanged. No graphical test, compositor/library build or historical
budget reservation is performed by this freeze. Run 1/1 and camera 1/1 remain
consumed; unused captures/clicks/moves cannot authorize another historical run.
The source audit also reads the pristine pinned SwayFX border command:
`fd71a6bdc061bd633b488ae7b83e8a6981d22f86:sway/commands/border.c`.
Its B_NONE handling and optional second-argument thickness assignment match
the retained candidate semantics. IPC thickness is stored metadata; effective
border/decor geometry is separate. The failure remains INCONCLUSIVE / UNDECIDED.
The separately authorized RGB/input/splith ticket must supply a corrected pure
predicate, replay actual IPC and audit the entire preflight before its own run.
`SWAYFX_MOSAIC_STATIC_FOCUS_FIXTURE_FREEZE=PASS`

View file

@ -0,0 +1,76 @@
# Static focus/input/standard-return fixture
EXPERIMENTAL / FROZEN HISTORICAL FAILURE. No production integration.
Read the [protocol](../../../docs/design/STEP20C_STATIC_RUNTIME_FOCUS_INPUT_STANDARD_PROBE.md).
The previous static campaign remains sealed and is never replayed.
This fixture creates one campaign-owned LAB outside Git, copies the hash-qualified
static compositor unchanged and rebuilds only three historical C17 clients from
manifest-verified source/generated protocols. The old client executables were
not manifest entries, so their observed hashes are contextual only. An exclusive
`~/swayfx-mosaic-static-followup-v1.owner` file binds the campaign to one LAB.
Never delete/reset it, its ledger, or replace the LAB to retry.
Offline checks before launch:
```sh
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime-followup/test-budget.py
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime-followup/test-offline.py
```
Historical commands, preserved for provenance only — DO NOT execute prepare.py
or run.py again; the historical budget is exhausted:
```sh
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime-followup/prepare.py
# Use the printed LAB path; execute the retained exact fixture copy there.
PYTHONDONTWRITEBYTECODE=1 python "$LAB/fixture/run.py" "$LAB"
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime-followup/collect.py "$LAB"
```
The flock ledger reserves run, PNG, named click, camera and pointer moves before
execution. No refund/reset. Runtime is private mode 0700; all swaymsg use explicit
-s, child environments discard inherited desktop sockets. Only owned process
groups are stopped. The deadline is 150 seconds, per-log bound 64 MiB. Sources,
raw protocol/client/scene logs, PNGs, IPC snapshots, timestamps, serials, exit
codes and result manifests stay external. The collector creates manifests once;
verification is read-only with `collect.py "$LAB" --verify`.
Reference border none is applied to its measured con_id, never to the shared
App ID (which also identifies the H1 filler). H2 full RGB must be identical
before input; no crop/tolerance. Clicks use measured painted interiors. The
standard-return command explicitly targets the H1 con_id after H2 gains focus.
Popup tests and native output-buffer/PNG identity remain NOT TESTED.
The protocol describes independent source reconstruction for a future authorized
campaign if historical binaries disappear. Do not silently rebuild or substitute
the compositor in this campaign. Historical LAB paths identify retained evidence,
not a permanent production dependency.
## Actual execution — no retry permitted
LAB `/home/fy59/swayfx-mosaic-static-followup-nhwejz6u`, run consumed.
Verdict INCONCLUSIVE, architecture UNDECIDED. The runner stopped before the
first PNG because it incorrectly required current_border_width=0 after border
none. Actual IPC had border none, stored width 2, empty deco_rect and 800×600
window_rect. The filler retained normal decorations; target/workspace 1 were
focused. The source confirms that border none changes the border enum, not
stored thickness. This is a fixture defect, not an observed rendered border.
The executed runner is intentionally unchanged, including the erroneous predicate.
It is historical experimental source, not a validated replay harness. A future
authorized campaign must correct that semantic check and add its regression
before a separately budgeted run; this campaign may not be resumed or repeated.
No PNG, click or splith command was executed. Camera 0→160 and applied clip were
observed. All principal RGB/input/standard gates remain NOT TESTED. The collector
records those omissions explicitly and verifies owned-process/socket cleanup.
## Freeze receipt
The preservation task verified every entry of the original 24-source and
46-evidence manifests without rewriting them. All Python files match the
sealed final-fixture; run.py matches the historical executed fixture, including
the invalid width assertion. Offline tests and --verify remain read-only
checks of this exhausted campaign. Publication preserves INCONCLUSIVE /
UNDECIDED, zero PNG and zero clicks; it does not convert the fixture into a
validated runner. A new campaign uses a separate directory and budget.

View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Offline collection of the single stopped run; never creates runtime evidence.
WHY: an IPC metadata predicate is not a pixel or input failure.
CONTRACT: retain the executed runner unchanged and label unexercised cells.
INVARIANT: seal once; --verify never rewrites results or manifests.
"""
import argparse, json, re, shutil
from pathlib import Path
from support import save, sha
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('--verify',action='store_true');a=p.parse_args();lab=a.lab.resolve()
assert (Path.home()/'swayfx-mosaic-static-followup-v1.owner').read_text().strip()==str(lab)
results=lab/'results'
if not a.verify:
assert not (results/'evidence-sha256.txt').exists()
runtime=json.loads((results/'runtime.json').read_text())
snap=json.loads((results/'preflight-snapshot.json').read_text())
def nodes(n):
yield n
for c in n.get('nodes',[])+n.get('floating_nodes',[]):yield from nodes(c)
ns=list(nodes(snap['tree']))
ref=next(n for n in ns if n.get('app_id')=='org.mosaic.StaticReference' and n['rect']['x']==0)
target=next(n for n in ns if n.get('app_id')=='org.mosaic.SavedBufferProbe')
filler=next(n for n in ns if n.get('app_id')=='org.mosaic.StaticReference' and n['id']!=ref['id'])
assert ref['border']=='none' and ref['current_border_width']==2
assert ref['deco_rect']==dict(x=0,y=0,width=0,height=0)
assert ref['window_rect']==dict(x=0,y=0,width=800,height=600)
assert filler['border']=='normal' and target['focused'] and not ref['focused']
assert runtime['status']=='INCONCLUSIVE' and runtime['failure']=='AssertionError: '
budget=json.loads((results/'budget.json').read_text())
assert budget['used']==dict(run=1,capture=0,camera=1,visible_click=0,forbidden_click=0,standard_click=0,move=0)
assert not list((lab/'run').glob('*.png')) and runtime['duration_seconds']<150
for name in ['target','reference','filler']:
text=(lab/'run'/(name+'.log')).read_text()
assert not any(s.startswith('BUTTON ') for s in text.splitlines())
assert 'error=0' in text
for rec in runtime['process_exit_codes']:assert not Path('/proc',str(rec['pid'])).exists()
assert not Path(runtime['runtime_removed']).exists()
compositor=(lab/'run/compositor.log').read_text()
owner=[s for s in compositor.splitlines() if 'PI_OWNER output=HEADLESS-1 ' in s and f' id={target["id"]} ' in s][-1]
clips=[s for s in compositor.splitlines() if 'PI_SCENE output=' in s]
save(lab,'fixture-incident',{'status':'CONFIRMED FIXTURE ERROR','failed_predicate':'reference current_border_width == 0','actual':ref,'filler':filler,'reason':'border none sets enum B_NONE without resetting stored thickness; IPC reports stored thickness even when rendered border is disabled','source_sites':['sway/commands/border.c:cmd_border','sway/ipc-json.c:ipc_json_describe_container','sway/desktop/transaction.c:arrange_container'],'runner_modified_after_run':False,'rerun':False})
save(lab,'owner-geometry',{'status':'OBSERVED scene and IPC only','target':target,'scene_trace':owner,'client_commits':[s for s in (lab/'run/target.log').read_text().splitlines() if s.startswith('COMMIT ')],'painted_content':'NOT TESTED; no PNG','native_buffer_rectangle':'NOT ESTABLISHED'})
save(lab,'clip-observations',{'status':'OBSERVED applied traces only','traces':clips,'pixel_validation':'NOT TESTED'})
for name in ['reference-preflight','reference-pixel-analysis','permitted-pixels','forbidden-pixels','target-input','reference-input','standard-return']:
save(lab,name,{'status':'NOT TESTED','reason':'run stopped at incorrect stored-border-width assertion before first capture or injection; no retry'})
matrix={'binary_identity':'PASS','loaded_libraries':'PASS','output_geometry':'PASS','reference_border_mode':'PASS IPC none; stored thickness 2','reference_visible_pixels':'NOT TESTED','initial_focus':'PASS target/workspace 1','camera':'PASS calculated +160, applied 0 to 160','applied_clip':'OBSERVED global [-800,0,800,600]','H2_preflight':'NOT TESTED','H1_pixels':'NOT TESTED','H2_pixels':'NOT TESTED','whole_H2_RGB':'NOT TESTED','visible_click':'NOT TESTED','forbidden_click':'NOT TESTED','reference_delivery':'NOT TESTED','focus_RGB_stability':'NOT TESTED','splith_return':'NOT TESTED','clip_removal':'NOT TESTED','standard_click':'NOT TESTED','popups':'NOT TESTED','damage':'available setup extents only; no PNG or every-frame proof','cleanup':'PASS'}
save(lab,'matrix',matrix)
save(lab,'summary',{'verdict':'INCONCLUSIVE','architecture':'UNDECIDED','reason':'incorrect fixture interpretation of current_border_width under border none stopped the only run before RGB/input gates','budget':budget['used'],'builds':{'SceneFX':0,'Mosaic':0,'historical_clients_only':3},'STEP20C':'NEXT','production_multi_output':'NOT YET SUPPORTED','reruns':0,'new_labs':1})
# Keep the executed fixture separate from the later offline collector.
shutil.copytree(Path(__file__).parent,lab/'final-fixture')
source=sorted(p for root in ['fixture','final-fixture','client-source'] for p in (lab/root).rglob('*') if p.is_file())
evidence=sorted(p for root in ['raw','run','results'] for p in (lab/root).rglob('*') if p.is_file())
evidence += [lab/name for name in ['swayfx-mosaic','saved-client','static-reference','pointer-daemon','campaign-owner']]
for name,files in [('source',source),('evidence',evidence)]:
with (results/(name+'-sha256.txt')).open('x') as out:
for f in files:out.write(f'{sha(f)} {f}\n')
for name in ['source','evidence']:
manifest=results/(name+'-sha256.txt');lines=manifest.read_text().splitlines()
for line in lines:
digest,path=line.split(' ',1);assert sha(path)==digest,path
print(name,len(lines),sha(manifest),'PASS')

View file

@ -0,0 +1,31 @@
"""Exact whole-image comparisons and measured interior click selection.
CONTRACT: no mask or tolerance may turn a whole-output difference into PASS.
INVARIANT: coordinates of every differing pixel are retained on failure.
"""
from PIL import Image, ImageChops
import re
COLORS = {(35,68,204),(32,208,160)}
def analyze(im):
mask=Image.new('L',im.size)
values=[255 if c in COLORS else 0 for c in im.get_flattened_data()]
mask.putdata(values)
return {'count':sum(v!=0 for v in values),'bbox':mask.getbbox()}
def compare(a,b):
assert a.size==b.size
points=[{'x':i%a.width,'y':i//a.width,'before':x,'after':y}
for i,(x,y) in enumerate(zip(a.get_flattened_data(),b.get_flattened_data())) if x!=y]
return {'equal':not points,'changed':len(points),'bbox':ImageChops.difference(a,b).getbbox(),'pixels':points}
def interior(im):
box=analyze(im)['bbox'];assert box
x1,y1,x2,y2=box;assert x2-x1>40 and y2-y1>40
x,y=(x1+x2)//2,(y1+y2)//2
assert all(im.getpixel((xx,yy)) in COLORS for xx in range(x-20,x+21) for yy in range(y-20,y+21))
return x,y
def button_pair(lines):
events=[re.search(r'BUTTON ns=(\d+) serial=(\d+) button=(\d+) state=(\d+)',x) for x in lines]
events=[tuple(map(int,x.groups())) for x in events if x]
assert len(events)==2 and [x[2:] for x in events]==[(273,1),(273,0)],events
assert all(x[1]>0 for x in events)
return [{'ns':x[0],'serial':x[1],'button':x[2],'state':x[3]} for x in events]

View file

@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Copy exact candidate and compile verified historical C17 clients only.
CONTRACT: exclusive campaign registration prevents another LAB/reset/retry.
WHY: historical client executable hashes were not sealed; source provenance was.
"""
import json, os, shutil, stat, subprocess, tempfile
from pathlib import Path
from support import sha, command, save
OLD=Path('/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt')
OWNER=Path.home()/'swayfx-mosaic-static-followup-v1.owner'
# Reserve campaign ownership before creating the only LAB, even on failure.
fd=os.open(OWNER,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
with os.fdopen(fd,'w') as f:
lab=Path(tempfile.mkdtemp(prefix='swayfx-mosaic-static-followup-',dir=Path.home()))
f.write(str(lab)+'\n');f.flush();os.fsync(f.fileno())
for name in ['results','raw']:(lab/name).mkdir()
(lab/'campaign-owner').write_text(str(OWNER)+'\n')
for name,digest in [('source','ed55bd418595ca977c022fa98917a899072cf3b6f0fb15457db823081046e797'),('evidence','e260ce5793ffc9a29ee4bfa2189663feeb6dc025bd46d3f5ddebccd225f3eef1')]:
manifest=OLD/'results'/(name+'-sha256.txt');assert sha(manifest)==digest
for line in manifest.read_text().splitlines():
h,p=line.split(' ',1);assert sha(p)==h,p
binary=OLD/'stage-static/usr/bin/swayfx-mosaic'
expected='f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b'
assert sha(binary)==expected and stat.S_IMODE(binary.stat().st_mode)==0o755
shutil.copy2(binary,lab/'swayfx-mosaic');assert sha(lab/'swayfx-mosaic')==expected
elf=command(lab,'binary-elf',['readelf','-hW','-nW','-dW',lab/'swayfx-mosaic'])
assert 'libscenefx' not in elf and '942db64269640778ee433e5970ba1100bc60f94c' in elf
assert '(RPATH)' not in elf and '(RUNPATH)' not in elf
loader=command(lab,'dependencies',['ldd','-r',lab/'swayfx-mosaic'])
assert 'not found' not in loader and 'undefined symbol' not in loader and 'libscenefx' not in loader
wl=Path('/usr/lib/libwlroots-0.20.so').resolve()
assert sha(wl)=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
assert str(wl) in loader
save(lab,'binary-identity',{'status':'PASS','source':str(binary),'copy':str(lab/'swayfx-mosaic'),'sha256':expected,'build_id':'942db64269640778ee433e5970ba1100bc60f94c','mode':'0755','wlroots':str(wl),'wlroots_sha256':sha(wl),'new_compositor_builds':0})
shutil.copytree(OLD/'client-source',lab/'client-source')
flags=subprocess.check_output(['pkg-config','--cflags','--libs','wayland-client'],text=True).split()
clients=[]
for name,proto in [('saved-client','xdg-shell'),('static-reference','xdg-shell'),('pointer-daemon','virtual-pointer')]:
src=lab/'client-source';argv=['cc','-std=c17','-Wall','-Wextra','-Werror','-Wno-unused-parameter','-I'+str(src),src/(name+'.c'),src/(proto+'-protocol.c'),*flags]
command(lab,name+'-syntax',[*argv,'-fsyntax-only'])
command(lab,name+'-compile',[*argv,'-o',lab/name])
clients.append({'name':name,'source_sha256':sha(src/(name+'.c')),'binary_sha256':sha(lab/name),'historical_binary_sha256_unsealed':sha(OLD/name)})
save(lab,'client-provenance',{'historical':json.loads((OLD/'results/client-provenance.json').read_text()),'source_manifest_verified':True,'clients':clients})
shutil.copytree(Path(__file__).parent,lab/'fixture')
print('LAB='+str(lab))

View file

@ -0,0 +1,249 @@
#!/usr/bin/env python3
"""One budgeted follow-up of the unchanged verified static compositor.
WHY: resolved relocations do not demonstrate real compositor execution.
CONTRACT: verify /proc executable/library identity before mapping any client.
INVARIANT: owned sockets/groups only, one camera correction, durable captures.
"""
import argparse
import json
import os
from pathlib import Path
import re
import shutil
import signal
import subprocess
import tempfile
import threading
import time
from PIL import Image,ImageChops
from support import environment,reserve,save,sha
from pixels import analyze, compare, interior, button_pair
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
assert json.loads((lab/'results/binary-identity.json').read_text())['status']=='PASS'
assert sha(lab/'swayfx-mosaic')=='f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b'
assert (Path.home()/'swayfx-mosaic-static-followup-v1.owner').read_text().strip()==str(lab)
reserve(lab,'run');run=lab/'run';run.mkdir();runtime=Path(tempfile.mkdtemp(prefix='msr-',dir='/tmp'))
runtime.chmod(0o700);(run/'runtime-path').write_text(str(runtime)+'\n')
(run/'config').write_text('output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0\noutput HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0\nworkspace 1 output HEADLESS-1\nworkspace 2 output HEADLESS-2\nfocus_follows_mouse no\nseat seat0 hide_cursor 100\n')
env=environment();env.update(XDG_RUNTIME_DIR=str(runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2',WLR_RENDERER='pixman')
binary=lab/'swayfx-mosaic';processes=[];files=[];failure=None;compositor=None;events=[]
started=time.monotonic();finished=threading.Event();expired=threading.Event()
def event(kind,**data):
row={'ns':time.monotonic_ns(),'kind':kind,**data};events.append(row)
with (run/'events.jsonl').open('a') as out:out.write(json.dumps(row)+'\n')
def stop_owned():
for proc in list(processes):
try:os.killpg(proc.pid,signal.SIGTERM)
except ProcessLookupError:pass
def watchdog():
while not finished.wait(.1):
if time.monotonic()-started>150 or any(x.stat().st_size>64*1024*1024 for x in run.glob('*.log')):
expired.set();stop_owned();return
thread=threading.Thread(target=watchdog,daemon=True);thread.start()
def spawn(name,argv,stdin=False,extra=None):
assert not expired.is_set()
out=(run/(name+'.log')).open('w');files.append(out)
proc=subprocess.Popen(list(map(str,argv)),env={**env,**(extra or {})},stdin=subprocess.PIPE if stdin else subprocess.DEVNULL,stdout=out,stderr=subprocess.STDOUT,text=True,start_new_session=True)
processes.append(proc);event('spawn',name=name,pid=proc.pid,argv=list(map(str,argv)));return proc
def wait(predicate,seconds=8):
end=time.monotonic()+seconds
while time.monotonic()<end:
if expired.is_set():raise RuntimeError('deadline/log bound exceeded')
if compositor and compositor.poll() is not None:raise RuntimeError('compositor exited '+str(compositor.returncode))
value=predicate()
if value:return value
time.sleep(.025)
raise RuntimeError('fixture wait timed out')
def log(name):return (run/(name+'.log')).read_text(errors='replace')
def ipc(*args):
assert sock.is_relative_to(runtime) and sock.is_socket()
result=subprocess.run(['swaymsg','-s',str(sock),*args],env=env,capture_output=True,text=True,timeout=5,check=True)
data=json.loads(result.stdout);event('ipc',args=args,result=data);return data
def cmd(value):
data=ipc(value);assert all(x.get('success') for x in data),(value,data)
def nodes(node):
yield node
for child in node.get('nodes',[])+node.get('floating_nodes',[]):yield from nodes(child)
def find(app):return next((n for n in nodes(ipc('-t','get_tree')) if n.get('app_id')==app),None)
def inject(action,x,y,kind='move'):
reserve(lab,kind)
assert -800<=x<800 and 0<=y<600
marker=f'INJECT_DONE {action} {x+800},{y}';before=log('pointer').count(marker)
pointer.stdin.write(f'{action} {x+800} {y}\n');pointer.stdin.flush()
wait(lambda:log('pointer').count(marker)>before)
time.sleep(.15);event('pointer',action=action,global_point=[x,y],target_tail=log('target').splitlines()[-6:] if (run/'target.log').exists() else [])
def capture(tag,output):
reserve(lab,'capture');path=run/(tag+'.png')
subprocess.run(['grim','-s','1','-o',output,str(path)],env=env,check=True,timeout=5,stdout=subprocess.DEVNULL)
im=Image.open(path).convert('RGB');assert im.size==(800,600)
event('capture',file=path.name,output=output,sha256=sha(path));return im
def measure(owner):
rows=[x for x in log('compositor').splitlines() if 'PI_OWNER output=HEADLESS-1 ' in x and f' id={owner["id"]} ' in x]
assert rows,'applied owner trace unavailable'
line=rows[-1];m=re.search(r'scene_valid=1 scene=(-?\d+),(-?\d+) applied_size=(\d+),(\d+)',line);assert m,line
x,y,w,h=map(int,m.groups());win=owner['window_rect']
return {'id':owner['id'],'scene':[x,y,w,h],'window_rect':win,'horizontal_content':[x+win['x'],win['width']],'raw':line,'IPC':owner}
# WHY: global focus becomes H2 after the forbidden-region click. Workspace and
# owner IDs, rather than global command context, bind the standard-return test.
focus_states=[]
def snapshot(tag):
tree=ipc('-t','get_tree');outputs=ipc('-t','get_outputs');workspaces=ipc('-t','get_workspaces')
row={'tag':tag,'ns':time.monotonic_ns(),'tree':tree,'outputs':outputs,'workspaces':workspaces,
'focused':[n['id'] for n in nodes(tree) if n.get('focused')]}
focus_states.append(row);save(lab,'focus-states',focus_states)
save(lab,tag+'-snapshot',row)
return tree
def workspace(tree,name):
return next(n for n in nodes(tree) if n['type']=='workspace' and n['name']==name)
def scene(output):
rows=[s for s in log('compositor').splitlines() if 'PI_SCENE output='+output+' ' in s]
assert rows,'scene trace absent';line=rows[-1]
m=re.search(r'workspace=(\d+) layout=(\d+) scroll=(-?[\d.]+) tile_scene=(-?\d+),(-?\d+) clip_valid=(\d+) clip=(-?\d+),(-?\d+),(\d+),(\d+)',line);assert m,line
v=list(map(float,m.groups()))
return {'workspace':int(v[0]),'layout_enum':int(v[1]),'scroll':v[2],
'tile_local':v[3:5],'clip_valid':bool(v[5]),'clip_local':v[6:10],
'global_clip':[(-800 if output=='HEADLESS-1' else 0)+v[3]+v[6],v[4]+v[7],v[8],v[9]],'raw':line}
def camera(tree):return workspace(tree,'1')['mosaic']['rows'][0]['scroll_x']
def buttons(name):return [x for x in log(name).splitlines() if x.startswith('BUTTON ')]
def measured_click(kind,point):
before={n:len(buttons(n)) for n in ['target','reference','filler']}
inject('right',*point,kind=kind)
row={'global_point':point,'events':{n:buttons(n)[before[n]:] for n in before},
'tails':{n:log(n).splitlines()[-15:] for n in before},'ns':time.monotonic_ns()}
save(lab,kind,row);return row
def whole(tag,baseline,im):
value=compare(baseline,im);save(lab,tag,value);assert value['equal'],'whole H2 differs: '+tag
return value
try:
compositor=spawn('compositor',[binary,'-d','-c',run/'config'])
sock=wait(lambda:next(iter(runtime.glob('sway-ipc.*.sock')),None))
display=wait(lambda:next((x for x in runtime.glob('wayland-*') if x.is_socket()),None))
env.update(SWAYSOCK=str(sock),WAYLAND_DISPLAY=display.name)
maps=Path(f'/proc/{compositor.pid}/maps').read_text();(run/'proc-maps.txt').write_text(maps)
exe=Path(f'/proc/{compositor.pid}/exe').resolve();assert exe==binary and sha(exe)==sha(binary)
paths=sorted({line.split()[-1] for line in maps.splitlines() if len(line.split())>=6 and line.split()[-1].startswith('/')})
wl=Path('/usr/lib/libwlroots-0.20.so').resolve()
assert not any('libscenefx' in x for x in paths)
assert [Path(x).resolve() for x in paths if 'libwlroots' in x]==[wl]
assert sha(wl)=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
assert not any('/swayfx-mosaic-' in x and Path(x)!=binary for x in paths)
save(lab,'dependency-mappings',{'status':'PASS','exe':str(exe),'paths':paths,'wlroots_sha256':sha(wl),'SceneFX_shared':False})
outputs=ipc('-t','get_outputs');actual={o['name']:(o['rect']['x'],o['rect']['y'],o['rect']['width'],o['rect']['height'],o['scale'],o['transform']) for o in outputs if o['active']}
assert actual=={'HEADLESS-1':(-800,0,800,600,1.0,'normal'),'HEADLESS-2':(0,0,800,600,1.0,'normal')},actual
save(lab,'output-geometry',outputs)
pointer=spawn('pointer',[lab/'pointer-daemon','1600','600'],stdin=True);wait(lambda:'POINTER_READY' in log('pointer'))
cmd('workspace 2');ref=spawn('reference',[lab/'static-reference'],stdin=True,extra={'WAYLAND_DEBUG':'1'})
reference=wait(lambda:find('org.mosaic.StaticReference'));refid=reference['id']
cmd(f'[con_id={refid}] border none')
cmd('workspace 1');filler=spawn('filler',[lab/'static-reference'],stdin=True)
wait(lambda:len([n for n in nodes(ipc('-t','get_tree')) if n.get('app_id')=='org.mosaic.StaticReference'])==2)
target=spawn('target',[lab/'saved-client'],stdin=True,extra={'WAYLAND_DEBUG':'1'})
wait(lambda:find('org.mosaic.SavedBufferProbe'))
cmd('layout mosaic');time.sleep(.6)
tree=snapshot('before-camera');owner=find('org.mosaic.SavedBufferProbe');targetid=owner['id'];wsid=workspace(tree,'1')['id']
before=measure(owner);sc_before=scene('HEADLESS-1');scroll_before=camera(tree)
assert sc_before['scroll']==scroll_before
x,w=before['horizontal_content'];desired=-w//2;delta=x-desired
reserve(lab,'camera');cmd(f'layout mosaic scroll {delta}')
time.sleep(.6);owner=find('org.mosaic.SavedBufferProbe');after=measure(owner)
tree=snapshot('preflight');scroll_after=camera(tree);sc_after=scene('HEADLESS-1')
save(lab,'camera-calibration',{'before':scroll_before,'delta':delta,'desired_left':desired,'after':scroll_after,'before_scene':sc_before,'after_scene':sc_after})
assert scroll_after==scroll_before+delta and sc_after['scroll']==scroll_after
x,w=after['horizontal_content'];assert x<0<x+w and -800<x
assert sc_after['clip_valid'] and sc_after['global_clip']==[-800,0,800,600]
assert sc_after['workspace']==wsid
reference=next(n for n in nodes(tree) if n['id']==refid)
assert reference['pid']==ref.pid and reference['border']=='none' and reference['current_border_width']==0 and reference['visible']
assert reference in list(nodes(workspace(tree,'2')))
assert reference['rect']=={'x':0,'y':0,'width':800,'height':600}
assert not reference['focused'] and owner['focused']
assert next(z for z in ipc('-t','get_workspaces') if z['focused'])['name']=='1'
save(lab,'owner-geometry',{'before':before,'after':after,'reference':reference,'last_client_commit':[s for s in log('target').splitlines() if s.startswith('COMMIT ')][-1]})
save(lab,'clip-observations',{'mosaic':sc_after,'H2':scene('HEADLESS-2')})
inject('move',-790,590);time.sleep(.6)
baseline=capture('h2-reference-1','HEADLESS-2');time.sleep(.6);second=capture('h2-reference-2','HEADLESS-2')
comparison=compare(baseline,second)
save(lab,'reference-preflight',{'H2_REFERENCE_STABLE':comparison['equal'],'H2_WHOLE_RGB_EQUAL':comparison['equal'],'reference':reference,'focus':focus_states[-1]['focused'],'hashes':{n:sha(run/n) for n in ['h2-reference-1.png','h2-reference-2.png']}})
save(lab,'reference-pixel-analysis',comparison)
assert comparison['equal'] and all(c==(48,48,56) for c in baseline.get_flattened_data()),'reference preflight failed'
left=capture('h1-mosaic','HEADLESS-1');right=capture('h2-mosaic','HEADLESS-2')
visible=analyze(left);forbidden=analyze(right)
save(lab,'permitted-pixels',visible);save(lab,'forbidden-pixels',forbidden)
assert visible['count']>0 and forbidden['count']==0
whole('v1-H2-comparison',baseline,right)
bx,by,bw,bh=visible['bbox'];assert bx-800==x and bw==800
last_commit=[s for s in log('target').splitlines() if s.startswith('COMMIT ')][-1]
dimensions=re.search(r'size=(\d+)x(\d+)',last_commit);assert dimensions
assert int(dimensions[1])==w and bh-by==int(dimensions[2])
# Paint proves vertical position; only horizontal continuation is inferred
# from the applied scene, IPC offset and actual configured client width.
px,py=interior(left);permitted=(px-800,py)
forbidden_point=(min(100,int(x+w)-30),py);assert forbidden_point[0]>=20
assert x<forbidden_point[0]<x+w and right.getpixel(forbidden_point)==(48,48,56)
save(lab,'click-points',{'permitted':permitted,'forbidden':forbidden_point,'paint_bbox':visible['bbox'],'horizontal_content':[x,w]})
click=measured_click('visible_click',permitted);events_visible=button_pair(click['events']['target'])
assert not click['events']['reference'] and not click['events']['filler']
save(lab,'target-input',{'status':'PASS visible','point':permitted,'events':events_visible})
tree=snapshot('after-visible');assert camera(tree)==scroll_after
assert measure(find('org.mosaic.SavedBufferProbe'))['horizontal_content']==[x,w]
click=measured_click('forbidden_click',forbidden_point);events_reference=button_pair(click['events']['reference'])
assert not click['events']['target'] and not click['events']['filler'],'invisible target or wrong client input'
save(lab,'reference-input',{'status':'PASS','point':forbidden_point,'events':events_reference,'invisible_target_events':click['events']['target']})
tree=snapshot('after-reference');assert camera(tree)==scroll_after
assert next(n for n in nodes(tree) if n['id']==refid)['focused']
inject('move',-790,590);time.sleep(.6)
whole('post-click-H2-comparison',baseline,capture('h2-after-clicks','HEADLESS-2'))
cmd(f'[con_id={targetid}] layout splith');time.sleep(.6)
tree=snapshot('standard');standard_ws=workspace(tree,'1');assert standard_ws['id']==wsid and standard_ws['layout']=='splith'
standard_owner=next(n for n in nodes(standard_ws) if n['id']==targetid);assert standard_owner['visible']
standard_left=capture('h1-standard','HEADLESS-1');standard_right=capture('h2-standard','HEADLESS-2')
whole('standard-H2-comparison',baseline,standard_right)
standard_scene=scene('HEADLESS-1');standard_h2=scene('HEADLESS-2')
assert standard_scene['workspace']==wsid and not standard_scene['clip_valid'] and not standard_h2['clip_valid']
sx,sy=interior(standard_left);point=(sx-800,sy)
click=measured_click('standard_click',point);events_standard=button_pair(click['events']['target'])
assert not click['events']['reference'] and not click['events']['filler']
snapshot('after-standard-click');inject('move',-790,590);time.sleep(.6)
whole('final-H2-comparison',baseline,capture('h2-standard-after-click','HEADLESS-2'))
save(lab,'standard-return',{'status':'STANDARD_RETURN_PASS','workspace':standard_ws,'owner':measure(standard_owner),'scene':standard_scene,'H2_scene':standard_h2,'point':point,'events':events_standard,'pixels':analyze(standard_left),'H2_equal':True})
assert all(p.poll() is None for p in [target,ref,filler,pointer])
cmd('exit');compositor.wait(timeout=5);assert compositor.returncode==0
except Exception as exc:
failure=f'{type(exc).__name__}: {exc}';event('failure',message=failure)
finally:
finished.set();thread.join(timeout=1);stop_owned()
for proc in processes:
try:proc.wait(timeout=3)
except subprocess.TimeoutExpired:
os.killpg(proc.pid,signal.SIGKILL);proc.wait(timeout=3)
try:os.killpg(proc.pid,signal.SIGKILL)
except ProcessLookupError:pass
for out in files:out.close()
shutil.rmtree(runtime)
save(lab,'runtime',{'status':'PASS' if failure is None else 'INCONCLUSIVE','failure':failure,'duration_seconds':time.monotonic()-started,'process_exit_codes':[{'pid':x.pid,'argv':x.args,'exit':x.returncode} for x in processes],'runtime_removed':str(runtime),'cleanup':'owned groups reaped, runtime removed'})
damage=[s for s in log('compositor').splitlines() if 'TEMPORAL_DAMAGE ' in s] if (run/'compositor.log').exists() else []
save(lab,'damage-analysis',{'extents':damage,'all_frames':'NOT TESTED','PNG_native_buffer_identity':'NOT PROVEN','client_damage':'wl_surface_damage_buffer full SHM at actual commits; see client source/protocol logs'})
print('STATIC_FOLLOWUP',failure or 'PASS')
if failure:raise SystemExit(1)

View file

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

View file

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

View file

@ -0,0 +1,18 @@
"""Exercise scoring against edge differences, actual paint and button events."""
from PIL import Image
from pixels import analyze, compare, interior, button_pair
x=Image.new('RGB',(800,600),(48,48,56));y=x.copy()
assert compare(x,y)['equal'];y.putpixel((799,599),(48,48,57))
r=compare(x,y);assert not r['equal'] and r['changed']==1 and r['pixels'][0]['x']==799
assert analyze(x)['count']==0
paint=Image.new('RGB',(800,600),(0,0,0))
for xx in range(482,800):
for yy in range(25,598):paint.putpixel((xx,yy),(35,68,204))
assert analyze(paint)=={'count':182214,'bbox':(482,25,800,598)}
assert interior(paint)==(641,311)
assert len(button_pair(['BUTTON ns=1 serial=3 button=273 state=1','BUTTON ns=2 serial=4 button=273 state=0']))==2
for lines in [[],['ENTER ns=1 x=0 y=0'],['BUTTON ns=1 serial=3 button=273 state=1']]:
try:button_pair(lines)
except AssertionError:pass
else:raise AssertionError('missing delivery accepted')
print('PASS exact RGB edge rejection, painted interior and real press/release scoring')