docs(mosaic): preserve static runtime and package contract evidence
This commit is contained in:
parent
4f4f3fefcd
commit
5e2961d05b
20 changed files with 1124 additions and 2 deletions
10
AGENTS.md
10
AGENTS.md
|
|
@ -115,7 +115,11 @@ Resolve contradictory canonical documents **before** implementing behavior.
|
|||
Mosaic-modified checkout. For documentation-only changes, validate links,
|
||||
consistency, and `git diff --check`; do not claim runtime tests were run.
|
||||
6. For packaging tasks, use **DESTDIR staging only** and verify the complete
|
||||
manifest contains exactly `/usr/bin/swayfx-mosaic`. No live install, sudo,
|
||||
manifest contains exactly `/usr/bin/swayfx-mosaic` for the current package.
|
||||
The approved private future STEP20C candidate may additionally stage only
|
||||
justified notices under `/usr/share/licenses/swayfx-mosaic/`. This exception
|
||||
does not authorize PKGBUILD changes or real installation; coexistence with
|
||||
standard SwayFX is mandatory. No live install, sudo,
|
||||
`pacman`, session switch or system library replacement without explicit
|
||||
user authorization.
|
||||
|
||||
|
|
@ -125,7 +129,9 @@ Resolve contradictory canonical documents **before** implementing behavior.
|
|||
`fd71a6bdc061bd633b488ae7b83e8a6981d22f86`,
|
||||
wlroots 0.20 and SceneFX 0.5. Never assume an upstream update is compatible.
|
||||
- The ordinary SwayFX package owns `/usr/bin/sway`; this project may stage
|
||||
only `/usr/bin/swayfx-mosaic`. No `provides`, `conflicts` or `replaces`
|
||||
only `/usr/bin/swayfx-mosaic` for the current package. The private future
|
||||
STEP20C candidate may also stage justified separate license notices as above.
|
||||
No `provides`, `conflicts` or `replaces`
|
||||
against sway/SwayFX. Do not alter or block normal SwayFX upgrades.
|
||||
- Remotes: `origin` = canonical Forgejo; `github` = public mirror;
|
||||
`upstream` = SwayFX fetch-only. Never push Mosaic to `upstream`.
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
### STEP20C experimental evidence
|
||||
|
||||
- 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.
|
||||
|
||||
- Preserved SceneFX/wlroots ABI/package audit: current stock build and exact-one-file staging PASS; experimental shared clip functions absent from stock despite common SONAME. Private static build/loader passes, but wlroots interposition, notices, security and version-maintenance gates remain. PARTIALLY_FEASIBLE; architectural decision required. No integration or system change; the historical ABI evidence is published separately from any later distribution-contract investigation.
|
||||
|
|
|
|||
|
|
@ -368,3 +368,40 @@ passes mechanically; legal distribution compliance is not approved.
|
|||
The upstream API dossier is preserved and not submitted. Production strategy:
|
||||
**NONE — USER DECISION REQUIRED**. Packaging/UX contracts are unchanged;
|
||||
STEP20C stays NEXT and production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
||||
The [private static runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md)
|
||||
is **EXPERIMENTAL / PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Real `/usr`
|
||||
static build/model/stage/notices/loaded-library identity/headless startup pass.
|
||||
Sampled H1 target pixels survive with zero target colors on H2, but whole-H2
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -116,3 +116,31 @@ Then review the patch and run the package checks in [Packaging](PACKAGING.md). `
|
|||
7. Publish only after the new base is validated. The normal SwayFX package continues updating independently.
|
||||
|
||||
See [Packaging](PACKAGING.md) and [Upstream](UPSTREAM.md) for release invariants. Desktop key bindings belong to user dotfiles and are not installed here.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
|
|
|||
|
|
@ -309,3 +309,40 @@ passes mechanically; legal distribution compliance is not approved.
|
|||
The upstream API dossier is preserved and not submitted. Production strategy:
|
||||
**NONE — USER DECISION REQUIRED**. Packaging/UX contracts are unchanged;
|
||||
STEP20C stays NEXT and production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
||||
The [private static runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md)
|
||||
is **EXPERIMENTAL / PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Real `/usr`
|
||||
static build/model/stage/notices/loaded-library identity/headless startup pass.
|
||||
Sampled H1 target pixels survive with zero target colors on H2, but whole-H2
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -63,3 +63,36 @@ are technically extractable in a disposable copy; complete/legal delivery is
|
|||
unqualified. A separate installed notice (N3) would need an explicit user
|
||||
decision changing the exact-one-file rule. PARTIALLY_FEASIBLE; no production
|
||||
strategy adopted. The historical evidence is preserved; it does not authorize production integration.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
||||
Measured private candidate staging: one real `/usr` binary plus four justified
|
||||
notice files, STATIC_STAGE_WITH_LICENSES=PASS. This does not change the current
|
||||
recipe, certify legal distribution closure or qualify runtime input/standard
|
||||
return. See the linked private gate for complete failed/untested cells.
|
||||
|
|
|
|||
|
|
@ -227,3 +227,40 @@ STEP20C stays NEXT and production multi-output NOT YET SUPPORTED.
|
|||
|
||||
The [static-contract source fixture](../tooling/probes/step20c-scenefx-static-contract/README.md)
|
||||
preserves the bounded audit/staging procedure; all generated results stay external.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
||||
The [private static runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md)
|
||||
is **EXPERIMENTAL / PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Real `/usr`
|
||||
static build/model/stage/notices/loaded-library identity/headless startup pass.
|
||||
Sampled H1 target pixels survive with zero target colors on H2, but whole-H2
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -313,3 +313,40 @@ passes mechanically; legal distribution compliance is not approved.
|
|||
The upstream API dossier is preserved and not submitted. Production strategy:
|
||||
**NONE — USER DECISION REQUIRED**. Packaging/UX contracts are unchanged;
|
||||
STEP20C stays NEXT and production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
||||
The [private static runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md)
|
||||
is **EXPERIMENTAL / PARTIALLY_FEASIBLE**, architecture **UNDECIDED**. Real `/usr`
|
||||
static build/model/stage/notices/loaded-library identity/headless startup pass.
|
||||
Sampled H1 target pixels survive with zero target colors on H2, but whole-H2
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -33,3 +33,31 @@ The maintained production delta is `patches/0001-mosaic-layout-viewport.patch` o
|
|||
7. Update [known limitations](KNOWN_LIMITATIONS.md), [roadmap](ROADMAP.md), and release notes with the observed result.
|
||||
|
||||
A failed patch check or changed compositor contract blocks that candidate release until resolved. It does not justify modifying the ordinary SwayFX package or using an ABI-incompatible binary workaround. The normal SwayFX package must remain independently updatable.
|
||||
|
||||
## Approved private STEP20C static candidate — 2026-10-13
|
||||
|
||||
The user approved four decisions for documentation and private prototypes only:
|
||||
private static SceneFX; separate complete notices under
|
||||
`/usr/share/licenses/swayfx-mosaic/`; project responsibility for embedded
|
||||
SceneFX maintenance; and qualification of each exact SceneFX/wlroots pair.
|
||||
See the [decision register and bounded runtime gate](design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
The current PKGBUILD, system SceneFX dependency, one-binary package, production
|
||||
sources and cumulative patch remain unchanged. The future private stage may
|
||||
contain only `/usr/bin/swayfx-mosaic` and justified regular notice files below
|
||||
the license directory. No private shared library, session, service, config or
|
||||
Sway symlink is allowed. No real installation is authorized.
|
||||
|
||||
Mosaic owns monitoring, impact review, rebuild and requalification of embedded
|
||||
SceneFX fixes, including renderer/shaders/dependencies/ABI/buffer lifetime/clip/
|
||||
damage changes. Before public distribution, a release maintainer and security
|
||||
reviewer must be designated; no names or SLA are assigned. Preserve exact source,
|
||||
patch, compiler/options/dependency, notice, test and artifact manifests.
|
||||
A new relevant dependency build requires header/symbol/shared-layout inspection,
|
||||
rebuild, relocation/model/staging/headless checks and graphical requalification
|
||||
of relevant paths, then a separate publication decision. No automatic update
|
||||
subsystem is added. Ordinary SwayFX upgrades remain independent and standard
|
||||
SwayFX remains the fallback. All other pairs are NOT QUALIFIED.
|
||||
|
||||
These decisions do not settle popup UX or close STEP20C production gates.
|
||||
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
|
||||
Production multi-output remains NOT YET SUPPORTED.
|
||||
|
|
|
|||
205
docs/design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md
Normal file
205
docs/design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# STEP20C private static runtime and package contract
|
||||
|
||||
**EXPERIMENTAL / PRESERVED HISTORICAL EVIDENCE.** No production integration or install.
|
||||
Protocol established before builds and runtime. Historical verdicts remain
|
||||
unchanged; [static distribution evidence](STEP20C_SCENEFX_STATIC_DISTRIBUTION_CONTRACT.md)
|
||||
was published separately. [Packaging](../PACKAGING.md) governs file ownership;
|
||||
[Architecture](../ARCHITECTURE.md) preserves compositor authority.
|
||||
|
||||
## Decision register — 2026-10-13
|
||||
|
||||
| Decision | Motivation and authorized scope | Prohibited / residual risks / revision gates |
|
||||
| --- | --- | --- |
|
||||
| D1 private static SceneFX | Historical ELF resolves both private clips without system SceneFX replacement; build/run private candidate | No production integration, algorithm change or installed library replacement; semantic interposition and full graphical matrix remain open |
|
||||
| D2 separate notices | Preserve full incorporated-code attributions and permissions without artificial one-file constraint; private future stage only | Current recipe unchanged; exact notice inventory must follow link inputs and generated protocols; legal clearance not asserted |
|
||||
| D3 project maintenance | Embedded code does not inherit system SceneFX fixes; project monitors/reviews/rebuilds/requalifies | No named owner/SLA; release maintainer/security reviewer required before distribution; no automatic updater |
|
||||
| D4 strict pair qualification | Unstable shared scene/renderer types and 85 overlaps require exact build evidence | No inference to later versions, no blocking stock upgrades; inspect headers/symbols/layouts, rebuild, relocations/model/stage/headless/graphics, separate release decision |
|
||||
|
||||
Current package: unchanged PKGBUILD, system SceneFX, only `/usr/bin/swayfx-mosaic`,
|
||||
no STEP20C implementation. Future experimental candidate: static SceneFX, shared
|
||||
wlroots, system libraries intact, binary plus inventoried notices only.
|
||||
Long-term direction remains SCENEFX_UPSTREAM_MAINTAINED_CLIP_API; reuse the
|
||||
historical unsubmitted dossier. Ownership/errors/bounds/lifetime/hit/damage/
|
||||
versioning need a maintained API; popup ownership remains Mosaic-specific.
|
||||
No contact or upstream adoption is presumed.
|
||||
|
||||
## Bounded protocol
|
||||
|
||||
One external LAB, one SceneFX static build, one Mosaic static build with
|
||||
`--prefix=/usr --bindir=bin -Dmosaic-package=true`, one Meson DESTDIR stage,
|
||||
one notice addition and one ELF/dependency audit. Reconstruct pinned Mosaic
|
||||
`cd550382b311efdbfe3ed892f4e1d281d852e754`, SceneFX
|
||||
`37ccd723bef49e6891156ffafce8f549f01446cc`, SwayFX
|
||||
`fd71a6bdc061bd633b488ae7b83e8a6981d22f86` and wlroots
|
||||
`d783533489e1f75d6886c2ab5c5960090ef268f8`; reuse the historical ordered
|
||||
eight private patches unchanged, verify source equivalence. wlroots stays shared.
|
||||
|
||||
A persistent exclusively locked ledger reserves every operation before execution;
|
||||
failures consume reservations. Test the ledger offline first. One principal run,
|
||||
optional one indispensable pristine control, at most eight PNGs, one calculated
|
||||
camera correction, no hide/return pair, 150 seconds per run. Explicit private
|
||||
mode-0700 runtime/Wayland/IPC sockets; staged executable only. Verify process
|
||||
mappings before clients or clipping: no SceneFX .so, exact system wlroots and
|
||||
no historical LAB. Wrong identity stops the run.
|
||||
|
||||
HEADLESS-1=(-800,0), HEADLESS-2=(0,0), each 800×600, scale 1, normal.
|
||||
Use verified historical ordinary SHM client/reference and virtual pointer; verify
|
||||
actual target crossing x=0, retained H1 pixels, excluded H2 pixels and measured
|
||||
hits. Return to splith and inspect clip removal. Root valid-grab popup may be
|
||||
reused only without widening protocol; otherwise NOT TESTED. No nested campaign.
|
||||
Damage extents are sampled and never establish every-frame coverage. Stop
|
||||
concerned scenario on crash/ABI/clip/hit leak; no algorithm repairs. Historical
|
||||
pristine evidence can provide reference semantics, without fresh campaign.
|
||||
|
||||
License inventory follows actual linker/compile/generated inputs, preserved
|
||||
full notices and attribution/provenance/hash/destination. Dynamic system-library
|
||||
licenses are not indiscriminately copied. Unclosed inventory prevents a ready
|
||||
distribution verdict. Reproducible procedure is distinct from bit reproducibility.
|
||||
|
||||
Results and raw commands/exit codes live outside Git. All untested matrix cells
|
||||
are NOT TESTED. FEASIBLE is limited to the tested matrix; PARTIALLY_FEASIBLE
|
||||
retains qualification limits; INCONCLUSIVE reflects unreliable/missing runtime
|
||||
evidence; BLOCKED requires a demonstrated incompatibility/crash/forbidden leak.
|
||||
STEP20C remains NEXT; production multi-output NOT YET SUPPORTED.
|
||||
|
||||
## Completed private campaign
|
||||
|
||||
LAB: `/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt`.
|
||||
The preceding static-distribution freeze commit was
|
||||
`4f4f3fefcdcf979f8be126d6671062ecb1d65b50`, published on both remotes.
|
||||
The runtime campaign below was phase B of that earlier ticket. Its evidence
|
||||
is now preserved by the separate static-runtime freeze ticket; it is not the
|
||||
new focus/input/standard-return campaign.
|
||||
|
||||
`SWAYFX_MOSAIC_STATIC_RUNTIME_PACKAGE_GATE=PARTIALLY_FEASIBLE`
|
||||
|
||||
Architecture: **UNDECIDED**. The single runtime comparison scenario is
|
||||
**INCONCLUSIVE**; no reproduced ABI crash or forbidden target hit is established.
|
||||
|
||||
| Gate | Observation |
|
||||
| --- | --- |
|
||||
| Source/build | Eight unchanged patches applied; full private source equivalence PASS; one static SceneFX and one Mosaic build, model 1/1 PASS |
|
||||
| Prefix | Real Meson `/usr`, bindir `bin`, compiled sysconfdir `/etc` (compiler spelling `//etc`); no copied `/usr/local` binary |
|
||||
| Staging | STATIC_STAGE_WITH_LICENSES=PASS; five regular files; user-owned private DESTDIR |
|
||||
| ELF | ELF64 x86-64, both private clip T definitions and direct calls, no SceneFX DT_NEEDED, no RPATH/RUNPATH, ldd -r without unresolved symbols |
|
||||
| Runtime | Staged executable actually loaded; exact system wlroots and no shared SceneFX in /proc maps; two prescribed outputs, IPC and ordinary clients mapped |
|
||||
| Renderer | Actual SceneFX FX / OpenGL ES 3.2 Mesa 26.2.4, AMD Radeon 780M; headless backend, no physical display session |
|
||||
| Interposition | 34 observed runtime wlroots scene bindings: 31 into executable, three wlroots helper providers unchanged |
|
||||
| Applied clipping | +160 sole camera correction, owner scene x=-320,width=640; local inherited clip=(160,0,800,600), both set/get paths observed through retained traces |
|
||||
| Pixels | 182,214 H1 target-color pixels, zero H2 target colors; H2 content crop identical |
|
||||
| Reference gate | Whole H2 FAIL: 23,807 changed pixels outside IPC content bounds, coherent with focused→unfocused decorations; cannot claim whole-output equality |
|
||||
| Input/standard return | NOT TESTED: run stopped at reference gate before button scoring and splith |
|
||||
| Popup | Root/nested grabbed popup NOT TESTED; historical controls retained, no fresh pristine run |
|
||||
| Damage | Native TEMPORAL_DAMAGE extents retained; complete frame coverage NOT TESTED |
|
||||
| Budget | scene=1,mosaic=1,stage=1,notices=1,ELF=1,principal run=1,control=0,camera=1,PNG=3; ~3.999 seconds, no retries/refunds |
|
||||
| Cleanup | Compositor exit 0 on owned SIGTERM cleanup, clients exit 0; pointer terminated -15; all owned groups reaped and private runtime removed |
|
||||
|
||||
The build/stage/executed binary SHA-256 is
|
||||
`f82557c1a9978a5e9665b9b4291b74880253725359e3df1c5b8d554921f7603b`,
|
||||
size 6,760,304 bytes. Different bytes/size from the historical binary are not
|
||||
a regression claim; prefix/source/build paths differ.
|
||||
Qualified shared wlroots build: Arch `wlroots0.20 0.20.2-1`, ELF SHA-256
|
||||
`7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`,
|
||||
Build ID `2cd3a56136647a9da32b9a682606911aa2498393`. Installed nongenerated
|
||||
headers match the pinned archive; selected historical layouts are retained,
|
||||
without rerunning the old ABI campaign. Other pairs NOT QUALIFIED. This pair's
|
||||
input, standard return and wider graphical behavior remain unqualified.
|
||||
|
||||
### Notice manifest and distribution boundary
|
||||
|
||||
Meson produced only `/usr/bin/swayfx-mosaic`. The future-package simulation
|
||||
added four 0644 files under `/usr/share/licenses/swayfx-mosaic/`:
|
||||
`SwayFX-LICENSE`, `SceneFX-LICENSE`, `wlroots-LICENSE`,
|
||||
`INCORPORATED-NOTICES`. Root texts are byte-identical to pinned sources;
|
||||
compiled generated protocol copyright/permission/warranty comments are included
|
||||
verbatim with their source provenance. wlroots notice covers copied code in
|
||||
SceneFX, not a dump of every dynamic system dependency. The inventory records
|
||||
compile commands, source hashes, protocol comments, destinations and root
|
||||
attribution. Notice hashes and the complete five-file manifest are in results.
|
||||
The technical stage passes; exact unused archive-member extraction, all copied
|
||||
header/shader notice closure and final legal review remain distribution gates.
|
||||
No definitive legal compliance is asserted.
|
||||
|
||||
### Failed fixture checks and reproducibility limits
|
||||
|
||||
The first offline multiprocessing ledger test failed for missing `__main__`
|
||||
guard on Python 3.14; corrected and PASS before runtime. The ELF audit completed
|
||||
its one diagnostic batch and notice addition, then failed an incorrect assertion
|
||||
looking for `/usr` in config.h. Stage validation instead verifies actual Meson
|
||||
options and compiler SYSCONFDIR. That erroneous assertion was corrected locally,
|
||||
without rerunning the audit batch or repeating staging. Initial direct loader
|
||||
trace yielded no scene bindings; real runtime LD_DEBUG provides the 31/3 result.
|
||||
The local audit fixture now uses diagnostic ldd -r for future reproduction; this
|
||||
correction has NOT been executed on a second binary.
|
||||
|
||||
The principal run compared H2 while focused at baseline and unfocused in Mosaic.
|
||||
The whole-output assertion stopped the scenario. Offline IPC/color comparison
|
||||
localizes all differences outside the unchanged content rectangle; this supports
|
||||
a reference-fixture mismatch, not a demonstrated forbidden clip leak. No new
|
||||
run, capture, algorithm fix or budget reset follows. The collector's initial
|
||||
crop double-counted decoration height; its corrected offline crop follows IPC
|
||||
rect/window_rect and passes. Failures are retained in the correction record.
|
||||
The initial audit source reconstruction is documented as reconstructed after
|
||||
correction, not contemporaneously hashed. Current fixture corrections are not
|
||||
additional executed evidence. Bit reproducibility needs independent builds and
|
||||
remains NOT TESTED under this one-build budget.
|
||||
|
||||
Next smallest justified ticket: establish a reference focus state before its
|
||||
baseline capture, then authorize one bounded same-static-binary run for measured
|
||||
clipped input and splith return. Keep notices/maintainer designation/full ABI and
|
||||
existing STEP20C graphics/generation/lifetime gates separate before any real
|
||||
PKGBUILD change. No integration approval follows from this partial result.
|
||||
|
||||
The [source-only fixture](../../tooling/probes/step20c-static-runtime/README.md)
|
||||
records commands, budgets and failed-check limitations. The run's field named
|
||||
`source_box` used applied owner scene x plus IPC window offsets; its y=0 was an
|
||||
estimate missing title decoration. Offline color bounds independently locate
|
||||
visible content at H1 local `(482,25)..(800,598)`, consistent with global
|
||||
x=-318 crossing. No claim of exact raw buffer y identity is made from that field.
|
||||
The final parent audit checks contracts, delta, raw provenance and limitations;
|
||||
no independent subagent review was invoked.
|
||||
|
||||
## Sealed integrity receipt
|
||||
|
||||
Source manifest: 1,397 entries, SHA-256
|
||||
`ed55bd418595ca977c022fa98917a899072cf3b6f0fb15457db823081046e797`.
|
||||
Evidence manifest: 146 entries, SHA-256
|
||||
`e260ce5793ffc9a29ee4bfa2189663feeb6dc025bd46d3f5ddebccd225f3eef1`.
|
||||
Both were created once and all entries reverified. This report and full delta
|
||||
are outside the manifest set to avoid circular digests. Historical 1,892/143
|
||||
manifests still verify through the pre-freeze snapshot mapping. No manifest
|
||||
was regenerated or overwritten.
|
||||
|
||||
Final AST, shell/recipe syntax, local Markdown links, source-only fixture,
|
||||
whitespace, stage identity/modes/hash, budget, protected system/source/recipe/
|
||||
patch checks and owned-process/socket cleanup PASS. Runtime reference gate
|
||||
FAIL is retained; input/standard return and broader tests NOT RUN / NOT TESTED.
|
||||
No B commit, push, production edit or installation occurred.
|
||||
|
||||
## Static-runtime evidence freeze — 2026-10-13
|
||||
|
||||
The new freeze audit starts at main `4f4f3fefcdcf979f8be126d6671062ecb1d65b50`,
|
||||
with both directly queried remote main references equal. Only the expected
|
||||
documentation and source-only static-runtime fixture are included. The historical
|
||||
PARTIALLY_FEASIBLE verdict and UNDECIDED architecture are unchanged.
|
||||
|
||||
All 1,397 source and 146 evidence entries verify against the original manifests.
|
||||
The ten fixture files, including its README, remain byte-identical because their
|
||||
absolute paths are sealed in the source manifest. Its LOCAL/UNCOMMITTED wording
|
||||
and this report's earlier A/B labels describe the historical campaign at closure,
|
||||
not the later publication state. No corrected runner is executed during freeze.
|
||||
|
||||
Independent offline RGB recount retains 23,807 changed H2 pixels and the complete
|
||||
image bounding box; the reference interior is identical, 182,214 target pixels
|
||||
remain on H1, and no target color appears on H2. These findings support a focus/
|
||||
decoration fixture explanation without replacing the failed whole-H2 comparison.
|
||||
The original scene-derived source_box is not an exact native buffer rectangle.
|
||||
Input, splith return and popups remain NOT TESTED for this historical run.
|
||||
|
||||
D1–D4 remain approved only for documentation and private experiments. Static
|
||||
linkage does not close semantic ABI, legal notice inventory, release/security
|
||||
ownership or wider graphical gates. The next separately bounded gate reuses
|
||||
the exact binary, normalizes the H2 reference before comparison, and measures
|
||||
three button deliveries and standard clip removal. No new build or old-LAB run
|
||||
is part of this freeze. STEP20C stays NEXT; production multi-output remains
|
||||
NOT YET SUPPORTED.
|
||||
79
tooling/probes/step20c-static-runtime/README.md
Normal file
79
tooling/probes/step20c-static-runtime/README.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Private static Mosaic runtime fixture
|
||||
|
||||
**EXPERIMENTAL / LOCAL / UNCOMMITTED; no production integration.**
|
||||
Read the [protocol and decisions](../../../docs/design/STEP20C_STATIC_RUNTIME_AND_PACKAGE_CONTRACT.md).
|
||||
Current package, production patch and installed desktop remain unchanged.
|
||||
|
||||
## Reproduction and one-laboratory ownership
|
||||
|
||||
The completed laboratory is `/home/fy59/swayfx-mosaic-static-runtime-hm5kbdkt`.
|
||||
Its run reservation is consumed. **Never rerun prepare/audit/run in that LAB,
|
||||
reset its budget, create a replacement LAB or execute the old campaigns.**
|
||||
Future independent reproduction requires a new scoped authorization/budget.
|
||||
Generated sources, build trees, binaries, PNGs, sockets and logs stay outside Git.
|
||||
|
||||
Dependencies: Git, Meson/Ninja, cc C17, pkg-config, readelf/nm/objdump/ldd,
|
||||
wayland-scanner, swaymsg, grim, Python/Pillow, installed pinned build libraries.
|
||||
No installation or downloads are performed by these scripts. Local SceneFX
|
||||
object store must contain the pinned commit. Historical wlroots archive and
|
||||
private source/patch hashes are verified through the preserved manifests
|
||||
before prepare; archive provenance remains the sealed ABI campaign.
|
||||
|
||||
For a separately authorized fresh LAB, the ordered commands are:
|
||||
|
||||
```sh
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/test-budget.py
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/prepare.py "$TASKLAB" \
|
||||
--scenefx-git "$SCENE_STORE" --historical "$VERIFIED_ABI_LAB"
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/audit.py "$TASKLAB"
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/stage.py "$TASKLAB"
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/clients.py "$TASKLAB"
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/run.py "$TASKLAB"
|
||||
PYTHONDONTWRITEBYTECODE=1 python tooling/probes/step20c-static-runtime/collect.py "$TASKLAB"
|
||||
```
|
||||
|
||||
Prepare reconstructs only the static chain, applies eight unchanged private
|
||||
patches and checks source equivalence. Mosaic is genuinely compiled with
|
||||
`--prefix=/usr --bindir=bin -Dmosaic-package=true`; only SceneFX is archived.
|
||||
Meson installation is DESTDIR only. Audit inventories exact root license texts
|
||||
and compiled/generated notice comments; exact unused archive-member extraction
|
||||
and legal clearance remain separate distribution gates. Stage refuses links,
|
||||
foreign files and modes; exactly one binary and four inventoried notice files
|
||||
were retained. `collect.py` recalculates structured observations offline.
|
||||
`seal.py` creates exclusive manifests once or verifies them read-only.
|
||||
|
||||
The budget uses a stable exclusive flock, atomic ledger replacement and fsync.
|
||||
Build/stage/notice/audit/run/capture/camera failures retain their reservations.
|
||||
No refund/reset exists. Offline tests exercise concurrent/exhausted reservations.
|
||||
Each runtime is mode 0700, with explicit private Wayland and swaymsg sockets,
|
||||
owned process groups, a 150-second watchdog and 64 MiB per-log stop bound.
|
||||
The compositor executes only the staged binary. /proc executable/maps identity
|
||||
is checked before any clients. No live desktop socket is used.
|
||||
|
||||
## Completed evidence and limitations
|
||||
|
||||
Build, model, real `/usr` prefix, stage, ELF, library identity, headless outputs,
|
||||
IPC and ordinary client mapping passed. Runtime measured the same 31 wlroots
|
||||
scene bindings into the executable and three retained wlroots helpers. The
|
||||
actual renderer was SceneFX FX / OpenGL ES 3.2, AMD Radeon 780M / Mesa 26.2.4;
|
||||
`WLR_RENDERER=pixman` did not select pixman in this FX path.
|
||||
|
||||
One correction of +160 produced an applied target scene x=-320, width=640;
|
||||
its measured H1 colored region retained 182,214 pixels, and H2 contained zero
|
||||
target-color pixels. The full H2 comparison failed: 23,807 decoration pixels
|
||||
changed after the reference lost focus. The IPC-derived content crop remained
|
||||
exact. This supports a fixture focus-state mismatch, without replacing the
|
||||
failed whole-output gate with a PASS. The run stopped before scored input and
|
||||
splith return; both and root/nested popups are NOT TESTED. No algorithm changed,
|
||||
no second run, no pristine run, three PNGs, one camera correction, ~4 seconds.
|
||||
All owned processes were reaped and the private runtime was removed.
|
||||
|
||||
Overall PARTIALLY_FEASIBLE; runtime comparison scenario INCONCLUSIVE;
|
||||
architectural conclusion UNDECIDED. The run itself does not demonstrate an
|
||||
ABI crash or forbidden target hit. Complete license closure, named release/
|
||||
security owners, broader graphical/ABI qualification and bit reproducibility
|
||||
remain open. Current package unchanged, STEP20C NEXT, production multi-output
|
||||
NOT YET SUPPORTED. See the retained fixture-corrections record for failed
|
||||
offline checks and corrected, unexecuted diagnostic behavior. A future ticket
|
||||
should first fix reference focus preflight, then explicitly authorize the
|
||||
minimal input and standard-return qualification.
|
||||
89
tooling/probes/step20c-static-runtime/audit.py
Normal file
89
tooling/probes/step20c-static-runtime/audit.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Single ELF audit and justified notice stage; never launches compositor code.
|
||||
|
||||
WHY: loader resolution, interposition and notice closure are separate gates.
|
||||
CONTRACT: only SceneFX is archived; system wlroots remains a shared provider.
|
||||
INVARIANT: each notice is verbatim, attributed and traced to a compiled input.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import stat
|
||||
from support import command,reserve,save,sha
|
||||
|
||||
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
|
||||
binary=lab/'stage-static/usr/bin/swayfx-mosaic';reserve(lab,'elf')
|
||||
raw={}
|
||||
for label,argv in [('elf-header',['readelf','-hW']),('elf-dynamic',['readelf','-dW']),('elf-symbols',['readelf','-Ws']),('elf-relocations',['readelf','-rW']),('elf-versions',['readelf','-VW']),('elf-notes',['readelf','-nW']),('elf-nm',['nm','--format=posix']),('elf-objdump',['objdump','-d']),('elf-ldd',['ldd','-r'])]:
|
||||
raw[label]=command(lab,label,[*argv,binary],timeout=60)
|
||||
assert 'libscenefx' not in raw['elf-dynamic'] and 'libscenefx' not in raw['elf-ldd']
|
||||
assert 'undefined symbol:' not in raw['elf-ldd'] and 'not found' not in raw['elf-ldd']
|
||||
assert 'libwlroots-0.20.so' in raw['elf-dynamic']
|
||||
assert '(RPATH)' not in raw['elf-dynamic'] and '(RUNPATH)' not in raw['elf-dynamic']
|
||||
clips=['wlr_scene_tree_set_clip','wlr_scene_tree_get_clip']
|
||||
for name in clips:
|
||||
assert re.search(r'^'+name+r' T ',raw['elf-nm'],re.M)
|
||||
assert re.search(r'call\s+[^\n]*<'+name+r'>',raw['elf-objdump']), 'static call site absent'
|
||||
needed=re.findall(r'\(NEEDED\).*\[([^]]+)\]',raw['elf-dynamic'])
|
||||
wl=Path('/usr/lib/libwlroots-0.20.so').resolve()
|
||||
wl_header=command(lab,'wlroots-elf',['readelf','-nW','-dW','-VW',wl],timeout=60)
|
||||
save(lab,'binary-elf',{'status':'PASS','path':str(binary),'sha256':sha(binary),'bytes':binary.stat().st_size,'DT_NEEDED':needed,'RPATH':[],'RUNPATH':[],'build_id':re.findall(r'Build ID: (\w+)',raw['elf-notes']),'header':raw['elf-header'],'versions_file':'raw/elf-versions.log'})
|
||||
save(lab,'static-symbols',{'status':'PASS','clips':clips,'definitions':'ordinary T executable symbols','call_sites':'raw/elf-objdump.log','semantic_ABI':'not established by ELF'})
|
||||
# Glibc diagnostic mode exits before the compositor entry point; no run debit.
|
||||
bindings=command(lab,'loader-bindings',['ldd','-r',binary],extra={'LD_DEBUG':'bindings'},timeout=60)
|
||||
records=[]
|
||||
for line in bindings.splitlines():
|
||||
m=re.search(r"binding file (.*?) \[0\] to (.*?) \[0\].*symbol `([^']+)'",line)
|
||||
if m and 'libwlroots' in m[1] and m[3].startswith('wlr_scene_'):
|
||||
rec={'consumer':m[1],'provider':m[2],'symbol':m[3]}
|
||||
if rec not in records:records.append(rec)
|
||||
save(lab,'dependency-resolution',{'status':'PASS','ldd_exit':0,'records':records,'executable_scene_bindings':sum(x['provider']==str(binary) for x in records),'wlroots_scene_bindings':sum('libwlroots' in x['provider'] for x in records),'runtime_identity':'must still be checked from /proc maps'})
|
||||
headers=[]
|
||||
for path in (lab/'wlroots-source/include').rglob('*.h'):
|
||||
rel=path.relative_to(lab/'wlroots-source/include');installed=Path('/usr/include/wlroots-0.20')/rel
|
||||
if installed.is_file():headers.append({'path':str(rel),'source_sha256':sha(path),'installed_sha256':sha(installed),'match':sha(path)==sha(installed)})
|
||||
assert headers and all(x['match'] for x in headers)
|
||||
metadata=list(Path('/var/lib/pacman/local').glob('wlroots0.20-*/desc'))
|
||||
save(lab,'wlroots-qualified-pair',{'SceneFX':'37ccd723bef49e6891156ffafce8f549f01446cc','wlroots_source':'d783533489e1f75d6886c2ab5c5960090ef268f8','wlroots_pkg_config':command(lab,'wlroots-pkg-config',['pkg-config','--modversion','--cflags','--libs','wlroots-0.20']),'library':str(wl),'sha256':sha(wl),'build_id':re.findall(r'Build ID: (\w+)',wl_header),'package_metadata':[{'path':str(x),'text':x.read_text()} for x in metadata],'headers':headers,'features_header':Path('/usr/include/wlroots-0.20/wlr/config.h').read_text(),'struct_layouts':'retained historical 17 structs/6 offsets only; no old ABI probe rerun','other_pairs':'NOT QUALIFIED'})
|
||||
# Compile inputs conservatively cover all SceneFX archive members. Some unused
|
||||
# members may be over-included; no dynamic system-library license dump is used.
|
||||
inputs=[];notice_records=[];payloads={}
|
||||
for tree in ('mosaic','scene'):
|
||||
build=lab/(tree+'-build');commands=json.loads((build/'compile_commands.json').read_text())
|
||||
for rec in commands:
|
||||
src=(build/rec['file']).resolve()
|
||||
if not src.is_file():continue
|
||||
inputs.append({'source':str(src),'sha256':sha(src),'object':rec.get('output'),'component':tree,'command':rec['command']})
|
||||
text=src.read_text(errors='strict')
|
||||
comments=[c for c in re.findall(r'/\*.*?\*/',text,re.S) if re.search(r'copyright|permission|redistribution|SPDX',c,re.I)]
|
||||
for comment in comments:
|
||||
# SPDX alone names a license; root license text is staged separately.
|
||||
if not re.search(r'copyright|permission|redistribution',comment,re.I):continue
|
||||
key=__import__('hashlib').sha256(comment.encode()).hexdigest()
|
||||
payloads.setdefault(key,comment)
|
||||
notice_records.append({'source':str(src),'source_sha256':sha(src),'notice_sha256':key,'component':tree,'generated':src.is_relative_to(build)})
|
||||
roots=[('SwayFX','swayfx-source/LICENSE'),('SceneFX','scene-source/LICENSE'),('wlroots','wlroots-source/LICENSE')]
|
||||
root_records=[]
|
||||
for name,rel in roots:
|
||||
src=lab/rel;root_records.append({'component':name,'source':str(src),'sha256':sha(src),'destination':f'/usr/share/licenses/swayfx-mosaic/{name}-LICENSE','reason':'compositor code' if name=='SwayFX' else 'static scene/renderer archive including copied wlroots code' if name=='SceneFX' else 'copied scene/renderer code within SceneFX; system wlroots stays dynamic'})
|
||||
assert notice_records and any(x['generated'] for x in notice_records)
|
||||
reserve(lab,'notices');dest=lab/'stage-static/usr/share/licenses/swayfx-mosaic';dest.mkdir(parents=True)
|
||||
for row in root_records:
|
||||
path=lab/'stage-static'/row['destination'].lstrip('/');path.write_bytes(Path(row['source']).read_bytes());path.chmod(0o644)
|
||||
# Each full comment is retained once with all provenance paths, including
|
||||
# generated protocol supporting-documentation and attribution clauses.
|
||||
notice_path=dest/'INCORPORATED-NOTICES'
|
||||
blocks=[]
|
||||
for key,text in payloads.items():
|
||||
sources=[r['source'] for r in notice_records if r['notice_sha256']==key]
|
||||
blocks.append('Sources:\n'+'\n'.join(sources)+'\n\n'+text)
|
||||
notice_path.write_text('\n\n'.join(blocks)+'\n');notice_path.chmod(0o644)
|
||||
save(lab,'license-inventory',{'status':'PARTIAL DISTRIBUTION QUALIFICATION; compiled/root/generated notices inventoried; legal/header/shader/exact extraction closure remains open','roots':root_records,'compiled_inputs':inputs,'source_notices':notice_records,'extra_archive_members':'conservative coverage of archive compile inputs; exact extraction of every unused member not claimed','dynamic_libraries':'not indiscriminately copied','notice_destination':'/usr/share/licenses/swayfx-mosaic/INCORPORATED-NOTICES'})
|
||||
save(lab,'license-files',[{'path':str(x.relative_to(lab/'stage-static')),'sha256':sha(x),'bytes':x.stat().st_size} for x in sorted(dest.iterdir())])
|
||||
# Compiled source provenance verifies actual config paths as well as Meson opts.
|
||||
config=(lab/'mosaic-build/include/config.h').read_text();(lab/'raw/compiled-config.h').write_text(config)
|
||||
compiled=(lab/'mosaic-build/compile_commands.json').read_text()
|
||||
assert '/usr/local' not in compiled and 'SYSCONFDIR' in compiled
|
||||
save(lab,'compiled-paths',{'prefix':'/usr','bindir':'bin','sysconfdir':'/etc (compiler spelling //etc)','source':'Meson build options, intro-installed.json and compile_commands.json'})
|
||||
print('PASS ELF, clip definitions/calls, relocations, header identity, notices; bindings',len(records))
|
||||
29
tooling/probes/step20c-static-runtime/clients.py
Normal file
29
tooling/probes/step20c-static-runtime/clients.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Compile byte-verified historical clients and the preserved pointer adaptation.
|
||||
|
||||
CONTRACT: no held transaction or new popup behavior is added to these fixtures.
|
||||
"""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from support import REPO,command,save,sha
|
||||
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve();src=lab/'client-source';src.mkdir()
|
||||
paths={'saved-client.c':'step20c-saved-target-timeout-input/saved-client.c','static-reference.c':'step20c-saved-target-timeout-input/static-reference.c','pointer-daemon.c':'step20c-valid-grab/pointer-daemon.c','virtual-pointer.xml':'step20c-valid-grab/wlr-virtual-pointer-unstable-v1.xml'}
|
||||
inventory=[]
|
||||
for name,rel in paths.items():
|
||||
origin=REPO/'tooling/probes'/rel;shutil.copy2(origin,src/name);assert sha(origin)==sha(src/name)
|
||||
inventory.append({'source':str(origin),'sha256':sha(origin),'destination':str(src/name)})
|
||||
patch=REPO/'tooling/probes/step20c-vertical-grab/pointer-extents.patch'
|
||||
command(lab,'pointer-check',['git','apply','--check',patch],src);command(lab,'pointer-apply',['git','apply',patch],src)
|
||||
for stem,xml in [('xdg-shell',Path('/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml')),('virtual-pointer',src/'virtual-pointer.xml')]:
|
||||
command(lab,stem+'-header',['wayland-scanner','client-header',xml,src/(stem+'-client.h')])
|
||||
command(lab,stem+'-code',['wayland-scanner','private-code',xml,src/(stem+'-protocol.c')])
|
||||
# The historical pointer include uses its original generated filename.
|
||||
shutil.copy2(src/'virtual-pointer-client.h',src/'virtual-pointer.h')
|
||||
flags=subprocess.check_output(['pkg-config','--cflags','--libs','wayland-client'],text=True).split()
|
||||
for name,proto in [('saved-client','xdg-shell'),('static-reference','xdg-shell'),('pointer-daemon','virtual-pointer')]:
|
||||
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])
|
||||
save(lab,'client-provenance',{'inputs':inventory,'pointer_patch':{'source':str(patch),'sha256':sha(patch)},'adapted_pointer_sha256':sha(src/'pointer-daemon.c'),'C17':'PASS'})
|
||||
63
tooling/probes/step20c-static-runtime/collect.py
Normal file
63
tooling/probes/step20c-static-runtime/collect.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Recalculate bounded results from retained raw evidence, without new execution.
|
||||
|
||||
WHY: a fixture stop must not be upgraded to a demonstrated compositor defect.
|
||||
CONTRACT: retain failed raw gates and explicit NOT TESTED downstream cells.
|
||||
INVARIANT: never alter old results/manifests or refund experiment reservations.
|
||||
"""
|
||||
import argparse
|
||||
from collections import Counter
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
from PIL import Image,ImageChops
|
||||
from support import REPO,save,sha
|
||||
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
|
||||
assert not (lab/'results/evidence-sha256.txt').exists(),'sealed results are immutable'
|
||||
run=lab/'run';text=(run/'compositor.log').read_text();runtime=json.loads((lab/'results/static-runtime.json').read_text())
|
||||
rows=[]
|
||||
for line in text.splitlines():
|
||||
m=re.search(r"binding file (.*?) \[0\] to (.*?) \[0\].*symbol `([^']+)'",line)
|
||||
if m and 'libwlroots' in m[1] and m[3].startswith('wlr_scene_'):
|
||||
row={'consumer':m[1],'provider':m[2],'symbol':m[3]}
|
||||
if row not in rows:rows.append(row)
|
||||
binary=lab/'stage-static/usr/bin/swayfx-mosaic';own=[r for r in rows if r['provider']==str(binary)];wl=[r for r in rows if 'libwlroots' in r['provider']]
|
||||
assert len(rows)==34 and len(own)==31 and len(wl)==3
|
||||
expected={'wlr_scene_subsurface_tree_create','wlr_scene_surface_try_from_buffer','wlr_scene_surface_create'}
|
||||
assert {r['symbol'] for r in wl}==expected
|
||||
save(lab,'runtime-bindings',{'status':'PASS','records':rows,'executable':31,'wlroots':3,'historical_pattern':'same 31/3 suppliers; full semantic ABI still unproven'})
|
||||
renderer=[s for s in text.splitlines() if re.search(r'\[render/fx_renderer/fx_renderer.c:\d+\].*(Using OpenGL|GL vendor:|GL renderer:)',s)]
|
||||
save(lab,'renderer',{'observed':renderer,'requested_env':'WLR_RENDERER=pixman','actual':'SceneFX FX / OpenGL ES, AMD Radeon 780M; headless outputs, not pixman renderer'})
|
||||
reference=Image.open(run/'h2-reference.png').convert('RGB');mosaic=Image.open(run/'h2-mosaic.png').convert('RGB');left=Image.open(run/'h1-mosaic.png').convert('RGB')
|
||||
# This fixed interior matches the mapped reference's IPC client bounds. The
|
||||
# comparison is supplemental evidence; it never replaces failed whole-H2 gate.
|
||||
events=[json.loads(x) for x in (run/'events.jsonl').read_text().splitlines()]
|
||||
trees=[e['result'] for e in events if e['kind']=='ipc' and e['args']==['-t','get_tree']]
|
||||
def nodes(n):
|
||||
yield n
|
||||
for child in n.get('nodes',[])+n.get('floating_nodes',[]):yield from nodes(child)
|
||||
refs=[n for n in nodes(trees[-1]) if n.get('app_id')=='org.mosaic.StaticReference' and n['rect']['x']>=0];assert len(refs)==1
|
||||
ref=refs[0];rect=ref['rect'];win=ref['window_rect'];deco=ref['deco_rect']
|
||||
# IPC rect/window_rect describe the standard reference content, while
|
||||
# applied Mosaic target geometry is retained separately from scene traces.
|
||||
x=rect['x']+win['x'];y=rect['y']+win['y'];box=(x,y,x+win['width'],y+win['height'])
|
||||
assert 0<=box[0]<box[2]<=800 and 0<=box[1]<box[3]<=600
|
||||
points=[(i%800,i//800,a,b) for i,(a,b) in enumerate(zip(reference.get_flattened_data(),mosaic.get_flattened_data())) if a!=b]
|
||||
interior_equal=ImageChops.difference(reference.crop(box),mosaic.crop(box)).getbbox() is None
|
||||
colors={(35,68,204),(32,208,160)}
|
||||
mask=Image.new('L',left.size);mask.putdata([255 if v in colors else 0 for v in left.get_flattened_data()]);target_box=mask.getbbox()
|
||||
save(lab,'pixel-reference-diagnostic',{'whole_H2_changed_pixels':len(points),'whole_H2_bbox':ImageChops.difference(reference,mosaic).getbbox(),'reference_IPC':ref,'measured_interior_box':box,'interior_equal':interior_equal,'all_differences_outside_interior':all(not(box[0]<=x<box[2] and box[1]<=y<box[3]) for x,y,_,_ in points),'color_changes':[{'before':a,'after':b,'count':c} for (a,b),c in Counter((a,b) for _,_,a,b in points).most_common(8)],'H1_target_color_bbox':target_box,'interpretation':'focus/decor changes supported by IPC and color changes; no whole-output PASS; no rerun'})
|
||||
for name in ['input-analysis','popup-ownership','standard-layout']:
|
||||
if not (lab/'results'/(name+'.json')).exists():save(lab,name,{'status':'NOT TESTED','reason':'single run stopped at whole-H2 reference gate before downstream measurement'})
|
||||
decisions=[{'id':'D1','decision':'private static SceneFX for immediate experimental STEP20C candidate'}, {'id':'D2','decision':'separate full notices for incorporated components in future private stage'}, {'id':'D3','decision':'project owns embedded SceneFX monitoring/review/rebuild/requalification; people and SLA unassigned'}, {'id':'D4','decision':'strict exact SceneFX/wlroots build pairs; stock updates independent; standard fallback'}]
|
||||
save(lab,'decisions',{'date':'2026-10-13','authority':'explicit user ticket','decisions':decisions,'scope':'documentation/private prototypes; current package unchanged','production':'not authorized'})
|
||||
save(lab,'security-maintenance-policy',{'responsible':'swayfx-mosaic project','individual_release_owner':None,'security_reviewer':None,'SLA':None,'required_before_public_distribution':'designate release maintainer and security reviewer','record':['SceneFX commit/provenance/checksums','ordered private patches','toolchain/options/dependencies','incorporated code/notices','qualified wlroots builds','tests','published binary hashes'],'monitor':['security fixes','renderer','shaders','dependencies','ABI','buffer lifetime','clipping','damage'],'workflow':['detect upstream changes/advisories','review relevance and exposure','review patch and clip interaction','rebuild','requalify pair','record evidence','separate publication decision'],'automatic_updates':False,'fallback':'ordinary SwayFX; never downgrade/replace system libraries for rollback'})
|
||||
save(lab,'upstream-direction',{'direction':'SCENEFX_UPSTREAM_MAINTAINED_CLIP_API','historical_dossier':str(REPO/'docs/design/STEP20C_SCENEFX_STATIC_DISTRIBUTION_CONTRACT.md'),'static':'transitory experimental preference','maintained_API':['ownership','errors','bounds','lifetime','hit testing','damage','versioning'],'popup_ownership':'Mosaic specific','contact':False,'adoption':'not presumed'})
|
||||
pair=json.loads((lab/'results/wlroots-qualified-pair.json').read_text());pair['qualification_scope']='build/model/stage/loader/headless startup and sampled tiled pixels; input/standard return unqualified';pair['required_after_update']=['identify exact pair','headers/symbols','shared layouts','rebuild','relocations','model tests','private stage/notices','authorized headless','relevant graphical paths','separate release decision'];save(lab,'wlroots-qualified-pair',pair)
|
||||
# Legal/source closure is intentionally distinguished from successful file staging.
|
||||
licenses=json.loads((lab/'results/license-inventory.json').read_text());licenses['status']='PARTIAL DISTRIBUTION QUALIFICATION; root/generated/source comments staged, exact archive extraction and final legal review remain open';save(lab,'license-inventory',licenses)
|
||||
not_tested=['root valid grab popup','nested popup','measured clipped input','standard layout return','all-frame damage','vertical/scaled/rotated geometry','migration/resize','saved buffers/effects/optimized blur','atomic applied generation','other wlroots versions','bit reproducibility','install/upgrade/rollback','final legal compliance']
|
||||
matrix={'build':'PASS','real_usr_prefix':'PASS','stage_with_notices':'PASS','ELF_and_relocations':'PASS','runtime_library_identity':'PASS','headless_startup_IPC_client_mapping':'PASS','static_clips_set_and_get':'OBSERVED','sampled_tiled_pixels':'retained H1 / zero target colors H2; reference interior identical','whole_H2_reference':'FAIL fixture gate; focus/decor mismatch','input':'NOT TESTED','standard_return':'NOT TESTED','runtime_scenario':runtime['status'],'other_cells':{x:'NOT TESTED' for x in not_tested}}
|
||||
save(lab,'matrix',matrix)
|
||||
save(lab,'summary',{'verdict':'PARTIALLY_FEASIBLE','architecture':'UNDECIDED','runtime_scenario':runtime['status'],'reason':'static build/stage/loader/headless function; reference fixture gate prevents input/standard return qualification; notice/security/full ABI gates remain','budget':json.loads((lab/'results/budget.json').read_text())['used'],'not_tested':not_tested,'reruns':0,'production':'NOT YET SUPPORTED','STEP20C':'NEXT'})
|
||||
print('PARTIALLY_FEASIBLE; runtime scenario INCONCLUSIVE; bindings 31/3; H2 interior equal',interior_equal)
|
||||
69
tooling/probes/step20c-static-runtime/prepare.py
Normal file
69
tooling/probes/step20c-static-runtime/prepare.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Reconstruct only the approved static chain in the one external LAB.
|
||||
|
||||
CONTRACT: unchanged historical patches, real /usr prefix, no live install.
|
||||
INVARIANT: reserve before each build/stage; failures are never retried.
|
||||
WHY: a staged historical /usr/local binary cannot qualify compiled /usr paths.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
from support import REPO,command,environment,reserve,save,sha
|
||||
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('lab',type=Path);p.add_argument('--scenefx-git',type=Path,required=True)
|
||||
p.add_argument('--historical',type=Path,required=True)
|
||||
a=p.parse_args();lab=a.lab.resolve();old=a.historical.resolve()
|
||||
assert lab.is_relative_to(Path.home()) and not lab.is_relative_to(REPO)
|
||||
assert not (lab/'prepare-started.json').exists()
|
||||
(lab/'prepare-started.json').write_text(json.dumps({'historical':str(old),'scene_store':str(a.scenefx_git)})+'\n')
|
||||
pins={'SwayFX':'fd71a6bdc061bd633b488ae7b83e8a6981d22f86',
|
||||
'Mosaic':'cd550382b311efdbfe3ed892f4e1d281d852e754',
|
||||
'SceneFX':'37ccd723bef49e6891156ffafce8f549f01446cc',
|
||||
'wlroots':'d783533489e1f75d6886c2ab5c5960090ef268f8'}
|
||||
for name,store,rev in [('mosaic-source',REPO,pins['Mosaic']),('scene-source',a.scenefx_git,pins['SceneFX']),('swayfx-source',REPO,pins['SwayFX'])]:
|
||||
arc=lab/(name+'.tar')
|
||||
with arc.open('wb') as out:subprocess.run(['git','-C',str(store),'archive',rev],stdout=out,check=True,env=environment())
|
||||
(lab/name).mkdir()
|
||||
with tarfile.open(arc) as tf:tf.extractall(lab/name,filter='data')
|
||||
# The sealed wlroots archive was hash-verified at A; copying never changes it.
|
||||
shutil.copy2(old/'wlroots-source.tar',lab/'wlroots-source.tar')
|
||||
(lab/'wlroots-source').mkdir()
|
||||
with tarfile.open(lab/'wlroots-source.tar') as tf:tf.extractall(lab/'wlroots-source',filter='data')
|
||||
chains={'scene-source':['step20c-valid-grab/scenefx-popup-plus-base.patch','step20c-temporal-damage/scenefx-timing.patch','step20c-optimized-blur-cache/scenefx-cache-trace.patch'],
|
||||
'mosaic-source':['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']}
|
||||
(lab/'patches').mkdir();inventory=[]
|
||||
for tree,chain in chains.items():
|
||||
for i,rel in enumerate(chain):
|
||||
src=REPO/'tooling/probes'/rel;dst=lab/'patches'/src.name
|
||||
assert sha(src)==sha(old/'patches'/src.name)
|
||||
shutil.copy2(src,dst);inventory.append({'source':str(src),'sha256':sha(src),'tree':tree,'order':i})
|
||||
command(lab,f'{tree}-patch-{i}-check',['git','apply','--check',dst],lab/tree)
|
||||
command(lab,f'{tree}-patch-{i}-apply',['git','apply',dst],lab/tree)
|
||||
historical=old/('scene-private' if tree=='scene-source' else 'mosaic-private')
|
||||
files={str(x.relative_to(historical)):sha(x) for x in historical.rglob('*') if x.is_file()}
|
||||
reconstructed={str(x.relative_to(lab/tree)):sha(x) for x in (lab/tree).rglob('*') if x.is_file()}
|
||||
assert files==reconstructed, 'historical source equivalence'
|
||||
save(lab,'source-versions',{'pins':pins,'patches':inventory,'source_equivalence':'PASS','archives':{x.name:sha(x) for x in lab.glob('*.tar')}})
|
||||
command(lab,'compiler',['cc','--version']);command(lab,'meson-version',['meson','--version'])
|
||||
command(lab,'dependencies',['pkg-config','--modversion','wlroots-0.20','scenefx-0.5','wayland-server','wayland-client','wayland-protocols','libdrm','xkbcommon','pixman-1'])
|
||||
reserve(lab,'scene_build')
|
||||
command(lab,'scene-setup',['meson','setup',lab/'scene-build',lab/'scene-source','-Ddefault_library=static','-Dexamples=false','-Dwerror=false'])
|
||||
command(lab,'scene-build',['meson','compile','-C',lab/'scene-build','-j','8'])
|
||||
reserve(lab,'mosaic_build')
|
||||
extra={'PKG_CONFIG_PATH':str(lab/'scene-build/meson-uninstalled')}
|
||||
flags=['--prefix=/usr','--bindir=bin','-Dwerror=false','-Dsd-bus-provider=libsystemd','-Dmosaic-package=true','-Dswaybar=false','-Dswaynag=false','-Dman-pages=disabled','-Ddefault-wallpaper=false']
|
||||
command(lab,'mosaic-setup',['meson','setup',lab/'mosaic-build',lab/'mosaic-source',*flags],extra=extra)
|
||||
command(lab,'mosaic-build',['meson','compile','-C',lab/'mosaic-build','-j','8'],extra=extra)
|
||||
command(lab,'mosaic-model',['meson','test','-C',lab/'mosaic-build','mosaic-model','--print-errorlogs'])
|
||||
options=json.loads((lab/'mosaic-build/meson-info/intro-buildoptions.json').read_text())
|
||||
values={x['name']:x['value'] for x in options};assert values['prefix']=='/usr' and values['bindir']=='bin'
|
||||
save(lab,'static-build',{'status':'PASS','flags':flags,'options':options,'archive_sha256':sha(lab/'scene-build/libscenefx-0.5.a'),'binary_sha256':sha(lab/'mosaic-build/sway/swayfx-mosaic'),'bit_reproducibility':'NOT TESTED; one build per contract'})
|
||||
reserve(lab,'stage')
|
||||
command(lab,'meson-stage',['meson','install','-C',lab/'mosaic-build','--no-rebuild','--destdir',lab/'stage-static'])
|
||||
assert sha(lab/'stage-static/usr/bin/swayfx-mosaic')==sha(lab/'mosaic-build/sway/swayfx-mosaic')
|
||||
save(lab,'meson-stage',{'files':[str(x.relative_to(lab/'stage-static')) for x in (lab/'stage-static').rglob('*') if not x.is_dir()]})
|
||||
print('PASS static builds, exact source equivalence, /usr prefix, model and single stage')
|
||||
169
tooling/probes/step20c-static-runtime/run.py
Normal file
169
tooling/probes/step20c-static-runtime/run.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/env python3
|
||||
"""One headless run of staged static compositor with measured tiled crossing.
|
||||
|
||||
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
|
||||
|
||||
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve()
|
||||
assert json.loads((lab/'results/static-stage-manifest.json').read_text())['status']=='PASS'
|
||||
assert json.loads((lab/'results/binary-elf.json').read_text())['status']=='PASS'
|
||||
reserve(lab,'run');run=lab/'run';run.mkdir();runtime=Path(tempfile.mkdtemp(prefix='msr-',dir='/tmp'))
|
||||
runtime.chmod(0o700);(run/'runtime-path').write_text(str(runtime)+'\n')
|
||||
(run/'config').write_text('output HEADLESS-1 resolution 800x600 scale 1 transform normal position -800 0\noutput HEADLESS-2 resolution 800x600 scale 1 transform normal position 0 0\nworkspace 1 output HEADLESS-1\nworkspace 2 output HEADLESS-2\nfocus_follows_mouse no\nseat seat0 hide_cursor 100\n')
|
||||
env=environment();env.update(XDG_RUNTIME_DIR=str(runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2',WLR_RENDERER='pixman')
|
||||
binary=lab/'stage-static/usr/bin/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):
|
||||
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,'source_box':[x+win['x'],y+win['y'],win['width'],win['height']],'raw':line}
|
||||
|
||||
try:
|
||||
compositor=spawn('compositor',[binary,'-d','-c',run/'config'],extra={'LD_DEBUG':'bindings','LD_BIND_NOW':'1'})
|
||||
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),'wrong executable'
|
||||
paths=sorted({line.split()[-1] for line in maps.splitlines() if len(line.split())>=6 and line.split()[-1].startswith('/')})
|
||||
pair=json.loads((lab/'results/wlroots-qualified-pair.json').read_text());wl=Path(pair['library'])
|
||||
assert not any('libscenefx' in x for x in paths),'wrong SceneFX shared mapping'
|
||||
loaded_wl=[Path(x) for x in paths if 'libwlroots' in x];assert len(loaded_wl)==1 and loaded_wl[0].resolve()==wl and sha(wl)==pair['sha256'],'wrong wlroots mapping'
|
||||
assert not any('/swayfx-mosaic-' in x and Path(x)!=binary for x in paths),'old LAB dependency'
|
||||
save(lab,'loader-mappings',{'status':'PASS','pid':compositor.pid,'exe':str(exe),'sha256':sha(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,'headless-outputs',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);wait(lambda:find('org.mosaic.StaticReference'))
|
||||
time.sleep(.4);inject('move',-790,590);baseline=capture('h2-reference','HEADLESS-2')
|
||||
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);wait(lambda:find('org.mosaic.SavedBufferProbe'))
|
||||
cmd('layout mosaic');time.sleep(.5)
|
||||
owner=find('org.mosaic.SavedBufferProbe');before=measure(owner);x,y,w,h=before['source_box'];desired=-w//2;delta=x-desired
|
||||
event('camera-calculation',before=before,desired_left=desired,delta=delta)
|
||||
if delta:reserve(lab,'camera');cmd(f'layout mosaic scroll {delta}')
|
||||
time.sleep(.5);owner=find('org.mosaic.SavedBufferProbe');after=measure(owner);x,y,w,h=after['source_box']
|
||||
assert x<0<x+w and -800<x,'real source crossing absent'
|
||||
save(lab,'scene-clips',{'before':before,'after':after,'delta':delta,'clips':[s for s in log('compositor').splitlines() if 'PI_SCENE output=HEADLESS-1' in s][-4:]})
|
||||
inject('move',-790,590);left=capture('h1-mosaic','HEADLESS-1');right=capture('h2-mosaic','HEADLESS-2')
|
||||
colors={(35,68,204),(32,208,160)};left_count=sum(v in colors for v in left.get_flattened_data());right_count=sum(v in colors for v in right.get_flattened_data())
|
||||
diff=ImageChops.difference(baseline,right).getbbox()
|
||||
save(lab,'pixel-analysis',{'retained_h1_target_pixels':left_count,'forbidden_h2_target_pixels':right_count,'whole_H2_difference_bbox':diff,'source_crossing':after['source_box']})
|
||||
assert left_count>0 and right_count==0 and diff is None,'forbidden pixel leakage/reference difference'
|
||||
visible_x=max(-780,min(-20,x+max(20,w//4)));visible_y=min(550,y+80)
|
||||
target_before=log('target').count('BUTTON ');inject('right',visible_x,visible_y);visible_buttons=log('target').count('BUTTON ')-target_before
|
||||
assert visible_buttons>=2,'visible target input missing'
|
||||
target_before=log('target').count('BUTTON ');ref_before=log('reference').count('BUTTON ')
|
||||
clipped_x=max(20,min(x+w-20,200));inject('right',clipped_x,visible_y)
|
||||
invisible_buttons=log('target').count('BUTTON ')-target_before;reference_buttons=log('reference').count('BUTTON ')-ref_before
|
||||
save(lab,'input-analysis',{'visible_point':[visible_x,visible_y],'visible_target_button_events':visible_buttons,'forbidden_point':[clipped_x,visible_y],'forbidden_target_button_events':invisible_buttons,'reference_button_events':reference_buttons})
|
||||
assert invisible_buttons==0 and reference_buttons>=2,'forbidden target hit/reference missing'
|
||||
inject('move',-790,590);repeat=capture('h2-mosaic-repeat','HEADLESS-2');assert ImageChops.difference(baseline,repeat).getbbox() is None,'unstable H2 reference'
|
||||
cmd('layout splith');time.sleep(.5);inject('move',-790,590)
|
||||
standard_left=capture('h1-standard','HEADLESS-1');standard_right=capture('h2-standard','HEADLESS-2')
|
||||
scene_lines=[s for s in log('compositor').splitlines() if 'PI_SCENE output=HEADLESS-1' in s]
|
||||
assert scene_lines and 'clip_valid=0' in scene_lines[-1],'residual Mosaic clip'
|
||||
standard_owner=find('org.mosaic.SavedBufferProbe');standard_measure=measure(standard_owner)
|
||||
sx,sy,sw,sh=standard_measure['source_box'];before_buttons=log('target').count('BUTTON ');inject('right',sx+sw//2,sy+min(80,sh//2));standard_buttons=log('target').count('BUTTON ')-before_buttons
|
||||
assert standard_buttons>=2 and ImageChops.difference(baseline,standard_right).getbbox() is None,'standard regression'
|
||||
save(lab,'standard-layout',{'status':'PASS sampled only','scene_trace':scene_lines[-1],'owner':standard_measure,'target_button_events':standard_buttons,'H2_equal':True})
|
||||
save(lab,'popup-ownership',{'status':'NOT TESTED','reason':'root-grab fixture not added to the single minimal tiled scenario; historical pristine/grab evidence retained'})
|
||||
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()
|
||||
elapsed=time.monotonic()-started
|
||||
save(lab,'static-runtime',{'status':'PASS' if failure is None else 'INCONCLUSIVE','failure':failure,'duration_seconds':elapsed,'binary':str(binary),'sha256':sha(binary),'process_exit_codes':[{'pid':x.pid,'argv':x.args,'exit':x.returncode} for x in processes],'runtime_removed':str(runtime),'cleanup':'all owned groups reaped'})
|
||||
shutil.rmtree(runtime)
|
||||
damage=[s for s in log('compositor').splitlines() if 'TEMPORAL_DAMAGE ' in s]
|
||||
save(lab,'damage-analysis',{'status':'OBSERVED' if damage else 'NOT TESTED','extents':damage,'all_frames':'NOT TESTED'})
|
||||
print('STATIC_RUNTIME',failure or 'PASS')
|
||||
if failure:raise SystemExit(1)
|
||||
33
tooling/probes/step20c-static-runtime/seal.py
Normal file
33
tooling/probes/step20c-static-runtime/seal.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Seal this completed LAB once, or verify every immutable manifest entry.
|
||||
|
||||
CONTRACT: exclusive creation; source files and raw/generated evidence separate.
|
||||
INVARIANT: no rewriting/refunds; report/diff stay outside sealed inventories.
|
||||
"""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from support import REPO,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 lab.is_relative_to(Path.home()) and not lab.is_relative_to(REPO)
|
||||
results=lab/'results'
|
||||
if not a.verify:
|
||||
assert not any((results/(n+'-sha256.txt')).exists() for n in ['source','evidence'])
|
||||
source=set(x for x in Path(__file__).parent.iterdir() if x.is_file())
|
||||
for tree in ['mosaic-source','scene-source','swayfx-source','wlroots-source','client-source','patches']:
|
||||
source.update(x for x in (lab/tree).rglob('*') if x.is_file())
|
||||
evidence=set(x for tree in ['raw','run','results','stage-static'] for x in (lab/tree).rglob('*') if x.is_file() and x.name not in ['source-sha256.txt','evidence-sha256.txt'])
|
||||
for build in ['mosaic-build','scene-build']:
|
||||
for name in ['build.ninja','compile_commands.json']:
|
||||
evidence.add(lab/build/name)
|
||||
evidence.update(x for x in (lab/build/'meson-info').glob('*.json'))
|
||||
evidence.update(x for x in (lab/build/'meson-logs').glob('*.txt'))
|
||||
evidence.add(lab/'scene-build/libscenefx-0.5.a')
|
||||
evidence.add(lab/'mosaic-build/sway/swayfx-mosaic')
|
||||
for name,files in [('source',source),('evidence',evidence)]:
|
||||
with (results/(name+'-sha256.txt')).open('x') as out:
|
||||
for path in sorted(files):out.write(f'{sha(path)} {path.resolve()}\n')
|
||||
for name in ['source','evidence']:
|
||||
path=results/(name+'-sha256.txt');lines=path.read_text().splitlines()
|
||||
for line in lines:
|
||||
digest,file=line.split(' ',1);assert sha(file)==digest,file
|
||||
print(name,len(lines),sha(path),'PASS')
|
||||
32
tooling/probes/step20c-static-runtime/stage.py
Normal file
32
tooling/probes/step20c-static-runtime/stage.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only stage verification; notices are added only by the one-shot audit.
|
||||
|
||||
CONTRACT: regular binary plus exactly inventoried regular notice files only.
|
||||
INVARIANT: links, devices, libraries, configs and unlisted files are rejected.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import stat
|
||||
from support import save,sha
|
||||
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);a=p.parse_args();lab=a.lab.resolve();stage=lab/'stage-static'
|
||||
licenses=json.loads((lab/'results/license-files.json').read_text())
|
||||
allowed={'usr/bin/swayfx-mosaic':sha(lab/'mosaic-build/sway/swayfx-mosaic')}
|
||||
allowed.update({x['path']:x['sha256'] for x in licenses});entries=[];seen=set()
|
||||
for path in sorted(stage.rglob('*')):
|
||||
s=path.lstat();rel=str(path.relative_to(stage));mode=stat.S_IMODE(s.st_mode)
|
||||
if stat.S_ISDIR(s.st_mode):
|
||||
assert any(x.startswith(rel+'/') for x in allowed),rel
|
||||
assert mode==0o755,(rel,mode)
|
||||
continue
|
||||
assert stat.S_ISREG(s.st_mode) and rel in allowed,rel
|
||||
assert sha(path)==allowed[rel] and mode==(0o755 if rel=='usr/bin/swayfx-mosaic' else 0o644),(rel,mode)
|
||||
if rel!='usr/bin/swayfx-mosaic':assert path.read_text().strip()
|
||||
seen.add(rel);entries.append({'path':'/'+rel,'type':'regular','mode':oct(mode),'uid':s.st_uid,'gid':s.st_gid,'bytes':s.st_size,'sha256':sha(path),'origin':'meson install' if rel=='usr/bin/swayfx-mosaic' else 'future package simulation'})
|
||||
assert seen==set(allowed)
|
||||
options=json.loads((lab/'mosaic-build/meson-info/intro-buildoptions.json').read_text());values={x['name']:x['value'] for x in options}
|
||||
assert values['prefix']=='/usr' and values['bindir']=='bin'
|
||||
compiled=(lab/'mosaic-build/compile_commands.json').read_text();assert '/usr/local' not in compiled
|
||||
save(lab,'compiled-paths',{'prefix':values['prefix'],'bindir':values['bindir'],'sysconfdir':values['sysconfdir'],'compiler_definition':'SYSCONFDIR=//etc; canonical /etc','compiled_command_provenance':'mosaic-build/compile_commands.json'})
|
||||
save(lab,'static-stage-manifest',{'status':'PASS','marker':'STATIC_STAGE_WITH_LICENSES=PASS','files':entries,'installation':'private user-owned DESTDIR; no package installed'})
|
||||
print('STATIC_STAGE_WITH_LICENSES=PASS',len(entries),'regular files')
|
||||
84
tooling/probes/step20c-static-runtime/support.py
Normal file
84
tooling/probes/step20c-static-runtime/support.py
Normal 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 = {'scene_build': 1, 'mosaic_build': 1, 'stage': 1, 'notices': 1,
|
||||
'elf': 1, 'run': 1, 'control': 1, 'capture': 8, 'camera': 1}
|
||||
|
||||
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')
|
||||
25
tooling/probes/step20c-static-runtime/test-budget.py
Normal file
25
tooling/probes/step20c-static-runtime/test-budget.py
Normal 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))==8
|
||||
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')
|
||||
Loading…
Reference in a new issue