swayfx-mosaic/docs/design/STEP20C_SCENEFX_STATIC_DISTRIBUTION_CONTRACT.md

41 KiB
Raw Permalink Blame History

STEP20C SceneFX static distribution and upstream contract

EXPERIMENTAL / PRESERVED HISTORICAL EVIDENCE / no production integration. Protocol recorded before new tests. Phase A frozen and published at bb3a112550f45667e501d05d5a08aa8f7d0c02c6; both remotes equal, clean worktree. STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED. Production multi-output NOT YET SUPPORTED.

Authority, protocol and decision boundary

Packaging, Architecture, Upstream, Development, Known limitations and Roadmap remain authoritative. No UX, production source, recipe, pin, cumulative patch or system dependency change is authorized. Production strategy: NONE — USER DECISION REQUIRED.

Reuse historical ABI evidence: current stock package CURRENT_PACKAGE_STAGE_PASS; private shared with stock SceneFX INCOMPATIBLE_WITH_STOCK_SCENEFX; static ELF resolves without SceneFX DT_NEEDED, with shared wlroots. Historical verdict PARTIALLY_FEASIBLE. 85 common symbols are not 85 defects; 34 actual wlroots scene bindings have 31 external providers and three wlroots providers. No visibility policy is authorized. Sizes/offsets and loader success do not establish full semantic ABI.

Pins: SwayFX 0.6 fd71a6bdc061bd633b488ae7b83e8a6981d22f86; experimental Mosaic cd550382b311efdbfe3ed892f4e1d281d852e754; SceneFX 0.5 37ccd723bef49e6891156ffafce8f549f01446cc; wlroots 0.20.2 d783533489e1f75d6886c2ab5c5960090ef268f8. No newer source is substituted.

Read-only historical laboratory: /home/fy59/swayfx-mosaic-abi-package-tp22xfwm. New laboratory: /home/fy59/swayfx-mosaic-static-contract-7_vlvpjw. Commands, return codes and all generated results remain there. Historical manifests are verified, never regenerated.

Planned non-graphical checks

Map source references and ELF definitions into A public used SceneFX, B private clip, C overlap with wlroots, D remaining dynamic and E executable-local symbols. Record visibility, version, actual retained binding, owner and risk. Inspect exported/dynamic tables, relocations and transitive package owners. Read pinned headers, implementations, renderer/build/protocol contracts.

One static staging uses the qualified binary, not a newly built compositor. Historical static prefix /usr/local requires a fresh private Meson install plan on identical source bytes with /usr prefix; reuse the verified binary without rebuild/strip, normal DESTDIR installation only. Inspect every entry including directories, regular files, links, mode, uid/gid and hashes. One-file shape says nothing about graphics, legal compliance or release readiness.

Audit exact source license texts and source-level notices for incorporated components. Compare N1 embedded/extractable notice, N2 package metadata and N3 separate installed notice. An optional disposable ELF-section roundtrip tests N1 mechanics only. Never evade a license condition to retain one file.

Propose explicit dependency/version gates, manual security ownership and reproducible source/patch/build provenance plus qualified rollback. Prepare an upstream API dossier without contact, submission or public-field changes. Compare maintained upstream API, private static, distinct private shared and no new SceneFX primitive. Recommendations are not authorizations.

Classification

FEASIBLE requires staging and relevant binding characterization plus explicit dependency/maintenance contracts. PARTIALLY_FEASIBLE retains unresolved notice, maintenance, version or ownership questions. INCONCLUSIVE means missing essential staging/binding evidence. BLOCKED means a reproducible invariant failure without validated candidate in scope. Unchosen license or packaging policy is not automatically a code defect. No graphical campaign is allowed.

Completed campaign and staging

SWAYFX_MOSAIC_STATIC_DISTRIBUTION_CONTRACT_PROBE=PARTIALLY_FEASIBLE

Architecture: STATIC_DISTRIBUTION_REQUIRES_CONTRACT_DECISION. Immediate experimental candidate: private static SceneFX. Conditional long-term maintenance direction: an upstream-maintained clipping API. Production strategy: NONE — USER DECISION REQUIRED. No option is integrated.

STATIC_STAGE_FILE_SHAPE_PASS is measured, not inferred. One normal Meson DESTDIR install returned 0. Complete non-directory manifest:

Path Kind Mode Actual uid:gid Bytes SHA-256
/usr/bin/swayfx-mosaic regular ELF 0755 1000:1000 6,760,600 ccd47ce5b74c132e19bcd46d022a80e90e8a944acc3db521a89427ee7913c614

Directories /usr and /usr/bin are recorded in the complete JSON manifest. No symlink, library, header, config, service, notice or second regular file. The future package would own this path as root:root; no ownership change was attempted. This is not an Arch package or legal distribution qualification.

The historical build has prefix /usr/local, bindir bin, debug build, strip=false, mosaic-package=true, swaybar/swaynag=false, man-pages=disabled, default-wallpaper=false. SceneFX alone has default_library=static; the compositor's default_library=shared does not make its explicit archive dynamic. The private new plan used the same package flags and static uninstalled pkg-config provider, changing only prefix to /usr for staging. Sources were copied and byte-verified against the sealed historical tree. The exact qualified binary was copied into that plan's build-relative target location. Meson setup performs its normal dependency/compiler configuration probes; no SceneFX or compositor source was compiled or linked again. Install used --no-rebuild, without tags or manual placement into DESTDIR. The staged hash still matches the historical binary. Its embedded /usr/local configuration strings were not rebuilt into /usr: runtime path suitability is NOT TESTED. This intentional limitation is why this result qualifies file shape only.

Preparation initially failed because the fixture assumed a subprojects directory absent from the historical archive. That Python failure preceded Meson setup/install. The fixture was corrected to copy only existing inputs; the retained preparation was resumed. Exactly one install attempt occurred. The persistent stage-install-started.json prohibits a second attempt; the historical laboratory and manifests were not changed.

ELF loading and the five symbol groups

The private binary is ELF64, little-endian x86-64. No RPATH or RUNPATH. Both clipping functions are FUNC/GLOBAL/DEFAULT, unversioned, in .symtab; nm reports ordinary T definitions. They are absent from .dynsym and have no unresolved dynamic relocation. They are executable definitions, not ELF LOCAL symbols. Their call sites include private transaction tiled and popup clipping, with get calls also in private output instrumentation. No public structure field was introduced by the preserved sidecar patch.

SceneFX is absent from DT_NEEDED and the retained recursive loader graph; libwlroots-0.20.so remains shared. Retained ldd -r returned 0 with no undefined-symbol diagnostics. Strong undefined imports in the ELF are normal shared-library references, not 891 loader failures. Weak optional imports are reported separately. New readelf header/dynamic/full-symbol/relocation/version and nm outputs are preserved; actual provider records are reused from the sealed trusted loader diagnostics. No compositor entry point was run.

The standalone historical graph contains no SceneFX shared library alongside the embedded archive. A future plugin, preload or additional dependency could introduce one; that graph is NOT TESTED. Static embedding does not isolate the process namespace. No export, rename, hidden-visibility or linker-policy change was made or recommended without a separate experiment/authorization.

Group Definition and evidence Current owner/provider Convention and upgrade risk
A 73 public SceneFX symbols required as strong imports by shared experimental compositor; static definitions and source references recorded individually SceneFX implementation embedded in static executable; this set covers compiled SwayFX/Mosaic, not only newly added layout code Unversioned C scene/effects/renderer functions, unstable public types; new header/body semantics need audit
B wlr_scene_tree_set_clip, wlr_scene_tree_get_clip; strong private shared imports, executable ordinary definitions Private SceneFX sidecar; direct static call resolution No official upstream API/version/error guarantee; private prototype only
C 85 names defined by pristine SceneFX and wlroots and exported by the static executable Scene, EGL, color and matrix functions; actual providers separately recorded Matching names alone establish overlap; only observed bindings prove selection; unobserved routes remain uncertain
D 891 distinct strong undefined dynamic symbol base names in static executable, with versions/visibility preserved Shared wlroots, Wayland, rendering and other system providers selected by retained loader records ELF version requirements and SONAME do not prove semantic compatibility; all recursive versions matter
E 1,275 ELF LOCAL definitions, including clip sidecar helpers and compositor internals; full table retained Executable internal implementations Local helpers have no dynamic ABI; they do not isolate the 85 shared scene/renderer exports

scene-symbol-ownership.json includes groups, source locations, definitions/ reference candidates, full ELF records, consumer/provider pairs and risk per symbol. A source occurrence is explicitly evidence of a reference candidate; ELF tables determine definition/import status. Missing runtime records mean NOT OBSERVED, not an invented provider. exported-symbols.json retains the complete table, including actual symbol versions. Public WLR/SceneFX scene functions have no ELF symbol-version compatibility namespace in this pair. Versioned libc/etc imports are retained rather than relabeled unversioned. Some helpers such as wlr_scene_subsurface_tree_set_clip remain dynamic wlroots imports; that existing surface/subsurface primitive is distinct from the two private inherited generic tree functions.

Interposition: intentional integration versus unresolved semantics

The preserved stock/private/static diagnostics each observe 34 wlroots scene bindings. The exact same three remain in wlroots:

  • wlr_scene_subsurface_tree_create
  • wlr_scene_surface_try_from_buffer
  • wlr_scene_surface_create

The other 31 bind to system/private SceneFX in shared controls and to the static executable in the candidate. Their exact names follow below. This pattern is coherent with ordinary SceneFX integration, already present in stock; it is not a newly demonstrated clipping failure.

Pinned wlroots-source/types/scene/xdg_shell.c:66, layer_shell_v1.c:128, drag_icon.c:49, subsurface_tree.c:246 and surface.c:432 show why mixed ownership matters: wlroots helpers construct SceneFX tree/buffer objects through interposed constructors, then manage surface listeners and protocol lifecycle. Renderer/color/EGL structs and callbacks similarly cross library boundaries. Preserving compatible object and function semantics is necessary for these routes. This necessity follows from source paths; no hide/rename counterfactual was tested. The minimum safe export set is therefore NOT ESTABLISHED.

The 31 measured bindings are intentional/coherent for the tested pair at the loader level, and helper construction routes substantiate their integration role. Complete semantic necessity of every binding and all remaining overlap paths is uncertain. Unobserved EGL/color/matrix routes must not be called qualified merely because the names match. Any other wlroots version or distro build is NOT QUALIFIED. A hypothetical distinct SONAME also does not fix this process-wide behavior by itself.

wlr_scene_buffer_create
wlr_scene_buffer_from_node
wlr_scene_buffer_send_frame_done
wlr_scene_buffer_set_buffer
wlr_scene_buffer_set_buffer_with_options
wlr_scene_buffer_set_color_encoding
wlr_scene_buffer_set_color_range
wlr_scene_buffer_set_dest_size
wlr_scene_buffer_set_opacity
wlr_scene_buffer_set_opaque_region
wlr_scene_buffer_set_primaries
wlr_scene_buffer_set_source_box
wlr_scene_buffer_set_transfer_function
wlr_scene_buffer_set_transform
wlr_scene_get_scene_output
wlr_scene_node_coords
wlr_scene_node_destroy
wlr_scene_node_place_above
wlr_scene_node_place_below
wlr_scene_node_set_enabled
wlr_scene_node_set_position
wlr_scene_output_build_state
wlr_scene_output_create
wlr_scene_output_destroy
wlr_scene_output_needs_frame
wlr_scene_output_send_frame_done
wlr_scene_output_set_position
wlr_scene_rect_from_node
wlr_scene_timer_finish
wlr_scene_tree_create
wlr_scene_tree_from_node

Dependency contract and conditional future recipe

The static executable's complete direct DT_NEEDED list is:

libcairo.so.2 libdrm.so.2 libjson-c.so.5 libevdev.so.2 libinput.so.10
libudev.so.1 libm.so.6 libpango-1.0.so.0 libgobject-2.0.so.0 libglib-2.0.so.0
libpcre2-8.so.0 libpixman-1.so.0 libwlroots-0.20.so libwayland-server.so.0
libxkbcommon.so.0 libEGL.so.1 libgbm.so.1 libGLESv2.so.2 liblcms2.so.2
libxcb.so.1 libxcb-icccm.so.4 libpangocairo-1.0.so.0 libc.so.6

Only SceneFX is statically embedded. EGL/GBM/GLESv2/lcms2 become direct DT_NEEDED instead of arriving through SceneFX. libdrm and pixman were already direct ELF needs, but are only makedepends in the current recipe. Static embedding must not rely on removing SceneFX while accidentally losing runtime providers previously brought in by it. The retained recursive graph also has Wayland client, Vulkan, seatd/libseat, display-info, liftoff, libudev, XCB extensions and renderer/allocator libraries. Their ownership remains with shared providers; GPU driver modules loaded later are outside loader-only proof.

Provider in observed system Candidate future runtime declaration Reason
wlroots0.20 retain, qualify exact build/version matrix still shared; cannot freeze ordinary SwayFX upgrades
wayland retain server direct, client/transitive and protocol generation
libglvnd add explicit runtime/build availability direct EGL and GLESv2
mesa add explicit runtime/build availability observed libgbm provider; actual renderer/driver gates remain
lcms2 add explicit runtime/build availability when enabled direct color-management provider in this build
libdrm, pixman promote to runtime depends direct DT_NEEDED, currently only makedepends
glib2, glibc, xcb-util-wm make direct ownership explicit in proposed recipe review GObject/GLib, libc/libm, libxcb-icccm; presently supplied transitively/base system
cairo, json-c, libevdev, libinput, systemd-libs, libxcb, libxkbcommon, pango, pcre2 retain direct providers and current enabled features
gdk-pixbuf2, ttf-font retain pending runtime feature audit absence in DT_NEEDED alone does not authorize removal of feature/data requirements
scenefx conditionally remove runtime dependency only after static architecture approval no SceneFX DT_NEEDED; replace with verified embedded-source build provenance
git, meson, ninja, pkgconf, python, wayland-protocols retain build tools/inputs source/patch/build/protocol generation
kitty, grim retain future check requirements not executed in this non-graphical ticket

This is a proposal, not an applied recipe. Package names describe observed Arch owners, not universal providers. Requalify clean dependency closure, feature toggles, strip/notice retention, reproducibility, staging and separate graphics checks before changing the recipe. No PKGBUILD byte changed.

Version and shared-structure contract

Only pinned SceneFX 0.5 source plus observed wlroots 0.20.2-1 binary is qualified for these non-graphical checks. Installed headers match the pinned wlroots source for 121 non-generated files; runtime/version metadata agree. That does not reconstruct the entire distro build patch set and toolchain. The Meson range >=0.20.0,<0.21.0 is a dependency acceptance expression, not a demonstrated compatibility range. 0.20.0, 0.20.1, another 0.20.2 build, later 0.20 releases and 0.21 are NOT TESTED.

WLR_USE_UNSTABLE exposes public scene nodes, tree, buffers, output, output_state, surfaces, renderer/buffer implementations, listeners, signals, color transform, EGL and render-pass conventions. The historical observer measures identical size/alignment for 17 selected structs and six offsets across installed, pristine and private headers. Examples: scene_node 112/8, tree 128/8, scene_buffer 472/8, output 624/8, surface 968/8; tree.children offset 112, node.parent 8, buffer.buffer 112. It does not show equality between native wlroots scene layouts and extended SceneFX structures, nor exhaustive layouts, callbacks, enum tags, ownership or semantics.

SceneFX requires Wayland server >=1.24, protocols >=1.41, libdrm >=2.4.129, xkbcommon >=1.8 and pixman >=0.43; compositor requirements differ in places. Generated XDG and other Wayland protocol code and scanner inputs are part of reproduction. The observed wlroots enables DRM, X11, libinput, Xwayland, GLES2, Vulkan, GBM, udmabuf, session and color management. SceneFX selects its FX/GLES renderer and observed lcms2 path. Build flags, generated config headers, protocol versions and actual compiler dependencies must accompany source pins. No additional renderer or protocol combination was tested here.

Candidate qualification matrix for each new shared wlroots build:

Gate Required evidence This ticket
1 header/source API, callbacks, enum and lifecycle diffs; exact distro patches pinned pair only, subset retained
2 symbols definitions/imports, versions, all consumer/provider bindings pinned pair only; visibility isolation untested
3 struct layouts sizes/alignments/offsets for every crossing type and callback ABI historical 17 structs/six offsets only
4 clean build fixed SceneFX/Mosaic sources and patch order, toolchain/features historical build PASS; no new compositor build
5 loader complete recursive graph, strong/weak relocations, no duplicate SceneFX historical trusted graph PASS
6 unit/model actual production model and library contracts historical mosaic-model 1/1 in three builds; new version NOT TESTED
7 private stage complete manifest, notices/strip and provenance current candidate file shape PASS only
8 separately authorized graphics standard/Mosaic/control, popups, effects, saved buffers, damage, input, scale/rotation/migration/generation NOT TESTED for static candidate in this ticket

Do not pin global wlroots to prevent normal SwayFX upgrades. If Mosaic's pair becomes unqualified, withhold its candidate release, rebuild/requalify or use standard SwayFX. File independence is established; update independence is not.

Candidate security ownership and reproducibility

Bundled SceneFX no longer receives fixes when the system scenefx package is updated. The Mosaic distributor/maintainer must explicitly accept ownership for monitoring and rebuilding embedded code, with a named reviewer and release owner. Nobody is assigned by this ticket; an unowned policy is an open gate. Upstream remains a source of fixes, not the security updater of this executable.

Proposed manual workflow, requiring adoption before distribution:

  1. Record SceneFX commit/tag provenance, Git object identity and SHA-256 source inventory; list exact private patches in order and their digests. Preserve SwayFX/Mosaic/wlroots pins, actual system package/build identities, scanner, protocols, toolchain, flags, generated config and link commands.
  2. An assigned maintainer reviews upstream releases/commits and security advisories affecting SceneFX and copied wlroots scene/renderer code; assess relevance and exposure. A second reviewer checks the fix and private clip interaction. Set a reporting channel and triage/release response expectations.
  3. Review a candidate source revision and rebase private patches explicitly; no silent cherry-pick, ABI substitution or automatic update. A fix may alter renderer paths, layouts or interposition and require redesign.
  4. Rebuild only in a controlled private environment with fixed dependency sources/build metadata and no fallback downloads. Recheck symbol/layout/ loader/dependency gates, model tests, staging/notices and separately authorized graphics. Two clean builds with path/timestamp controls are needed before claiming bit reproducibility; not performed here.
  5. Record artifact/source/notices hashes, review approvals and matrix results. Publish only under a separate release authorization. Retain the previous qualified artifact and full build manifest for rollback, including compatible dependency identities. No updater or release package is created here.

The cost is recurring SceneFX plus copied-wlroots security triage, patch rebases, shared-wlroots qualification, legal-notice inventory and graphics/release testing. It is more than rebuilding one compositor after a source change. A reproducible procedure is proposed; bit-identical artifacts and security response are NOT ESTABLISHED. Source provenance is necessary but does not itself certify security.

Rollback must never restore system SceneFX/wlroots or overwrite /usr/bin/sway. Replacing/removing only Mosaic preserves ordinary package ownership. An older Mosaic artifact may fail with newer shared wlroots/renderer providers; it is not automatically a working rollback. Retain/requalify compatible manifests, or fall back to the separately maintained standard SwayFX session. Actual install/update/uninstall/rollback remain NOT TESTED.

Licenses, incorporated components and notice options

Pinned SwayFX, SceneFX and wlroots root licenses contain MIT copyright, permission and warranty notices. SceneFX's root notice names Drew DeVault and Jari Vetoniemi; wlroots additionally names its contributors. Preserve complete texts, not merely license=('MIT'). The licenses and source notice candidates are retained verbatim in private license-notices.json; no notice dump enters Git. Compiled-source provenance, generated protocol comments, archive symbol input, compile commands and linker plan are retained separately. The proof does not claim every archive member is extracted into the final executable.

Important additional condition: generated protocol code carries its own copyright/permission text. XDG includes Kristian Høgsberg, Rafael Antognolli, Jasper St. Pierre, Intel, Samsung and Red Hat notices. Generated wlr-layer-shell and output-power code use permission/warranty language that includes supporting documentation and restrictions on advertising with copyright-holder names. An umbrella MIT label alone does not cover this inventory. The existing asset license and tinywl example notices appear in the broad source inventory but are not thereby proven incorporated: wallpaper/examples are disabled. Shared EGL, GBM, GLESv2, lcms2, Wayland, pixman/libdrm and wlroots are not newly copied as static libraries; their package license metadata is contextual evidence, not a declaration that all their source is embedded.

The disposable .mosaic_notices objcopy probe includes exact root license and generated protocol texts. A byte-for-byte section extraction passed. This is an oversized candidate inventory, not a completed incorporated-code notice closure, public notice interface or approved compliance solution. The copied binary was never executed or staged. Original and staged qualified binaries are unchanged. Strip/signing/package transformations and accessible notice delivery still need tests. No public/source API or visibility policy changes.

Option Feasibility/accessibility One-file contract Risk and required decision
N1 embedded section/metadata add/extract byte identity PASS on disposable ELF; document extraction with binutils without compositor execution structurally compatible complete notice closure, end-user discoverability, strip/retention and legal sufficiency not established; user adoption/review required
N2 package metadata/source archive normal license metadata identifies a license; it does not automatically deliver its full notices structurally compatible installed manifest raw binary transfer and package/source separation can lose notices; actual complete notice-delivery design and legal review required; metadata label alone insufficient
N3 separate installed license/notice conventional readable file, no specialized ELF extraction incompatible with current exact-one-file rule requires explicit user packaging-contract decision plus complete notices/review; preferred practical distribution direction if rule can be changed

Do not drop notice obligations to preserve one installed file. A credible N1 candidate exists technically; a sufficiently solid one-file compliance solution is NOT YET QUALIFIED. Conditional recommendation: allow a distinct installed notice file (N3) for clarity and discoverability if the user authorizes that contract change. This ticket neither changes the contract nor asserts final legal compliance. The protocol-specific supporting-documentation condition needs assessment for any N1/N2 route.

Primary references inspected: pinned SceneFX license, Meson DESTDIR/no-rebuild, GNU objcopy sections and Arch PKGBUILD license metadata. Local pinned source texts and retained generated comments govern the inventory. This is a technical/legal-obligation dossier, not legal clearance.

Separate upstream API dossier — not submitted

An API could express a generic inherited scene-tree clip, not Mosaic popup ownership or UX. Known experimental signatures, retained only as candidates:

void wlr_scene_tree_set_clip(struct wlr_scene_tree *tree,
    const struct wlr_box *clip);
bool wlr_scene_tree_get_clip(struct wlr_scene_tree *tree,
    struct wlr_box *clip);

No signature is frozen. In particular a void setter cannot report the private sidecar's allocation failure. A maintained API may need a result/error enum or another upstream-owned storage/lifecycle design. No such code, fields or ABI change is implemented here. Public structures must not grow without maintainer agreement on ABI/version policy and all cross-library consumers.

Topic Demonstrated private/source behavior Proposed maintained contract / required tests
coordinates copied tree-local integer wlr_box; descendant traversal intersects ancestor clips logical tree-local half-open rectangle, explicit conversion to output/buffer; checked addition/narrowing/overflow
ownership sidecar owns copied box; tree pointer borrowed; destroy listener frees sidecar caller may discard box after successful set; tree owns clip lifetime; main scene-thread use only, no independent worker authority
NULL setter clears, absent clear is no-op; getter asserts non-NULL output NULL rectangle removes own clip; invalid tree/out pointer must have documented error/assertion policy; get absent returns false and define untouched out value
empty/negative zero dimensions accepted; negative dimensions asserted zero area excludes descendant pixels/hits; negative sizes rejected explicitly; no silent signed overflow or allocation failure
update/idempotence same box memcmp returns early; clear/update invokes scene_node_update compare semantic coordinates rather than struct padding; same effective clip idempotent; allocation error preserves old state and reported status
moving/reparenting traversal follows parent chain and local→global coordinates own clip moves with tree; inherited effective clip recomputed on reparent; old/new output damage invalidated; cyclic/invalid reparent rejected by existing ownership rules
destruction listener removes and frees sidecar no references/listeners after tree destruction; recursive destruction, clear-before-destroy, failed allocation and destruction during callbacks tested
inheritance intersections in private descendant render/hit traversal getter distinguishes own clip from effective inherited clip; empty ancestor dominates; nested trees and negative origins tested
rendering/hit sampled historical tiled/popup render/hit behavior only same applied effective bounds for render and hit; other node types, occlusion and buffer-local coordinates must agree
damage private node update and sampled damage; complete proof absent invalidate exposed/vacated regions on set/clear/move/reparent/output changes; all-frame stale-pixel coverage and cache invalidation
effects sampled shadows/blur; shared edge limitation unresolved define whether clip limits final coverage versus blur source sampling, shadows/rounded corners/optimized cache; preserve ordinary effects semantics
saved buffers sampled historical held/timeout paths, no complete GPU lifetime proof consistent applied geometry for saved nodes; destroy/release/ACK/timeout and cross-output lifetime tests
rotation/scaling separate sampled historical probes, not static-candidate graphics output-transform and fractional rounding policy, damage expansion, conservative raster bounds with exact logical hit contract
performance private linear global sidecar lookup scalable lookup/storage, bounded per-frame work, no unbounded allocator churn; benchmark deep trees/many clips, cache mutation coherency
ABI/version two private functions, unchanged measured public header layouts maintained feature/version exposure, compile/link guards, cross-wlroots compatibility policy; exact paired-version qualification

Mosaic must separately retain tiled-owner identity, root/nested popup hierarchy, applied workspace/output viewport and owner visibility. The private empty clip for a fully hidden owner is Mosaic-owned policy evidence, not a generic SceneFX requirement to know workspaces or XDG grab semantics. Offscreen hide, dismiss or reposition remains undecided UX. No universal click-through promise: valid grabs still have their ordinary dismissal behavior, compared with stock. SceneFX cannot alone assign a Sway transaction generation or popup owner.

Questions for a future authorized maintainer discussion: is inherited tree clip an acceptable primitive? Which coordinate and empty/error policy? Can own/effective queries remain separate? Where may storage live without incompatible public layouts? Which consumers must retain interposed providers? What damage/effects/ saved-buffer semantics can be guaranteed? How should capabilities, ABI and wlroots pairs be versioned? What tests/benchmarks belong upstream? Would an error-reporting setter be preferable? No contact, issue, PR or submission occurs.

Required future tests include C API syntax/invalid arguments, box overflow and allocation failures, idempotence, nested clips, reparent and destruction, render/hit agreement, complete damage and effects/cache/saved-buffer coverage, scaling/rotation/migration, shared-object crossings and performance. A successful compile or signature proposal does not close those tests.

Architectural decision matrix

Dimension A maintained upstream API B private static SceneFX C distinct private dynamic identity D no SceneFX extension
evidence private primitive only; upstream acceptance unknown pinned build/ELF/bindings and one stage PASS private same-SONAME selection observed; distinct identity NOT TESTED alternative clipping mechanism not demonstrated
maintenance upstream primitive if accepted; Mosaic owner/viewport still ours embedded source/security/rebase/version owner required private library/security/rebase/version owner required broader Mosaic rendering/input maintenance possible
system dependencies supported maintained SceneFX/wlroots pair shared wlroots/graphics, no shared SceneFX need shared wlroots plus private SceneFX file existing SceneFX/wlroots, unknown new needs
SwayFX coexistence standard packages intact, normal upstream transition standard binary/libraries intact; file shape PASS extra private path must be approved possible filesystem independence, architecture unproved
ABI/interposition official contract still to negotiate; existing integration persists 85 overlaps; 31 measured wlroots routes into executable distinct SONAME separates lookup, not namespace/struct semantics no new clip exports, existing unstable APIs still matter
upgrades paired versions and release qualification no automatic SceneFX fixes, wlroots upgrades unqualified private fixes plus shared wlroots qualification unknown forward-port cost
packaging possible one binary, notices remain one binary staged; notice/security contracts open extra library and notices require contract change unknown final shape
licenses/security preserve notices; upstream plus Mosaic release owners complete embedded notices and project security owner notices plus private-library security owner inventory remains; project owns alternate mechanism
rollback old release only with compatible dependencies; stock fallback old embedded code may be vulnerable/incompatible; stock fallback must roll binary/private library together, shared graph still matters unqualified alternate rollback
delivery complexity external agreement, maintained release and integration controlled source/archive/patch/provenance/notices builds private identity/path/RUNPATH/transitive binding contract largest unexplored implementation risk
residual risks API acceptance and graphical gates semantics, versions, notices, ownership and graphics same plus double library/lookup risk popup stacking/hits/effects/damage/saved buffers/generation
minimum next step authorize discussion after local API/notice review user contract decisions, then separately scoped version/notice tests user new-file contract then bounded identity/binding prototype architecture study; no renderer implementation authorized

B is the best demonstrated immediate experimental candidate: no new owned SceneFX library file, no absent system clip symbol dependency, explicit ELF and staging evidence. A is the conditional best long-term maintenance orientation, because a maintained generic primitive could avoid perpetual private patches. This is an engineering recommendation, not evidence of upstream commitment. C is not qualified by the existing same-SONAME build; D is not established by output scissor or surface cropping alone.

Required user decisions: candidate architecture or continued investigation; N1/N2/N3 notice route and any packaging-contract change; named security/release owner and response policy; supported exact wlroots build qualification policy without blocking standard upgrades; acceptable rollback/support boundary; permission for any upstream contact; scope of separately authorized graphics, version, reproducibility and delivery tests. No production choice is applied.

Reproduction, validation and unclosed gates

Historical source/evidence manifests remain unchanged and fully verified with the explicit README snapshot mapping from phase A. This campaign reuses ELF and actual bindings, without rerunning historical builds or loader tracing. The new audit fixture preserves its own command/return-code/raw records. It allows repeat static analyses before sealing, but has an exclusive staging attempt marker and refuses sealed results.

For an independent future reconstruction, archive exact pinned Mosaic and SceneFX objects; apply preserved patches in order:

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

Use the historical fixture's exact Meson static-chain flags, setting Mosaic prefix /usr/bindir bin initially, with the static SceneFX uninstalled pkg-config directory only for that build. Keep wlroots shared and archive its pinned source for audits. Verify each patch/digest and qualified installed header/build inputs. No stock/shared chain is needed. A future reproduction requires its own authorization and provenance rather than expecting the old laboratory to exist or silently accepting a different library build. Do not run historical prepare.py just to analyze symbols: it builds unnecessary chains. The qualified-source regeneration path is independent of historical binaries; bit identity and reproducibility across machines remain NOT TESTED.

Current result files include complete staging/ELF/export/ownership/interposition, transitive dependencies, versions, security, licenses/notice options, upstream API, architectural matrix, unresolved gates and summary. Private source/evidence manifests seal the final inventories, excluding themselves and this report's integrity appendix to avoid cyclic digests. No generated file enters Git.

NOT TESTED: static-candidate graphics/GPU/readback, another wlroots version or build, distinct-SONAME/private-path delivery, all-frame damage/effects/cache/ saved-buffer lifetimes, applied-generation consistency, upgrade/rollback, strip/sign/package notice retention, complete legal compliance and bit reproducibility. Historical graphical subset results are not transferred into a new static distribution qualification. STEP20C remains NEXT.

Final ELF count: 88 dynamic definitions, comprising the 85 common SceneFX/ wlroots exports plus libc-related optarg, optind, stderr data records. These data records are not additional SceneFX functions. Seven generated protocol notice records are preserved from compiled inputs.

Final checks PASS: Python AST, existing shell/recipe syntax, local Markdown links, source-only fixture/whitespace, complete stage/hash, retained ELF/bindings, structured policy dossiers, protected sources/recipe/patch/UX/AGENTS, unchanged canonical packaging contract, installed sway/SceneFX/wlroots hashes and no LAB processes/sockets. Meson model results are historical, not reexecuted. The parent performed one closeout dossier/fixture review; this is not an independent subagent review. Failures and corrections are explicitly retained.

Final inventory integrity and local state

Source manifest: 1892 entries, SHA-256 5ad64eaf0aec2290689c46b7754eb4563980396ba2a06e852d1c8e903086f138.

Evidence manifest: 143 entries, SHA-256 85d034f4075436bbdda302afdfa94955365aa51adcfb7e8b04ed6bb28377f649.

Both new manifests are sealed and reverified; original 1,463/204 historical manifests remain unchanged. The report itself and complete diff are excluded from those inventories to avoid cyclic digests. Phase A HEAD remains bb3a112550f45667e501d05d5a08aa8f7d0c02c6 on local, origin and github. Phase B remains unstaged/uncommitted/unpublished: report/fixture plus Architecture, Known Limitations, Packaging evidence appendix, Roadmap, README and Changelog only. No protected production/system/session change, installation, package creation/publication, upstream submission or STEP20D work occurred.

Recommended next smallest step: user review of notice route (N3 recommended conditionally), security/release ownership and exact wlroots qualification policy, then a separately scoped test authorization. No production integration is authorized by this dossier.

SWAYFX_MOSAIC_STATIC_DISTRIBUTION_CONTRACT_PROBE=PARTIALLY_FEASIBLE

STATIC_DISTRIBUTION_REQUIRES_CONTRACT_DECISION

Subsequent user decisions — 2026-10-13

After this campaign, the user approved private static SceneFX for the immediate STEP20C candidate, separate complete license notices for a future candidate, project responsibility for embedded SceneFX maintenance, and strict qualification of each SceneFX/wlroots pair. These decisions authorize documentation and private prototypes only; the current package, production patch and source remain unchanged. No individual owner or response SLA is assigned. The historical verdict and architectural conclusion above are unchanged. A separate runtime/package gate must build a real /usr binary and qualify loaded libraries and sampled clipping.

The evidence-freeze audit verified all 1,892 source and 143 evidence entries before changing publication wording. Original manifests were neither regenerated nor overwritten. For later verification, map the seven updated documentation entries through /home/fy59/swayfx-mosaic-static-contract-freeze-v1/snapshot-map.json; all other entries use their original paths. Snapshot bytes are retained outside Git, with original hashes verified by verify-freeze.py.