docs(mosaic): record partial STEP20C overnight evidence

Record the annotated rebuild, bounded graphics matrices, private release-shape smoke, and remaining integration gates. Preserve STEP20C NEXT and production multi-output NOT YET SUPPORTED.
This commit is contained in:
fy59 2026-10-14 01:47:19 +02:00
parent 4ef16a4019
commit 30dc3182ab
38 changed files with 3147 additions and 0 deletions

View file

@ -127,3 +127,19 @@ STEP20C NEXT; production multi-output NOT YET SUPPORTED.
preserving the original README as a sealed pre-execution protocol. Final batch
audit is PASS / ACCEPTED with no BLOCKING finding. No production source, UX, package, public ABI
or cumulative patch change; STEP20C NEXT, multi-output NOT YET SUPPORTED.
## 2026-10-14 — preserved private STEP20C overnight qualification
- Record the fresh annotated private rebuild, exact local ABI and five-file
DESTDIR stage, 18 unique graphics attempts/196 captures, and separately built
release-shape horizontal smoke with its own binary identity.
- Preserve positive sampled clip/input observations alongside D2 fixture
ambiguity, saved-buffer NOT EXERCISED, failed whole-H1 effects restoration,
stale-coordinate F assertion, terminal IPC incidents and unproven lifetime,
atomic generation, full damage, legal and package qualification.
- Add the 22-question production integration decision draft and compact
source-only provenance. PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE remains a
recommendation; STEP20C NEXT and production multi-output NOT YET SUPPORTED.
Final independent review is ACCEPTED / audit PASS for experimental evidence
fidelity; documentary publication is prepared, not executed at this edition. No production
source, current package, UX or historical evidence contract is changed.

View file

@ -516,3 +516,28 @@ for normal publication.
supersede the original README's sealed pre-execution status only.
Architecture remains UNDECIDED for production integration; STEP20C NEXT,
production multi-output NOT YET SUPPORTED.
## Preserved overnight STEP20C qualification — 2026-10-14
The [overnight dossier](design/STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1.md)
records fresh private annotated and release-shape binaries with separate exact
identities. Sampled owner-derived root popup clipping and fully hidden-owner
empty clips agree with recorded pixels/input in the qualified windows;
release-shape G has its own horizontal smoke only. These are experimental
observations, not adopted runtime architecture or a production dependency.
Applied scene callbacks, client input-region commits and native output
commit/present events remain separate observations. No atomic transaction,
scene, hit or presented-buffer generation is established. Saved-buffer/GPU
reference and release lifetime, complete frame damage and instrumentation
neutrality remain NOT PROVEN. G retains PI listeners and framebuffer
bind-before-failure ordering after removing reviewed logging statements.
Local header/layout/symbol checks qualify only the exact recorded pair.
The compositor and its native tiling tree remain authoritative; Row 0 borrows
workspace->tiling and no additional runtime row or UX policy is adopted.
STEP20C remains NEXT, production multi-output NOT YET SUPPORTED. The decision
recommendation is PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE, not integration.
Final independent review is ACCEPTED / audit PASS for experimental evidence
fidelity. Documentary publication is prepared, not executed at this edition;
final publication identities belong to the private receipt and final handoff.

View file

@ -144,3 +144,18 @@ 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.
## Preserved overnight experimental reproduction
The [annotated rebuild/private-stage report](design/STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md)
and [source-only qualification index](../tooling/probes/step20c-overnight/qualification/README.md)
record exact pins, ordered patches, private Meson options, local ABI checks,
staging manifests and an immutable 18-run budget outcome. Reproduce only in a
new disposable LAB with verified retained inputs and a separately authorized
budget; the original budget is exhausted and no retry is authorized. Private
patches, source archives and future recipe remain local references, not a
self-contained publicly distributed build kit. Build/model/ELF/stage passed
locally; makepkg metadata/source verification/full build was NOT RUN. No binary
bit-reproducibility or distribution readiness is claimed. Experimental helper
fixtures do not add a production dependency. See the dossier for transport,
cleanup and unexercised-case limits before interpreting runtime results.

View file

@ -460,3 +460,29 @@ errors, separate from B's accepted104 tests and the historical206 result.
Final batch audit is PASS / ACCEPTED. Publication identities are recorded in
Git history and the private closeout receipt. STEP20C remains NEXT, production
multi-output NOT YET SUPPORTED; no daily-driver qualification follows.
## Preserved overnight STEP20C qualification — 2026-10-14
The [overnight qualification dossier](design/STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1.md)
records a fresh annotated private rebuild and its own bounded graphics evidence,
plus a separately built release-shape candidate with its own horizontal smoke.
The [exact matrix](design/STEP20C_ANNOTATED_GRAPHICS_MATRIX.md) contains 18 unique
runs and 196 captures, without retries. Horizontal C is FEASIBLE for sampled
cases; the overall result is PARTIALLY_ADVANCED and the integration recommendation
is PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE, a draft decision, not adoption.
Vertical, scaled, R90 and resize/migration observations remain bounded. D2's
fixture root association stopped before input/final qualification; saved-buffer
E2 was NOT EXERCISED; whole-H1 effects restoration failed in both controls;
F's stale-coordinate assertion stopped its remaining mutations. Atomic
transaction/input/present identity, complete damage and GPU buffer lifetime
remain NOT PROVEN. G is SMOKE_FEASIBLE horizontally, without inheriting A's
matrix or establishing instrumentation neutrality. Exact local ABI/staging
checks and future recipe syntax/input checks do not qualify future pairs,
complete makepkg reproduction, actual notice incorporation or legal distribution.
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
Production multi-output remains NOT YET SUPPORTED. No production, UX or current
package contract is changed. Final independent review is ACCEPTED / audit PASS for experimental evidence
fidelity. Documentary publication is prepared, not executed at this edition;
production integration remains undecided.

View file

@ -363,3 +363,14 @@ and the private closeout receipt. Complete damage,
atomic generation, wider static graphics/output coverage, maintained ABI and
legal/maintenance gates remain open. STEP20C NEXT, production multi-output
NOT YET SUPPORTED; no production/UX/packaging contract change.
Preserved [overnight qualification dossier](design/STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1.md):
EXPERIMENTAL / PARTIALLY_ADVANCED, with a [fresh annotated rebuild and private stage](design/STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md),
[18-run graphics matrix](design/STEP20C_ANNOTATED_GRAPHICS_MATRIX.md),
[lifecycle/effects/native-ordering limits](design/STEP20C_LIFECYCLE_EFFECTS_AND_NATIVE_ORDERING.md),
and [22-question production integration decision draft](design/STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md).
The [compact source-only qualification index](../tooling/probes/step20c-overnight/qualification/README.md)
preserves exact identities, budget and private provenance references. Recommendation:
PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE; no production decision applied.
STEP20C NEXT; production multi-output NOT YET SUPPORTED. Final independent review is ACCEPTED / audit PASS for experimental evidence
fidelity. Documentary publication is prepared, not executed at this edition.

View file

@ -459,3 +459,27 @@ maintained ABI, notice/legal closure and named maintenance roles remain gates.
This is STEP20C evidence, not implementation of roadmap STEP20D. STEP20A DONE;
STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED. Production multi-output
remains NOT YET SUPPORTED. No production/UX/packaging contract changes.
## Preserved overnight STEP20C qualification — 2026-10-14
The [overnight qualification dossier](design/STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1.md)
advances private evidence through a fresh annotated rebuild, 18 unique graphics
attempts/196 captures and a separately built release-shape horizontal smoke.
C horizontal is FEASIBLE for sampled cases; the overall scientific result is
PARTIALLY_ADVANCED. PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE is the proposed
integration decision, not an applied decision or READY status.
The next justified gate is targeted evidence with a new authorized scope and
budget: trigger an actual saved configure, diagnose whole-H1 effects restoration
against pristine, tie input assertions to applied geometry, and establish
relevant native lifetime/transaction/damage and instrumentation-neutrality
proof. Exact-pair requalification, complete package reproduction, actual notice
incorporation/legal review and release/security nominations remain open.
Popup hide/dismiss/reposition and grab click-through policy remain undecided.
Relevant canonical contracts must precede any future implementation.
STEP20A DONE; STEP20B EXPERIMENTAL; STEP20C NEXT; STEP20D–G PLANNED.
Production multi-output remains NOT YET SUPPORTED. No STEP20D work or production
integration follows automatically from documentary publication. Final independent
review is ACCEPTED / audit PASS for experimental evidence fidelity. Documentary
publication is prepared, not executed at this edition.

View file

@ -61,3 +61,17 @@ 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.
## Overnight private maintenance evidence
The [annotated reconstruction report](design/STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md)
qualifies only the exact private SceneFX 0.5 source and shared wlroots 0.20.2
build recorded there. Local header/layout/symbol/relocation checks establish no
future semantic ABI guarantee. Any relevant source or dependency build change
requires impact review, rebuild and fresh qualification of the new binary;
no previous SHA's graphics results are inherited. Release and security roles
remain unnamed, and no SLA is established. Future recipe and notices remain
PARTIALLY_FEASIBLE: syntax/input correspondence passed, complete makepkg and
human legal/distribution review remain open. The [decision draft](design/STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md)
records proposed monitoring, requalification and rollback without adopting a
production integration or changing normal SwayFX upgrade independence.

View file

@ -0,0 +1,72 @@
# STEP20C — matrice graphique du binaire annoté
> EXPERIMENTAL — édition documentaire du 2026-10-14, après revue finale ACCEPTED / audit PASS du dossier expérimental ; publication préparée, non exécutée à cette édition. Les résultats concernent des laboratoires privés ; aucune intégration production, installation ou décision UX. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED. Multi-output de production : NOT YET SUPPORTED. Les anciens rapports restent historiques et leurs résultats ne se transfèrent pas à un nouveau SHA.
L’autorité brute est le LAB privé `/home/fy59/swayfx-mosaic-overnight-mcolx0gq`. Les chemins LAB ci-dessous sont des références locales de preuve, pas des fichiers distribués. Les sources de fixture préservées se trouvent dans [tooling/probes/step20c-overnight](../../tooling/probes/step20c-overnight/qualification/README.md). La [provenance compacte](../../tooling/probes/step20c-overnight/qualification/provenance.json) lie les identités et reçus sans publier logs, images, binaires, archives ou patches candidats.
## Matrice réellement exécutée
18 tentatives uniques, aucun retry, 196 captures compositeur. Chaque run utilise un runtime/socket privé. Le renderer observé dans les startups est SceneFX FX, OpenGL ES 3.2 Mesa 26.2.4-arch1.1, AMD Radeon 780M ; `WLR_RENDERER=pixman` demandé ne constitue pas une preuve pixman. Identités de spawn et proc maps contrôlées.
| Identité | SHA256 | Build ID |
| --- | --- | --- |
| A annoté, utilisé C1/C2 et Mosaic D/E/F | `979dcd95ab43fa1e465d6e9d262485b73d186cedacd0f957059dd24f482ce6f4` | `fabfeb823754a0f5107f83eb077323ad17ddedd1` |
| G release-shape, smoke propre | `8063478b2d5948da3eb8b1a329978882dc1d045cf36932cc2145a78af20ba76a` | `83896fdb36f3fd21522becec0e8c5219e15f4b8e` |
| Contrôle pristine retenu | `8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494` | `298a9626b331139ff2f081ade9c4efdde2d4fb19` |
Paire locale : SceneFX 0.5.0 statique privé, wlroots 0.20.2 partagé, bibliothèque SHA256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`. Tous les autres builds/paires sont NOT QUALIFIED.
Géométries : C, D4, E, F et G ont H1 `[-800,0,800,600]`, H2 `[0,0,800,600]`, modes 800×600, scale1, normal ; D1 H1 `[0,0,800,600]`, H2 `[0,600,800,600]`. D2 H1 logique `[-800,0,800,600]`, mode1000×750, scale1,25/normal ; D3 même logique, mode750×1000, scale1,25/R90. H2 reste 800×600 scale1/normal. Les cinq marqueurs asymétriques réellement capturés établissent la frame PNG1000×750 avant les conversions D2/D3 : une conversion logique→PNG est appliquée une seule fois. La migration/resize D4 est une mutation mesurée, pas un autre run. Les rectangles utilisent des bornes half-open. Native SHM buffer, configure XDG, destination tiling/IPC assigned, scène applied et origine client globale, viewport logique, mode physique et pixels PNG restent des espaces distincts ; les mesures de destination ne sont pas une identité de buffer. Les adaptations de scale/rotation sont corroborées par marqueurs et wire, sans mélanger l’origine current et applied.
| Run | Binaire | PNG | Mesure / runtime / cleanup | Portée |
| --- | --- | --- | --- | --- |
| C0 | pristine | 10 | PASS / PASS / PASS | contrôle pristine horizontal |
| C1 | A | 10 | PASS / PASS / PASS | contrôle annoté splith |
| C2 | A | 17 | PASS / PASS / PASS | Mosaic, deux chaînes et retour standard |
| D1-pristine | pristine | 8 | PASS / INCONCLUSIVE / PASS | overflow vertical standard |
| D1-mosaic | A | 8 | PASS / INCONCLUSIVE / PASS | clip vertical + input/dismissal |
| D2-pristine | pristine | 9 | PASS / INCONCLUSIVE / PASS | scale1,25 standard + input |
| D2-mosaic | A | 7 | INCONCLUSIVE / INCONCLUSIVE / PASS | pixels/grabs ; association root fixture ambiguë |
| D3-pristine | pristine | 9 | PASS / INCONCLUSIVE / PASS | R90 standard + input |
| D4-pristine | pristine | 17 | PASS / INCONCLUSIVE / PASS | resize/migration standard |
| D4-mosaic | A | 17 | PASS / INCONCLUSIVE / PASS | resize/migration Mosaic ; input pendant migration non échantillonné |
| E1-mosaic | A | 12 | PASS / INCONCLUSIVE / PASS | hide/return même chaîne |
| E2-mosaic | A | 2 | INCONCLUSIVE / INCONCLUSIVE / PASS | saved configure non déclenché |
| E3-pristine | pristine | 16 | INCONCLUSIVE / INCONCLUSIVE / FAIL clients | ROI cache A/B/A ; full restoration échoue |
| E3-mosaic | A | 16 | INCONCLUSIVE / INCONCLUSIVE / FAIL clients | ROI/cache USE ; full restoration échoue |
| F-pristine | pristine | 8 | INCONCLUSIVE / INCONCLUSIVE / PASS | input regions ; sans trace privée native |
| F-mosaic | A | 4 | INCONCLUSIVE / INCONCLUSIVE / PASS | input regions ; assertion de géométrie périmée |
| G-smoke | G | 17 | PASS / PASS / PASS | preuve propre G horizontal seulement |
| D3-mosaic | A | 9 | PASS / INCONCLUSIVE / PASS | R90 v3 première exécution ; revue runtime acceptée |
Les trois runs C sont FEASIBLE dans leur scope ; D est PARTIALLY_FEASIBLE globalement. Les receipts structurés préservent serials réels, actions et géométries de chaque run dans la provenance compacte. Les mesures absentes ne sont pas inférées.
## Pixels, protocoles et input
C0/C1 : TOPLEVEL→A→B réel, deux grabs serial33/42, configure/ACK/SHM. A[-298,105,350,170], B[-198,165,300,130] traversent x0 ; entrée B puis A réelle sur H2. Huit images homologues C0/C1 entières exactement égales en RGB, sans masque ni tolérance. Fermeture B conservant A et fermeture owner normales, référence H2 finale exacte.
C2 : deux owners distincts, grabs33/42 puis91/100. A[-218,105,350,170], B[-118,165,300,130], owner appliqué[-318,25,636,573]. H1 conserve les pixels A/B ; H2 complet exact. B visible reçoit51/52 ; B-H2[91,195] ne reçoit aucun bouton, puis done/destroy B→A ; référence62/63 observée. Le retour splith du même owner/root/hierarchie est reconstitué sur deux callbacks old-Mosaic puis six applied-standard sans réactivation du clip, avec B111/112 puis A117/118. Aucun click-through universel.
D1 : A/B traversent y600 de75/115. Pristine H1 A21200/B2550 et H2 A6000/B19550 ; Mosaic H1 mêmes nombres, H2 A0/B0 et RGB exact. Grabs31/40 pristine,33/42 Mosaic. B H2 réel standard, absent côté Mosaic ; dismissal et référence62/63 observés. Le champ helper historique `crossing` x0 NOT EXERCISED n’est pas le prédicat vertical : revue y600 indépendante.
D2 : pristine H1 A45150/B40261, H2 A3120/B13260 ; Mosaic H1 A37650/B23961, H2 A0/B0, grabs35/44 réels. Le layer wallpaper de marqueurs crée avant l’owner un host descriptor POPUP ; le fixture exigeait à tort un root global unique/index1 alors que le root XDG est index2. Association source+temps+cardinalité, sans owner-ID natif direct. Arrêt consommé avec7 PNG et2 clics d’ouverture ; input B visible/interdit, dismissal et finals NOT EXERCISED. Aucun rerun D2 ni défaut compositor démontré.
D3 : pristine contrôle réel avec grabs35/44 et marqueurs. Mosaic v3 a été réparé/revu avant sa première exécution, freeze `7ae5f3ad8cc8cb738740c6a32fe7e1f336d20226b7813a03e22be3030d1507fb`, 1172 entrées ; archive v2 exécutée578 SHA conservée. Mesures runner PASS,9 PNG/4 clics/2 grabs35/44,1 caméra ; H1 A37650/B23961, H2 A0/B0 et480000 pixels exacts ; B visible, interdit puis dismissal final observés. La revue runtime indépendante `reviews/rotation-v3-runtime-review.md` est ACCEPTED / FEASIBLE_SAMPLED_R90 : 11 callbacks baseline avant target owner9 (les trois derniers concernent filler8), puis un callback mapped complet. Neuf négatifs sont refusés ; owner/root association bornée sans IDs natifs. Aucun retour splith testé dans D3.
D4 : resize H1→960 puis800, workspace/owner H1→H2→H1, IDs et ordre natif préservés, ancienne sortie restaurée exactement. Trois cellules, largeur minimale640/gap0, caméra demandée relative1920 clampée à960. Input durant migration NOT TESTED ; aucune identité de génération atomique.
G :17 PNG/8 clics/4 grabs,2 chaînes,2 caméras,1 retour splith. Maps G exact et absence SceneFX dynamique. Clips root/owner, H2 Mosaic/final exact, standard H2 A3120/B13260 et input wire corroborés ; cleanup propre. Verdict SMOKE_FEASIBLE horizontal, aucune matrice G complète ni neutralité héritée de A.
## Transport, limites et budgets
Toutes les IPC terminales ont code1 « Unable to receive IPC response », stdout vide, compositor0. Les politiques C/G permettent runtime PASS lorsque mesures/fermetures complètes ; matrix conserve runtime INCONCLUSIVE. Cette différence explicite n’est pas harmonisée et la cause de la perte de réponse n’est pas prouvée. E3 : deux clients par run code1/error32 après interruption, cleanup scientifique FAIL ; tous processus sont pourtant reaped et runtimes supprimés, sans watchdog/rescue. Les autres cleanups sont PASS.
Registre durable flock + remplacement atomique + fsync :2/2 SceneFX,2/2 Mosaic,2/2 stages,18/18 runs,196/220 captures, maximum20/run et12 pour F,150 s/run. Local C3,D1–D4 chacun2,E1=1,E2=1,E3=2,F=2,G=1. Quatre PNG de marqueurs générés sont des inputs séparés :200 nouvelles images ;78 copies d’assets upstream ne sont pas des captures. Aucun remboursement, reset ou relance. Hash final ledger `6ebedc65f30bf2c81f45b1487adf974a390a0049f632cfbb18bbbb00e3bfae3f`.
Revue indépendante C, premiers D et lifecycle/G acceptées pour ces faits seulement ; preuves/négatifs conservés même lorsqu’un premier script d’analyse a échoué puis été corrigé hors ligne. Revue R90 runtime acceptée ; revue finale du dossier expérimental ACCEPTED / audit PASS, distincte des gates production. Damage de toutes frames, lifetime GPU, génération atomique, sortie réelle non headless et UX popup universelle NOT PROVEN / NOT TESTED.
## Revue finale observée et publication préparée
Revue indépendante finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental : 0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; 180 checks hors ligne et 15 mutations de preuves réelles rejetées. Rapport privé `reviews/overnight-final-review.md`, SHA256 `bb4000dffb51b07b59e86f91e8dd76bc2c4f0b9513b48a52c9f6bd6c8c3f45af` ; reçu JSON SHA256 `72ba252092ca3375d344faae1c2026c8d5626fd190e87771d5c0c1ad8212c9b0`. Index scientifique post-exécution `seals/index.json`, SHA256 `cdf034e1f8466a9ae8a0e0a33aa8d0d0195273f0d1bc782667b50c3bcf682505`, contexte de preuve courant, pas un binding pré-exécution.
NB-01 est complété dans l’index compact depuis les grabs wire réels de chacun des huit runs concernés, avec lignes et hashes sources dans `parent/metadata-serial-completion.json`. Les stops fixtures, échecs E3 et incident IPC restent documentés. Les gates lifetime/génération/damage, couverture G, maintenance ABI/paquet/juridique et robustesse/coût des sidecars privés restent ouverts. L’acceptation documentaire ne qualifie pas la production et n’applique aucune décision. Publication préparée, pas encore exécutée à cette édition ; les refs/SHAs finaux et le résultat de publication feront foi dans le reçu privé et le handoff final.

View file

@ -0,0 +1,90 @@
# STEP20C — reconstruction annotée et staging privé
> EXPERIMENTAL — édition documentaire du 2026-10-14, après revue finale ACCEPTED / audit PASS du dossier expérimental ; publication préparée, non exécutée à cette édition. Les résultats concernent des laboratoires privés ; aucune intégration production, installation ou décision UX. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED. Multi-output de production : NOT YET SUPPORTED. Les anciens rapports restent historiques et leurs résultats ne se transfèrent pas à un nouveau SHA.
L’autorité brute est le LAB privé `/home/fy59/swayfx-mosaic-overnight-mcolx0gq`. Les chemins LAB ci-dessous sont des références locales de preuve, pas des fichiers distribués. Les sources de fixture préservées se trouvent dans [tooling/probes/step20c-overnight](../../tooling/probes/step20c-overnight/qualification/README.md). La [provenance compacte](../../tooling/probes/step20c-overnight/qualification/provenance.json) lie les identités et reçus sans publier logs, images, binaires, archives ou patches candidats.
## Reconstruction exacte A et portée H
A : PASS pour rejeu source, annotations, build, modèle, ELF, ABI locale et stage ; H : PARTIALLY_FEASIBLE pour préparation du paquet et maintenance. La compilation ne prouve pas un comportement graphique. Le nouveau A a ensuite ses propres preuves C/D/E/F ; G possède une identité et un smoke séparés.
| Source épinglée | Commit |
| --- | --- |
| SwayFX 0.6 | `fd71a6bdc061bd633b488ae7b83e8a6981d22f86` |
| Mosaic source historique | `cd550382b311efdbfe3ed892f4e1d281d852e754` |
| SceneFX 0.5 | `37ccd723bef49e6891156ffafce8f549f01446cc` |
| wlroots 0.20.2 | `d783533489e1f75d6886c2ab5c5960090ef268f8` |
La revue `reviews/phase-a-review.md` a vérifié les labels commit pax des quatre archives, leurs hashes et le rejeu strict. L’absence de `.git` dans une extraction n’invalide pas cette provenance ; l’avertissement initial d’inventaire est résolu par cette preuve. Huit patches fonctionnels/instrumentation sont rejoués dans l’ordre, puis deux patches de commentaires. `git apply --check` précède chaque application normale ; chaque arbre parent/résultat et les quatre fichiers annotés sont vérifiés. Une comparaison lexicale tenant compte des littéraux conserve tous les tokens C actifs. Les insertions peuvent décaler `__LINE__`. Le NOT REBUILT historique décrivait l’ancien D, pas A.
| Arbre / ordre | Patch | SHA256 |
| --- | --- | --- |
| scene-source / 0 | scenefx-popup-plus-base.patch | `c74c515b98da8f5f637e260e6a3755c7a75bd78d1c39e54a67850f1c383e30e7` |
| scene-source / 1 | scenefx-timing.patch | `7a7ef1875b559f65a3240a0016592da1da6a8537929d0e96b8aaa2d20ecd46fa` |
| scene-source / 2 | scenefx-cache-trace.patch | `a0fd5a64156a79b1605ff3b52bd9accb20390199b2b36e1970389f5086b68f39` |
| mosaic-source / 0 | mosaic-popup-plus-base.patch | `d416f6413ea097a48a44d231e50ffba363914f46c8687f0ea2e4dcacd5f31fe9` |
| mosaic-source / 1 | hidden-guard.patch | `35785a8f3e863b3c1fd87932d8ff0c2949b03cb04382cc34e1126170968f802b` |
| mosaic-source / 2 | mosaic-output-timing.patch | `4419059231e038675f50a6321889f27ba981c5d8df2779ccbc4a61e02f1d6668` |
| mosaic-source / 3 | presentation-events.patch | `dbf1f1d11c9c741b641916d25ac22bcb13c3455ee3002e1f534aa6794cd8a538` |
| mosaic-source / 4 | sway-source-trace.patch | `b83ff12bf24ee879cc713a134b0c312e9b8bd1b4034ecc2aea124d55a743ebb7` |
| annotations | scenefx-comments.patch | `21f057ff4180bb5dd35af35070aa4a95923be83f81abb864793e5bfb93643b01` |
| annotations | mosaic-comments.patch | `7fee137101abc0cd5024cea172c5904268ddf995bc5855c570e4dfabd643c970` |
| Identité | SHA256 | Build ID |
| --- | --- | --- |
| A annoté, utilisé C1/C2 et Mosaic D/E/F | `979dcd95ab43fa1e465d6e9d262485b73d186cedacd0f957059dd24f482ce6f4` | `fabfeb823754a0f5107f83eb077323ad17ddedd1` |
| G release-shape, smoke propre | `8063478b2d5948da3eb8b1a329978882dc1d045cf36932cc2145a78af20ba76a` | `83896fdb36f3fd21522becec0e8c5219e15f4b8e` |
| Contrôle pristine retenu | `8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494` | `298a9626b331139ff2f081ade9c4efdde2d4fb19` |
Paire locale : SceneFX 0.5.0 statique privé, wlroots 0.20.2 partagé, bibliothèque SHA256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`. Tous les autres builds/paires sont NOT QUALIFIED.
L’ancien D `616aa504334d88aada5b1596df624957f39b0f1ae975a1a04105fbf73aea6dd0` et l’ancien candidat C `f82557…` sont historiques. Aucun résultat de ces identités ne qualifie A ou G. Les patches cumulatifs annotés privés sont `candidate-cumulative.patch` (`8fea59b98907f5c57f841e624c53437d0fe886b56ccf80f472be8d08389b8ca0`) et `scenefx-cumulative.patch` (`7e1549d68d33945620690d26ee6b1a6cc2f3942e4000c9ac89d79a317f136302`) ; leurs hashes diffèrent volontairement des cumulatifs historiques non annotés.
## Reproduction bornée
Reconstituer les inputs vérifiés dans un nouveau LAB ; ne pas utiliser le checkout production comme pristine. Le point d’entrée source est `tooling/probes/step20c-overnight/replay_annotated.py LAB --historical HISTORICAL`, où HISTORICAL est le dossier privé `integration-candidate` retenu avec ses archives, patches et scripts (`/home/fy59/swayfx-mosaic-e2-completion-dk1hq9an/integration-candidate` dans cette campagne). Ce script initialise un nouveau registre, vérifie le rejeu et exécute les builds/stage ; son lancement n’est pas autorisé par ce rapport documentaire et ne doit jamais viser le LAB consommé. Les dépendances privées doivent être conservées et vérifiées avant une reproduction. Les commandes Meson ci-dessous décrivent celles du script, pas une voie permettant de contourner les réservations. Les scripts de fixture et leurs manifests décrivent le rejeu. Le registre réserve avant setup/build/stage, sans reset ni remboursement. Les commandes A effectivement enregistrées dans `raw/commands.jsonl` sont reproduites ci-dessous avec `LAB` désignant ce nouveau répertoire ; `PKG_CONFIG_PATH` reste limité à chaque processus.
```sh
meson setup "$LAB/scene-build" "$LAB/scene-source" --prefix=/usr \
-Ddefault_library=static -Dexamples=false -Dwerror=false
meson compile -C "$LAB/scene-build" -j 8
PKG_CONFIG_PATH="$LAB/scene-build/meson-uninstalled" meson setup \
"$LAB/mosaic-build" "$LAB/mosaic-source" --prefix=/usr --bindir=bin \
-Dwerror=false -Dsd-bus-provider=libsystemd -Dmosaic-package=true \
-Dswaybar=false -Dswaynag=false -Dman-pages=disabled -Ddefault-wallpaper=false
PKG_CONFIG_PATH="$LAB/scene-build/meson-uninstalled" meson compile -C "$LAB/mosaic-build" -j 8
meson test -C "$LAB/mosaic-build" --no-rebuild mosaic-model --print-errorlogs
meson install -C "$LAB/mosaic-build" --no-rebuild --destdir "$LAB/stage-static"
```
Les notices sont ajoutées par simulation privée du futur paquet, puis le manifeste complet est audité. A : SceneFX C23, Mosaic C11 ; audit de layouts C17 `-Wall -Wextra -Werror`. Les 132 commandes A, leurs dates Unix, durées et codes sont conservées ; les dates UTC exactes des setups/compilations figurent dans la provenance compacte. Compilation SceneFX environ 0,90 s, Mosaic environ 7,81 s ; setup séparé. Les deux `git diff --no-index` code 1 produisent les patches privés, sans échec de compilation. Les commandes ultérieures `g-*` dans le même journal ne font pas partie des 132 commandes A.
G : second build/stage autorisé, exécuté le 2026-10-14T00:35:52+02:00 avec les mêmes options et chemins `release-shape/`. Modèle 1/1, audit C17 et stage PASS. Sortie intégrale Meson/Ninja non conservée par l’adaptateur : les journaux/résultats/extraits existent, sans prétendre à un stdout complet. Les spans `.ninja_log` sont des timings internes et ne remplacent pas une durée murale. Un avertissement `LIBINPUT_CONFIG_SCROLL_CIRCULAR` dans `sway/ipc-json.c:1175` est conservé.
## ELF et ABI locale
A et G : ELF64 x86-64 PIE, aucune dépendance SceneFX DT_NEEDED, wlroots partagé attendu, RPATH/RUNPATH vides, `ldd -r` sans symbole indéfini, fonctions clip définies et appels directs inspectés. Comparaison locale de 17 structures et 6 offsets identique ; 121 headers wlroots épinglés ; 85 noms communs caractérisés, distincts des 34 bindings réellement observés par le loader diagnostique. Ni les 85 fournisseurs runtime universels ni l’ABI sémantique future, l’interposition générale ou la neutralité graphique ne sont démontrés.
## Stage et notices
Exactement cinq fichiers réguliers dans chacun des deux DESTDIR, binaire 0755 et notices 0644. Le manifeste A est ci-dessous ; G garde le même périmètre mais ses propres hashes, liés dans la provenance. Aucun système installé.
| Chemin | Octets A | SHA256 A |
| --- | --- | --- |
| `/usr/bin/swayfx-mosaic` | 6760288 | `979dcd95ab43fa1e465d6e9d262485b73d186cedacd0f957059dd24f482ce6f4` |
| `/usr/share/licenses/swayfx-mosaic/INCORPORATED-NOTICES` | 255102 | `cf69048aad394c4d3e3dc875c1b9aae1922946af861fec18e337f0b45aacd7fb` |
| `/usr/share/licenses/swayfx-mosaic/SceneFX-LICENSE` | 1096 | `ffd3737a478b83a8b51b42757d3bf909ef36694508355879722e11fc1fa6736b` |
| `/usr/share/licenses/swayfx-mosaic/SwayFX-LICENSE` | 1061 | `bd35722bff46327f5eded1d211207277bbe043d2a22048b282c42ef801593b8b` |
| `/usr/share/licenses/swayfx-mosaic/wlroots-LICENSE` | 1140 | `35d427c043dcafe8893b9e7247348f599847c81d9a067703587c80707f3d58df` |
L’inventaire A couvre conservativement 895 inputs, 599 headers, 12 shaders, 7 XML et 282 blocs de notice ; G 894 inputs, 598 headers, 12 shaders, 7 XML, 16 entrées générées et 282 blocs. `parent/package-input-verification.json` a vérifié les 895 hashes A et les cinq fichiers du stage sans mismatch. Ce sont des contributions potentielles de compilation, pas une preuve d’extraction exacte de chaque membre d’archive ni des branches préprocesseur réellement incorporées. Validation humaine juridique et distribution : PARTIAL / NOT QUALIFIED.
`PKGBUILD.future` reste privé, SHA256 `50b098411d1234bc42f1abc748b312ce1be7ada8efe24be7d9ddb29481cba43d`. Syntaxe shell et Python du heredoc, sept inputs sources et allowlist vérifiés PASS. `makepkg --printsrcinfo`, `--verifysource`, build complet et installation : NOT RUN. Recette reproductible au sens d’un dossier d’inputs local contrôlé seulement ; aucune reproductibilité binaire publique ou distribution Arch validée. Le PKGBUILD réel, le patch production et la dépendance système restent inchangés.
H propose surveillance, revue d’impact, rebuild/requalification de toute paire modifiée, notices, stage, décision séparée et rollback. Release maintainer et security reviewer non nommés ; aucun SLA. Voir [décision draft](STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md).
## Revue finale observée et publication préparée
Revue indépendante finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental : 0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; 180 checks hors ligne et 15 mutations de preuves réelles rejetées. Rapport privé `reviews/overnight-final-review.md`, SHA256 `bb4000dffb51b07b59e86f91e8dd76bc2c4f0b9513b48a52c9f6bd6c8c3f45af` ; reçu JSON SHA256 `72ba252092ca3375d344faae1c2026c8d5626fd190e87771d5c0c1ad8212c9b0`. Index scientifique post-exécution `seals/index.json`, SHA256 `cdf034e1f8466a9ae8a0e0a33aa8d0d0195273f0d1bc782667b50c3bcf682505`, contexte de preuve courant, pas un binding pré-exécution.
NB-01 est complété dans l’index compact depuis les grabs wire réels de chacun des huit runs concernés, avec lignes et hashes sources dans `parent/metadata-serial-completion.json`. Les stops fixtures, échecs E3 et incident IPC restent documentés. Les gates lifetime/génération/damage, couverture G, maintenance ABI/paquet/juridique et robustesse/coût des sidecars privés restent ouverts. L’acceptation documentaire ne qualifie pas la production et n’applique aucune décision. Publication préparée, pas encore exécutée à cette édition ; les refs/SHAs finaux et le résultat de publication feront foi dans le reçu privé et le handoff final.

View file

@ -0,0 +1,63 @@
# STEP20C — lifecycle, effets et ordre natif
> EXPERIMENTAL — édition documentaire du 2026-10-14, après revue finale ACCEPTED / audit PASS du dossier expérimental ; publication préparée, non exécutée à cette édition. Les résultats concernent des laboratoires privés ; aucune intégration production, installation ou décision UX. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED. Multi-output de production : NOT YET SUPPORTED. Les anciens rapports restent historiques et leurs résultats ne se transfèrent pas à un nouveau SHA.
L’autorité brute est le LAB privé `/home/fy59/swayfx-mosaic-overnight-mcolx0gq`. Les chemins LAB ci-dessous sont des références locales de preuve, pas des fichiers distribués. Les sources de fixture préservées se trouvent dans [tooling/probes/step20c-overnight](../../tooling/probes/step20c-overnight/qualification/README.md). La [provenance compacte](../../tooling/probes/step20c-overnight/qualification/provenance.json) lie les identités et reçus sans publier logs, images, binaires, archives ou patches candidats.
## E1 — owner caché et retour
Même chaîne TOPLEVEL→A→B, mêmes identités et grabs ; hide donne clip root vide et pixels A/B nuls, puis le retour conserve la hiérarchie sans recreation et restaure l’entrée B réelle. Les fenêtres hide/return sont rejouées, H2 RGB exact. Une motion sans enter pendant hide est observée ; elle ne prouve pas tous les hits possibles. Mesures PASS, runtime INCONCLUSIVE, cleanup PASS. Aucun UX hide/dismiss/reposition final ni click-through universel décidé.
## E2 — saved buffers non exercés
Configure29 puis30 ACKés et `hold_next_configure` armé sont réels ; aucun configure ultérieur ni HELD, deux PNG de référence seulement. Source A `sway/commands/gaps.c` : `gaps inner 30` avec argc2 appelle `gaps_set_defaults`, modifie la config sans arranger le workspace courant ; la branche runtime demande quatre arguments. Le succès IPC n’est pas un configure mesuré. Saved-node identity/render, timeout effectif et lifetime : NOT EXERCISED / NOT PROVEN ; runtime INCONCLUSIVE. Aucun timeout artificiellement prolongé ni rerun.
L’audit source décrit `view_save_buffer` : arbre saved frère de content_tree, destination/source/transform copiés, buffer courant locké via SceneFX set_buffer ; suppression détruit l’arbre et réactive le contenu. Cela reste SOURCE AUDIT ONLY sur A, pas une observation runtime des identités saved/locks/releases. Les anciens SB/BI/ASI/HIT instruments et résultats appartiennent à d’autres binaires et ne sont pas incorporés ici.
## E3 — effets et cache : résultat partiel
Deux tentatives,16 PNG chacune. A/B modifie226908 pixels pristine et182214 Mosaic. Whole-H1 A/A-return reste différent de33883 pixels pristine (bbox402,25–798,598) et32132 Mosaic (bbox482,25–800,598). Le critère de restauration entière échoue réellement dans les deux contrôles ; aucune causalité propre à Mosaic n’est établie. La ROI centrale64×64 change ses4096 pixels A→B puis retrouve exactement A ; quatre ROI adjacentes retrouvent A. Fraîcheur RGB échantillonnée positive, restauration full/cache/damage complet NON démontrée.
Native Mosaic observe régénération naturelle réussie et optimized=1 USE d’un buffer non nul. Sept états H2 Mosaic restent identiques à la référence. Shadow-on ne change aucun pixel H1, mais déborde sur21598 pixels H2 pristine (bbox0,0–36,600) ; H2 Mosaic reste exact. Les captures classic-floating off/on sont entièrement égales, ROI268,268–332,332 et métrique adjacente0→0 : filtration classic NOT EXERCISED. Classic tiled Mosaic NOT TESTED et optimized tiled spill standard non exercé par ce glass intérieur.
Les deux runs s’arrêtent avant effects-evidence et fermeture normale/final capture ; glass/background code1/error32 après teardown. Cleanup scientifique FAIL malgré tous les PID reapés et runtime supprimé sans rescue. Ne pas transformer l’absence de résidu en fermeture normale réussie. Verdict PARTIAL_RGB_FRESHNESS_WHOLE_RESTORATION_FAIL_INCONCLUSIVE.
## F — état appliqué, entrée et ordre natif
Pristine : trois clics/commits region réels, owner ne reçoit pas de bouton lorsque region vide, restauration mesurée ;8 PNG, verdict INCONCLUSIVE et aucune PI privée native dans ce contrôle non modifié.
Mosaic : input full→empty→full observé, trois clics,4 PNG, deux livraisons owner autour de l’état vide. Assertion échoue parce qu’elle conserve l’origine initiale(-318,25) pour le clic(-314,30), attend(4,5), alors que l’origine appliquée après restauration est(-638,25), cohérente avec motion wire(324,5) et bouton45/46. Aucun défaut de routing n’en découle ; focus/resize/layout/close suivants NOT EXERCISED. Runtime INCONCLUSIVE, cleanup PASS.
Les12 premiers couples output/commit_seq natifs analysés sur57 commits bruts ont10 PAIRED et2 MISSING_PRESENT : seq2 H1 et H2. Les45 autres commits restent dans le brut sans être transformés en preuve de pairing. Un event headless present `presented=1` ne démontre pas les pixels du buffer ni une présentation physique. Il serait incorrect de dire12/12 presents ou lifetime qualifié.
Source wlroots : incrément commit_seq et signal commit synchrone, present headless différé idle, idle annulé au destroy. Source instrumentation : PI_SCENE/OWNER/POPUP échantillonne la scène appliquée dans le callback output commit, PI_POPUP sans owner-ID natif. Les flux séparés ne forment pas une génération atomique.
| Contrat demandé | État réel |
| --- | --- |
| Géométrie/clip/input observés dans les fenêtres documentées | Échantillonnés, cohérents pour les mesures citées |
| Mutation naturelle commit→input change→present discriminante | NOT EXERCISED |
| Epoch/génération atomique transactions/scène/hit/present | NOT PROVEN |
| Buffer exact présenté et identité native hit | NOT PROVEN |
| Locks/références/releases GPU, saved, teardown exhaustifs | NOT PROVEN |
| Damage complet de toutes frames et stale pixels intermédiaires | NOT PROVEN |
| Damage minimal | NOT PROVEN |
TEMPORAL_DAMAGE donne temps monotoniques/extents, distincts des captures. Des extents échantillonnés ne prouvent ni toutes frames ni tous pixels ; pas de nouvelle conclusion d’atomicité ou damage-to-present. Aucun GPU readback ajouté à A/G.
## G et neutralité de l’instrumentation
Le patch privé `release-shape/release-removal.patch` (`6e8a5b50149732ac70ecd79ab8db9258956f59036aba1bf17a477866bc44a5c1`) retire26 statements de log dans7 fichiers et les helpers démontrés orphelins de trace ; hashes parents/résultats et bindings de revue vérifiés. Les patches fonctionnels clip/owner guard restent. PI listeners et ordre FBO bind avant le chemin d’échec restent : cet ordre peut avoir un effet non passif et sa neutralité n’est pas prouvée. « Sans instrumentation » serait faux.
G a un build/stage et un smoke horizontal propres, SMOKE_FEASIBLE ; aucune qualification complète de son lifecycle/effets ni équivalence générale trace-off/on héritée. Un nouveau SHA nécessitera contrôles et matrice appropriés.
## Validation et limites de provenance
Revue lifecycle/G :32 checks hors ligne sur vrais logs/PNG plus négatifs parent/grab/root/owner/input stale/present/RGB ; archive exécutée v2 exacte578 entrées. Revue premiers D :14 négatifs ; C :7 mutations de preuves ; R90 v3 :9 négatifs. Les échecs initiaux d’extraction/analyse et corrections hors ligne sont conservés, sans réexécution graphique. Les preuves sont `reviews/matrix-lifecycle-runtime-review.{md,json}`, `matrix-lifecycle-analysis.json` et `parent/native-source-audit.json` dans le LAB.
Tous les runtimes matrix ont terminal IPC1 conservé ; le chemin individuel de runtime matrix n’a pas été journalisé. `runtime_removed=true`, tous PID absents et audit parent de résidus sont observés ; suppression de chaque ancien chemin nommé n’est pas indépendamment revérifiable. La clôture parent vérifie absence de PID/socket privés. Le hash SceneFX système initial n’a pas été enregistré ; hash final seul et ctime antérieure ne doivent pas être décrits comme une comparaison initial/final. Aucun usage IPC de session desktop ni installation système.
## Revue finale observée et publication préparée
Revue indépendante finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental : 0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; 180 checks hors ligne et 15 mutations de preuves réelles rejetées. Rapport privé `reviews/overnight-final-review.md`, SHA256 `bb4000dffb51b07b59e86f91e8dd76bc2c4f0b9513b48a52c9f6bd6c8c3f45af` ; reçu JSON SHA256 `72ba252092ca3375d344faae1c2026c8d5626fd190e87771d5c0c1ad8212c9b0`. Index scientifique post-exécution `seals/index.json`, SHA256 `cdf034e1f8466a9ae8a0e0a33aa8d0d0195273f0d1bc782667b50c3bcf682505`, contexte de preuve courant, pas un binding pré-exécution.
NB-01 est complété dans l’index compact depuis les grabs wire réels de chacun des huit runs concernés, avec lignes et hashes sources dans `parent/metadata-serial-completion.json`. Les stops fixtures, échecs E3 et incident IPC restent documentés. Les gates lifetime/génération/damage, couverture G, maintenance ABI/paquet/juridique et robustesse/coût des sidecars privés restent ouverts. L’acceptation documentaire ne qualifie pas la production et n’applique aucune décision. Publication préparée, pas encore exécutée à cette édition ; les refs/SHAs finaux et le résultat de publication feront foi dans le reçu privé et le handoff final.

View file

@ -0,0 +1,72 @@
# SWAYFX_MOSAIC_STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1
> EXPERIMENTAL — édition documentaire du 2026-10-14, après revue finale ACCEPTED / audit PASS du dossier expérimental ; publication préparée, non exécutée à cette édition. Les résultats concernent des laboratoires privés ; aucune intégration production, installation ou décision UX. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED. Multi-output de production : NOT YET SUPPORTED. Les anciens rapports restent historiques et leurs résultats ne se transfèrent pas à un nouveau SHA.
L’autorité brute est le LAB privé `/home/fy59/swayfx-mosaic-overnight-mcolx0gq`. Les chemins LAB ci-dessous sont des références locales de preuve, pas des fichiers distribués. Les sources de fixture préservées se trouvent dans [tooling/probes/step20c-overnight](../../tooling/probes/step20c-overnight/qualification/README.md). La [provenance compacte](../../tooling/probes/step20c-overnight/qualification/provenance.json) lie les identités et reçus sans publier logs, images, binaires, archives ou patches candidats.
## Bilan proposé
Ticket exact : **SWAYFX_MOSAIC_STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1**. État global proposé **PARTIALLY_ADVANCED**, sous réserve de publication observée, recommandation **PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE** ; jamais PRODUCTION_READY. Aucun gate production fermé par ce ticket. Le budget est clos après18 tentatives uniques et196 captures, sans retry. A et G ont chacun build/stage propres ; les mesures positives et les limites ci-dessous restent simultanément vraies.
## Phases A–J et verdicts structurés
| Phase | Verdict enregistré | Preuve / limite |
| --- | --- | --- |
| A replay annoté | PASS | Pins archives pax,8 patches +2 commentaires,4 fichiers tokens équivalents ; nouveau SHA propre |
| A build/model/ELF | PASS local | SceneFX statique/Mosaic, modèle1/1, ELF/relocations/layouts ; ABI future non établie |
| A stage | STATIC_STAGE_WITH_LICENSES=PASS |5 fichiers exacts, DESTDIR privé ; legal PARTIAL |
| B préflight | PASS | Contrôles fixture/identité/budget prélaunch revus ; erreurs préflight corrigées hors ligne avant les runs concernés |
| C horizontal | FEASIBLE sampled |3 runs pristine/annotated-standard/Mosaic→standard ; preuve A propre |
| D géométries | PARTIALLY_FEASIBLE |D1/D3/D4 mesures positives, D2-Mosaic INCONCLUSIVE association fixture ; runtime tous INCONCLUSIVE |
| E1 hide/return | SAMPLED_MEASUREMENTS_PASS_RUNTIME_INCONCLUSIVE |Même hiérarchie, clips/pixels/input échantillonnés |
| E2 saved | NOT_EXERCISED_INCONCLUSIVE |Trigger defaults n’arrange pas courant, aucun configure HELD |
| E3 effets/cache | PARTIAL_RGB_FRESHNESS_WHOLE_RESTORATION_FAIL_INCONCLUSIVE |ROI A/B/A fraîche ; full-H1 return échoue dans les deux contrôles ; cleanup clients FAIL |
| F ordre/input | SAMPLED_INPUT_WITH_CEILINGS_INCONCLUSIVE |Assertion geometry stale, atomic/ref/release NOT PROVEN,10/12 pairs initiaux présents |
| G retrait/build | PASS local / BUILDABLE |26 logs/7 fichiers, revue delta exact ; PI/FBO ordering restent |
| G smoke | SMOKE_FEASIBLE |17 PNG, preuve G propre horizontale ; runtime PASS selon politique C/G avec IPC1 conservé |
| H package/maintenance | PARTIALLY_FEASIBLE |Recette syntaxe/inputs PASS, makepkg NOT RUN, notices/legal PARTIAL, nominations absentes |
| I décision intégration | DRAFT / PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE |22 réponses ; aucune décision appliquée |
| J docs/revue/publication | Revue finale ACCEPTED / audit PASS ; publication préparée, non exécutée |0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; aucun commit/push revendiqué à cette édition |
## Évidence et discipline
[Reconstruction annotée](STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md), [matrice graphique](STEP20C_ANNOTATED_GRAPHICS_MATRIX.md), [lifecycle/effets/native ordering](STEP20C_LIFECYCLE_EFFECTS_AND_NATIVE_ORDERING.md) et [22 réponses de décision](STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md) sont les rapports de cette tranche. Ils ne réécrivent pas les freezes historiques. Provenance compacte liée dans leur en-tête ; source fixtures seulement, sans artefacts générés.
Préflight corrigé avant lancement : C inverse déco16 tests ; matrix association owner/root/temps et préservation transport33 tests ; R90 v3 association host38 tests ; G-SMOKE01/02 compteurs offline9 tests. Ledger5 tests avec rejets/concurrence ; revue runtime C7 mutations, premiers D14 négatifs, lifecycle32 checks, R90 runtime9 négatifs. Tous diagnostics initiaux et refus sont conservés. Correction offline de validateurs ne constitue pas un retry compositor. La revue source G et le smoke ont leur propre acceptation/gel.
Budget final : SceneFX2/2, Mosaic2/2, stages2/2, runs18/18, captures196/220 ; C3,D1–D4 chacun2,E1=1,E2=1,E3=2,F=2,G=1. Registre flock/atomique/fsync,18 tokens consommés une fois, aucun remboursement/reset.4 PNG marqueurs inputs séparés donnent200 nouvelles images ;78 assets upstream copiés ne sont pas des captures. Hash ledger `6ebedc65f30bf2c81f45b1487adf974a390a0049f632cfbb18bbbb00e3bfae3f`.
## Risques et critères de sortie proposés
Ce tableau est un registre de risques, pas une politique adoptée. P0 : aucun défaut bloquant confirmé dans l’échantillon. P1 : preuve essentielle avant intégration selon périmètre retenu ; P2 : maintenance/instrumentation/notice et décisions humaines ; P3 : couverture future hors tranche. Factuel ou hypothétique est indiqué. Tous environnements mesurés sont privés headless FX/GLES AMD780M et paire exacte A/G ; extrapolation interdite.
| ID / sévérité | Fait ou hypothèse / source / reproduction | Contrat affecté et travail minimal | Acceptation proposée / dépendance / humain |
| --- | --- | --- | --- |
| R-P0-01 / P0 |Aucun clip/input bloquant confirmé C/D1/D3/E1/G ; hypothèse de défaut ailleurs non testée ; replay PNG/wire |Ne pas conclure absence universelle ; conserver scope exact |Aucun P0 ouvert confirmé ; revue scientifique requise si nouvelle preuve |
| R-LIFE-01 / P1 |Refs/releases GPU/saved non observées ; source audit F-SOURCE-02/07 et E2 non exercé |Ownership/lifetime : fixture configure runtime attesté puis identités natives locks/releases/teardown |Protocole et critères lifetime avant nouveau budget ; dépend revue source/ABI ; reviewer humain |
| R-TXN-01 / P1 |F full→empty→full ;12 couples initiaux10 PAIRED/2 missing,57 commits ; fenêtre discriminante absente |État appliqué render/input/present : mesure native discriminante, sans forcer calendrier pour fabriquer PASS |Preuve d’identité et cohérence de génération selon contrat accepté ; dépend instrumentation/lifetime ; décision architecture |
| R-DAMAGE-01 / P1 |E3 full-H1 return33883 pristine/32132 Mosaic différences, ROI exact ; causalité ouverte |Damage/stale pixels : contrôle comparable et diagnostic fenêtres/effets, all-frame scope explicite |Critères full-frame/frames intermédiaires pertinents satisfaits, sans masque opportuniste ; dépend effets/txn ; revue scientifique |
| R-FIX-01 / P1 |D2 root ambiguity, E2 defaults, F rect stale ; sources/review raw reproductibles offline |Fixtures : owner-derived association, configure runtime attesté, geometry de la fenêtre appliquée |Nouveaux freezes/négatifs acceptés avant première exécution autorisée ; pas de retry du ticket ; reviewer |
| R-G-01 / P1 |G horizontal smoke seul, matrice étendue NOT TESTED |Qualification exact SHA G : relevant matrix complète et contrôles propres |Aucun héritage A ; dépend risque FBO/fixtures ; gate humain |
| R-ABI-01 / P2 |17 structs/6 offsets/121 headers/85 overlaps/34 bindings locaux, aucune ABI future |Paire épinglée ; audit headers/layout/providers/rebuild à chaque build pertinent |Nouvelle paire entièrement requalifiée ; dépend mainteneur/reviewer sécurité nommés |
| R-INST-01 / P2 |G retire26 logs mais PI listeners/FBO bind-before-failed conservés ; neutralité hypothétique |Auditer effets de callbacks/binds, comparer contrôles adéquats |Neutralité pertinente démontrée sans comparer des états non homologues ; dépend R-G/ABI ; reviewer |
| R-LIC-01 / P2 |895/894 inputs conservateurs, exact incorporation/legal PARTIAL |Textes/shaders/protocols/contributions conditionnelles et extraction archive |Inventaire incorporation et notices suffisantes examinés humainement avant distribution ; dépend package complet/release |
| R-PKG-01 / P2 |PKGBUILD.future syntaxe/inputs PASS, makepkg NOT RUN |Build paquet isolé, manifeste exact5 fichiers notices justifiées, coexistence |Validation intégrale source/package/stage et choix publication distinct ; dépend R-LIC/ABI ; release maintainer |
| R-ROLE-01 / P2 |Aucun release/security nominee/SLA ; H proposition privée |Veille/impact/rebuild/requalification/notices et décision release |Nominations humaines avant public distribution ; aucun SLA inventé |
| R-UX-01 / P2 |Hide/dismiss/reposition/click-through ouverts ; routing mesuré seulement |Décision UX explicite avant comportement shipped ; contracts first |Acceptation utilisateur puis docs UX/architecture pertinentes ; pas de changement ici |
| R-TRANSPORT-01 / P2 |IPC exit1 tous runs ; C/G runtime PASS policy, matrix INCONCLUSIVE |Conserver incident et distinguer fermeture process/réponse transport |Cause NOT PROVEN ; protocole futur doit documenter ce critère, sans silent upgrade ; reviewer |
| R-FUTURE-01 / P3 |Sorties physiques/backends autres, matrices exhaustives/future pairs NOT TESTED |Qualification au besoin, pas extension générale du fork |Scope et budget séparés ; STEP20D–G restent PLANNED ; décision utilisateur |
## État du dépôt et publication
HEAD initial `4ef16a40192149d353831695fd7dcaeb594fdf01`, branche main ; le répertoire source-only overnight était non suivi avant cette mise à jour. Sept fichiers protégés comparés avant/après identiques dans `parent/protected-integrity-before-publication.json`. Production, UX, Packaging, AGENTS, PKGBUILD, patch cumulatif et fixtures historiques préservés. Aucun stage Git/commit/push par l’auteur documentaire.
Audit parent `cleanup-after-graphs.json` : aucun PID/socket privé restant ; runtimes supprimés selon cleanup exécuté, chemins matrix individuels non consignés. Aucun live IPC, installation ou remplacement système. SceneFX système initial SHA non enregistré ; le SHA final ne permet pas une égalité initial/final inventée.
Revue finale indépendante ACCEPTED / audit PASS pour le dossier expérimental. Publication origin/github préparée, non exécutée à cette édition ; le reçu privé et le handoff final porteront le résultat réel et les SHAs finaux, sans autoréférence au futur hash de commit. La publication documentaire ne transforme pas la recommandation en décision production. Prochaine petite étape justifiée : validation du delta documentaire final et publication autorisée, puis gates ciblés dans une nouvelle tranche avant toute intégration.
## Revue finale observée et publication préparée
Revue indépendante finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental : 0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; 180 checks hors ligne et 15 mutations de preuves réelles rejetées. Rapport privé `reviews/overnight-final-review.md`, SHA256 `bb4000dffb51b07b59e86f91e8dd76bc2c4f0b9513b48a52c9f6bd6c8c3f45af` ; reçu JSON SHA256 `72ba252092ca3375d344faae1c2026c8d5626fd190e87771d5c0c1ad8212c9b0`. Index scientifique post-exécution `seals/index.json`, SHA256 `cdf034e1f8466a9ae8a0e0a33aa8d0d0195273f0d1bc782667b50c3bcf682505`, contexte de preuve courant, pas un binding pré-exécution.
NB-01 est complété dans l’index compact depuis les grabs wire réels de chacun des huit runs concernés, avec lignes et hashes sources dans `parent/metadata-serial-completion.json`. Les stops fixtures, échecs E3 et incident IPC restent documentés. Les gates lifetime/génération/damage, couverture G, maintenance ABI/paquet/juridique et robustesse/coût des sidecars privés restent ouverts. L’acceptation documentaire ne qualifie pas la production et n’applique aucune décision. Publication préparée, pas encore exécutée à cette édition ; les refs/SHAs finaux et le résultat de publication feront foi dans le reçu privé et le handoff final.

View file

@ -0,0 +1,109 @@
# STEP20C — décision d’intégration production, draft
> EXPERIMENTAL — édition documentaire du 2026-10-14, après revue finale ACCEPTED / audit PASS du dossier expérimental ; publication préparée, non exécutée à cette édition. Les résultats concernent des laboratoires privés ; aucune intégration production, installation ou décision UX. STEP20A DONE ; STEP20B EXPERIMENTAL ; STEP20C NEXT ; STEP20D–G PLANNED. Multi-output de production : NOT YET SUPPORTED. Les anciens rapports restent historiques et leurs résultats ne se transfèrent pas à un nouveau SHA.
L’autorité brute est le LAB privé `/home/fy59/swayfx-mosaic-overnight-mcolx0gq`. Les chemins LAB ci-dessous sont des références locales de preuve, pas des fichiers distribués. Les sources de fixture préservées se trouvent dans [tooling/probes/step20c-overnight](../../tooling/probes/step20c-overnight/qualification/README.md). La [provenance compacte](../../tooling/probes/step20c-overnight/qualification/provenance.json) lie les identités et reçus sans publier logs, images, binaires, archives ou patches candidats.
## Recommandation soumise, non appliquée
**PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE**. L’état global proposé est PARTIALLY_ADVANCED, sous réserve de publication observée, jamais PRODUCTION_READY. Aucun défaut P0 clip/input confirmé dans le scope mesuré, mais les gates lifetime, damage/transactions, effets, ABI-maintenance, packaging/notices et politique humaine restent ouverts. L’issue globale scientifique est PARTIALLY_ADVANCED ; ce draft ne décide pas une intégration et n’autorise ni distribution ni STEP20D.
Les réponses sont liées aux [matrices exactes](STEP20C_ANNOTATED_GRAPHICS_MATRIX.md), au [build/stage](STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md) et aux [limites lifecycle](STEP20C_LIFECYCLE_EFFECTS_AND_NATIVE_ORDERING.md). Elles distinguent preuve locale et contrat futur.
## 1. Le nouveau binaire annoté est-il réellement qualifié?
A est qualifié localement pour source/build/model/ELF/stage et possède ses propres mesures graphiques C/D/E/F. FEASIBLE pour C horizontal ; PARTIALLY_FEASIBLE globalement. Ni qualification production complète ni READY.
## 2. Quelles matrices ont été exercées?
18 runs uniques : C0/C1/C2 horizontal négatif ; D1 vertical pair ; D2 scale1,25 pair ; D3 R90/1,25 pair ; D4 resize/migration pair ; E1 hide/return ; E2 saved trigger ; E3 effets/cache pair ; F regions/ordering pair ; G smoke. Les limites et actions non exercées restent explicites dans la matrice.
## 3. Quels contrôles pristine sont disponibles?
C0 et D1/D2/D3/D4/E3/F pristine réellement exécutés sur le SHA retenu. C0/C1 full RGB égal aux huit états homologues ; D2/D3 markers et spill/input ordinary effectifs. Pristine F non modifié ne porte aucune PI privée. Ces contrôles ne qualifient pas un autre SHA.
## 4. Quels résultats restent partiels?
D2-Mosaic association root ambiguë avant input/final ; E2 saved NOT EXERCISED ; E3 full-H1 return FAIL dans les deux contrôles, classic NOT EXERCISED ; F géométrie fixture périmée et suite non exercée ; G horizontal smoke seul ; ABI future/notices/legal/package complets non qualifiés.
## 5. Existe-t-il un défaut bloquant de clipping ou d’input?
Aucun défaut bloquant démontré dans les fenêtres échantillonnées C/D1/D3/D4/E1/G. D2/F stops sont expliqués par le fixture, sans PASS rétroactif. Cela ne prouve pas l’absence universelle de défaut.
## 6. Existe-t-il un risque de lifetime non résolu?
Oui : identité native buffers, locks/références/releases GPU/saved, teardown exhaustif et scénarios de commit rejeté ne sont pas prouvés. Cleanup PID propre est une preuve de processus, pas de lifetime GPU. G garde FBO bind-before-failed potentiellement non passif.
## 7. Quel est l’état réel du damage?
Échantillons et traces extents conservés, H2 exact dans les états citées ; whole-H1 A-return effets échoue dans les deux contrôles. Damage exhaustif, minimal, stale pixels intermédiaires et contenu exact présenté NOT PROVEN.
## 8. Quelle est la portée des preuves de transactions?
Source applied clip et callbacks échantillonnés, regions full/empty/full observées. Sur12 couples initiaux F,10 PAIRED/2 MISSING_PRESENT ;57 commits bruts. Pas d’epoch atomique ni de fenêtre naturelle discriminante ni d’identité pixels/hit/present complète.
## 9. Le candidat release-shape existe-t-il?
Oui, G privé source/build/stage exact existe, SHA8063478b2d5948da3eb8b1a329978882dc1d045cf36932cc2145a78af20ba76a, build ID83896fdb36f3fd21522becec0e8c5219e15f4b8e. Aucun candidat binaire/package public distribué.
## 10. Son comportement est-il testé ou seulement compilé?
G est buildable et possède un smoke propre17 PNG/8 clics/4 grabs/retour splith, indépendamment revu. SMOKE_FEASIBLE horizontal seulement ; matrice G étendue et neutralité complète NOT TESTED/NOT PROVEN.
## 11. Quels patches fonctionnels doivent être conservés?
Dans le candidat privé : subtree clip SceneFX, clip owner-derived popup, guard owner totalement caché et delta natif Mosaic. Les commentaires token-equivalent sont acceptés. Une extraction de production exige encore revue exacte source/ownership/lifetime et décision, sans appliquer ces patches maintenant.
## 12. Quels patches de trace peuvent être retirés?
Seulement le retrait privé G déjà revu :26 logs/7 fichiers et helpers réellement orphelins. Timing/cache/PI ne se retirent pas en bloc sans audit ; PI listeners et FBO bind ordering restent. Aucun patch de production réécrit.
## 13. Quelle ABI SceneFX est réellement qualifiée?
SceneFX0.5 statique exact local, ELF appels clip directs,17 structs/6 offsets/85 overlaps/34 diagnostic bindings. Pas une ABI externe stable, ni tous providers runtime, ni une ABI future ou full semantic safety.
## 14. Quelle paire wlroots est réellement qualifiée?
Paire locale SceneFX37ccd723bef49e6891156ffafce8f549f01446cc + wlroots0.20.2 d783533489e1f75d6886c2ab5c5960090ef268f8, libSHA7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837 et121 headers épinglés. Tous autres builds/paires NOT QUALIFIED ; changement implique rebuild/requalification.
## 15. La future recette Arch est-elle reproductible?
Inputs privés hashés, syntaxe shell/Python et sources PASS ; makepkg metadata/source verification/build NOT RUN. Pas de reproductibilité binaire bit à bit ni de paquet distribution publique validé. PKGBUILD.future ne modifie pas PKGBUILD réel.
## 16. Quels textes de licence restent à valider?
Les quatre fichiers stage incluent LICENSE SceneFX/SwayFX/wlroots et INCORPORATED-NOTICES. Inventaires895 A/894 G conservateurs ; incorporation exacte, extraction archives et contributions conditionnelles puis suffisance juridique nécessitent validation humaine. Aucun legal PASS.
## 17. Qui devra assurer les releases et la sécurité?
Responsabilité projet pour SceneFX embarqué déjà approuvée dans le périmètre privé ; release maintainer et security reviewer doivent être désignés avant distribution. Aucun nom ni SLA inventé. Veille, impact, rebuild, tests, notices et publication documentés comme procédure proposée.
## 18. Quelle politique popup UX reste à décider?
Hide/dismiss/reposition de popup offscreen et comportement sous grab/click-through restent ouverts. Les observations de routing ne fixent pas une politique universelle. Aucun changement UX dans cette tranche.
## 19. Quelles corrections sont nécessaires avant une intégration dans main?
Nouveau fixture ciblé saved provoquant configure runtime attesté, diagnostic full-H1 effets avec contrôles, assertion input liée à géométrie appliquée, mesure lifetime/native refs/releases/temporal discriminante et neutralité G/FBO. Chaque gate pertinent doit avoir critères acceptés et nouvelle autorisation/budget ; aucun rerun dans le budget clos. Puis politiques humaines ABI/notices/release/security/UX, contrats canoniques avant code.
## 20. Quelle est la plus petite tranche de production envisageable après décision utilisateur?
Après décision humaine et gates pertinents clos seulement : clip natif optionnel dans workspace Sway ordinaire, Row0 empruntant workspace->tiling, owner popup clip et hidden-owner guard revus, restauration standard locale préservée. Contrats Architecture/UX/Packaging/Upstream/Development d’abord, puis petit delta revu/testé. Aucun STEP20D, ligne runtime additionnelle, animation ou taille finale ; aucune implementation ici.
## 21. Quels tests doivent impérativement être refaits après une intégration?
Nouveau exact SHA : compile/model/headless IPC et standard pristine, export cumulatif forward apply/source-equivalence/checksum, ELF/header/layout/providers/relocations, manifests stage/notices et package complet, toute matrice graphique C/D/E/F pertinente et smoke propre G, input/popups/grabs/retour standard, geometry scale/rotation/migration, saved/effects/damage/lifetime. Pas d’héritage automatique du LAB.
## 22. Quel plan de rollback protège SwayFX standard?
Proposition future : conserver package SwayFX et /usr/bin/sway indépendants, sélectionner sa session existante après quitter la candidate ; retirer seulement fichiers appartenant à Mosaic vérifiés. Aucun provides/conflicts/replaces, système lib/config/dotfiles remplacé. Déploiement et rollback nécessitent décision distincte, jamais exécutés ici.
## Conditions de décision
Ce dossier apporte un candidat et des preuves reviewables. La revue finale indépendante est ACCEPTED / audit PASS pour la fidélité de l’évidence expérimentale, sans adoption production. Publication documentaire préparée, non exécutée à cette édition. Publication des docs/fixtures et adoption en production sont des actes distincts ; le résultat réel et les SHAs finaux origin/github feront foi dans le reçu privé et le handoff final, sans faux hash futur dans ce draft.
## Revue finale observée et publication préparée
Revue indépendante finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental : 0 BLOCKING, 4 NON_BLOCKING, 3 FUTURE_SCOPE ; 180 checks hors ligne et 15 mutations de preuves réelles rejetées. Rapport privé `reviews/overnight-final-review.md`, SHA256 `bb4000dffb51b07b59e86f91e8dd76bc2c4f0b9513b48a52c9f6bd6c8c3f45af` ; reçu JSON SHA256 `72ba252092ca3375d344faae1c2026c8d5626fd190e87771d5c0c1ad8212c9b0`. Index scientifique post-exécution `seals/index.json`, SHA256 `cdf034e1f8466a9ae8a0e0a33aa8d0d0195273f0d1bc782667b50c3bcf682505`, contexte de preuve courant, pas un binding pré-exécution.
NB-01 est complété dans l’index compact depuis les grabs wire réels de chacun des huit runs concernés, avec lignes et hashes sources dans `parent/metadata-serial-completion.json`. Les stops fixtures, échecs E3 et incident IPC restent documentés. Les gates lifetime/génération/damage, couverture G, maintenance ABI/paquet/juridique et robustesse/coût des sidecars privés restent ouverts. L’acceptation documentaire ne qualifie pas la production et n’applique aucune décision. Publication préparée, pas encore exécutée à cette édition ; les refs/SHAs finaux et le résultat de publication feront foi dans le reçu privé et le handoff final.

View file

@ -0,0 +1,104 @@
# Exact annotated horizontal fixture
EXPERIMENTAL / PREPARATION. Runtime **NOT RUN**, independent review **PENDING**.
No production, UX, PKGBUILD, cumulative patch, installation or desktop action.
STEP20C stays NEXT; production multi-output NOT YET SUPPORTED.
Only the explicitly owned overnight LAB is accepted. `phase-a.json` supplies the
new annotated staged binary; the previous D binary cannot satisfy the frozen
identity. The retained pristine control is separately bound to its fd71 source
archive, SceneFX37ccd source archive, original build receipt, exact binary and
mapped SceneFX library. Both use the current exact wlroots0.20 library. Native
startup log lines establish the actual FX/GL renderer; requested environment
`WLR_RENDERER=pixman` is metadata, never renderer proof.
C0 runs pristine splith, C1 annotated splith, C2 annotated Mosaic. Every case has
H1=(-800,0,800,600), H2=(0,0,800,600), scale1, transform normal. H2 is a static
opaque SHM XDG client #303038; two separated initial whole RGB images must be
identical and uniform. No mask, channel tolerance or excluded pixels.
The clients are copied privately from the actual retained static_cross sources
and compiled with C17. Scanner regeneration must reproduce every retained XDG
and virtual-pointer generated file byte for byte; XML and source hashes are
frozen. TOPLEVEL→A→B hierarchy is checked against actual object IDs, get_popup
wire requests, genuine left-button serials, grab-before-initial-commit,
configure/ACK, buffer dimensions and mapping order. Each serial is consumed by
one distinct grab; C2's second chain uses new owner/PID and four distinct ordered
serials across both chains.
NativeSHM, XDG configure, IPC assigned destination, applied owner scene,
global logical coordinates, output logical rectangles and physical PNG pixels
remain separate. At scale1 normal the logical-to-physical transform is identity
apart from output origin; all rectangles use half-open bounds. C0 has no private
scene trace: its current IPC assignment is explicitly labelled, then compared
to measured paint and fresh pointer wire coordinates. C1 also corroborates the
assignment against the actual applied PI_OWNER and ordinary unclipped PI_SCENE.
Neither establishes an atomic transaction generation or screenshot/native
buffer identity.
C0/C1 open a genuine crossing A/B chain, measure full standard popup pixels on
both outputs, click B inside its actual H2 paint, explicitly close B, verify A
survives with no B residual on either output, click A inside its fresh H2 paint,
then close the owner normally. Final whole H2 equals the initial RGB image;
final H1 has no A/B colors. Four complete clicks, two grabs, one chain,
10 captures, five pointer placements; no camera or transition.
C2 opens E1 after a reserved camera correction derived from measured applied
owner geometry. Owner, A and B must actually cross x=0. H1 retains A/B pixels;
whole H2 stays equal. A real right click inside H1 B must reach B. A real right
click in B's hypothetical H2 extent must reach neither popup; actual popup_done
and destruction order must be B then A. Any reference delivery is recorded as
outside-grab routing, not promised universal click-through. E1 H2 must be clean.
The same compositor then opens fresh E2. A con_id-targeted splith preserves live
A/B; all selected callback records preserve old Mosaic then show two coherent
standard observations and clip removal through B input. Actual standard H2 A/B
pixels, B input, explicit child close/A survival, A input and normal owner close
are checked as in controls. Final whole H2 equality is exact. Eight clicks,
four grabs, two chains, up to two camera corrections, one transition,
17 captures and ten pointer placements. No retry or calibration loop.
`horizontal_adapter.py` applies explicit unique-occurrence transformations to
the sealed historical E2 runner. Generated runners exist only in LAB, are AST
compiled, source-hashed and reviewed with every reused dependency. The original
historical fixture and its source/receipts remain read only. The transition
validator is rebound to the exact annotated output/transaction/XDG sources;
old binary/source acceptance is never transferred.
Global ledger:18 runs,220 PNG and exactly three C runs, durable reservation before
compositor creation/capture. Local action ledgers additionally reserve every
click, move, grab, chain, camera and transition before the corresponding action.
Reserved and observed consumed counts remain separate; failures never refund.
Per-run maximum150s, active watchdog143s and bounded logs/process waits. Private
runtime0700, inherited desktop/loader sockets removed, explicit `swaymsg -s`
on every IPC command. Only owned process groups are stopped/reaped; runtime and
sockets are removed. Actual terminal IPC/client/compositor codes are preserved.
The C controls are comparison evidence, not automatic dispatch vetoes. A shared
fixture defect requires review before continuing; otherwise each case can pass
its own preflight. Missing/failed control prevents a full comparative verdict
and must be reported explicitly; no inferred pristine PASS.
## Preparation and review
`horizontal_gate.py prepare LAB` compiles only C clients; it creates no socket
and reserves no graphical run. `horizontal_tests.py --output RECEIPT` executes
pure/isolated negative tests. Failed first-pass receipts are preserved separately.
`horizontal_gate.py seal LAB` freezes final fixture/dependency/client/generated
runner/binary/library/source/provenance/test bytes; it cannot create acceptance.
A genuine independent read-only reviewer writes LAB/horizontal/review.json:
status ACCEPTED, independent=true, read_only=true,
scope OVERNIGHT_HORIZONTAL_FINAL_BYTES, blockers=[],
freeze_sha256=SHA256(freeze.json), files=the exact freeze files mapping,
report_file=absolute report path, report_sha256=its exact SHA256.
No historical or synthetic acceptance is valid. Final byte changes invalidate
acceptance. `horizontal_gate.py preflight LAB --case C0` is read only and requires
that review. `run.py LAB C0` then consumes a unique O_EXCL case token before the
run debit; C1/C2 have separate tokens. Existing runs, sockets, bad identity,
changed reviewed bytes and exhausted budgets fail closed. No actual run has been
performed during fixture preparation.
Remaining gates include wider outputs/migration, complete damage/intermediate
frames, atomic generation, static effects/hidden-owner/saved buffers and
ABI/distribution/legal/maintenance. Successful C alone is bounded horizontal
feasibility, never STEP20C production readiness.

View file

@ -0,0 +1,131 @@
"""Bounded explicit transformations of the sealed E2 runner, never a broad copy.
Generated runners are private frozen review artifacts. Reused validators retain
native/configured/assigned/applied distinctions and half-open coordinate rules.
"""
from pathlib import Path
import ast
import hashlib
BASE = Path(__file__).resolve().parent.parent / 'step20c-static-e2-completion'
BASE_SHA = '1bd67d5269b5492f5d9ac6e7b670a4db0345b0e2cd3f89930dcbd154c4473504'
REUSE = ('model.py','observations.py','ipc_predicates.py','pixels.py','predicates.py','lifecycle.py','transition.py','support.py')
def replace_one(text, old, new):
if text.count(old) != 1:
raise ValueError('sealed transformation occurrence mismatch: '+old[:80])
return text.replace(old,new,1)
STANDARD_EXTERIOR = "def splith_exterior(owner, layout):\n # Pinned sway/ipc-json.c:899-906: count=1 unless parent is stacked.\n # This inverse is deliberately scoped to the ordinary splith controls.\n require(layout == 'splith', 'standard-splith-only')\n assigned = owner['rect']; deco = owner['deco_rect']['height']\n require(type(deco) is int and deco >= 0, 'standard-decoration-height')\n return [assigned['x'], assigned['y'] - deco,\n assigned['width'], assigned['height'] + deco]\n\n"
STANDARD_OPEN = '''def open_standard(name):
global stage,baseline
reserve(lab,'chain');cmd('workspace 1')
proc=spawn(name,[lab/'nested-client','static_cross'],debug=True)
wait(lambda:find('org.mosaic.NestedGrabProbe',proc.pid) and 'TOPLEVEL MAP ' in log(name));time.sleep(.6)
snap=snapshot(name+'-assigned');ws=workspace(snap['tree'],'1')
owner=next(n for n in nodes(ws) if n.get('pid')==proc.pid)
require(ws['layout']=='splith' and owner['visible'] and owner['focused'],'standard-owner')
require(owner['app_id']=='org.mosaic.NestedGrabProbe' and owner['shell']=='xdg_shell','standard-owner-identity')
refstate=validate_reference(snap,refid,ref.pid)
inject('move',[-790,590]);first=capture('H2-reference-1','HEADLESS-2');time.sleep(.6)
second=capture('H2-reference-2','HEADLESS-2');baseline=first
save(lab,'reference-stability',dict(status='PASS',comparison=reference(first,second),reference=refstate))
topim=capture(name+'-top','HEADLESS-1');bbox=paint(topim,'TOPLEVEL')['bbox'];require(bbox is not None,'standard-owner-paint')
# C0 has no private scene trace. Current IPC assignment is explicitly named;
# the actual painted origin and fresh pointer wire coordinates corroborate it.
assigned=dict(owner['rect']);exterior=splith_exterior(owner,ws['layout'])
measured=dict(id=owner['id'],workspace=ws['id'],scene=exterior,IPC=owner,
raw='CURRENT IPC raw assignment; splith exterior inverse ipc-json.c:899-906; actual painting/input checked separately')
applied=applied_model(measured,owner,log(name),name+'-standard-assigned-observation')
content_rect=content(applied);origin=[content_rect.x,content_rect.y]
require([bbox[0]-800,bbox[1]]==origin,'standard-painted-assigned-origin')
out=output_model(snap,'HEADLESS-1',content_rect.provenance)
verify_paint(content_rect,out,bbox,paint(topim,'TOPLEVEL')['count'])
if CASE=='C1':
scene=scene_from_log(log('compositor'),'HEADLESS-1');real=measure_owner(log('compositor'),owner)
require(not scene['clip_valid'] and scene['layout_enum']==1,'standard-no-native-clip')
require(real['scene']==measured['scene'],'standard-applied-scene-matches-assignment')
owners[name]=dict(id=owner['id'],pid=proc.pid,workspace=ws['id'],assigned=assigned,exterior=exterior,content_origin=origin,
native=applied.native.rectangle.values(),configured=applied.configured.values(),paint=paint(topim,'TOPLEVEL'))
save(lab,'applied-owner-geometry',owners)
xy=point(topim,'TOPLEVEL',-800);require(applied.native.rectangle.contains([xy[0]-origin[0],xy[1]-origin[1]]),'opening-native-bounds')
cp=click(name+'-open-A',name,xy,True,'left');wait(lambda:'POPUP_A MAP ' in log(name));time.sleep(.25)
correlate_pointer(name,xy,content_rect.values(),'TOPLEVEL',cp['pointer_protocol'][name])
aim=capture(name+'-A','HEADLESS-1');xyA=point(aim,'POPUP_A',-800)
cpB=click(name+'-open-B',name,xyA,True,'left');wait(lambda:'POPUP_B MAP ' in log(name));time.sleep(.3)
prot=protocol(log(name));opening_pair(cp['events'][name],'TOPLEVEL',prot['grabs'][0]['serial'])
opening_pair(cpB['events'][name],'POPUP_A',prot['grabs'][1]['serial'])
require(not any(cp['events'][n] or cpB['events'][n] for n in ('reference','filler')),'opening-other-client')
g=geometry(log(name),origin,content_rect.provenance);require(all(v=='EXERCISED' for v in g['crossing'].values()),'standard-root-child-crossing')
correlate_pointer(name,xyA,g['A'],'POPUP_A',cpB['pointer_protocol'][name])
left=capture('standard-H1','HEADLESS-1');right=capture('standard-H2','HEADLESS-2')
rgb=popup_pixels(left,right,g,False,baseline);verify_owner_occlusion(left,right,content_rect,g,False)
chains[name]=dict(protocol=prot,geometry=g,pixels=rgb,owner=owners[name])
save(lab,'xdg-protocol-order',{name:prot});save(lab,'standard-popup-pixels',rgb)
return proc,owner,g,left,right,content_rect
'''
E1 = ''' E1,owner1,g1,im1=open_chain('E1')
stage='E1-visible-B';xy=point(im1,'POPUP_B',-800);row=click(stage,'E1',xy)
result=delivery(row,'E1','POPUP_B');coords=correlate_pointer('E1',xy,g1['B'],'POPUP_B',row['pointer_protocol']['E1']);mapped(log('E1'))
save(lab,'mosaic-input',dict(status='PASS',pair=result,coordinates=coords,click=row))
stage='E1-forbidden-B';B=g1['B'];xy=[int((max(0,B[0])+min(800,B[0]+B[2]))//2),B[1]+30]
require(B[0]<=xy[0]<B[0]+B[2] and 8<=xy[0]<792 and B[1]<=xy[1]<B[1]+B[3],'forbidden-point-real-child-H2')
cut=len(log('E1'));row=click(stage,'E1',xy);time.sleep(.3)
ds=dismissal(log('E1')[cut:],row['events']['E1'])
require(not row['events']['filler'],'forbidden-wrong-filler-recipient')
refs=row['events']['reference'];require(len(refs) in (0,2),'outside-reference-pair')
if refs:
require(all('button=273 ' in s for s in refs) and ['state=1' in refs[0],'state=0' in refs[1]]==[True,True],'outside-reference-button')
save(lab,'forbidden-input',dict(status='PASS',click=row,dismissal=ds,reference_events=refs,
interpretation='outside dismissal; reference routing recorded, no universal click-through claim'))
inject('move',[-790,590]);clean=capture('E1-dismissed-H2','HEADLESS-2');require(equal(baseline,clean)['equal'],'dismissal-whole-H2-residue')
close_client(E1,'E1','close_owner')
E2,owner2,g2,im2=open_chain('E2')
fresh_chain(dict(owner=owner1['id'],pid=E1.pid,serials=[g['serial'] for g in chains['E1']['protocol']['grabs']]),
dict(owner=owner2['id'],pid=E2.pid,serials=[g['serial'] for g in chains['E2']['protocol']['grabs']]))
'''
def generate(case):
if case not in ('C0','C1','C2'):raise ValueError('unknown C case')
text=(BASE/'run.py').read_text()
if hashlib.sha256(text.encode()).hexdigest()!=BASE_SHA:raise ValueError('sealed base changed')
text=replace_one(text,'from authorization import authorize\n','')
begin=text.index('p=argparse.ArgumentParser()');end=text.index("run=lab/'run';run.mkdir()")
text=text[:begin]+'''# Gate, unique consumption and run reservation precede even runtime directory creation.
from horizontal_gate import begin_run, debit, binding_sources, finish_run
LAB,CASE,lab,auth=begin_run()
BINARY=Path(auth['binary']);BINARY_SHA=auth['binary_sha256'];WLROOTS_SHA=auth['wlroots_sha256']
def reserve(unused,*kinds):return debit(LAB,CASE,lab,*kinds)
def source_bindings():return binding_sources(auth)
import transition
transition.source_bindings=source_bindings
'''+text[end:]
text=replace_one(text,"reserve(lab,'capture');time.sleep(.20)","reserve(lab,'capture');time.sleep(.20)")
text=replace_one(text,"assert Path(f'/proc/{comp.pid}/exe').resolve()==lab/'swayfx-mosaic'","assert Path(f'/proc/{comp.pid}/exe').resolve()==BINARY.resolve() and sha(BINARY)==BINARY_SHA")
text=replace_one(text,"assert not any('libscenefx' in x for x in paths)","assert CASE=='C0' or not any('libscenefx' in x for x in paths)")
text=replace_one(text," save(lab,'dependency-mappings',"," if CASE=='C0':\n scene_paths=[Path(x).resolve() for x in paths if 'libscenefx' in x]\n require(scene_paths==[Path(auth['pristine']['scene_path'])] and sha(scene_paths[0])==auth['pristine']['scene_sha256'],'pristine mapped SceneFX identity')\n save(lab,'dependency-mappings',")
text=replace_one(text,"SceneFX_shared=False","SceneFX_shared=any('libscenefx' in x for x in paths)")
marker=" outputs=ipc('-t','get_outputs');validate_outputs(outputs);save(lab,'output-geometry',outputs)"
text=replace_one(text,marker,""" actual=[s for s in log('compositor').splitlines() if any(k in s for k in ('Creating scenefx FX renderer','GL renderer:','Using OpenGL ES','GL vendor:'))]
require(any('Creating scenefx FX renderer' in s for s in actual) and any('GL renderer:' in s for s in actual),'actual-renderer-log-required')
save(lab,'actual-renderer',dict(status='PASS',source='actual compositor startup log',lines=actual,requested_env_renderer=env.get('WLR_RENDERER')))
"""+marker)
text=replace_one(text," comp=spawn('compositor',[lab/'swayfx-mosaic','-d','-c',run/'config'])"," comp=spawn('compositor',[BINARY,'-d','-c',run/'config'])")
text=replace_one(text," verify(lab/'results/fixture-freeze-sha256.txt')"," finish_run(LAB,CASE,lab)")
if case=='C2':
text=replace_one(text," if name=='E2':"," if name=='E1':")
text=replace_one(text," content_rect=content(applied);origin=[content_rect.x,content_rect.y]"," content_rect=content(applied);origin=[content_rect.x,content_rect.y]\n require(content_rect.x < 0 < content_rect.end[0],'real-owner-crossing')")
text=replace_one(text," E2,owner2,g2,im2=open_chain('E2')\n",E1)
else:
text=text[:text.index('\ndef open_chain(name):')]+ '\n'+STANDARD_EXTERIOR+STANDARD_OPEN+text[text.index('\ntry:\n comp='):]
text=replace_one(text,";cmd('layout mosaic')",";cmd('layout splith')")
start=text.index(" E2,owner2,g2,im2=open_chain('E2')")
end=text.index(" stage='E2-standard-B';")
text=text[:start]+" E2,owner2,stdg,stdleft,stdright,stdcontent=open_standard('E2')\n"+text[end:]
start=text.index(' # Re-examine every trace through the B input')
end=text.index(" before_close=log('E2');",start)
text=text[:start]+text[end:]
ast.parse(text)
return text

View file

@ -0,0 +1,168 @@
"""Offline preparation and fail-closed independent review / exact-byte runtime gate."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import stat
import subprocess
import sys
import time
import fcntl
from horizontal_adapter import BASE,REUSE,generate
import ledger
HERE=Path(__file__).resolve().parent
DEFAULT_LAB=Path('/home/fy59/swayfx-mosaic-overnight-mcolx0gq')
OLD_CLIENTS=Path('/home/fy59/swayfx-mosaic-static-nested-runtime-pvn0osxm/client-build')
PRISTINE=Path('/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n/pristine-build/sway/sway')
PRISTINE_SHA='8ac865481bc0487344210cd0e16d8e6c7655b29db887cd58ddc11b0ca22fe494'
WLROOTS=Path('/usr/lib/libwlroots-0.20.so').resolve()
PLANS={'C0':dict(chain=1,grab=2,click=4,capture=10,camera=0,transition=0,move=5),
'C1':dict(chain=1,grab=2,click=4,capture=10,camera=0,transition=0,move=5),
'C2':dict(chain=2,grab=4,click=8,capture=17,camera=2,transition=1,move=10)}
def sha(p):return hashlib.sha256(Path(p).read_bytes()).hexdigest()
def read(p):return json.loads(Path(p).read_text())
def require(ok,msg):
if not ok:raise ValueError(msg)
def exclusive(p,data):
fd=os.open(p,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
with os.fdopen(fd,'w') as f:
json.dump(data,f,indent=2);f.write('\n');f.flush();os.fsync(f.fileno())
def durable(p,data):
from importlib.util import spec_from_file_location,module_from_spec
spec=spec_from_file_location('horizontal_support',BASE/'support.py');m=module_from_spec(spec);spec.loader.exec_module(m)
m.durable(p,data)
def env():
spec=__import__('importlib.util',fromlist=['spec_from_file_location']).spec_from_file_location('horizontal_support',BASE/'support.py')
m=__import__('importlib.util',fromlist=['module_from_spec']).module_from_spec(spec);spec.loader.exec_module(m)
return m.environment()
def binding_sources(auth):
rows=auth['trace_sources'];require(all(sha(p)==h for p,h in rows.items()),'annotated trace source changed')
return {Path(p).name:h for p,h in rows.items()}
def prepare(lab):
lab=Path(lab).resolve();require(lab==DEFAULT_LAB,'only registered overnight lab')
a=read(lab/'phase-a.json');require(a['status']=='PASS' and sha(a['binary']['path'])==a['binary']['sha256'],'phase A exact binary')
require(sha(PRISTINE)==PRISTINE_SHA,'pristine binary identity')
work=lab/'horizontal';work.mkdir();(work/'clients').mkdir()
rows=[]
names=['nested-client.c','pointer-daemon.c','static-reference.c','saved-client.c','xdg-shell-client.h','xdg-shell-protocol.c','virtual-pointer-client.h','virtual-pointer-protocol.c']
for n in names:
target=work/'clients'/n;shutil.copyfile(OLD_CLIENTS/n,target);rows.append(dict(source=str(OLD_CLIENTS/n),path=str(target),sha256=sha(target)))
contract=read(BASE/'client-contract.json');require(sha(work/'clients/nested-client.c')==contract['adapted_nested_sha256'],'actual static_cross')
for n,h in contract['protocols'].items():require(sha(work/'clients'/n)==h,'actual generated protocol')
receipts=[]
flags=subprocess.run(['pkg-config','--cflags','--libs','wayland-client'],capture_output=True,text=True,check=True).stdout.split()
for name,sources in [('nested-client',['nested-client.c','xdg-shell-protocol.c']),('pointer-daemon',['pointer-daemon.c','virtual-pointer-protocol.c']),('static-reference',['static-reference.c','xdg-shell-protocol.c'])]:
argv=['cc','-std=c17','-O2','-Wall','-Wextra',*[str(work/'clients'/s) for s in sources],'-o',str(work/'clients'/name),*flags]
p=subprocess.run(argv,env=env(),capture_output=True,text=True,timeout=30)
receipts.append(dict(argv=argv,exit=p.returncode,stdout=p.stdout,stderr=p.stderr,binary_sha256=sha(work/'clients'/name) if p.returncode==0 else None))
require(p.returncode==0,'C17 client compilation')
exclusive(work/'compile.json',dict(status='PASS',transfers=rows,commands=receipts,protocol_provenance=str(BASE/'client-contract.json'),client_execution='NOT RUN'))
for case in PLANS:
d=work/case;d.mkdir();(d/'results').mkdir()
for name in ('nested-client','pointer-daemon','static-reference'):(d/name).symlink_to(work/'clients'/name)
(d/'runner.py').write_text(generate(case))
trace={str(lab/'mosaic-source/sway/desktop'/n):sha(lab/'mosaic-source/sway/desktop'/n) for n in ('output.c','transaction.c','xdg_shell.c')}
scene=Path('/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n/scene0-build/libscenefx-0.5.so').resolve()
exclusive(work/'identity.json',dict(status='PREPARED',lab=str(lab),phase_a_sha256=sha(lab/'phase-a.json'),
annotated_binary=a['binary'],pristine=dict(path=str(PRISTINE),sha256=PRISTINE_SHA,scene_path=str(scene),scene_sha256=sha(scene)),
wlroots=dict(path=str(WLROOTS),sha256=sha(WLROOTS)),trace_sources=trace,plans=PLANS,review='PENDING',runtime='NOT RUN'))
print('HORIZONTAL=PREPARED_CLIENTS_COMPILED_RUNTIME_NOT_RUN')
def paths(lab):
w=lab/'horizontal';ident=read(w/'identity.json')
out=[p for p in HERE.iterdir() if p.is_file()]+[BASE/n for n in REUSE]+[BASE/'run.py',BASE/'client-contract.json']
out+=[p for p in (w/'clients').iterdir() if p.is_file()]
out+=[w/'compile.json',w/'identity.json',w/'offline-tests-repair-v1.json',w/'pristine-provenance.json',lab/'phase-a.json']
out+=[w/c/'runner.py' for c in PLANS]+[Path(p) for p in ident['trace_sources']]
out+=[Path(ident['annotated_binary']['path']),PRISTINE,WLROOTS,Path(ident['pristine']['scene_path'])]
provenance=read(w/'pristine-provenance.json')
out+=[Path(provenance['historical_build_receipt']),w/'provenance_check.py']
out+=[Path(row['path']) for check in provenance['source_equivalence'] for row in check['files']]
out+=[Path(row['source']) for row in read(w/'compile.json')['transfers']]
out+=[w/'protocol-scanner.json',w/'offline-tests.json']
out+=[Path(row[k]) for row in read(w/'protocol-scanner.json')['commands'] for k in ('protocol_xml','generated')]
retained=Path('/home/fy59/swayfx-mosaic-static-nested-runtime-pvn0osxm')
out+=[retained/p for p in ('results/standard-snapshot.json','results/standard-offline-observation.json',
'run/compositor.log','run/E2.log','run/standard-H1.png','run/standard-H2.png')]
out+=[lab/'swayfx-source/sway/ipc-json.c',w/'offline-tests-repair-attempt1.json']
out+=[Path(shutil.which(name)).resolve() for name in ('swaymsg','grim')]
return sorted(set(out))
def seal(lab):
require(read(lab/'horizontal/offline-tests-repair-v1.json')['status']=='PASS','executed offline tests required')
exclusive(lab/'horizontal/freeze.json',dict(status='READY_FOR_REVIEW',files={str(p):sha(p) for p in paths(lab)},plans=PLANS))
def verify_review(lab,review=True):
w=lab/'horizontal';f=read(w/'freeze.json')
require(f['status']=='READY_FOR_REVIEW' and f['files']=={str(p):sha(p) for p in paths(lab)} and f['plans']==PLANS,'complete final frozen bytes')
ident=read(w/'identity.json');a=read(lab/'phase-a.json')
require(a['status']=='PASS' and ident['phase_a_sha256']==sha(lab/'phase-a.json') and ident['annotated_binary']==a['binary'],'exact phase A')
require(ident['pristine']['sha256']==PRISTINE_SHA and sha(PRISTINE)==PRISTINE_SHA,'pristine bytes')
provenance=read(w/'pristine-provenance.json');require(provenance['status']=='PASS','pristine source equivalence')
require(all(sha(row['path'])==row['sha256'] for check in provenance['source_equivalence'] for row in check['files']),'pristine sources actual frozen equality')
require(read(w/'offline-tests-repair-v1.json')['status']=='PASS','executed tests')
require(read(w/'protocol-scanner.json')['status']=='PASS','scanner provenance')
if review:
r=read(w/'review.json')
require(r.get('status')=='ACCEPTED' and r.get('independent') is True and r.get('read_only') is True and r.get('blockers')==[] and r.get('scope')=='OVERNIGHT_HORIZONTAL_FINAL_BYTES','genuine independent acceptance')
require(r.get('freeze_sha256')==sha(w/'freeze.json') and r.get('files')==f['files'],'review final bindings')
require(sha(r['report_file'])==r['report_sha256'],'review report bytes')
return ident
def preflight(lab,case,review=True):
require(lab==DEFAULT_LAB and case in PLANS and not sys.flags.optimize,'registered lab/case assertions')
require(lab.stat().st_uid==os.getuid() and stat.S_IMODE(lab.stat().st_mode)==0o700,'owned lab 0700')
ident=verify_review(lab,review)
w=lab/'horizontal'/case
require(not (w/'run').exists() and not (w/'consumed.json').exists(),'no retry or existing runtime')
require(not any(stat.S_ISSOCK(p.lstat().st_mode) for p in (lab/'horizontal').rglob('*')),'no stale sockets')
budget=read(lab/'budget.json');require(case not in budget['runs'] and budget['used']['run']<18 and budget['campaign_used']['C']<3,'unspent global run')
require(budget['used']['capture']+PLANS[case]['capture']<=220,'available planned PNG budget')
# Controls are scientific comparison prerequisites, not invented dispatch vetoes.
# Shared-fixture defects must be reviewed before proceeding; no status is transferred.
# No compositor/client process exists in this fresh per-case directory.
for proc in Path('/proc').iterdir():
if proc.name.isdigit():
try:exe=(proc/'exe').resolve(strict=True)
except (FileNotFoundError,PermissionError,ProcessLookupError):continue
require(exe!=Path(ident['annotated_binary']['path']),'annotated binary already running')
binary=ident['pristine'] if case=='C0' else ident['annotated_binary']
require(sha(binary['path'])==binary['sha256'] and sha(ident['wlroots']['path'])==ident['wlroots']['sha256'],'binary/wlroots preflight')
return dict(binary=binary['path'],binary_sha256=binary['sha256'],wlroots_sha256=ident['wlroots']['sha256'],trace_sources=ident['trace_sources'],pristine=ident['pristine'])
def begin_run():
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('case',choices=PLANS);a=p.parse_args()
lab=a.lab.resolve();case=a.case;w=lab/'horizontal'/case
auth=preflight(lab,case)
exclusive(w/'consumed.json',dict(case=case,freeze_sha256=sha(lab/'horizontal/freeze.json'),at_ns=time.monotonic_ns()))
ledger.reserve(lab/'budget.json','run',campaign='C',run_id=case,duration_s=150)
exclusive(w/'actions.json',dict(planned=PLANS[case],reserved=dict.fromkeys(PLANS[case],0),events=[]))
return lab,case,w,auth
def debit(lab,case,w,*kinds):
with (w/'actions.lock').open('a+') as lock:
fcntl.flock(lock,fcntl.LOCK_EX);state=read(w/'actions.json')
require(bool(kinds) and all(k in PLANS[case] for k in kinds),'known actions')
require(all(state['reserved'][k]+kinds.count(k)<=state['planned'][k] for k in kinds),'local action budget exhausted')
for k in kinds:
if k=='capture':ledger.reserve(lab/'budget.json','capture',run_id=case)
state['reserved'][k]+=1;state['events'].append(dict(kind=k,ns=time.monotonic_ns(),sequence=len(state['events'])))
durable(w/'actions.json',state)
def finish_run(lab,case,w):
verify_review(lab);state=read(w/'actions.json');events=[json.loads(s) for s in (w/'run/events.jsonl').read_text().splitlines()]
consumed=dict(capture=sum(e['kind']=='capture' for e in events),click=sum(e['kind']=='injection' and e['action']!='move' for e in events),
move=sum(e['kind']=='injection' for e in events),grab=sum(e['kind']=='injection' and e.get('grab',False) for e in events))
state['consumed_observed']=consumed;durable(w/'actions.json',state)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('action',choices=['prepare','seal','preflight']);p.add_argument('lab',type=Path);p.add_argument('--case',choices=PLANS,default='C0');a=p.parse_args()
if a.action=='prepare':prepare(a.lab)
elif a.action=='seal':seal(a.lab)
else:print(json.dumps(preflight(a.lab.resolve(),a.case),indent=2))

View file

@ -0,0 +1,139 @@
"""Executed offline negative tests; never mint an independent acceptance."""
import argparse
import ast
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
from horizontal_adapter import BASE,STANDARD_EXTERIOR,generate,replace_one
import horizontal_gate as gate
sys.path.insert(1,str(BASE))
from model import Rect,Space,Provenance,Invalid,pointer_local,popup,require
from predicates import dismissal,reference,protocol
from PIL import Image
from observations import applied_model,verify_owner_occlusion
from ipc_predicates import nodes,workspace,measure_owner
from model import content
from dataclasses import replace
import copy
class Offline(unittest.TestCase):
def test_generated_all_compile(self):
for c in gate.PLANS:
code=generate(c);compile(code,c,'exec')
self.assertIn('begin_run()',code);self.assertNotIn("reserve(lab,'run')",code)
self.assertLess(code.index('begin_run()'),code.index('tempfile.mkdtemp'))
self.assertIn("['swaymsg','-s',str(sock)",code)
def test_repository_import_closure_matches_frozen_paths(self):
frozen = set(gate.paths(gate.DEFAULT_LAB))
roots = (gate.HERE, BASE)
pending = [ast.parse(generate(c)) for c in gate.PLANS]
seen = set()
while pending:
tree = pending.pop()
for node in ast.walk(tree):
names = [a.name for a in node.names] if isinstance(node, ast.Import) else ([node.module] if isinstance(node, ast.ImportFrom) and node.module else [])
for name in names:
matches = [root / (name.split('.')[0] + '.py') for root in roots]
local = next((p for p in matches if p.is_file()), None)
if local is None:
continue
self.assertIn(local, frozen, 'executed repository import absent: ' + name)
if local not in seen:
seen.add(local);pending.append(ast.parse(local.read_text()))
self.assertNotIn(BASE / 'authorization.py', seen)
def retained_standard(self):
old = Path('/home/fy59/swayfx-mosaic-static-nested-runtime-pvn0osxm')
snap = json.loads((old/'results/standard-snapshot.json').read_text())
ws = workspace(snap['tree'], '1')
owner = next(n for n in nodes(ws) if n['id'] == 10)
env = {'require': require};exec(STANDARD_EXTERIOR, env)
scene = env['splith_exterior'](owner, ws['layout'])
measured = measure_owner((old/'run/compositor.log').read_text(), owner)
self.assertEqual(scene, measured['scene'])
measured['workspace'] = ws['id']
applied = applied_model(measured, owner, (old/'run/E2.log').read_text(), 'repair-retained-offline')
return old, owner, applied, env['splith_exterior']
def test_retained_standard_exterior_content_and_actual_png(self):
old, owner, applied, inverse = self.retained_standard()
self.assertEqual(owner['rect'], dict(x=-400,y=25,width=400,height=575))
self.assertEqual(inverse(owner,'splith'), [-400,0,400,600])
rect = content(applied);self.assertEqual(rect.values(), [-398,25,396,573])
obs = json.loads((old/'results/standard-offline-observation.json').read_text())
verify_owner_occlusion(Image.open(old/'run/standard-H1.png').convert('RGB'),
Image.open(old/'run/standard-H2.png').convert('RGB'), rect, obs['geometry'], False)
def test_retained_standard_invalid_decoration_model_destination(self):
old, owner, applied, inverse = self.retained_standard()
with self.assertRaises(Invalid):inverse(owner,'stacked')
altered=copy.deepcopy(owner);altered['deco_rect']['height']=-1
with self.assertRaises(Invalid):inverse(altered,'splith')
for bad in (replace(applied, titlebar=26),
replace(applied, scene=replace(applied.scene,y=25,height=575)),
replace(applied, window=replace(applied.window,height=572))):
with self.assertRaises(Invalid):content(bad)
altered=copy.deepcopy(owner);altered['deco_rect']['height']=26
with self.assertRaises(Invalid):
require(inverse(altered,'splith') == applied.scene.values(), 'standard-applied-scene-matches-assignment')
def test_unique_transform_rejects(self):
for s in ('missing','x x'):
with self.assertRaises(ValueError):replace_one(s,'x','y')
def test_standard_has_no_camera_or_mosaic_command(self):
for c in ('C0','C1'):
code=generate(c);self.assertNotIn('open_chain(',code);self.assertNotIn('layout mosaic scroll',code)
self.assertNotIn('layout mosaic\')',code)
self.assertNotIn('validate_applied_standard_transition(request',code)
def test_C2_contract_actions(self):
code=generate('C2');self.assertIn("open_chain('E1')",code);self.assertIn('fresh_chain(',code)
self.assertIn('dismissal(log',code);self.assertIn('validate_applied_standard_transition(request',code)
self.assertIn("layout splith",code);self.assertIn('final_restoration(baseline,final)',code)
def test_half_open_and_wrong_pointer_reject(self):
p=Provenance(1,2,'window','actual');r=Rect(Space.GLOBAL,-200,20,300,130,p)
self.assertTrue(r.contains((99,149)));self.assertFalse(r.contains((100,149)))
for xy,local in [((100,20),(300,0)),((0,20),(0,0))]:
with self.assertRaises(Invalid):pointer_local(xy,r,local)
def test_parent_identity_and_ack_reject(self):
p=Provenance(1,2,'window','actual');r=Rect(Space.GLOBAL,-200,20,300,130,p);c=Rect(Space.POPUP,10,10,30,30,p)
for parent,ack,mapped in [(22,3,True),(11,0,True),(11,3,False)]:
with self.assertRaises(Invalid):popup(r,c,parent,11,ack,mapped)
def test_reference_unmasked_single_pixel_reject(self):
a=Image.new('RGB',(800,600),(48,48,56));b=a.copy();b.putpixel((799,599),(48,48,57))
with self.assertRaises(AssertionError):reference(a,b)
def test_forbidden_button_rejects(self):
with self.assertRaises(RuntimeError):dismissal('POPUP_B POPUP_DONE \nPOPUP_A POPUP_DONE \nPOPUP_B DESTROY \nPOPUP_A DESTROY \n',['POINTER_BUTTON target=POPUP_B serial=2 button=273 state=1'])
def test_dismissal_missing_wrong_order_reject(self):
for text in ('','POPUP_A POPUP_DONE \nPOPUP_B POPUP_DONE \nPOPUP_B DESTROY \nPOPUP_A DESTROY \n'):
with self.assertRaises(AssertionError):dismissal(text,[])
def test_protocol_missing_real_wire_reject(self):
text=Path('/home/fy59/swayfx-mosaic-e2-completion-dk1hq9an/run/E2.log').read_text()
# Retained full log ends with explicit destruction; cut before actual close.
text=text[:text.index('POPUP_B DESTROY ')];protocol(text)
with self.assertRaises((AssertionError,Invalid)):protocol(text.replace('.grab(wl_seat#','.fake_grab(wl_seat#'))
def test_review_pending_rejected_before_effect(self):
with patch.object(gate,'verify_review',side_effect=ValueError('review pending')),patch.object(gate.ledger,'reserve') as debit:
with self.assertRaises(ValueError):gate.preflight(gate.DEFAULT_LAB,'C0')
debit.assert_not_called()
def test_local_budget_reject_no_global_capture(self):
with tempfile.TemporaryDirectory() as d:
w=Path(d);gate.exclusive(w/'actions.json',dict(planned=gate.PLANS['C0'],reserved={**dict.fromkeys(gate.PLANS['C0'],0),'capture':10},events=[]))
with patch.object(gate.ledger,'reserve') as debit:
with self.assertRaises(ValueError):gate.debit(w,'C0',w,'capture')
debit.assert_not_called()
def test_unknown_action_rejected(self):
with tempfile.TemporaryDirectory() as d:
w=Path(d);gate.exclusive(w/'actions.json',dict(planned=gate.PLANS['C0'],reserved=dict.fromkeys(gate.PLANS['C0'],0),events=[]))
with self.assertRaises(ValueError):gate.debit(w,'C0',w,'retry')
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True);a=p.parse_args()
suite=unittest.defaultTestLoader.loadTestsFromTestCase(Offline);names=[t.id() for t in suite]
result=unittest.TextTestRunner(verbosity=2).run(suite)
gate.exclusive(a.output,dict(status='PASS' if result.wasSuccessful() else 'FAIL',executed=result.testsRun,
tests=names,failures=[(str(t),s) for t,s in result.failures],errors=[(str(t),s) for t,s in result.errors],
runtime='NOT RUN',review_acceptance_created=False))
raise SystemExit(not result.wasSuccessful())

View file

@ -0,0 +1,122 @@
"""EXPERIMENTAL persistent overnight budget.
WHY: failures consume the same budget as successes.
CONTRACT: reserve before action; no refunds or reset operation.
INVARIANT: exclusive flock covers validation and durable atomic replacement.
"""
import fcntl
import json
import os
from pathlib import Path
import tempfile
import time
LIMITS = {'scene_build': 2, 'mosaic_build': 2, 'stage': 2, 'run': 18, 'capture': 220}
CAMPAIGNS = {'C': 3, 'D1': 2, 'D2': 2, 'D3': 2, 'D4': 2, 'E1': 1, 'E2': 1, 'E3': 2, 'F': 2, 'G': 1}
def _sync_dir(parent):
fd = os.open(parent, os.O_RDONLY | os.O_DIRECTORY)
try:
os.fsync(fd)
finally:
os.close(fd)
def initialize(path):
path = Path(path)
with path.with_suffix('.lock').open('a') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
state = {'limits': LIMITS, 'campaign_limits': CAMPAIGNS,
'used': dict.fromkeys(LIMITS, 0), 'campaign_used': dict.fromkeys(CAMPAIGNS, 0),
'runs': {}, 'events': []}
# O_EXCL makes reinitialization an error, never an implicit reset.
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, 'w') as out:
json.dump(state, out, indent=2); out.write('\n'); out.flush(); os.fsync(out.fileno())
_sync_dir(path.parent)
return state
def _validate(state):
"""WHY: corrupted persisted counters must fail closed before any action."""
if not isinstance(state, dict) or state.get('limits') != LIMITS or state.get('campaign_limits') != CAMPAIGNS:
raise RuntimeError('budget contract changed or corrupt')
for key, limits in [('used', LIMITS), ('campaign_used', CAMPAIGNS)]:
values = state.get(key)
if not isinstance(values, dict) or set(values) != set(limits):
raise RuntimeError('corrupt counter keys')
if any(type(values[k]) is not int or not 0 <= values[k] <= limits[k] for k in limits):
raise RuntimeError('corrupt counter values')
events, runs = state.get('events'), state.get('runs')
if not isinstance(events, list) or not isinstance(runs, dict):
raise RuntimeError('corrupt event/run data')
counted = dict.fromkeys(LIMITS, 0)
campaigns = dict.fromkeys(CAMPAIGNS, 0)
replay = {}
for i, event in enumerate(events):
if not isinstance(event, dict) or type(event.get('sequence')) is not int or event['sequence'] != i:
raise RuntimeError('corrupt event sequence')
kind, amount = event.get('kind'), event.get('amount')
if kind not in LIMITS or type(amount) is not int or amount <= 0 or type(event.get('reserved_at_ns')) is not int or event['reserved_at_ns'] <= 0:
raise RuntimeError('corrupt reservation event')
counted[kind] += amount
campaign, run_id = event.get('campaign'), event.get('run_id')
if kind == 'run':
if amount != 1 or campaign not in CAMPAIGNS or not isinstance(run_id, str) or not run_id or run_id in replay:
raise RuntimeError('corrupt run event')
replay[run_id] = {'campaign': campaign, 'captures': 0}
campaigns[campaign] += 1
elif kind == 'capture':
if run_id not in replay or campaign is not None:
raise RuntimeError('corrupt capture event')
replay[run_id]['captures'] += amount
elif campaign is not None or run_id is not None:
raise RuntimeError('unexpected build event association')
if counted != state['used'] or campaigns != state['campaign_used'] or set(replay) != set(runs):
raise RuntimeError('events do not reconcile counters')
for run_id, expected in replay.items():
run = runs[run_id]
if not isinstance(run, dict) or set(run) != {'campaign', 'captures', 'duration_s'} or run.get('campaign') != expected['campaign'] or type(run.get('captures')) is not int or run['captures'] != expected['captures']:
raise RuntimeError('corrupt run record')
if not 0 <= run['captures'] <= (12 if run['campaign'] == 'F' else 20) or type(run.get('duration_s')) not in (int, float) or not 0 < run['duration_s'] <= 150:
raise RuntimeError('corrupt run bounds')
def reserve(path, kind, *, campaign=None, run_id=None, amount=1, duration_s=150):
path = Path(path)
if kind not in LIMITS or type(amount) is not int or amount <= 0:
raise ValueError('invalid reservation')
with path.with_suffix('.lock').open('a') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
state = json.loads(path.read_text())
_validate(state)
if state['used'][kind] + amount > LIMITS[kind]:
raise RuntimeError('global budget exhausted: ' + kind)
if kind == 'run':
if amount != 1 or campaign not in CAMPAIGNS or not isinstance(run_id, str) or not run_id or run_id in state['runs']:
raise ValueError('unique run and known campaign required')
if type(duration_s) not in (int, float) or not 0 < duration_s <= 150:
raise ValueError('run duration exceeds 150 seconds')
if state['campaign_used'][campaign] >= CAMPAIGNS[campaign]:
raise RuntimeError('campaign budget exhausted')
state['campaign_used'][campaign] += 1
state['runs'][run_id] = {'campaign': campaign, 'captures': 0, 'duration_s': duration_s}
elif kind == 'capture':
if campaign is not None or run_id not in state['runs']:
raise ValueError('capture requires reserved run')
run = state['runs'][run_id]
if run['captures'] + amount > (12 if run['campaign'] == 'F' else 20):
raise RuntimeError('run capture budget exhausted')
run['captures'] += amount
elif campaign is not None or run_id is not None:
raise ValueError('build cannot associate run')
state['used'][kind] += amount
event = {'sequence': len(state['events']), 'kind': kind, 'amount': amount,
'campaign': campaign, 'run_id': run_id, 'reserved_at_ns': time.time_ns()}
state['events'].append(event)
fd, temporary = tempfile.mkstemp(prefix='.budget-', dir=path.parent)
try:
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)
_sync_dir(path.parent)
finally:
if os.path.exists(temporary):
os.unlink(temporary)
return event

View file

@ -0,0 +1,173 @@
# Prospective v3: D3-mosaic only
**EXPERIMENTAL / Runtime NOT RUN; independent review PENDING.**
This revision is usable only for the unused `D3-mosaic` case. Every other case
is refused by the entry gate. Consumed v2 results, tokens and receipts are
unchanged. D2-mosaic remains INCONCLUSIVE and cannot be retried.
Before editing, all 578 files of accepted v2, its freeze/review and all matrix
modules were copied with exact SHA256 verification into
`LAB/matrix/executed-v2-archive/`. `archive-map.json` explicitly binds original
paths to archival paths. The original freeze hash remains
`a20ccb39095ca35363119b5cfe4b5c839c1a75863bd72a097686aeac76dd7d7f`.
Original source paths now contain this prospective revision; verification of
executed v2 uses the archival bytes. The v3 freeze binds and verifies that
complete archive, current import closure, binaries and clients, reports and
executed offline receipt. Its separate scope is
`OVERNIGHT_MATRIX_D3_PROSPECTIVE_V3`; it grants no author acceptance.
The actual pinned layer-shell constructor creates a descriptor popup host for
wallpaper even without an XDG popup. Before launching the owner, D3 records the
one live marker process, exactly one wallpaper layer creation, no IPC popup
owner, and at least two complete callbacks containing the same pre-existing
host. All baseline callbacks are retained. Opening then requires exactly this
unchanged known host and one new stable root; unknown or additional hosts,
missing known host, reordered indexes, missing owner/workspace, wrong geometry
and every erroneous comparable root clip are refused. Raw callbacks retain
both hosts and their original indexes. Identity selection uses only membership
in the pre-owner set, never the latest box or clipped index. The existing live
XDG parent/grab hierarchy and exact IPC ancestry bind the new root to the owner.
Constructor + timing + cardinality give bounded association. Native layer/root
owner IDs, host child count and atomic transaction generation are not observed.
The D2 replay tests validate only this fixture algorithm and never upgrade D2's
result. There is no compositor, client, package or production change.
The v2 preparation description below is historical context. Its general
fourteen-case dispatch is superseded by the single prospective v3 gate.
# Overnight output/lifetime/effects/ordering matrix
**EXPERIMENTAL / PREPARATION. Runtime NOT RUN; independent review PENDING.**
This fixture changes no compositor, UX, package, cumulative patch, desktop or
historical LAB. It recompiles only private C17 clients, preserving every source,
protocol XML/scanner input and output, command and resulting binary hash.
STEP20C remains NEXT; production multi-output NOT YET SUPPORTED.
`protocols.py` defines fourteen unique cases. The private `protocols.json` is
compared with that exact mapping by the gate. Every case specifies the scientific
question, output physical/logical geometry, preconditions, client placement,
ordered actions, point computation, ceilings, stop criteria and raw retention.
`engine.py` provides one small shared Session; `cases.py` supplies bounded
scenarios. No massive historical runner is copied. Existing model/protocol
functions are imported with their exact dependency hashes in a separate matrix
freeze. C's reviewer cannot grant matrix acceptance.
| Cases | Actual planned captures per run | Scientific ceiling |
| --- | ---: | --- |
| D1 pristine/Mosaic | 8 | sampled vertical true A/B grabs/crossing/input/dismissal |
| D2 pristine/Mosaic | 9 | sampled scale1.25 geometry after actual asymmetric markers |
| D3 pristine/Mosaic | 9 | sampled R90 scale1.25 geometry after actual asymmetric markers |
| D4 pristine/Mosaic | 17 | resize/clamp/migration sampled geometry; no atomic generation |
| E1 Mosaic | 12 | sampled fully hidden owner and same live chain return |
| E2 Mosaic | 8 | always INCONCLUSIVE for saved identity/render: native traces absent |
| E3 pristine/Mosaic | 18 | sampled effects/cache RGB, never complete damage/cache equivalence |
| F pristine/Mosaic | 8 | INCONCLUSIVE for native buffer identity/atomic input epoch |
Matrix158 + C37 + optional G17 =212/220 captures. Matrix14 + C3 + optional G1
=18/18 runs. These are fixed planned allocations below the ticket ceilings;
failures consume reservations and are never refunded. Each case independently
checks remaining global budget. Failure in another independent case does not
invent a dispatch veto, transfer a PASS or authorize a retry.
D1 uses real vertical outputs(0,0)/(0,600), native575-height vertical client and
actual y600 root/child crossing. D2 uses logical H1(-800,0,800,600) physical
1000x750 scale1.25, H2 normal. D3 uses the same logical boxes and H1 physical
750x1000 transform string90. Five distinct marker colors, positions and actual
PNG dimensions establish the frame before any coordinate conversion. The
marker source PNG is generated client content, recorded separately from actual
captures. Every injection uses the observed logical output union, never applies
scale twice, and recomputes a solid measured interior and fresh wire/local
coordinates. Rectangles remain half open.
The pristine standard origin uses IPC rect+window offset: exported rect.y already
includes decoration. Its exterior is the source-backed inverse(rect.y-deco,
rect.height+deco). Candidate applied origins come from PI_OWNER exterior scene
plus actual decoration/window offsets. Assignment, applied scene, native SHM,
configure, output logical, physical mode and PNG frame are distinct observations.
Root association requires the unique live XDG hierarchy and exact owner ancestry;
PI_POPUP has no owner field. No private generation is invented.
D4 opens clients before camera calibration, records startup blank H1, resizes H1
to960 atx-960 then back800 atx-800, and migrates workspace1 H1→H2→H1 once.
The Mosaic request derives maximum from frozen minimum640 policy, known gaps0
and exact row child count; it requests maximum+960 once and checks actual clamp.
Pristine clamp is NOT EXERCISED. Captures preserve all migration states, logical
order/IDs and legitimate popup dismissal. Input is never sent to an old invisible
surface. Closing owner/fillers restores blank H1 and exact full H2.
E1 preopens two prefix and two trailing fillers around the owner, then calibrates
once, opens two genuine grabs, clicks visible B, computes a hide camera from the
applied owner's endpoint, requires zero owner intersection and empty root clip,
checks no popup pixels on either output and no popup enter, returns by the exact
inverse, requires unchanged object IDs/no popup_done/recreation, clicks B again
and closes normally. The optional second invisible click is omitted.
E2 keeps the compiled default200ms timeout. It receives a genuine configure,
records HELD serial and independent wire configure with no ACK, captures only
what the real scheduling permits, and waits for the natural transaction timeout.
A capture named held may already follow timeout; the receipt explicitly says
NOT EXERCISED for native saved content. The responsive client continues dispatch;
natural ping responses are retained if present. No forced ACK, timer override,
code-level save or invented saved-buffer node is used. H2 sampled equality cannot
upgrade native saved identity or rendering from INCONCLUSIVE.
E3 uses the retained duplicate-configure-safe checker layer and translucent XDG
client. Shadow/no-shadow comparisons precede a classic floating glass wholly
inside H1 (source selects classic for floating). This samples retained ordinary floating
classic filtering, not Mosaic-tiled classic clipping. That same client is then retiled
and recalibrated once for settled tiled optimized blur. Explicit background
A/B/A commands drive real cache reconstruction. Every state has whole H2 capture.
An absent visible shadow or classic ROI is recorded NOT EXERCISED and does not
abort independent optimized measurements. Pristine lacks OBC trace. Native cache
observations, final drawing and input remain distinct; historical maxRGB6 is no
new threshold. No shader/cache/readback edit or compositor rebuild is used.
F uses a nongrab toplevel with real wl_surface input-region commits. Full/empty/
full right-button recipients, focus, gaps geometry, candidate camera forward/
inverse and standard/Mosaic return are sampled within bounds. Native pairs are
selected by exact(output,commit_seq), at most12, with missing presents explicit.
Independent client wire input, PI scene/owner/root, TEMPORAL_DAMAGE and commit/
present remain separate. Native buffer references/releases and hit epochs are
absent from the candidate. No scheduler manipulation, interval-seeking flood,
forced timing or GPU readback is used. Unobserved natural interleaving remains
NOT EXERCISED; buffer identity/atomic generation remain NOT PROVEN.
Global run reservation precedes runtime/socket/process creation; local durable
reservations precede every camera, chain, grab, click/move, configuration,
mutation and capture. Typed local counters reconcile exact event sequences and
immutable limits. A unique O_EXCL token makes each failed attempt unretryable.
Private runtime0700 and cleared inherited desktop/loader environment are required;
every swaymsg has explicit -s. Actual renderer startup lines and proc maps bind
candidate/pristine, wlroots and static/private SceneFX. Active watchdog143s,
run150s, per-log32MiB and shared cleanup deadlines bound all owned process groups.
Actual terminal IPC codes, process exit codes, rescue and runtime removal survive
failures. Cleanup failure downgrades the outcome and keeps measurement status.
## Separate independent review
Preparation entry: `PYTHONDONTWRITEBYTECODE=1 python matrix/prepare.py`.
This compiles clients only and is O_EXCL for its new private directory. Two
failed offline preparations on absent historical close_a context are retained
under matrix-client-preparation-failure01/02; the final region client reuses only
the exact command branch, with unique-line matching. No graphical attempt followed.
Initial offline tests23PASS/1FAIL asserted205 instead of actual203 planned PNG;
G was subsequently assigned17 captures, so final whole-batch planning is212.
that failed receipt stays preserved. Final negative tests and AST checks are
actual execution, not an acceptance generator or proof of graphics.
`gate.freeze()` writes LAB/matrix/freeze.json with every final source, reused
module, client source/protocol/binary, source audit, exact binary/library, pristine
provenance and executed receipt. No author-generated acceptance exists.
An independent read-only reviewer writes LAB/matrix/review.json with:
status ACCEPTED, independent=true, read_only=true, blockers=[],
scope OVERNIGHT_MATRIX_FINAL_BYTES, freeze_sha256=SHA256(freeze.json),
files=the exact frozen files mapping, report_file=absolute report,
report_sha256=its exact SHA256. Changed bytes invalidate acceptance.
Only after acceptance: `PYTHONDONTWRITEBYTECODE=1 python matrix/run.py D1-pristine`
(or another exact case key). The gate fails before any token/run reservation if
review, source/client/binary identity, ownership, socket/process state or budget
is invalid. The author performs no run and grants no acceptance. Independent
review may refuse technically incomplete predicates before any case consumes
its budget; raw results never upgrade unsupported scientific claims.

View file

@ -0,0 +1,9 @@
"""Offline per-case bounded evidence summary; never invent missing native identity."""
import argparse,json
from pathlib import Path
from gate import LAB
from protocols import RUNS
p=argparse.ArgumentParser();p.add_argument('run_id',choices=RUNS);a=p.parse_args();w=LAB/'matrix'/a.run_id/'run'
raw=json.loads((w/'outcome.json').read_text());spec=RUNS[a.run_id]
print(json.dumps(dict(case=spec['id'],runtime=raw,status=raw['status'],source_observability_ceilings=spec.get('ceilings',[]),
complete_damage='NOT PROVEN',atomic_generation='NOT PROVEN',production='NOT YET SUPPORTED',historical_acceptance_transferred=False),indent=2))

View file

@ -0,0 +1,266 @@
"""Small campaign scenarios over the same reviewed Session; no historical runner copy."""
import re,time
from PIL import ImageChops
from engine import *
from geometry import *
def init_reference(s,prefix_fillers=1):
s.cmd('workspace 2');s.launch_client('reference','static-reference');rid=s.find('reference')['id'];s.cmd(f'[con_id={rid}] border none')
s.cmd('workspace 1')
for i in range(prefix_fillers):s.launch_client('filler'+str(i),'static-reference')
s.cmd('layout mosaic' if s.spec['mode']=='mosaic' else 'layout splith')
return rid
def stable_reference(s):
s.inject('move',[s.outputs[0]['logical'][0]+10,s.outputs[0]['logical'][1]+590]);time.sleep(.15)
a=s.capture('H2-reference1','HEADLESS-2');time.sleep(.6);b=s.capture('H2-reference2','HEADLESS-2');exact(a,b)
need(all(v==(48,48,56) for v in a.get_flattened_data()),'static uniform H2');s.save('reference-stability',dict(status='PASS',exact=exact(a,b)));return a
def marker_host_baseline(s):
"""D3-only pre-XDG association: source + creation timing + cardinality.
This does not observe native owner IDs or host child count. No root is
selected by clip/index after the owner exists.
"""
from transition import observed_epochs
need(s.spec['id']=='D3-mosaic' and 'owner' not in s.proc,'prospective pre-owner marker baseline')
marker=s.proc.get('marker-background');need(marker is not None and marker.poll() is None,'live marker layer client')
start=next(e['ns'] for e in s.events if e['kind']=='spawn' and e['name']=='marker-background')
end=time.monotonic_ns();trace=s.log('compositor')
creations=[l for l in trace.splitlines() if 'new layer surface: namespace ' in l]
need(len(creations)==1 and 'namespace wallpaper layer 0 ' in creations[0],'one wallpaper layer creation')
snapshot=s.snapshot('pre-XDG-marker-host')
need(not any(n.get('app_id')=='org.mosaic.NestedGrabProbe' for n in nodes(snapshot['tree'])),'no popup owner before baseline')
epochs=observed_epochs(trace,'HEADLESS-1',start,end);tree=None;observed=[]
for e in epochs:
need(e['scene'] is not None,'complete baseline callback')
roots=e['roots']
if not roots:
need(tree is None,'marker host disappeared before owner');continue
need(len(roots)==1,'unique pre-XDG host')
r=roots[0]
need(r.get('local_index')=='1' and r.get('enabled')=='1' and r.get('clip_valid')=='0' and r.get('clip')=='0,0,0,0','source-backed ordinary layer host')
if tree is None:tree=r['tree']
need(r['tree']==tree,'stable pre-XDG host')
observed.append(e)
need(len(observed)>=2,'two complete pre-XDG host observations')
baseline=dict(tree=tree,window=[start,end],epochs=epochs,layer_creation=creations[0],marker_pid=marker.pid,snapshot=snapshot,
correlation='single layer constructor + pre-owner timing + stable cardinality; no native layer ID or child count')
s.save('marker-host-baseline',baseline);s.marker_host=baseline
return baseline
def associated_root(roots,baseline=None,commit_ns=None):
if baseline is None:
need(len(roots)==1 and roots[0].get('local_index')=='1','unique first popup root');return roots[0]
need(commit_ns is not None and commit_ns>baseline['window'][1],'root callback after pre-XDG baseline')
need(len(roots)==2 and len({r.get('tree') for r in roots})==2,'one known layer host plus one new XDG root')
need([r.get('local_index') for r in roots]==['1','2'],'native host order retained')
known=[r for r in roots if r.get('tree')==baseline['tree']]
need(len(known)==1 and known[0].get('enabled')=='1' and known[0].get('clip_valid')=='0' and known[0].get('clip')=='0,0,0,0','same unchanged known layer host')
return next(r for r in roots if r['tree']!=baseline['tree'])
def root_state(s,state,origin,window_start,expect_hidden=False,previous=None,expected_tree=None):
from transition import observed_epochs
from ipc_predicates import ancestry
end=time.monotonic_ns();output=state['workspace']['output']
out=next(o['logical'] for o in s.outputs if o['name']==output)
need(state['workspace']['layout']=='mosaic' and origin==state['origin'],'current Mosaic origin')
path=ancestry(state['snapshot']['tree'],state['owner']['id'])
need(path and path[-1]['id']==state['owner']['id'],'exact IPC owner ancestry')
need([n['id'] for n in path if n.get('type')=='workspace']==[state['workspace']['id']] and
[n['name'] for n in path if n.get('type')=='output']==[output],'exact workspace output ancestry')
live=[n for n in nodes(state['snapshot']['tree']) if n.get('app_id')==state['owner']['app_id'] and n.get('visible')]
need(len(live)==1 and live[0]['id']==state['owner']['id'],'unique live XDG owner')
prot=protocol(s.log('owner'));need(len(prot['grabs'])==2,'two real grabs')
epochs=observed_epochs(s.log('compositor'),output,window_start,end)
rows=[];trees=set();applied=False
for e in epochs:
c,sc,roots=e['commit'],e['scene'],e['roots']
need(c.get('has_buffer') in ('0','1') and sc is not None and sc.get('workspace')==str(state['workspace']['id']) and sc.get('layout')=='5','complete current callback workspace/layout')
owners=[o for o in e['owners'] if o.get('id')==str(state['owner']['id'])]
need(len(owners)==1 and owners[0].get('scene_valid')=='1','exact applied owner')
r=associated_root(roots,getattr(s,'marker_host',None),int(c['ns']))
o=owners[0];trees.add(r['tree'])
need(len(trees)==1 and (expected_tree is None or r['tree']==expected_tree),'same live root')
need(int(c['ns'])<int(sc['ns'])<int(o['ns'])<int(r['ns']),'callback record order')
scene=list(map(int,o['scene'].split(',')))+list(map(int,o['applied_size'].split(',')))
target=scene==state['scene']
old=previous is not None and scene==previous['scene']
need(target or (old and not applied),'unexpected or stale applied owner geometry')
chosen=state if target else previous
need(o['current']==','.join(str(chosen['owner']['rect'][k] if k!='y' else chosen['owner']['rect'][k]-chosen['owner']['deco_rect']['height']) for k in ('x','y','width','height')) or
list(map(int,o['current'].split(',')))==exterior_ipc(chosen['owner']),'current owner geometry')
win=chosen['owner']['window_rect'];actual=[scene[0]+win['x'],scene[1]+chosen['owner']['deco_rect']['height']+win['y']]
need(actual==chosen['origin'],'applied content origin')
box=list(map(int,r['clip'].split(',')));global_box=[box[0]+actual[0],box[1]+actual[1],*box[2:]]
hidden=intersection(scene,out)[2]==0 or intersection(scene,out)[3]==0
need(r['enabled']=='1' and r['clip_valid']=='1','enabled clipped root')
need((box[2]==0 or box[3]==0) if hidden else global_box==out,'every comparable root clip')
tile=list(map(int,sc['tile_scene'].split(',')));clip=list(map(int,sc['clip'].split(',')))
need(sc['clip_valid']=='1' and [out[0]+tile[0]+clip[0],out[1]+tile[1]+clip[1],*clip[2:]]==out,'applied tiling viewport')
if target:
need(hidden==expect_hidden,'target visibility');applied=True
rows.append(dict(state='applied-target' if target else 'old-applied',owner=o,scene=sc,root=r,commit=c,local_clip=box,global_clip=global_box,raw=e['raw'],all_roots=roots,known_layer_host=getattr(s,'marker_host',{}).get('tree')))
need(applied,'target applied callback missing')
return dict(tree=next(iter(trees)),states=rows,window=[window_start,end],ids=prot['ids'],marker_baseline=getattr(s,'marker_host',None),correlation='bounded unique live XDG hierarchy and exact IPC ancestry; no native parent ID or atomic generation')
def open_chain(s,tail_fillers=0):
if s.spec['id']=='D3-mosaic':marker_host_baseline(s)
s.debit('chain');s.launch_client('owner',s.spec['client'],s.spec['placement'])
for i in range(tail_fillers):s.launch_client('tail'+str(i),'static-reference')
s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner');baseline=stable_reference(s)
im=s.capture('owner-top','HEADLESS-1');rect=[*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']]
xy=solid_point(im,COLORS['TOPLEVEL'],s.outputs[0],s.frames['HEADLESS-1'],rect)
need(contains([*state['origin'],420,575 if s.spec['campaign']=='D1' else 320],xy),'opening native bounds')
ta,ba=s.click('owner',xy,'left',True);s.wait(lambda:'POPUP_A MAP ' in s.log('owner'));time.sleep(.2)
aim=s.capture('popup-A','HEADLESS-1');prov=Provenance(state['owner']['id'],state['workspace']['id'],'bounded-opening-window',state['evidence'])
xyA=solid_point(aim,COLORS['POPUP_A'],s.outputs[0],s.frames['HEADLESS-1'])
tb,bb=s.click('owner',xyA,'left',True);s.wait(lambda:'POPUP_B MAP ' in s.log('owner'));time.sleep(.2)
window=time.monotonic_ns();time.sleep(.15);p=protocol(s.log('owner'));g=geometry(s.log('owner'),state['origin'],prov)
s.delivery('owner',rect,xy,ta,ba,'TOPLEVEL',p['grabs'][0]['serial']);s.delivery('owner',g['A'],xyA,tb,bb,'POPUP_A',p['grabs'][1]['serial'])
need(all(crossing(g[k],s.spec['axis'],s.spec['boundary']) for k in ('A','B')),'actual root and child crossing NOT EXERCISED')
left,right=s.pair_capture('mapped')
for name in ('POPUP_A','POPUP_B'):
need(count(left,COLORS[name])>0,'permitted popup pixels missing')
if s.spec['mode']=='pristine':need(count(right,COLORS[name])>0,'standard popup continuation missing')
if s.spec['mode']=='mosaic':exact(baseline,right);root=root_state(s,state,state['origin'],window)
else:root=dict(status='NOT OBSERVABLE',meaning='unmodified control has no private clip instrumentation')
s.save('chain',dict(protocol=p,geometry=g,state=state,root=root,H2_exact=s.spec['mode']=='mosaic',color_counts={n:[count(left,c),count(right,c)] for n,c in COLORS.items()}))
return state,baseline,p,g,left,right
def popup_campaign(s):
init_reference(s);state,baseline,p,g,left,right=open_chain(s)
xy=solid_point(left,COLORS['POPUP_B'],s.outputs[0],s.frames['HEADLESS-1'],g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
forbidden=intersection(g['B'],s.outputs[1]['logical']);need(forbidden[2]>6 and forbidden[3]>6,'H2 crossing interior missing')
xy=[forbidden[0]+forbidden[2]//2,forbidden[1]+forbidden[3]//2]
if s.spec['mode']=='pristine':
# The point must be real standard paint, not just a requested extent.
xy=solid_point(right,COLORS['POPUP_B'],s.outputs[1],s.frames['HEADLESS-2'],g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
else:
tails,buttons=s.click('owner',xy);ds=dismissal(tails['owner'],buttons['owner']);s.save('dismissal',dict(proof=ds,reference_buttons=buttons.get('reference'),interpretation='outside grab dismissal; no universal click-through claim'))
need(not any('target=POPUP_A ' in l or 'target=POPUP_B ' in l for v in buttons.values() for l in v),'forbidden popup delivery')
s.close_client('owner');time.sleep(.2);final=s.pair_capture('final');exact(baseline,final[1]);need(not any(count(im,COLORS[n]) for im in final for n in ('POPUP_A','POPUP_B')),'popup residual')
return 'PASS'
def hidden_campaign(s):
init_reference(s,2);state,baseline,p,g,left,right=open_chain(s,2)
xy=solid_point(left,COLORS['POPUP_B'],s.outputs[0],'logical',g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
delta=state['scene'][0]+state['scene'][2]-s.outputs[0]['logical'][0]+16;start=time.monotonic_ns();s.scroll('owner',delta);hidden=s.owner('owner')
need(intersection(hidden['scene'],s.outputs[0]['logical'])[2]==0,'owner not completely hidden')
images=s.pair_capture('fully-hidden');need(not any(count(im,COLORS[n]) for im in images for n in ('POPUP_A','POPUP_B')),'hidden popup residual');exact(baseline,images[1])
root=root_state(s,hidden,hidden['origin'],start,True,previous=state);cut=len(s.log('owner'));s.inject('move',[-790,590]);need('target=POPUP_A ' not in s.log('owner')[cut:] and 'target=POPUP_B ' not in s.log('owner')[cut:],'hidden popup pointer enter')
return_start=time.monotonic_ns();s.scroll('owner',-delta);restored=s.owner('owner');need(restored['origin']==state['origin'],'same owner origin restored')
images=s.pair_capture('returned');exact(baseline,images[1]);returned_root=root_state(s,restored,restored['origin'],return_start,previous=hidden,expected_tree=root['tree']);fresh=protocol(s.log('owner'));need(fresh['ids']==p['ids'] and fresh['grabs']==p['grabs'],'unchanged live chain')
xy=solid_point(images[0],COLORS['POPUP_B'],s.outputs[0],'logical',g['B']);tails,buttons=s.click('owner',xy);s.delivery('owner',g['B'],xy,tails,buttons,'POPUP_B')
s.save('hidden-return',dict(status='PASS',hidden=hidden,empty_root=root,returned=restored,returned_root=returned_root,ids=p['ids']))
s.close_client('owner');final=s.pair_capture('final');exact(baseline,final[1]);return 'PASS'
def dynamic_campaign(s):
blank=s.capture('startup-vacated-H1','HEADLESS-1');init_reference(s,2);s.cmd('gaps inner 0');state,baseline,p,g,left,right=open_chain(s)
owner_id=state['owner']['id'];ws_id=state['workspace']['id'];order=state['workspace'].get('mosaic',{}).get('rows',[{}])[0].get('children')
s.cmd('output HEADLESS-1 resolution 960x600 position -960 0',True);s.outputs[0]={**s.outputs[0],'logical':[-960,0,960,600],'mode':[960,600]};time.sleep(.35)
snap=s.snapshot('resized960');s.pair_capture('resized960')
clamp=dict(status='NOT EXERCISED',reason='standard control has no Mosaic camera')
if s.spec['mode']=='mosaic':
ws=workspace(snap['tree'],'1');row=ws['mosaic']['rows'][0];n=len(row['children']);gap=max(0,ws.get('gaps',{}).get('inner',0));cell=max(640,(960-(n-1)*gap)//n);maximum=max(0,n*cell+(n-1)*gap-960)
request=maximum+960;before=row['scroll_x'];s.scroll('owner',request);settled=s.snapshot('clamped960');actual=workspace(settled['tree'],'1')['mosaic']['rows'][0]['scroll_x']
need(actual==maximum,'source-derived clamp mismatch');clamp=dict(status='PASS',count=n,gap=gap,minimum_cell_width=640,source='frozen mosaic.c125-152',maximum=maximum,before=before,requested_relative=request,observed=actual)
s.cmd('output HEADLESS-1 resolution 800x600 position -800 0',True);s.outputs[0]=dict(s.spec['outputs'][0]);time.sleep(.35);s.snapshot('resized-back');s.pair_capture('resized-back')
stages=[]
for out,tag in [('HEADLESS-2','migrated-H2'),('HEADLESS-1','migrated-back-H1')]:
s.cmd('workspace 1');s.cmd(f'move workspace to output {out}',True);time.sleep(.35);snap=s.snapshot(tag);ws=workspace(snap['tree'],'1');owner=s.find('owner');need(ws['id']==ws_id and ws['output']==out and owner['id']==owner_id,'migrated exact owner/workspace')
if order is not None:need(ws['mosaic']['rows'][0]['children']==order,'logical row order changed')
pictures=s.pair_capture(tag);stages.append(dict(tag=tag,workspace=ws,owner=owner,popup_done=re.findall(r'(POPUP_[AB]) POPUP_DONE',s.log('owner'))))
if out=='HEADLESS-2':need(not any(count(pictures[0],COLORS[n]) for n in ('POPUP_A','POPUP_B')),'old output retained popup pixels')
s.save('dynamic-ownership',dict(status='PASS',clamp=clamp,stages=stages,ids_before=p['ids'],ids_after=protocol(s.log('owner'))['ids'] if 'POPUP_DONE ' not in s.log('owner') else 'legitimate dismissal recorded',atomic_generation='NOT PROVEN'))
s.close_client('owner')
for name in list(s.proc):
if name.startswith('filler'):s.close_client(name,'close')
time.sleep(.2);final=s.pair_capture('final');exact(baseline,final[1]);exact(blank,final[0]);return 'PASS'
def saved_campaign(s):
init_reference(s,2);s.launch_client('owner','saved-client');s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner');baseline=stable_reference(s)
s.client_cmd('owner','hold_next_configure','COMMAND hold_next_configure DONE');cut=len(s.log('owner'));cut_comp=len(s.log('compositor'));request=time.monotonic_ns();s.cmd('gaps inner 30',True)
s.wait(lambda:'HELD ' in s.log('owner')[cut:]);held=s.log('owner')[cut:];m=re.search(r'HELD ns=(\d+) serial=(\d+)',held);need(m is not None,'real held configure serial')
held_serial=int(m[2]);need(re.search(rf'xdg_surface#\d+\.configure\({held_serial}\)',held) is not None,'held actual wire configure');need(f'ack_configure({held_serial})' not in held,'held serial was ACKed')
held_images=s.pair_capture('held');held_capture_observation='NOT EXERCISED: native saved identity absent; capture may follow natural200ms timeout'
s.wait(lambda:'timed out (' in s.log('compositor')[cut_comp:],seconds=3);need(s.proc['owner'].poll() is None,'held client alive')
after=s.pair_capture('natural-timeout');exact(baseline,after[1]);s.save('saved-observability',dict(status='INCONCLUSIVE',request_ns=request,held_wire=held,natural_timeout=[l for l in s.log('compositor')[cut_comp:].splitlines() if 'timed out (' in l],default_timeout_ms=200,held_serial=held_serial,
saved_node_identity='NOT OBSERVABLE',saved_render_identity='NOT PROVEN',held_capture_observation=held_capture_observation,meaning='source-backed saved path and held configure; sampled SHM/pixels do not identify a saved scene node',ping_causality='wire responses recorded if natural ping occurs; no forced ping'))
s.close_client('owner','close');final=s.pair_capture('final');exact(baseline,final[1]);return 'INCONCLUSIVE'
def interior_roi(s,name):
owner=s.find(name)
if s.spec['mode']=='mosaic' and not owner.get('floating','auto_off').endswith('on'):
origin,scene,raw=actual_origin(owner,s.log('compositor'))
else:origin=assigned_origin(owner)
win=owner['window_rect'];r=intersection([*origin,win['width'],win['height']],s.outputs[0]['logical'])
need(r[2]>=80 and r[3]>=80,'effect central ROI not visible')
center=[r[0]+r[2]//2,r[1]+r[3]//2];p=global_png(center,s.outputs[0],s.frames['HEADLESS-1'])
return [p[0]-32,p[1]-32,p[0]+32,p[1]+32]
def adjacent_metric(image,roi):
im=image.crop(roi);total=0
for y in range(im.height):
for x in range(im.width-1):total+=sum(abs(a-b) for a,b in zip(im.getpixel((x,y)),im.getpixel((x+1,y))))
return total/(im.height*(im.width-1)*3)
def effects_campaign(s):
init_reference(s,1);baseline=stable_reference(s);s.launch_client('glass','fx-clients','glass');s.cmd(f'[con_id={s.find("glass")["id"]}] focus');s.calibrate('glass')
bg=s.spawn('background',[LAB/'matrix/clients/fx-clients','background'],True);s.wait(lambda:'BACKGROUND_CONFIGURED' in s.log('background'));s.client_cmd('background','A','COMMAND A DONE');time.sleep(.2)
s.cmd('gaps inner 24');s.cmd('blur_xray disable');s.cmd('blur_radius 5');s.cmd('blur_passes 3');s.cmd('blur_noise 0');s.cmd('blur_brightness 1');s.cmd('blur_contrast 1');s.cmd('blur_saturation 1')
off=s.pair_capture('FX-off');gid=s.find('glass')['id']
s.cmd('shadow_offset 20 0');s.cmd('shadow_blur_radius 16');s.cmd('shadow_color #000000A0');s.cmd(f'[con_id={gid}] shadows enable',True);time.sleep(.25);shadow=s.pair_capture('shadow-on')
shadow_exercised=ImageChops.difference(off[0],shadow[0]).getbbox() is not None
s.cmd(f'[con_id={gid}] shadows disable',True)
# Native source selects classic for a floating owner, optimized for settled
# tiled owners. Keep this classic glass wholly inside H1, then retile.
s.cmd(f'[con_id={gid}] floating enable',True);s.cmd(f'[con_id={gid}] resize set width 300 px height 200 px');s.cmd(f'[con_id={gid}] move absolute position -650 200');time.sleep(.25)
classic_off=s.pair_capture('classic-off');classic_roi=interior_roi(s,'glass');s.cmd(f'[con_id={gid}] blur enable',True);time.sleep(.25);classic=s.pair_capture('classic-blur')
classic_adjacent_off=adjacent_metric(classic_off[0],classic_roi);classic_adjacent_on=adjacent_metric(classic[0],classic_roi)
classic_exercised=classic_off[0].crop(classic_roi).tobytes()!=classic[0].crop(classic_roi).tobytes() and classic_adjacent_on<classic_adjacent_off
floating=s.find('glass');need(floating['rect']['x']>=-800 and floating['rect']['x']+floating['rect']['width']<=0,'classic floating ROI wholly H1')
s.cmd(f'[con_id={gid}] floating disable',True);time.sleep(.25);s.calibrate('glass')
# A real background layer below blur_tree drives the native optimized path;
# no shader/cache call is made by the runner.
s.cmd(f'[con_id={gid}] blur disable',True);s.cmd(f'[con_id={gid}] blur enable',True);time.sleep(.25);optA=s.pair_capture('optimized-A');optimized_roi=interior_roi(s,'glass')
s.client_cmd('background','B','COMMAND B DONE');time.sleep(.25);optB=s.pair_capture('optimized-B')
s.client_cmd('background','A','COMMAND A DONE');time.sleep(.25);optA2=s.pair_capture('optimized-A-return')
need(optA[0].crop(optimized_roi).tobytes()!=optB[0].crop(optimized_roi).tobytes(),'fresh central glass A-B RGB difference');exact(optA[0],optA2[0])
if s.spec['mode']=='mosaic':
for imgs in (off,shadow,classic_off,classic,optA,optB,optA2):exact(baseline,imgs[1])
log=s.log('compositor');native_optimized='OPT_REGEN_RESULT' in log and 'OPT_BLUR_USE' in log and any('optimized=1' in l and 'chosen=(nil)' not in l for l in log.splitlines() if 'OPT_BLUR_USE' in l)
else:native_optimized='NOT OBSERVABLE on pristine'
s.save('effects-evidence',dict(status='PARTIALLY_FEASIBLE',standard_shadow_spill='EXERCISED' if s.spec['mode']=='pristine' and ImageChops.difference(off[1],shadow[1]).getbbox() else 'NOT EXERCISED',shadow_actual_RGB='EXERCISED' if shadow_exercised else 'NOT EXERCISED',classic_blur_actual_RGB='EXERCISED' if classic_exercised else 'NOT EXERCISED',native_optimized=native_optimized,classic_filtering=dict(roi=classic_roi,adjacent_off=classic_adjacent_off,adjacent_on=classic_adjacent_on),optimized_center_roi=optimized_roi,A_B_A_actual=True,
classic_vs_optimized_identity='source-selected classic floating wholly H1 versus settled tiled optimized; native chosen/cache observations retained, no whole-cache equivalence claim; Mosaic-tiled classic clipping NOT TESTED',edges='recorded full PNG; historical max6 not presumed',complete_damage='NOT PROVEN'))
s.close_client('glass','close');s.close_client('background','close');time.sleep(.2);final=s.pair_capture('final');exact(baseline,final[1]);return 'PARTIALLY_FEASIBLE'
def parse_pairs(text,limit=12):
commits=[];presents={}
for line in text.splitlines():
if 'PI_OUTPUT_COMMIT ' in line or 'PI_OUTPUT_PRESENT ' in line:
fields=dict(re.findall(r'(\w+)=([^\s]+)',line));need(all(k in fields for k in ('output','commit_seq','ns')),'native pair format');key=(fields['output'],int(fields['commit_seq']))
if 'PI_OUTPUT_COMMIT ' in line:commits.append((key,fields,line))
else:need(key not in presents,'duplicate native present');presents[key]=(fields,line)
rows=[]
for key,fields,raw in commits[:limit]:
pr=presents.get(key);rows.append(dict(output=key[0],seq=key[1],commit=raw,present=pr[1] if pr else None,status='PAIRED' if pr else 'MISSING_PRESENT'))
return dict(analyzed=rows,limit=limit,total_commits=len(commits),unselected_retained_raw=True,buffer_identity='NOT PROVEN')
def ordering_campaign(s):
init_reference(s);s.launch_client('owner','region-client',s.spec['placement']);s.cmd(f'[con_id={s.find("owner")["id"]}] focus');state=s.calibrate('owner');baseline=stable_reference(s)
im=s.capture('input-full-before','HEADLESS-1');rect=[*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']];xy=solid_point(im,COLORS['TOPLEVEL'],s.outputs[0],'logical',rect)
tails,buttons=s.click('owner',xy);s.delivery('owner',rect,xy,tails,buttons,'TOPLEVEL')
s.client_cmd('owner','region_empty_top','COMMAND region_empty_top DONE');tails,buttons=s.click('owner',xy);need(not buttons['owner'],'empty input region delivered owner button');s.capture('region-empty','HEADLESS-1')
s.client_cmd('owner','region_full_top','COMMAND region_full_top DONE');tails,buttons=s.click('owner',xy);s.delivery('owner',rect,xy,tails,buttons,'TOPLEVEL')
s.cmd(f'[con_id={s.find("filler0")["id"]}] focus',True);s.cmd(f'[con_id={s.find("owner")["id"]}] focus',True)
s.cmd('gaps inner 30',True);s.cmd('gaps inner 0',True);time.sleep(.25);s.capture('resize-settled','HEADLESS-1')
if s.spec['mode']=='mosaic':
s.scroll('owner',20);s.scroll('owner',-20);s.cmd(f'[con_id={s.find("owner")["id"]}] layout splith',True);s.cmd(f'[con_id={s.find("owner")["id"]}] layout mosaic',True)
s.capture('layout-settled','HEADLESS-1');s.close_client('owner');final=s.pair_capture('final');exact(baseline,final[1])
pairs=parse_pairs(s.log('compositor'));s.save('ordering',dict(status='INCONCLUSIVE',pairs=pairs,
native_atomic_epoch='NOT PROVEN',native_hit_path='NOT OBSERVABLE',buffer_reference_release='NOT PROVEN',natural_commit_mutation_present='NOT EXERCISED unless independent timestamp analysis finds a real window',
damage=[l for l in s.log('compositor').splitlines() if 'TEMPORAL_DAMAGE' in l],client_input='actual wire/button/region commits retained separately'))
return 'INCONCLUSIVE'
CAMPAIGN={'D1':popup_campaign,'D2':popup_campaign,'D3':popup_campaign,'D4':dynamic_campaign,'E1':hidden_campaign,'E2':saved_campaign,'E3':effects_campaign,'F':ordering_campaign}

View file

@ -0,0 +1,188 @@
"""Bounded owned headless session, usable only after the separate matrix review gate."""
import json,os,re,shutil,signal,subprocess,tempfile,threading,time
from pathlib import Path
from PIL import Image,ImageDraw
from geometry import *
from gate import ROOT,BASE,LAB,sha,clean_environment,action_debit
import sys
sys.path.insert(1,str(BASE))
from ipc_predicates import nodes,workspace
from predicates import protocol,geometry,pair,dismissal
from lifecycle import opening_pair
from model import Provenance
COLORS={'TOPLEVEL':(35,68,204),'POPUP_A':(224,192,32),'POPUP_B':(32,208,160)}
class Session:
def __init__(self,spec,auth):
self.spec=spec;self.auth=auth;self.folder=LAB/'matrix'/spec['id'];self.run=self.folder/'run';self.run.mkdir()
self.runtime=Path(tempfile.mkdtemp(prefix='m20-',dir='/tmp'));self.runtime.chmod(0o700)
self.env=clean_environment();self.env.update(XDG_RUNTIME_DIR=str(self.runtime),WLR_BACKENDS='headless',WLR_HEADLESS_OUTPUTS='2',OPT_TRACE='1')
self.outputs=[dict(o) for o in spec['outputs']];self.frames={o['name']:'logical' for o in self.outputs}
self.proc={};self.handles=[];self.started=time.monotonic();self.finished=threading.Event();self.expired=False;self.events=[];self.pointer=None
def start(self):
spec=self.spec;auth=self.auth
cfg='\n'.join(f"output {o['name']} resolution {o['mode'][0]}x{o['mode'][1]} scale {o['scale']} transform {o['transform']} position {o['logical'][0]} {o['logical'][1]}" for o in self.outputs)
cfg+='\nworkspace 1 output HEADLESS-1\nworkspace 2 output HEADLESS-2\nfocus_follows_mouse no\nseat seat0 hide_cursor 100\nshadows disable\nblur disable\n'
if spec['campaign'] in ('D2','D3'):
marker=Image.new('RGB',(800,600),(30,30,46));draw=ImageDraw.Draw(marker)
for k,box in [('TL',(15,15,44,44)),('TR',(755,15,784,44)),('BL',(15,555,44,584)),('BR',(755,555,784,584)),('C',(385,285,414,314))]:draw.rectangle(box,fill=MARKERS[k])
marker.save(self.run/'marker-source.png');self.marker_source=self.run/'marker-source.png';self.record('generated-content-PNG',path=str(self.marker_source),counts_as_capture=False)
(self.run/'config').write_text(cfg);self.watch=threading.Thread(target=self.watchdog,daemon=True);self.watch.start()
self.comp=self.spawn('compositor',[auth['binary'],'-d','-c',self.run/'config'])
self.sock=self.wait(lambda:next(iter(self.runtime.glob('sway-ipc.*.sock')),None));display=self.wait(lambda:next((p for p in self.runtime.glob('wayland-*') if p.is_socket()),None))
self.env.update(WAYLAND_DISPLAY=display.name,SWAYSOCK=str(self.sock));need(Path(f'/proc/{self.comp.pid}/exe').resolve()==Path(auth['binary']).resolve(),'actual executable')
maps=Path(f'/proc/{self.comp.pid}/maps').read_text();(self.run/'proc-maps.txt').write_text(maps)
paths={s.split()[-1] for s in maps.splitlines() if len(s.split())>=6 and s.split()[-1].startswith('/')}
wl=[Path(p).resolve() for p in paths if 'libwlroots' in p];need(wl==[Path(auth['wlroots'])] and sha(wl[0])==auth['wlroots_sha256'],'mapped wlroots')
scene=[Path(p).resolve() for p in paths if 'libscenefx' in p]
if spec['mode']=='mosaic':need(not scene,'static SceneFX')
else:need(scene==[Path(auth['pristine_scene'])] and sha(scene[0])==auth['pristine_scene_sha256'],'mapped pristine SceneFX')
render=[s for s in self.log('compositor').splitlines() if any(x in s for x in ('Creating scenefx FX renderer','GL renderer:','GL vendor:','Using OpenGL ES'))]
need(any('Creating scenefx FX renderer' in s for s in render) and any('GL renderer:' in s for s in render),'actual renderer')
self.record('actual-renderer',lines=render);self.snapshot('startup')
if spec['campaign'] in ('D2','D3'):
self.spawn('marker-background',['swaybg','-o','HEADLESS-1','-i',self.marker_source,'-m','fill']);time.sleep(.35)
im=self.capture('marker-H1','HEADLESS-1',orientation_pending=True);m=marker_frame(im,self.outputs[0]['scale'],self.outputs[0]['transform']=='90');self.frames['HEADLESS-1']=m['frame'];self.record('marker-frame',**m)
self.pointer=self.spawn('pointer',[LAB/'matrix/clients/pointer-daemon']);self.wait(lambda:'POINTER_READY' in self.log('pointer'))
def record(self,kind,**fields):
e=dict(kind=kind,ns=time.monotonic_ns(),**fields);self.events.append(e)
with (self.run/'events.jsonl').open('a') as f:f.write(json.dumps(e)+'\n')
def save(self,name,value):
(self.run/(name+'.json')).write_text(json.dumps(value,indent=2)+'\n')
def debit(self,*kinds):action_debit(self.spec['id'],*kinds)
def spawn(self,name,argv,debug=False):
need(not self.expired,'expired before spawn');f=(self.run/(name+'.log')).open('w');self.handles.append(f)
p=subprocess.Popen(list(map(str,argv)),stdin=subprocess.PIPE,stdout=f,stderr=subprocess.STDOUT,text=True,start_new_session=True,env={**self.env,**({'WAYLAND_DEBUG':'1'} if debug else {})})
self.proc[name]=p;self.record('spawn',name=name,pid=p.pid,argv=list(map(str,argv)));return p
def log(self,name):return (self.run/(name+'.log')).read_text(errors='replace')
def wait(self,fn,seconds=5):
end=min(self.started+143,time.monotonic()+seconds)
while time.monotonic()<end:
need(not self.expired,'watchdog')
if hasattr(self,'comp'):need(self.comp.poll() is None,'unexpected compositor exit')
v=fn()
if v:return v
time.sleep(.02)
raise Ambiguous('bounded wait timeout')
def watchdog(self):
while not self.finished.wait(.1):
if time.monotonic()-self.started>143 or any(p.stat().st_size>32*1024*1024 for p in self.run.glob('*.log')):
self.expired=True
for p in list(self.proc.values()):
try:os.killpg(p.pid,signal.SIGTERM)
except ProcessLookupError:pass
return
def ipc(self,*args,strict=True):
need(self.sock.is_relative_to(self.runtime) and self.sock.is_socket(),'private socket')
self.record('ipc-request',args=args)
p=subprocess.run(['swaymsg','-s',str(self.sock),*args],env=self.env,capture_output=True,text=True,timeout=4)
self.record('ipc-reply',args=args,exit=p.returncode,stdout=p.stdout,stderr=p.stderr)
if strict:need(p.returncode==0,'IPC command failure')
return json.loads(p.stdout) if p.returncode==0 else None
def cmd(self,command,business=False):
self.debit('mutation' if business else 'configuration');reply=self.ipc(command);need(all(r['success'] for r in reply),'command rejected')
def snapshot(self,tag,validate=True):
snap=dict(tree=self.ipc('-t','get_tree'),outputs=self.ipc('-t','get_outputs'),workspaces=self.ipc('-t','get_workspaces'),ns=time.monotonic_ns())
if validate:expected_outputs(snap['outputs'],self.outputs)
self.save(tag+'-snapshot',snap);return snap
def find(self,name):
pid=self.proc[name].pid;rows=[n for n in nodes(self.ipc('-t','get_tree')) if n.get('pid')==pid];need(len(rows)<=1,'client owner ambiguity');return rows[0] if rows else None
def launch_client(self,name,client,*args):
p=self.spawn(name,[LAB/'matrix/clients'/client,*args],True);self.wait(lambda:self.find(name));time.sleep(.25);return p
def capture(self,tag,out,orientation_pending=False):
self.debit('capture');spec=next(o for o in self.outputs if o['name']==out);p=self.run/(tag+'.png');need(not p.exists(),'capture unique')
t=time.monotonic_ns();r=subprocess.run(['grim','-s',str(spec['scale']),'-o',out,str(p)],env=self.env,capture_output=True,text=True,timeout=4)
self.record('capture',tag=tag,output=out,start_ns=t,end_ns=time.monotonic_ns(),exit=r.returncode,path=str(p),sha256=sha(p) if p.exists() else None)
need(r.returncode==0,'capture failure');im=Image.open(p).convert('RGB')
logical=tuple(round(v*spec['scale']) for v in spec['logical'][2:]);physical=tuple(spec['mode'])
need(im.size==logical or (orientation_pending and im.size==physical) or (self.frames[out]=='physical90' and im.size==physical),'PNG size')
return im
def pair_capture(self,tag):return [self.capture(tag+'-H1','HEADLESS-1'),self.capture(tag+'-H2','HEADLESS-2')]
def inject(self,action,point,grab=False):
need(action in ('move','left','right'),'injection action');outs=self.ipc('-t','get_outputs');active=[o for o in outs if o['active']]
rects=[[o['rect'][k] for k in ('x','y','width','height')] for o in active];need(any(contains(r,point) for r in rects),'logical point outside output')
minx=min(r[0] for r in rects);miny=min(r[1] for r in rects);w=max(r[0]+r[2] for r in rects)-minx;h=max(r[1]+r[3] for r in rects)-miny
self.debit('move',*(['click'] if action!='move' else []),*(['grab'] if grab else []))
sent=[point[0]-minx,point[1]-miny];marker=f'INJECT_DONE {action} {sent[0]},{sent[1]}';before=self.log('pointer').count(marker)
self.pointer.stdin.write(f'{action} {sent[0]} {sent[1]} {w} {h}\n');self.pointer.stdin.flush();self.wait(lambda:self.log('pointer').count(marker)>before)
self.record('injection',action=action,global_logical=point,sent=sent,logical_extent=[w,h],grab=grab);time.sleep(.12)
def click(self,name,point,action='right',grab=False):
before={n:len(self.log(n)) for n in self.proc if n not in ('compositor','pointer','background')};t=time.monotonic_ns();self.inject(action,point,grab)
tails={n:self.log(n)[i:] for n,i in before.items()};lines={n:[s for s in v.splitlines() if s.startswith(('POINTER_BUTTON ','BUTTON '))] for n,v in tails.items()}
self.record('click-observation',name=name,point=point,start_ns=t,tails=tails,buttons=lines)
return tails,lines
def delivery(self,name,rect,point,tails,lines,target,opening_serial=None):
need(not any(v for n,v in lines.items() if n!=name),'wrong client received popup button')
proof=opening_pair(lines[name],target,opening_serial) if opening_serial else pair(lines[name],target)
rows=re.findall(rf'POINTER_(?:ENTER|MOTION) target={target} .*?x=([\d.-]+) y=([\d.-]+)',tails[name]);need(rows,'fresh pointer local coordinates')
local=list(map(float,rows[-1]));need(local==[point[0]-rect[0],point[1]-rect[1]],'logical pointer correlation')
for e in proof:need(re.search(rf'wl_pointer#\d+\.button\({e[1]}, \d+, {e[2]}, {e[3]}\)',tails[name]) is not None,'button actual wire')
return proof
def owner(self,name):
snap=self.snapshot(name+'-owner');owner=self.find(name);need(owner is not None and owner.get('visible'),'owner visibility')
ws=workspace(snap['tree'],'1');need(any(n['id']==owner['id'] for n in nodes(ws)),'owner workspace ancestry')
if self.spec['mode']=='mosaic':origin,scene,raw=actual_origin(owner,self.log('compositor'))
else:origin=assigned_origin(owner);scene=exterior_ipc(owner);raw='current IPC assigned exterior inverse; paint/pointer corroboration required'
data=dict(owner=owner,workspace=ws,origin=origin,scene=scene,evidence=raw,snapshot=snap);self.save(name+'-geometry',data);return data
def calibrate(self,name):
state=self.owner(name)
if self.spec['mode']=='mosaic' and self.spec['axis']==0:
delta=int(state['origin'][0]+state['owner']['window_rect']['width']//2)
if delta:self.scroll(name,delta)
state=self.owner(name);need(crossing([*state['origin'],state['owner']['window_rect']['width'],state['owner']['window_rect']['height']],0,0),'owner real x0 crossing')
return state
def scroll(self,name,delta):
self.debit('camera');self.cmd(f'[con_id={self.find(name)["id"]}] layout mosaic scroll {int(delta)}');time.sleep(.35)
self.record('camera',owner=self.find(name)['id'],delta=int(delta))
def client_cmd(self,name,cmd,marker=None):
self.debit('configuration' if cmd.startswith('close') else 'mutation');before=len(self.log(name));self.record('client-request',name=name,command=cmd)
self.proc[name].stdin.write(cmd+'\n');self.proc[name].stdin.flush()
if marker:self.wait(lambda:marker in self.log(name)[before:])
def close_client(self,name,command='close_owner'):
self.client_cmd(name,command);code=self.proc[name].wait(timeout=3);need(code==0,'normal client exit');self.wait(lambda:self.find(name) is None)
def cleanup(self,measurement_status,failure=None):
terminal=None;rescue=False
if hasattr(self,'comp') and self.comp.poll() is None and hasattr(self,'sock') and self.sock.is_socket():
try:
p=subprocess.run(['swaymsg','-s',str(self.sock),'exit'],env=self.env,capture_output=True,text=True,timeout=2)
terminal=dict(ipc_exit=p.returncode,stdout=p.stdout,stderr=p.stderr)
self.save('terminal-ipc',terminal)
except Exception as e:
terminal=dict(ipc_error=str(e));self.save('terminal-ipc',terminal)
if terminal is not None and 'ipc_exit' in terminal:
try:self.comp.wait(timeout=1)
except Exception as e:
terminal['wait_error']=str(e);self.save('terminal-ipc',terminal)
codes=[]
# One shared cleanup deadline prevents N owned clients multiplying waits.
cleanup_end=time.monotonic()+2
if self.pointer and self.pointer.poll() is None and self.pointer.stdin and not self.pointer.stdin.closed:self.pointer.stdin.close()
for name,p in reversed(list(self.proc.items())):
if p.poll() is None:
try:p.wait(timeout=max(.01,cleanup_end-time.monotonic()))
except subprocess.TimeoutExpired:pass
residual=[p for p in self.proc.values() if p.poll() is None]
if residual:
rescue=True
for p in residual:
try:os.killpg(p.pid,signal.SIGKILL)
except ProcessLookupError:pass
kill_end=time.monotonic()+1
for p in residual:p.wait(timeout=max(.01,kill_end-time.monotonic()))
for name,p in self.proc.items():
codes.append(dict(name=name,pid=p.pid,exit=p.returncode))
if p.stdin and not p.stdin.closed:p.stdin.close()
self.finished.set();self.watch.join(timeout=1)
for h in self.handles:h.close()
for p in self.proc.values():
try:os.killpg(p.pid,0)
except ProcessLookupError:pass
else:rescue=True;os.killpg(p.pid,signal.SIGKILL)
reaped=all(not Path('/proc',str(p.pid)).exists() for p in self.proc.values());need(reaped,'owned process residual');shutil.rmtree(self.runtime)
seconds=time.monotonic()-self.started
protocol_errors=[n for n in self.proc if n not in ('compositor','pointer') and re.search(r'wl_display[^\n]*\.error\(|protocol error|invalid_popup_parent|not_the_topmost_popup',self.log(n),re.I)]
cleanup_pass=bool(not rescue and reaped and seconds<=150 and not self.expired and not protocol_errors and all(p['exit']==0 for p in codes))
transport_pass=terminal is not None and terminal.get('ipc_exit')==0 and 'wait_error' not in terminal and 'ipc_error' not in terminal
final_status=measurement_status if cleanup_pass and transport_pass else 'INCONCLUSIVE'
self.save('outcome',dict(status=final_status,measurements_status=measurement_status,failure=failure,terminal=terminal,processes=codes,rescue=rescue,reaped=reaped,runtime_removed=not self.runtime.exists(),seconds=seconds,watchdog=self.expired,protocol_errors=protocol_errors,cleanup_pass=cleanup_pass,transport_status='PASS' if transport_pass else 'INCONCLUSIVE'))
return final_status

View file

@ -0,0 +1,109 @@
"""Separate exact-byte gate: no case can borrow C's review or runtime budget."""
import fcntl,json,os,stat,sys,time
from pathlib import Path
HERE=Path(__file__).resolve().parent;ROOT=HERE.parent;BASE=ROOT.parent/'step20c-static-e2-completion'
sys.path.insert(1,str(ROOT))
from horizontal_gate import DEFAULT_LAB as LAB,sha,env as clean_environment,exclusive,durable,PRISTINE,PRISTINE_SHA,WLROOTS
import ledger
from protocols import RUNS
PROSPECTIVE_CASES=['D3-mosaic']
REVIEW_SCOPE='OVERNIGHT_MATRIX_D3_PROSPECTIVE_V3'
PLANNED_PNG={'D1':8,'D2':9,'D3':9,'D4':17,'E1':12,'E2':8,'E3':18,'F':8}
def read(p):return json.loads(Path(p).read_text())
def need(ok,msg):
if not ok:raise ValueError(msg)
def verify_executed_archive():
archive=LAB/'matrix/executed-v2-archive';f=read(archive/'freeze.json');mapping=read(archive/'archive-map.json')
need(sha(archive/'freeze.json')=='a20ccb39095ca35363119b5cfe4b5c839c1a75863bd72a097686aeac76dd7d7f','original executed v2 freeze retained')
r=read(archive/'review.json')
need(r.get('status')=='ACCEPTED' and r.get('independent') is True and r.get('freeze_sha256')==sha(archive/'freeze.json') and r.get('files')==f['files'],'original executed v2 acceptance retained')
need(set(mapping)==set(f['files']),'complete original path archive binding')
for original,digest in f['files'].items():
p=Path(mapping[original]);need(p.is_relative_to(archive/'frozen-files') and sha(p)==digest,'archived executed bytes:'+original)
return mapping
def frozen_paths():
w=LAB/'matrix';archive=verify_executed_archive();out=[p for p in HERE.iterdir() if p.is_file()]
out+=[Path(p) for p in archive.values()]+[w/'executed-v2-archive/archive-map.json']
out+=[ROOT/'ledger.py',ROOT/'horizontal_gate.py',ROOT/'horizontal_adapter.py']
out+=[BASE/n for n in ('model.py','predicates.py','pixels.py','lifecycle.py','ipc_predicates.py','support.py','transition.py')]
out+=[LAB/'phase-a.json',LAB/'parent/native-source-audit.json',w/'protocols.json',w/'compile.json',w/'offline-tests-rotation-v3-frozen.json',w/'readiness.json']
out+=[p for p in (w/'clients').iterdir() if p.is_file()]
out+=[w/'offline-tests-rotation-v3-complete-deps.json',w/'rotation-v3-readonly-validation-attempt1.json']
out+=[LAB/'matrix/rotation-v3-repair-report.json',LAB/'matrix/rotation-v3-source.patch',LAB/'matrix/offline-tests-rotation-v3-attempt1.json',LAB/'mosaic-source/sway/desktop/layer_shell.c']
out+=[LAB/'reviews/matrix-first-runtime-review.md',LAB/'reviews/matrix-first-runtime-review.json',LAB/'matrix/executed-v2-archive/freeze.json',LAB/'matrix/executed-v2-archive/review.json']
out+=[LAB/'matrix/D2-mosaic/run'/n for n in ('compositor.log','owner.log','owner-geometry.json','events.jsonl','startup-snapshot.json')]
out+=[w/'offline-tests-initial.json',w/'offline-tests.json',w/'actual-review-refusal.json',LAB/'horizontal/C2/run/compositor.log',LAB/'horizontal/C2/run/E2.log']
out+=[Path(r['source']) for r in read(w/'compile.json')['sources']]
out+=[Path(read(LAB/'phase-a.json')['binary']['path']),WLROOTS,PRISTINE]
provenance=read(LAB/'horizontal/pristine-provenance.json');out+=[LAB/'horizontal/pristine-provenance.json',Path(provenance['historical_build_receipt'])]
out+=[Path(r['path']) for c in provenance['source_equivalence'] for r in c['files']]
out+=[LAB/r['path'] for r in read(LAB/'parent/native-source-audit.json')['sources']]
out+=[Path('/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n/scene0-build/libscenefx-0.5.so').resolve()]
import shutil
out+=[Path(shutil.which(t)).resolve() for t in ('swaymsg','grim','swaybg')]
return sorted(set(out))
def freeze():
need(read(LAB/'matrix/protocols.json')==RUNS,'private protocol exact source mapping')
need(read(LAB/'matrix/offline-tests-rotation-v3-frozen.json')['status']=='PASS','executed negative tests')
need(sum(PLANNED_PNG[g]*sum(r['campaign']==g for r in RUNS.values()) for g in PLANNED_PNG)+37+17<=220,'whole batch planned PNG allocation')
exclusive(LAB/'matrix/freeze.json',dict(scope=REVIEW_SCOPE,status='READY_FOR_REVIEW',prospective_cases=PROSPECTIVE_CASES,previous_freeze_sha256='a20ccb39095ca35363119b5cfe4b5c839c1a75863bd72a097686aeac76dd7d7f',files={str(p):sha(p) for p in frozen_paths()},protocols=RUNS,planned_png=PLANNED_PNG,
total_matrix_png=sum(PLANNED_PNG[r['campaign']] for r in RUNS.values()),C_allocation=37,G_allocation=17))
def verify(review=True):
w=LAB/'matrix';f=read(w/'freeze.json');need(f['files']=={str(p):sha(p) for p in frozen_paths()} and f['protocols']==RUNS and f.get('scope')==REVIEW_SCOPE and f.get('prospective_cases')==PROSPECTIVE_CASES,'final frozen bytes/schema')
need(read(w/'readiness.json')['status']=='PREPARED_FOR_REVIEW','readiness')
if review:
r=read(w/'review.json');need(r.get('status')=='ACCEPTED' and r.get('independent') is True and r.get('read_only') is True and r.get('scope')==REVIEW_SCOPE and r.get('blockers')==[],'genuine independent matrix review')
need(r.get('freeze_sha256')==sha(w/'freeze.json') and r.get('files')==f['files'],'review binding')
need(sha(r['report_file'])==r['report_sha256'],'review report bytes')
return f
def preflight(run_id,review=True):
need(run_id in RUNS and not sys.flags.optimize,'known immutable case and assertions')
verify(review);need(run_id in PROSPECTIVE_CASES,'only unused D3-mosaic prospective v3');w=LAB/'matrix'/run_id
need(not (w/'consumed.json').exists() and not (w/'run').exists(),'no retry')
need(stat.S_IMODE(LAB.stat().st_mode)==0o700 and LAB.stat().st_uid==os.getuid(),'owned0700 LAB')
need(not any(stat.S_ISSOCK(p.lstat().st_mode) for p in w.rglob('*')),'stale case socket')
a=read(LAB/'phase-a.json');need(a['status']=='PASS','phase A')
need(read(LAB/'matrix/compile.json')['status']=='PASS','C17 clients')
budget=read(LAB/'budget.json');ledger._validate(budget);spec=RUNS[run_id]
need(run_id not in budget['runs'] and budget['used']['run']<18 and budget['campaign_used'][spec['campaign']]<ledger.CAMPAIGNS[spec['campaign']],'global run budget')
need(budget['used']['capture']+PLANNED_PNG[spec['campaign']]<=220,'planned PNG availability')
mode=spec['mode'];binary=a['binary']['path'] if mode=='mosaic' else str(PRISTINE);digest=a['binary']['sha256'] if mode=='mosaic' else PRISTINE_SHA
need(sha(binary)==digest and sha(WLROOTS)=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837','exact candidate/pristine/wlroots')
for p in Path('/proc').iterdir():
if p.name.isdigit():
try:exe=(p/'exe').resolve(strict=True)
except (FileNotFoundError,PermissionError,ProcessLookupError):continue
need(exe!=Path(binary).resolve(),'same binary already running')
scene=Path('/home/fy59/swayfx-mosaic-optimized-edge-a55yjb8n/scene0-build/libscenefx-0.5.so').resolve()
return dict(binary=binary,binary_sha256=digest,wlroots=str(WLROOTS),wlroots_sha256=sha(WLROOTS),pristine_scene=str(scene),pristine_scene_sha256=sha(scene))
def begin(run_id):
auth=preflight(run_id);w=LAB/'matrix'/run_id;exclusive(w/'consumed.json',dict(id=run_id,freeze_sha256=sha(LAB/'matrix/freeze.json'),ns=time.monotonic_ns()))
ledger.reserve(LAB/'budget.json','run',campaign=RUNS[run_id]['campaign'],run_id=run_id,duration_s=150)
limits=action_limits(run_id)
exclusive(w/'actions.json',dict(planned=limits,reserved=dict.fromkeys(limits,0),events=[]));return auth
def action_limits(run_id):
limits={k:v for k,v in RUNS[run_id]['limits'].items() if k not in ('duration_s','active_watchdog_s')}
limits['capture']=PLANNED_PNG[RUNS[run_id]['campaign']];limits['configuration']=48;return limits
def local_valid(run_id,state):
limits=action_limits(run_id);need(state.get('planned')==limits,'immutable planned action limits')
values=state.get('reserved');events=state.get('events');need(isinstance(values,dict) and set(values)==set(limits) and isinstance(events,list),'local schema')
need(all(type(values[k]) is int and 0<=values[k]<=limits[k] for k in limits),'local typed counters')
need(len(events)==sum(values.values()),'local event total')
need(all(isinstance(e,dict) and e.get('sequence')==i and e.get('kind') in limits and type(e.get('ns')) is int and e['ns']>0 for i,e in enumerate(events)),'local event sequence')
need(all(sum(e['kind']==k for e in events)==values[k] for k in limits),'local reconciliation')
return state
def action_debit(run_id,*kinds):
w=LAB/'matrix'/run_id
with (w/'actions.lock').open('a+') as lock:
fcntl.flock(lock,fcntl.LOCK_EX);state=local_valid(run_id,read(w/'actions.json'))
need(kinds and all(k in state['planned'] for k in kinds),'known actions')
need(all(state['reserved'][k]+kinds.count(k)<=state['planned'][k] for k in kinds),'planned local action exhausted')
for k in kinds:
if k=='capture':ledger.reserve(LAB/'budget.json','capture',run_id=run_id)
state['reserved'][k]+=1;state['events'].append(dict(kind=k,ns=time.monotonic_ns(),sequence=len(state['events'])))
durable(w/'actions.json',state)

View file

@ -0,0 +1,66 @@
"""Explicit logical global, IPC assignment, physical mode and PNG-frame adapters."""
from dataclasses import dataclass
import re
from PIL import Image
class Ambiguous(ValueError):pass
def need(ok,msg):
if not ok:raise Ambiguous(msg)
def box_end(r):return [r[0]+r[2],r[1]+r[3]]
def contains(r,p):return r[0]<=p[0]<r[0]+r[2] and r[1]<=p[1]<r[1]+r[3]
def crossing(r,axis,boundary):return r[axis]<boundary<r[axis]+r[axis+2]
def intersection(a,b):
x=max(a[0],b[0]);y=max(a[1],b[1]);return [x,y,max(0,min(a[0]+a[2],b[0]+b[2])-x),max(0,min(a[1]+a[3],b[1]+b[3])-y)]
def assigned_origin(owner):
# IPC rect already excludes top decoration (ipc-json.c899–906).
return [owner['rect']['x']+owner['window_rect']['x'],owner['rect']['y']+owner['window_rect']['y']]
def exterior_ipc(owner):
r=owner['rect'];d=owner['deco_rect']['height'];return [r['x'],r['y']-d,r['width'],r['height']+d]
def actual_origin(owner,trace):
rows=[s for s in trace.splitlines() if 'PI_OWNER output=' in s and f' id={owner["id"]} ' in s]
need(rows,'applied owner trace absent');m=re.search(r'scene_valid=1 scene=(-?\d+),(-?\d+) applied_size=(\d+),(\d+)',rows[-1]);need(m is not None,'applied owner malformed')
r=list(map(int,m.groups()));w=owner['window_rect'];return [r[0]+w['x'],r[1]+owner['deco_rect']['height']+w['y']],r,rows[-1]
MARKERS={'TL':(255,32,32),'TR':(32,255,32),'BL':(32,32,255),'BR':(255,255,32),'C':(192,32,255)}
def marker_frame(image,scale,rotated):
positions={k:[] for k in MARKERS};lookup={v:k for k,v in MARKERS.items()}
for y in range(image.height):
for x in range(image.width):
k=lookup.get(image.getpixel((x,y)))
if k:positions[k].append((x,y))
need(all(len(v)>=100 for v in positions.values()),'marker color missing')
c={k:[sum(p[i] for p in v)/len(v) for i in (0,1)] for k,v in positions.items()}
logical=image.size==(round(800*scale),round(600*scale)) and c['TL'][0]<c['TR'][0] and c['TL'][1]<c['BL'][1]
physical=rotated and image.size==(round(600*scale),round(800*scale)) and c['TL'][0]>c['BL'][0] and c['TL'][1]<c['TR'][1]
need(logical or physical,'PNG orientation unestablished')
return dict(frame='logical' if logical else 'physical90',centers=c,counts={k:len(v) for k,v in positions.items()},size=list(image.size))
def png_global(px,output,frame):
scale=output['scale'];x,y=px;ox,oy,w,h=output['logical']
if frame=='physical90':x,y=y,round(h*scale)-1-x
need(frame in ('logical','physical90'),'unknown PNG frame')
# Sample integer logical centers only; resample at the chosen center later.
return [ox+int(x/scale),oy+int(y/scale)]
def global_png(p,output,frame):
x=(p[0]-output['logical'][0])*output['scale'];y=(p[1]-output['logical'][1])*output['scale']
if frame=='physical90':x,y=output['logical'][3]*output['scale']-1-y,x
return [round(x),round(y)]
def solid_point(image,color,output,frame,rectangle=None,margin=3):
for y in range(margin,image.height-margin,3):
for x in range(margin,image.width-margin,3):
if image.getpixel((x,y))!=color:continue
p=png_global((x,y),output,frame)
if rectangle is not None and not contains(rectangle,p):continue
pp=global_png(p,output,frame)
if not (margin<=pp[0]<image.width-margin and margin<=pp[1]<image.height-margin):continue
if all(image.getpixel((xx,yy))==color for yy in range(pp[1]-margin,pp[1]+margin+1) for xx in range(pp[0]-margin,pp[0]+margin+1)):
return p
raise Ambiguous('no measured solid logical interior')
def count(image,color):return sum(v==color for v in image.get_flattened_data())
def exact(a,b):need(a.size==b.size and a.tobytes()==b.tobytes(),'whole RGB differs');return dict(equal=True,pixels=a.width*a.height)
def expected_outputs(outputs,spec):
active=[o for o in outputs if o['active']];need(len(active)==2,'active output count')
for want in spec:
rows=[o for o in active if o['name']==want['name']];need(len(rows)==1,'output identity')
o=rows[0];need([o['rect'][k] for k in ('x','y','width','height')]==want['logical'],'logical output geometry')
need(o['scale']==want['scale'] and o['transform']==want['transform'],'scale/IPC transform string')
need([o['current_mode'][k] for k in ('width','height')]==want['mode'],'physical mode')
return active

View file

@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Private C17 client preparation; never reserves or launches a compositor."""
from pathlib import Path
import hashlib,json,os,shutil,subprocess,sys
HERE=Path(__file__).resolve().parent;ROOT=HERE.parent;REPO=ROOT.parents[2]
sys.path.insert(0,str(ROOT));from horizontal_gate import DEFAULT_LAB,BASE,sha,env,exclusive,OLD_CLIENTS
from protocols import RUNS
lab=DEFAULT_LAB/'matrix';lab.mkdir();dest=lab/'clients';dest.mkdir();commands=[];sources=[]
def command(argv,cwd=None):
r=subprocess.run(list(map(str,argv)),cwd=cwd,env=env(),capture_output=True,text=True,timeout=30)
commands.append(dict(argv=list(map(str,argv)),cwd=str(cwd),exit=r.returncode,stdout=r.stdout,stderr=r.stderr))
if r.returncode:raise RuntimeError('private client preparation failed')
return r.stdout
def copy(src,name):
p=dest/name;shutil.copyfile(src,p);sources.append(dict(source=str(src),sha256=sha(src),destination=str(p)));return p
for n in ('nested-client.c','pointer-daemon.c','saved-client.c','static-reference.c','xdg-shell-client.h','xdg-shell-protocol.c','virtual-pointer-client.h','virtual-pointer-protocol.c'):copy(OLD_CLIENTS/n,n)
# An independent base is patched privately; original repository/historical bytes stay read only.
copy(REPO/'tooling/probes/step20c-nested-grab/nested-client.c','vertical-client.c')
patch=REPO/'tooling/probes/step20c-vertical-grab/client-vertical.patch'
p=dest/'vertical-patch';p.write_text(patch.read_text().replace('nested-client.c','vertical-client.c'))
command(['patch','--batch','--forward','--fuzz=0','-p1','-i',p],cwd=dest);sources.append(dict(source=str(patch),sha256=sha(patch),destination=str(p)))
# Extents are supplied for every injection so resized output unions remain explicit.
pointer=dest/'pointer-daemon.c';old='if (sscanf(line,"%15s %d %d",action,&x,&y)!=3) continue;'
new='unsigned width,height;\n if (sscanf(line,"%15s %d %d %u %u",action,&x,&y,&width,&height)!=5 || !width || !height || x<0 || y<0 || (unsigned)x>=width || (unsigned)y>=height || (strcmp(action,"move") && strcmp(action,"left") && strcmp(action,"right"))) return 3;'
s=pointer.read_text();assert s.count(old)==1;s=s.replace(old,new).replace('x,y,1600,600','x,y,width,height');pointer.write_text(s)
copy(REPO/'tooling/probes/step20c-nested-grab/nested-client.c','region-client.c')
patch=REPO/'tooling/probes/step20c-mutation-coverage/mutation-client.patch';p=dest/'region-patch';p.write_text(patch.read_text().replace('nested-client.c','region-client.c'))
# Reuse only the exact added command branch; historical close_a context is absent.
minus=[line[1:] for line in patch.read_text().splitlines() if line.startswith('-') and not line.startswith('---')]
plus=[line[1:] for line in patch.read_text().splitlines() if line.startswith('+') and not line.startswith('+++')]
r=dest/'region-client.c';text=r.read_text();assert len(minus)==1 and text.count(minus[0])==1
r.write_text(text.replace(minus[0],'\n'.join(plus),1));sources.append(dict(source=str(patch),sha256=sha(patch),destination=str(p)))
copy(REPO/'tooling/probes/step20c-optimized-blur-edge/clients.c','fx-clients.c')
xml=REPO/'tooling/probes/step20c-optimized-blur-cache/wlr-layer-shell-unstable-v1.xml'
copy(xml,'wlr-layer-shell-unstable-v1.xml')
for mode,suffix in [('client-header','-client.h'),('private-code','-protocol.c')]:command(['wayland-scanner',mode,xml,dest/('layer-shell'+suffix)])
# XDG/virtual-pointer regeneration must equal the retained compiled client's wire API.
for stem,xml in [('xdg-shell',Path('/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml')),('virtual-pointer',REPO/'tooling/probes/step20c-valid-grab/wlr-virtual-pointer-unstable-v1.xml')]:
for mode,suffix in [('client-header','-client.h'),('private-code','-protocol.c')]:
p=dest/(stem+suffix+'.regenerated');command(['wayland-scanner',mode,xml,p]);assert p.read_bytes()==(dest/(stem+suffix)).read_bytes()
sources.append(dict(source=str(xml),sha256=sha(xml),destination=None))
flags=command(['pkg-config','--cflags','--libs','wayland-client']).split()
for name,protocol in [('nested-client','xdg-shell'),('vertical-client','xdg-shell'),('region-client','xdg-shell'),('saved-client','xdg-shell'),('static-reference','xdg-shell'),('pointer-daemon','virtual-pointer'),('fx-clients','xdg-shell')]:
command(['cc','-std=c17','-O2','-Wall','-Wextra',dest/(name+'.c'),dest/(protocol+'-protocol.c'),*([dest/'layer-shell-protocol.c'] if name=='fx-clients' else []),'-o',dest/name,*flags])
exclusive(lab/'compile.json',dict(status='PASS',sources=sources,commands=commands,files={str(p):sha(p) for p in sorted(dest.iterdir()) if p.is_file()},runtime='NOT RUN'))
exclusive(lab/'protocols.json',RUNS)
for run_id in RUNS:(lab/run_id).mkdir()
print('MATRIX_CLIENTS_C17=PASS RUNS=NOT_RUN')

View file

@ -0,0 +1,44 @@
"""Frozen bounded questions/actions; source absence limits the scientific verdict."""
C=[dict(name='HEADLESS-1',logical=[-800,0,800,600],mode=[800,600],scale=1.,transform='normal'),dict(name='HEADLESS-2',logical=[0,0,800,600],mode=[800,600],scale=1.,transform='normal')]
D1=[dict(name='HEADLESS-1',logical=[0,0,800,600],mode=[800,600],scale=1.,transform='normal'),dict(name='HEADLESS-2',logical=[0,600,800,600],mode=[800,600],scale=1.,transform='normal')]
D2=[{**C[0],'mode':[1000,750],'scale':1.25},C[1]]
D3=[{**C[0],'mode':[750,1000],'scale':1.25,'transform':'90'},C[1]]
LIMITS=dict(duration_s=150,active_watchdog_s=143,capture=20,click=8,grab=2,camera=2,mutation=24,move=16,chain=1)
SPECS={
'D1':dict(outputs=D1,client='vertical-client',placement='v_both_cross',axis=1,boundary=600,
question='Do genuine root/child vertical crossings preserve upper pixels/input and exclude lower popup pixels/input under Mosaic?',
limits={**LIMITS,'capture':16,'camera':1},steps=['stable-H2-reference-pair','open-owner','real-left-TOPLEVEL-open-A','real-left-A-open-B','actual-y600-crossing-A-B','upper-B-right-input','lower-B-right-standard-or-forbidden-Mosaic','record-dismissal','normal-close','whole-H2-restoration']),
'D2':dict(outputs=D2,client='nested-client',placement='static_cross',axis=0,boundary=0,
question='Does scale1.25 use logical pointer coordinates once and preserve sampled crossing popup clipping?',
limits={**LIMITS,'capture':16,'camera':1},steps=['five-asymmetric-output-markers','observe-logical-PNG-frame-and-physical-mode','stable-H2-reference-pair','owner-once-calibration','two-real-grabs','actual-A-B-x0-crossing','visible-B-right','H2-standard-or-forbidden-B-right','record-dismissal','normal-close','whole-H2-restoration']),
'D3':dict(outputs=D3,client='nested-client',placement='static_cross',axis=0,boundary=0,
question='Does R90 scale1.25 preserve measured logical popup and pointer geometry?',
limits={**LIMITS,'capture':16,'camera':1},steps=['five-asymmetric-output-markers','observe-IPC-transform-string90-and-PNG-frame','stable-H2-reference-pair','owner-once-calibration','two-real-grabs','actual-A-B-x0-crossing','visible-B-right','H2-standard-or-forbidden-B-right','record-dismissal','normal-close','whole-H2-restoration']),
'D4':dict(outputs=C,client='nested-client',placement='static_cross',axis=0,boundary=0,
question='Do output resize/clamp and workspace migration preserve observed owner/order and clear old output pixels?',
limits=LIMITS,steps=['startup-vacated-H1-reference','all-clients-open-before-camera','H2-reference-pair','owner-calibration','two-real-grabs','initial-identities-order-camera-clip','H1-mode960x600-position-960','single-known-extent-large-scroll-source-clamp','H1-mode800x600-position-800','workspace1-move-to-H2','workspace1-move-back-H1','record-live-identities-or-legitimate-dismissal','normal-close','final-vacated-H1-and-whole-H2']),
'E1':dict(outputs=C,client='nested-client',placement='static_cross',axis=0,boundary=0,
question='Does a fully hidden applied owner hide the same root/child and allow an unchanged live chain to return?',
limits={**LIMITS,'capture':16,'grab':4,'chain':2,'camera':4},steps=['two-prefix-fillers-owner-two-tail-fillers-preopened','calibrate-once','H2-reference-pair','two-real-grabs-crossing','visible-B-right','hide-owner-camera-computed-from-applied-endpoint','empty-root-clip-and-no-popup-colors-H1-H2','motion-no-popup-enter','return-exact-inverse-camera','same-object-IDs-no-popup_done-or-recreation','visible-B-right','normal-close','whole-H2-restoration']),
'E2':dict(outputs=C,client='saved-client',placement=None,axis=0,boundary=0,
question='What is actually observed during one natural200ms held XDG transaction, without saved-node traces?',
limits={**LIMITS,'capture':12,'click':2,'move':2,'camera':1,'grab':0,'chain':0},steps=['all-clients-preopened','one-crossing-camera','H2-reference-pair','arm-hold_next_configure','normal-gaps-inner30-configure-request','received-serial-HOLD-wire','held-capture-if-before-timeout','natural-default200ms-timeout-no-forced-ACK','client-alive-wire-ping-dispatch','post-timeout-capture','normal-close','whole-H2-restoration'],
ceilings=['saved-node identity NOT OBSERVABLE','native saved render NOT PROVEN','saved clipping verdict INCONCLUSIVE even if sampled H2 RGB passes']),
'E3':dict(outputs=C,client='fx-clients',placement=None,axis=0,boundary=0,
question='Are shadow/classic/optimized blur actual RGB effects and fresh A/B/A pixels sampled with protected whole H2?',
limits={**LIMITS,'mutation':10,'capture':20,'grab':0,'chain':0},steps=['static-H2-reference-pair','real-H1-checker-layer-explicit-A','translucent-XDG-and-fillers','single-crossing-camera-or-standard-spill','FX-off-pair','shadow-on-pair-visible-ROI-change','shadow-off-classic-floating-glass-wholly-H1-off-pair','classic-blur-on-pair-actual-filtered-RGB','retile-and-once-recalibrate-for-optimized','optimized-on-natural-cache-A-pair','explicit-background-B-pair','explicit-background-A-pair','central-A-B-A-fresh-RGB','Mosaic-whole-H2-protected-every-state','normal-close'],
ceilings=['pristine has no OBC trace','sampling/cache-source and final-draw clip are distinct','classic floating wholly H1 does not qualify Mosaic-tiled classic clipping','standard glass tiled at output edge does not establish optimized glass crossing; H2 shadow spill recorded separately','common edge delta measured anew, historical max6 not a threshold']),
'F':dict(outputs=C,client='region-client',placement='both_cross',axis=0,boundary=0,
question='What natural applied scene/client input/damage/native commit-present ordering is actually sampled?',
limits={**LIMITS,'capture':12,'click':12,'move':12,'mutation':24,'camera':3,'grab':0,'chain':0},steps=['H2-reference-pair','owner-current-assignment-and-applied-scene','baseline-right-input','region-empty-real-client-commit','same-point-right-no-owner-delivery','region-full-real-client-commit','same-point-right-owner-delivery','focus-filler-and-owner','gaps-inner30-and0','one-camera-forward-and-inverse-if-Mosaic','layout-splith-and-Mosaic-if-candidate','sample-settled-pixels-and-whole-H2','analyze-at-most12-exact-output-seq-commit-present-pairs','record-missing-present-and-natural-window-NOT_EXERCISED','normal-close'],
ceilings=['native buffer reference/release identity NOT PROVEN','atomic applied epoch NOT PROVEN','no GPU readback, forced timing or interval-seeking loop'])}
RUNS={}
for group,spec in SPECS.items():
modes=('mosaic',) if group in ('E1','E2') else ('pristine','mosaic')
for mode in modes:
run_id=group+'-'+mode
RUNS[run_id]={**spec,'id':run_id,'campaign':group,'mode':mode,
'preconditions':['independent final-byte review','exact annotated979dcd95 or pinned pristine8ac86548','exact wlroots7edca9','owned0700 runtime and explicit sockets','unused unique run token','global/local budget','complete client/protocol source provenance'],
'click_computation':'measured solid color interior converted via observed PNG frame/scale/output origin; recompute against latest live configured/applied geometry before each injection; half-open logical bounds; no double scale',
'stop_criteria':['deadline143s','log32MiB','ambiguous owner/root/popup ancestry','output mismatch','missing actual marker frame for scale/rotation','crossing not actually exercised','protocol error','unobservable mandatory predicate','local/global budget exhausted'],
'retention':['all raw stdout/stderr/process and terminal IPC codes','all IPC snapshots/request intervals','client WAYLAND_DEBUG with object/serial/ACK/buttons','renderer and proc maps','original full RGB PNGs','every reservation and observed consumption','independent source windows without atomic generation claim']}

View file

@ -0,0 +1,24 @@
#!/usr/bin/env python3
"""Exactly one reviewed matrix case, with preserved failures and owned cleanup."""
import argparse,json,time
from pathlib import Path
from gate import begin,LAB,verify,read,durable
from protocols import RUNS
from engine import Session
from cases import CAMPAIGN
p=argparse.ArgumentParser();p.add_argument('run_id',choices=RUNS);a=p.parse_args()
auth=begin(a.run_id);s=None;status='INCONCLUSIVE';failure=None
try:
s=Session(RUNS[a.run_id],auth);s.start();status=CAMPAIGN[RUNS[a.run_id]['campaign']](s)
except Exception as e:
failure=dict(type=type(e).__name__,message=str(e));status='INCONCLUSIVE'
finally:
if s is not None:status=s.cleanup(status,failure)
state=read(LAB/'matrix'/a.run_id/'actions.json')
if s is not None:
state['consumed_observed']=dict(capture=sum(e['kind']=='capture' for e in s.events),
click=sum(e['kind']=='injection' and e['action']!='move' for e in s.events),
move=sum(e['kind']=='injection' for e in s.events),grab=sum(e['kind']=='injection' and e.get('grab',False) for e in s.events))
durable(LAB/'matrix'/a.run_id/'actions.json',state);verify()
print(json.dumps(dict(id=a.run_id,status=status,failure=failure)))
raise SystemExit(1 if failure else 0)

View file

@ -0,0 +1,248 @@
"""Executed pure/isolated negative tests, no synthetic independent review."""
import argparse,ast,copy,json,os,sys,tempfile,unittest
from pathlib import Path
from unittest.mock import patch,Mock
from PIL import Image,ImageDraw
import gate
from protocols import RUNS
from geometry import *
from cases import parse_pairs,root_state,marker_host_baseline,associated_root
from transition import observed_epochs
from types import SimpleNamespace
class Tests(unittest.TestCase):
def test_all_source_compile(self):
for p in Path(__file__).parent.glob('*.py'):compile(p.read_text(),str(p),'exec')
def test_all_campaigns_exact_unique(self):
self.assertEqual(len(RUNS),14);self.assertEqual(set(r['campaign'] for r in RUNS.values()),{'D1','D2','D3','D4','E1','E2','E3','F'})
for key,r in RUNS.items():
self.assertEqual(key,r['id']);self.assertTrue(r['steps'] and r['preconditions'] and r['retention'] and r['stop_criteria'])
def test_global_planned_png_fits(self):
total=sum(gate.PLANNED_PNG[r['campaign']] for r in RUNS.values())+37+17
self.assertLessEqual(total,220);self.assertEqual(total,212)
def test_limits(self):
for r in RUNS.values():
self.assertLessEqual(r['limits']['duration_s'],150);self.assertLessEqual(gate.PLANNED_PNG[r['campaign']],12 if r['campaign']=='F' else 20)
def test_exact_binary_review_pending_refusal_no_action(self):
with patch.object(gate,'verify',side_effect=ValueError('missing independent review')),patch.object(gate.ledger,'reserve') as reserve:
for key in RUNS:
with self.assertRaises(ValueError):gate.preflight(key)
reserve.assert_not_called()
def test_half_open_negative_origin(self):
self.assertTrue(contains([-800,0,800,600],[-1,599]));self.assertFalse(contains([-800,0,800,600],[0,599]));self.assertFalse(contains([-800,0,800,600],[-1,600]))
def test_real_crossing_axis(self):
self.assertTrue(crossing([70,585,170,130],1,600));self.assertFalse(crossing([70,600,170,130],1,600));self.assertFalse(crossing([70,470,170,130],1,600))
def test_splith_ipc_decoration_not_double_added(self):
o=dict(rect=dict(x=-400,y=25,width=400,height=575),window_rect=dict(x=2,y=0,width=396,height=573),deco_rect=dict(height=25))
self.assertEqual(assigned_origin(o),[-398,25]);self.assertEqual(exterior_ipc(o),[-400,0,400,600])
def test_applied_origin_source_identity(self):
o=dict(id=42,window_rect=dict(x=2,y=0),deco_rect=dict(height=25))
origin,r,_=actual_origin(o,'PI_OWNER output=HEADLESS-1 ns=1 id=42 scene_valid=1 scene=-400,0 applied_size=400,600')
self.assertEqual(origin,[-398,25])
with self.assertRaises(Ambiguous):actual_origin(o,'PI_OWNER output=HEADLESS-1 ns=1 id=99 scene_valid=1 scene=-400,0 applied_size=400,600')
def test_scale_once_pointer_roundtrip(self):
o=RUNS['D2-mosaic']['outputs'][0]
for p in ([-700,100],[-500,300],[-200,400]):
self.assertEqual(png_global(global_png(p,o,'logical'),o,'logical'),p)
def test_rotated_pointer_roundtrip(self):
o=RUNS['D3-mosaic']['outputs'][0]
for p in ([-700,100],[-500,300],[-200,400]):self.assertEqual(png_global(global_png(p,o,'physical90'),o,'physical90'),p)
def test_unknown_frame_reject(self):
with self.assertRaises(Ambiguous):png_global([3,5],RUNS['D2-mosaic']['outputs'][0],'guessed')
def test_marker_actual_orientation_and_missing_reject(self):
im=Image.new('RGB',(1000,750));d=ImageDraw.Draw(im)
for k,b in [('TL',(10,10,40,40)),('TR',(950,10,980,40)),('BL',(10,700,40,730)),('BR',(950,700,980,730)),('C',(480,350,510,380))]:d.rectangle(b,fill=MARKERS[k])
self.assertEqual(marker_frame(im,1.25,False)['frame'],'logical')
self.assertEqual(marker_frame(im.transpose(Image.Transpose.ROTATE_270),1.25,True)['frame'],'physical90')
im.putpixel((10,10),(0,0,0));blank=Image.new('RGB',im.size)
with self.assertRaises(Ambiguous):marker_frame(blank,1.25,True)
def test_exact_rgb_one_pixel_reject(self):
a=Image.new('RGB',(800,600),(48,48,56));b=a.copy();b.putpixel((0,599),(48,48,55))
with self.assertRaises(Ambiguous):exact(a,b)
def test_output_physical_logical_scale_mismatch_reject(self):
spec=RUNS['D3-mosaic']['outputs'];outputs=[]
for o in spec:outputs.append(dict(active=True,name=o['name'],rect=dict(zip(('x','y','width','height'),o['logical'])),scale=o['scale'],transform=o['transform'],current_mode=dict(zip(('width','height'),o['mode']))))
expected_outputs(outputs,spec)
for key in ('scale','transform'):
bad=copy.deepcopy(outputs);bad[0][key]=1 if key=='scale' else 90
with self.assertRaises(Ambiguous):expected_outputs(bad,spec)
def test_no_synthetic_saved_identity(self):
r=RUNS['E2-mosaic'];self.assertTrue(any('saved-node' in x for x in r['ceilings']))
code=Path(__file__).with_name('cases.py').read_text();self.assertIn("saved_node_identity='NOT OBSERVABLE'",code);self.assertNotIn('release_configure',code)
def test_pairs_same_output_seq_only_and_missing(self):
text='PI_OUTPUT_COMMIT output=H1 commit_seq=7 ns=1\nPI_OUTPUT_PRESENT output=H2 commit_seq=7 ns=2\n'
a=parse_pairs(text);self.assertEqual(a['analyzed'][0]['status'],'MISSING_PRESENT')
a=parse_pairs(text+'PI_OUTPUT_PRESENT output=H1 commit_seq=7 ns=3\n');self.assertEqual(a['analyzed'][0]['status'],'PAIRED')
def test_duplicate_present_rejected(self):
line='PI_OUTPUT_PRESENT output=H1 commit_seq=7 ns=2\n'
with self.assertRaises(Ambiguous):parse_pairs(line+line)
def test_pair_limit12(self):
text=''.join(f'PI_OUTPUT_COMMIT output=H1 commit_seq={i} ns={i+1}\n' for i in range(30));r=parse_pairs(text)
self.assertEqual(len(r['analyzed']),12);self.assertEqual(r['total_commits'],30)
def test_owned_cleanup_and_explicit_IPC_source(self):
tree=ast.parse(Path(__file__).with_name('engine.py').read_text());code=ast.unparse(tree)
self.assertIn("['swaymsg', '-s', str(self.sock)",code);self.assertIn('start_new_session=True',code);self.assertIn('0o700',Path(__file__).with_name('engine.py').read_text())
self.assertNotIn('shell=True',code);self.assertNotIn('os.killpg(0',code)
def test_every_campaign_callable(self):
from cases import CAMPAIGN
self.assertEqual(set(CAMPAIGN),set(r['campaign'] for r in RUNS.values()))
def test_cleanup_available_before_start(self):
from engine import Session
self.assertTrue(hasattr(Session,'start') and hasattr(Session,'cleanup'))
def test_local_exhaustion_before_global(self):
with tempfile.TemporaryDirectory() as d:
lab=Path(d);w=lab/'matrix/D1-mosaic';w.mkdir(parents=True)
gate.exclusive(w/'actions.json',dict(planned={'capture':1},reserved={'capture':1},events=[]))
with patch.object(gate,'LAB',lab),patch.object(gate.ledger,'reserve') as reserve:
with self.assertRaises(ValueError):gate.action_debit('D1-mosaic','capture')
reserve.assert_not_called()
def test_corrupt_local_ledger_rejected(self):
limits=gate.action_limits('D1-mosaic');state=dict(planned=limits,reserved=dict.fromkeys(limits,0),events=[])
gate.local_valid('D1-mosaic',state)
for kind in ('typed','reconciliation','limits'):
bad=copy.deepcopy(state)
if kind=='typed':bad['reserved']['capture']=True
elif kind=='reconciliation':bad['reserved']['capture']=1
else:bad['planned']['capture']+=1
with self.assertRaises(ValueError):gate.local_valid('D1-mosaic',bad)
def test_unknown_action_before_global(self):
with tempfile.TemporaryDirectory() as d:
lab=Path(d);w=lab/'matrix/D1-mosaic';w.mkdir(parents=True)
gate.exclusive(w/'actions.json',dict(planned={'capture':1},reserved={'capture':0},events=[]))
with patch.object(gate,'LAB',lab):
with self.assertRaises(ValueError):gate.action_debit('D1-mosaic','force_timer')
class RepairTests(unittest.TestCase):
def fixture(self):
run=gate.LAB/'horizontal/C2/run'
trace=(run/'compositor.log').read_text();client=(run/'E2.log').read_text().split('POPUP_B DESTROY')[0]
epoch=next(e for e in observed_epochs(trace,'HEADLESS-1',0,999999999999999999)
if e['scene'] and e['scene'].get('layout')=='5' and len(e['roots'])==1)
o=next(o for o in epoch['owners'] if o['id']=='9');scene=list(map(int,o['scene'].split(',')))+list(map(int,o['applied_size'].split(',')))
x,y,w,h=map(int,o['current'].split(','));owner=dict(id=9,type='con',app_id='org.mosaic.NestedGrabProbe',visible=True,rect=dict(x=x,y=y+25,width=w,height=h-25),window_rect=dict(x=2,y=0,width=w-4,height=h-27),deco_rect=dict(height=25))
ws=dict(id=6,type='workspace',layout='mosaic',output='HEADLESS-1',nodes=[owner],floating_nodes=[])
tree=dict(id=1,type='root',nodes=[dict(id=2,type='output',name='HEADLESS-1',nodes=[ws],floating_nodes=[])],floating_nodes=[])
state=dict(owner=owner,workspace=ws,scene=scene,origin=[scene[0]+2,scene[1]+25],snapshot=dict(tree=tree))
raw='\n'.join(epoch['raw']);session=SimpleNamespace(outputs=[dict(name='HEADLESS-1',logical=[-800,0,800,600])],log=lambda n:raw if n=='compositor' else client)
return session,state,raw
def test_real_C_callback_positive(self):
s,state,raw=self.fixture();self.assertEqual(root_state(s,state,state['origin'],0)['states'][0]['state'],'applied-target')
def test_malformed_callbacks_rejected(self):
s,state,raw=self.fixture()
mutations=[raw.replace(' id=9 ',' id=99 '), '\n'.join(l for l in raw.splitlines() if 'PI_OWNER ' not in l),raw.replace('local_index=1','local_index=2'),raw.replace('workspace=6','workspace=99'),raw.replace('output=HEADLESS-1','output=HEADLESS-2'),raw+'\n'+next(l for l in raw.splitlines() if 'PI_POPUP ' in l),raw.replace('clip=-482,-25,800,600','clip=-481,-25,800,600')]
# Mutate the observed root x regardless of this retained callback's origin.
import re
mutations.append(re.sub(r'(PI_POPUP .*clip=)(-?\d+)',lambda m:m[1]+str(int(m[2])+1),raw))
for bad in mutations:
with self.subTest(bad=bad[-120:]),self.assertRaises((ValueError,AssertionError)):
s.log=lambda n:bad if n=='compositor' else (gate.LAB/'horizontal/C2/run/E2.log').read_text().split('POPUP_B DESTROY')[0];root_state(s,state,state['origin'],0)
def test_missing_ancestry_and_origin_rejected(self):
s,state,raw=self.fixture();bad=copy.deepcopy(state);bad['origin'][0]+=1
with self.assertRaises(ValueError):root_state(s,bad,bad['origin'],0)
bad=copy.deepcopy(state);bad['workspace']['id']=99
with self.assertRaises(ValueError):root_state(s,bad,bad['origin'],0)
def test_real_hierarchy_parent_and_missing_grab_rejected(self):
s,state,raw=self.fixture();client=(gate.LAB/'horizontal/C2/run/E2.log').read_text().split('POPUP_B DESTROY')[0]
for bad in (client.replace('parent_xdg=12','parent_xdg=999'),client.replace('POPUP_B GRAB_REQUEST','POPUP_B OMITTED_GRAB_REQUEST')):
s.log=lambda n:raw if n=='compositor' else bad
with self.assertRaises((ValueError,AssertionError)):root_state(s,state,state['origin'],0)
def test_complete_nonbuffer_callback_preserved(self):
s,state,raw=self.fixture();client=(gate.LAB/'horizontal/C2/run/E2.log').read_text().split('POPUP_B DESTROY')[0]
s.log=lambda n:raw.replace('has_buffer=1','has_buffer=0') if n=='compositor' else client
self.assertEqual(len(root_state(s,state,state['origin'],0)['states']),1)
def test_old_applied_hidden_return_and_stale_rejection(self):
import re
s,state,raw=self.fixture();hidden=copy.deepcopy(state)
hidden['scene'][0]-=1600;hidden['origin'][0]-=1600;hidden['owner']['rect']['x']-=1600
def shifted(text):
text=re.sub(r'ns=(\d+)',lambda m:'ns='+str(int(m[1])+100000),text)
text=re.sub(r'(PI_OWNER .*id=9 current=)(-?\d+)',lambda m:m[1]+str(int(m[2])-1600),text)
text=re.sub(r'(PI_OWNER .*id=9 .*scene=)(-?\d+)',lambda m:m[1]+str(int(m[2])-1600),text)
return re.sub(r'(PI_POPUP .*clip=)[^ ]+',r'\g<1>0,0,0,0',text)
hidden_raw=shifted(raw);client=(gate.LAB/'horizontal/C2/run/E2.log').read_text().split('POPUP_B DESTROY')[0]
s.log=lambda n:raw+'\n'+hidden_raw if n=='compositor' else client
result=root_state(s,hidden,hidden['origin'],0,True,previous=state)
self.assertEqual([r['state'] for r in result['states']],['old-applied','applied-target'])
returned=re.sub(r'ns=(\d+)',lambda m:'ns='+str(int(m[1])+200000),raw)
s.log=lambda n:hidden_raw+'\n'+returned if n=='compositor' else client
self.assertEqual(len(root_state(s,state,state['origin'],0,previous=hidden)['states']),2)
stale=re.sub(r'ns=(\d+)',lambda m:'ns='+str(int(m[1])+300000),hidden_raw)
s.log=lambda n:hidden_raw+'\n'+returned+'\n'+stale if n=='compositor' else client
with self.assertRaises(ValueError):root_state(s,state,state['origin'],0,previous=hidden)
def test_earlier_bad_clip_then_good_rejected(self):
import re
s,state,raw=self.fixture();client=(gate.LAB/'horizontal/C2/run/E2.log').read_text().split('POPUP_B DESTROY')[0]
bad=re.sub(r'(PI_POPUP .*clip=)(-?\d+)',lambda m:m[1]+str(int(m[2])+1),raw)
good=re.sub(r'ns=(\d+)',lambda m:'ns='+str(int(m[1])+100000),raw)
s.log=lambda n:bad+'\n'+good if n=='compositor' else client
with self.assertRaises(ValueError):root_state(s,state,state['origin'],0)
def test_cleanup_preserves_actual_ipc_on_wait_timeout(self):
from engine import Session
import subprocess,time,threading
with tempfile.TemporaryDirectory() as d:
s=Session.__new__(Session);s.run=Path(d);s.runtime=s.run/'runtime';s.runtime.mkdir();s.sock=Mock();s.sock.is_socket.return_value=True;s.env={};s.pointer=None;s.handles=[];s.started=time.monotonic();s.expired=False;s.finished=threading.Event();s.watch=Mock()
comp=Mock(pid=2147483647,returncode=0,stdin=None);comp.poll.side_effect=[None,0,0];comp.wait.side_effect=subprocess.TimeoutExpired('compositor',1);s.comp=comp;s.proc={'compositor':comp}
with patch('engine.subprocess.run',return_value=SimpleNamespace(returncode=1,stdout='actual IPC stdout',stderr='actual IPC stderr')),patch('engine.os.killpg',side_effect=ProcessLookupError):
self.assertEqual(s.cleanup('PASS'),'INCONCLUSIVE')
terminal=json.loads((s.run/'terminal-ipc.json').read_text());out=json.loads((s.run/'outcome.json').read_text())
self.assertEqual(terminal['ipc_exit'],1);self.assertEqual(terminal['stdout'],'actual IPC stdout');self.assertEqual(terminal['stderr'],'actual IPC stderr');self.assertIn('wait_error',terminal);self.assertEqual(out['measurements_status'],'PASS');self.assertEqual(out['processes'][0]['exit'],0)
class RotationTests(unittest.TestCase):
def fixture(self):
run=gate.LAB/'matrix/D2-mosaic/run';events=[json.loads(l) for l in (run/'events.jsonl').read_text().splitlines()]
owner_start=next(e['ns'] for e in events if e['kind']=='spawn' and e['name']=='owner')
trace=(run/'compositor.log').read_text()
# Retain only the complete pre-owner native prefix for baseline. The
# selected mapped epoch is from actual D2; it does not reclassify D2.
# Later textual lines without ns could include cleanup: preserve the
# prefix only, as live observation would do before owner creation.
first_owner_line=next(i for i,l in enumerate(trace.splitlines()) if 'PI_OWNER ' in l and ' id=9 ' in l)
before='\n'.join(trace.splitlines()[:first_owner_line])
first_after=next(i for i,l in enumerate(before.splitlines()) if 'ns=' in l and int(__import__('re').search(r'ns=(\d+)',l)[1])>=owner_start)
before='\n'.join(before.splitlines()[:first_after])
session=SimpleNamespace(spec={'id':'D3-mosaic'},proc={'marker-background':Mock(pid=77)},events=[e for e in events if e['ns']<owner_start],log=lambda n:before,
snapshot=lambda tag:json.loads((run/'startup-snapshot.json').read_text()),save=Mock())
session.proc['marker-background'].poll.return_value=None
with patch('cases.time.monotonic_ns',return_value=owner_start-1):baseline=marker_host_baseline(session)
state=json.loads((run/'owner-geometry.json').read_text())
epoch=next(e for e in observed_epochs(trace,'HEADLESS-1',0,999999999999999999) if e['scene'] and e['scene'].get('layout')=='5' and len(e['roots'])==2 and any(o.get('id')=='9' and list(map(int,o['scene'].split(',')))==state['scene'][:2] for o in e['owners']))
raw='\n'.join(epoch['raw']);client=(run/'owner.log').read_text().split('POPUP_B DESTROY')[0].split('POPUP_B POPUP_DONE')[0]
session.outputs=[dict(name='HEADLESS-1',logical=[-800,0,800,600])];session.log=lambda n:raw if n=='compositor' else client
return session,state,raw,client,baseline
def test_actual_D2_known_preowner_host_and_new_root(self):
s,state,raw,client,baseline=self.fixture();result=root_state(s,state,state['origin'],baseline['window'][1])
self.assertNotEqual(result['tree'],baseline['tree']);self.assertEqual(len(result['states'][0]['all_roots']),2)
self.assertEqual(result['states'][0]['root']['local_index'],'2')
def test_unknown_host_multiple_new_and_missing_known_rejected(self):
s,state,raw,client,baseline=self.fixture();roots=observed_epochs(raw,'HEADLESS-1',0,999999999999999999)[0]['roots']
for bad in ([roots[1]],roots+[dict(roots[1],tree='0xunknown',local_index='3')], [dict(roots[0],tree='0xunknown'),roots[1]], [dict(roots[0],clip_valid='1'),roots[1]], [roots[0],dict(roots[1],tree=roots[0]['tree'])]):
with self.assertRaises(ValueError):associated_root(bad,baseline,baseline['window'][1]+1)
with self.assertRaises(ValueError):associated_root(roots,baseline,baseline['window'][1])
def test_wrong_owner_workspace_and_earlier_clip_rejected(self):
import re
s,state,raw,client,baseline=self.fixture()
root=observed_epochs(raw,'HEADLESS-1',0,999999999999999999)[0]['roots'][1]
badclip=raw.replace('clip='+root['clip'],'clip='+str(int(root['clip'].split(',')[0])+1)+','+','.join(root['clip'].split(',')[1:]))
later=re.sub(r'ns=(\d+)',lambda m:'ns='+str(int(m[1])+100000),raw)
for bad in (raw.replace(' id=9 ',' id=99 '),raw.replace('workspace=6','workspace=99'),badclip+'\n'+later):
s.log=lambda n:bad if n=='compositor' else client
with self.assertRaises(ValueError):root_state(s,state,state['origin'],baseline['window'][1])
def test_v3_gate_disallows_every_old_case_before_debit(self):
with patch.object(gate,'verify',return_value={}),patch.object(gate.ledger,'reserve') as reserve:
for key in gate.RUNS:
if key!='D3-mosaic':
with self.assertRaisesRegex(ValueError,'only unused D3'):gate.preflight(key)
reserve.assert_not_called()
def test_executed_v2_archive_exact(self):
archive=gate.LAB/'matrix/executed-v2-archive';f=gate.read(archive/'freeze.json');mapping=gate.read(archive/'archive-map.json')
self.assertEqual(gate.sha(archive/'freeze.json'),'a20ccb39095ca35363119b5cfe4b5c839c1a75863bd72a097686aeac76dd7d7f')
self.assertEqual(set(mapping),set(f['files']))
for original,digest in f['files'].items():self.assertEqual(gate.sha(mapping[original]),digest,original)
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True);a=p.parse_args()
suite=unittest.TestSuite([unittest.defaultTestLoader.loadTestsFromTestCase(c) for c in (Tests,RepairTests,RotationTests)]);names=[t.id() for group in suite for t in group];r=unittest.TextTestRunner(verbosity=2).run(suite)
gate.exclusive(a.output,dict(status='PASS' if r.wasSuccessful() else 'FAIL',executed=r.testsRun,tests=names,failures=[(str(t),e) for t,e in r.failures],errors=[(str(t),e) for t,e in r.errors],runtime='NOT RUN',acceptance_created=False))
raise SystemExit(not r.wasSuccessful())

View file

@ -0,0 +1,18 @@
# STEP20C overnight — qualification documentaire
EXPERIMENTAL. Ce sous-répertoire contient seulement un index et des métadonnées compactes ; les modules et fixtures gelés du répertoire parent ne sont pas réécrits.
- [Provenance exacte](provenance.json) : pins, binaires, builds/stages, runs, budgets, freezes et références privées.
- [Bilan et gate](../../../../docs/design/STEP20C_OVERNIGHT_QUALIFICATION_AND_INTEGRATION_GATE_V1.md).
- [Reconstruction et stage](../../../../docs/design/STEP20C_ANNOTATED_REBUILD_AND_PRIVATE_STAGE.md).
- [Matrice graphique](../../../../docs/design/STEP20C_ANNOTATED_GRAPHICS_MATRIX.md).
- [Lifecycle, effets et ordre natif](../../../../docs/design/STEP20C_LIFECYCLE_EFFECTS_AND_NATIVE_ORDERING.md).
- [Décision d’intégration draft](../../../../docs/design/STEP20C_PRODUCTION_INTEGRATION_DECISION_DRAFT.md).
Les sources de fixture restent dans le répertoire parent ; leur lecture et les manifestes gelés sont requis avant une reproduction en LAB neuf. Les références locales de provenance ne fournissent pas les archives, bibliothèques ou patches privés. Reproduction source contrôlée possible avec ces inputs retenus, archive autonome publique et reproductibilité binaire NOT QUALIFIED. Aucun raw log, PNG, binaire, archive, recette future ou fork complet n’est publié ici.
STEP20C NEXT ; production multi-output NOT YET SUPPORTED. Résultat proposé PRIVATE_CANDIDATE_NEEDS_TARGETED_EVIDENCE ; aucune décision appliquée. Revue finale ACCEPTED / audit PASS pour la fidélité du dossier expérimental, sans adoption production. Publication préparée, non exécutée à cette édition ; le reçu privé et le handoff final porteront les résultats/SHAs réels, sans futur hash inventé.
Les statuts NOT RUN/PENDING des documents de fixture gelés décrivent leur préparation avant lancement. Ils sont conservés sans réécriture ; les rapports de qualification ci-dessus et leurs reçus indépendants décrivent le résultat exécuté ultérieur. Les gates sont liés au LAB et aux bytes exacts de cette campagne : une nouvelle campagne exige préparation/identités/budget et revue distincts, sans copier une acceptation ni réutiliser les tokens consommés.
La revue finale compte 0 BLOCKING, 4 NON_BLOCKING et 3 FUTURE_SCOPE, avec 180 checks hors ligne et 15 négatifs sur preuves réelles. Ses hashes et le contexte de l’index scientifique post-exécution sont dans la provenance compacte. NB-01 complète huit listes de serials par lecture de leurs propres logs wire ; les lignes/hashes sources restent dans le reçu privé référencé. Les anciens manifestes/scellés sont préservés ; le delta documentaire après revue est enregistré séparément dans `parent/docs-post-final-delta.json`. Publication et gates production restent distincts.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,11 @@
# Fixture G privé — release-shape
Statut : **EXPERIMENTAL, prêt pour revue indépendante des octets gelés**. Le fixture reprend exactement le scénario C2 horizontal déjà préparé et remplace uniquement l’import du gate. Il cible le binaire G en stage statique, lié à son SHA256 `8063478b2d5948da3eb8b1a329978882dc1d045cf36932cc2145a78af20ba76a`, Build ID `83896fdb36f3fd21522becec0e8c5219e15f4b8e`, aux trois sources Mosaic de traces et à wlroots 0.20.2 SHA256 `7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837`.
Le plan d’action reste celui de C2 : 2 chaînes, 4 grabs, 8 clics, 17 captures 800×600, 2 mouvements de caméra, une transition vers `splith`, et 10 mouvements du pointeur. Les prédicats C2 réutilisés vérifient le propriétaire et les popups root/enfant dans H2, la limitation visuelle et d’entrée, la fermeture/dismissal, le retour à la disposition standard, puis la restauration. Le fixture ne revendique aucun résultat graphique; `generic_author_generator_negatives` reste **NOT_PROVEN** puisque les preuves de ces négatifs n’ont pas été conservées.
Préparation hors ligne : depuis le dépôt, exécuter `python tooling/probes/step20c-overnight/release_shape/dispatch.py prepare`, puis `.../dispatch.py tests`, puis `.../dispatch.py freeze`. Le freeze couvre les entrées C2 réutilisées, le code et les imports G, les deux arbres source G, le patch et sa revue ACCEPTED, les reçus de build/stage/ELF, les clients/protocoles et les dépendances de référence.
Le préflight refuse de lancer tant que `release-shape/reviews/release-smoke-review.json` n’existe pas et ne lie pas exactement `smoke/freeze.json`, tous ses fichiers, le rapport de revue et le scope `OVERNIGHT_G_SMOKE_FINAL_BYTES`. Un refus préflight ne crée ni jeton one-shot, ni runtime, ni réservation. Après revue acceptée, la commande sera `python tooling/probes/step20c-overnight/release_shape/dispatch.py preflight`, puis `python tooling/probes/step20c-overnight/release_shape/dispatch.py run` depuis le répertoire du fixture. Le runner dure au plus 143 s; le reçu global réserve au plus 150 s. Chaque action est durable avant mutation et chaque capture réserve d’abord le compteur global.
Aucun build, stage ou smoke n’a été exécuté pour préparer ce fixture. Neutralité **NOT PROVEN**; le résultat ne transfère aucun résultat graphique de A, C, D ni de la matrice séparée.

View file

@ -0,0 +1 @@
"""Private experimental fixture for the reviewed G headless smoke."""

View file

@ -0,0 +1,24 @@
"""Bounded G smoke derivation from the frozen accepted C2 fixture."""
from pathlib import Path
import ast
import hashlib
import sys
REPO = Path(__file__).resolve().parents[4]
OVERNIGHT = REPO / 'tooling/probes/step20c-overnight'
BASE = REPO / 'tooling/probes/step20c-static-e2-completion'
sys.path.insert(0, str(OVERNIGHT))
from horizontal_adapter import generate, replace_one
IMPORT = 'from horizontal_gate import begin_run, debit, binding_sources, finish_run'
G_IMPORT = 'from gate import begin_run, debit, binding_sources, finish_run'
def generate_g():
text = generate('C2')
text = replace_one(text, IMPORT, G_IMPORT)
ast.parse(text, filename='G-smoke/runner.py')
return text
if __name__ == '__main__':
runner = generate_g()
print('G_RUNNER_SHA256', hashlib.sha256(runner.encode()).hexdigest())

View file

@ -0,0 +1,91 @@
"""Prepare, freeze, check, or explicitly preflight G; RUN needs review acceptance."""
import argparse
import json
import os
from pathlib import Path
import sys
HERE=Path(__file__).resolve().parent
OVERNIGHT=HERE.parent
sys.path[:0]=[str(HERE),str(OVERNIGHT)]
from adapter import generate_g
import gate
def prepare():
w=gate.WORK
os.chmod(w,0o700);os.chmod(w/'results',0o700)
if (w/'run').exists() or (w/'consumed.json').exists() or (w/'freeze.json').exists():
raise RuntimeError('G workspace already consumed/frozen; no replacement or retry')
for name in ('nested-client','pointer-daemon','static-reference'):
p=w/name
if not p.is_symlink() or p.resolve()!=(gate.LAB/'horizontal/clients'/name).resolve():
raise RuntimeError('C2 client symlink must reference exact compiled fixture: '+name)
# Bind complete reused C2 runner source and its accepted input before writing the generated G copy.
c2=gate.LAB/'horizontal/C2/runner.py'
from horizontal_adapter import generate
if c2.read_text()!=generate('C2'):
raise RuntimeError('frozen C2 fixture source mismatch')
runner=generate_g()
fd=os.open(w/'runner.py',os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
with os.fdopen(fd,'w') as stream:
stream.write(runner);stream.flush();os.fsync(stream.fileno())
dfd=os.open(w,os.O_RDONLY|os.O_DIRECTORY)
try:os.fsync(dfd)
finally:os.close(dfd)
print('G_SMOKE_RUNNER_PREPARED runtime NOT RUN')
def freeze():
w=gate.WORK
if (w/'freeze.json').exists(): raise RuntimeError('freeze already exists; no replacement')
tests=json.loads((w/'results/prepare-tests.json').read_text())
if tests.get('status')!='PASS': raise RuntimeError('offline fixture tests not PASS')
files=gate.expected_files()
# Verify all entries still exist and bind the current persistent budget without making it a post-run immutable input.
budget_sha=gate.sha(gate.LAB/'budget.json')
data={'status':'READY_FOR_REVIEW','scope':'OVERNIGHT_G_SMOKE_FINAL_BYTES',
'files':files,'budget_before_run_sha256':budget_sha,
'binary':{'path':str(gate.BINARY),'sha256':gate.BINARY_SHA,'build_id':gate.BUILD_ID},
'source_bindings':gate.TRACE_SOURCES,
'plan':gate.PLANS,'graphics':'NOT TESTED','neutrality':'NOT PROVEN',
'generic_author_generator_negatives':'NOT_PROVEN: mechanism and execution receipt not retained',
'runtime':'NOT RUN'}
gate.exclusive(w/'freeze.json',data)
# The persisted freeze is read back and checked before report.
gate._frozen_files()
print('G_SMOKE_FREEZE_READY_FOR_INDEPENDENT_REVIEW')
def missing_review_preflight():
before=(gate.LAB/'budget.json').read_bytes()
token=gate.WORK/'consumed.json';run=gate.WORK/'run'
try:gate.preflight(require_review=True)
except Exception as exc:
refusal={'status':'REFUSED','reason':str(exc),'review_missing':not (gate.GROOT/'reviews/release-smoke-review.json').exists(),
'budget_unchanged':(gate.LAB/'budget.json').read_bytes()==before,
'consumed_token_created':token.exists(),'runtime_created':run.exists()}
if not refusal['review_missing'] or not refusal['budget_unchanged'] or refusal['consumed_token_created'] or refusal['runtime_created']:
raise RuntimeError('missing-review refusal side-effect invariant failed') from exc
out=gate.WORK/'results/missing-review-preflight.json'
fd=os.open(out,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
with os.fdopen(fd,'w') as stream:json.dump(refusal,stream,indent=2);stream.write('\n');stream.flush();os.fsync(stream.fileno())
print(json.dumps(refusal,indent=2));return 2
print(json.dumps({'status':'PASS','scope':'reviewed G bytes only','binary':str(gate.BINARY),
'binary_sha256':gate.BINARY_SHA,'build_id':gate.BUILD_ID,
'runtime':'NOT RUN','token_created':False},indent=2))
return 0
def main():
p=argparse.ArgumentParser();p.add_argument('action',choices=('prepare','tests','freeze','preflight','run'));a=p.parse_args()
if a.action=='prepare':prepare()
elif a.action=='tests':
import subprocess
return subprocess.run([sys.executable,str(HERE/'tests.py'),'--output',str(gate.WORK/'results/prepare-tests.json')],check=False).returncode
elif a.action=='freeze':freeze()
elif a.action=='preflight':return missing_review_preflight()
else:
gate.preflight(require_review=True)
runner=gate.WORK/'runner.py'
if runner.read_text()!=generate_g():raise RuntimeError('G runner differs from freeze generator')
sys.path[:0]=[str(HERE),str(OVERNIGHT),str(gate.BASE)]
exec(compile(runner.read_text(),str(runner),'exec'),{'__name__':'__main__','__file__':str(runner)})
return 0
if __name__=='__main__':raise SystemExit(main())

View file

@ -0,0 +1,333 @@
"""Fail-closed, one-shot gate for the private G release-shape smoke."""
import fcntl
from collections import Counter
import hashlib
import json
import os
from pathlib import Path
import stat
import subprocess
import sys
import time
HERE = Path(__file__).resolve().parent
REPO = HERE.parents[3]
OVERNIGHT = REPO / 'tooling/probes/step20c-overnight'
BASE = REPO / 'tooling/probes/step20c-static-e2-completion'
LAB = Path('/home/fy59/swayfx-mosaic-overnight-mcolx0gq')
GROOT = LAB / 'release-shape'
WORK = GROOT / 'smoke'
CASE = 'G-smoke'
BINARY = GROOT / 'stage-static/usr/bin/swayfx-mosaic'
BINARY_SHA = '8063478b2d5948da3eb8b1a329978882dc1d045cf36932cc2145a78af20ba76a'
BUILD_ID = '83896fdb36f3fd21522becec0e8c5219e15f4b8e'
WLROOTS = Path('/usr/lib/libwlroots-0.20.so').resolve()
WLROOTS_SHA = '7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
PATCH_SHA = '6e8a5b50149732ac70ecd79ab8db9258956f59036aba1bf17a477866bc44a5c1'
REVIEW = LAB / 'reviews/release-patch-review.json'
REVIEW_SHA = '29e7ea8b9124fae59a231ff6abcab3981e1ac9c697da51c05c334e888970897a'
BUILD_JSON = GROOT / 'results/phase-g-build.json'
PLANS = dict(chain=2, grab=4, click=8, capture=17, camera=2, transition=1, move=10)
# Contract: the move budget counts pointer relocations, including the eight
# click injections; the event trace therefore has only two standalone `move`
# actions plus eight `left`/`right` click actions.
# The three Mosaic source files whose G runtime traces bind the smoke.
TRACE_SOURCES = {
str(GROOT / 'mosaic-source/sway/desktop/output.c'): 'f703fab0828ee886ce690acfa727a1f70d0dad5f9ec1f5c153db646c73d10485',
str(GROOT / 'mosaic-source/sway/desktop/transaction.c'): '1bb30b3f171f680f8b2e6ed28aa7aad0a6854d8d77360ac8b8e314918c0982f2',
str(GROOT / 'mosaic-source/sway/desktop/xdg_shell.c'): '123313e80fbf5d55a8e9bf2edca5a0f15a7601643a5af2904da6ba9b3914d29f',
}
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def read(path):
return json.loads(Path(path).read_text())
def require(ok, message):
if not ok:
raise ValueError(message)
def validate_binary_identity(path, digest):
require(Path(path).resolve() == BINARY.resolve() and digest == BINARY_SHA and sha(path) == BINARY_SHA, 'only exact G ELF accepted')
def validate_budget_room(state):
sys.path.insert(0, str(OVERNIGHT))
import ledger
ledger._validate(state)
require(CASE not in state['runs'] and state['used']['run'] < ledger.LIMITS['run'], 'run quota/unique id')
require(state['campaign_used']['G'] < ledger.CAMPAIGNS['G'], 'G campaign quota')
require(state['used']['capture'] + PLANS['capture'] <= ledger.LIMITS['capture'], 'global PNG quota')
require(all(state['used'][k] == ledger.LIMITS[k] for k in ('scene_build','mosaic_build','stage')), 'G smoke must not extend exhausted build/stage budget')
def validate_wlroots(path, digest):
require(Path(path).resolve()==WLROOTS and digest==WLROOTS_SHA and sha(path)==WLROOTS_SHA, 'wlroots exact bytes')
def validate_file_map(files):
require(isinstance(files,dict) and bool(files), 'frozen file map missing')
for path,digest in files.items():
require(Path(path).is_file() and sha(path)==digest, 'frozen input changed: '+path)
def validate_one_shot(work):
work=Path(work)
require(not (work/'run').exists() and not (work/'consumed.json').exists(), 'G smoke is one-shot; no retry')
def validate_capture_batch(work, captures):
from PIL import Image
require(len(captures)==17 and len({row.get('file') for row in captures})==17, 'incomplete/duplicate G capture batch')
expected={'H2-reference-1.png','H2-reference-2.png','E1-top.png','E1-A.png','E1-mosaic-H1.png','E1-mosaic-H2.png','E1-dismissed-H2.png','E2-top.png','E2-A.png','E2-mosaic-H1.png','E2-mosaic-H2.png','standard-H1.png','standard-H2.png','standard-A-H1.png','standard-A-H2.png','final-H2.png','final-H1.png'}
require({row.get('file') for row in captures}==expected, 'G capture frame set incomplete')
for row in captures:
path=Path(work)/'run'/row['file']
require(path.is_file() and sha(path)==row['sha256'] and row.get('exit')==0, 'G capture bytes changed')
with Image.open(path) as image: require(image.size==(800,600), 'G capture frame dimensions')
def validate_window_results(work):
names=('grab-serials.json','xdg-protocol-order.json','popup-root-geometry.json','popup-nested-geometry.json','applied-clips.json','mosaic-pixels.json','standard-return.json','standard-popup-input.json','final-restoration.json','cleanup.json','terminal-exit.json')
missing=[name for name in names if not (Path(work)/'results'/name).is_file()]
require(not missing, 'G window/lifecycle evidence incomplete: '+','.join(missing))
def durable(path, data):
path = Path(path)
tmp = path.with_name('.' + path.name + '.tmp-' + str(os.getpid()))
fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
try:
with os.fdopen(fd, 'w') as stream:
json.dump(data, stream, indent=2); stream.write('\n'); stream.flush(); os.fsync(stream.fileno())
os.replace(tmp, path)
dfd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try: os.fsync(dfd)
finally: os.close(dfd)
finally:
if tmp.exists(): tmp.unlink()
def exclusive(path, data):
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
with os.fdopen(fd, 'w') as stream:
json.dump(data, stream, indent=2); stream.write('\n'); stream.flush(); os.fsync(stream.fileno())
dfd = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
try: os.fsync(dfd)
finally: os.close(dfd)
def source_bindings(auth):
rows = auth['trace_sources']
require(rows == TRACE_SOURCES, 'G source binding set changed')
require(all(sha(path) == digest for path, digest in rows.items()), 'G source hash changed')
return {Path(path).name: digest for path, digest in rows.items()}
def _check_build_identity():
build = read(BUILD_JSON)
require(build['status'] == 'BUILDABLE', 'G build receipt status')
require(build['source']['accepted_review'] == 'ACCEPTED', 'G accepted patch review')
require(build['source']['patch_sha256'] == PATCH_SHA, 'G patch identity')
require(build['source']['review_json_sha256'] == REVIEW_SHA, 'G review receipt binding')
require(build['source']['release_manifest_sha256'] == sha(GROOT/'release-manifest.json'), 'G release manifest receipt binding')
manifest=read(GROOT/'release-manifest.json')
require(manifest['patch_sha256']==PATCH_SHA and manifest['result_source_hashes'], 'G release manifest identity')
for rel,digest in manifest['result_source_hashes'].items(): require(sha(GROOT/rel)==digest, 'G release source changed: '+rel)
require(build['elf']['sha256'] == BINARY_SHA and build['elf']['build_id'] == BUILD_ID, 'G ELF receipt identity')
require(Path(build['elf']['path']).resolve() == BINARY.resolve(), 'G stage path identity')
require(build['headless_runtime'] == 'NOT RUN' and build['graphics'] == 'NOT TESTED', 'no inherited G runtime result')
validate_binary_identity(BINARY, BINARY_SHA)
readelf = subprocess.run(['readelf', '-n', str(BINARY)], capture_output=True, text=True, timeout=5)
require(readelf.returncode == 0 and BUILD_ID in readelf.stdout, 'G ELF build ID bytes')
validate_wlroots(WLROOTS, WLROOTS_SHA)
review = read(REVIEW)
require(sha(REVIEW) == REVIEW_SHA and review['status'] == 'ACCEPTED' and review['independent'] is True, 'accepted exact G patch review')
require(review['patch_sha256'] == PATCH_SHA, 'reviewed G patch digest')
require(sha(review['report_file']) == review['report_sha256'] == '52e0db6963c44d6e17eab92404c9a882ca03a0bbae9d59ff15c43bdfb0aab0b8', 'accepted patch review report bytes')
plan=read(LAB/'reviews/release-plan-review.json')
require(plan.get('status')=='ACCEPTED_PLAN_ONLY' and plan.get('plan_sha256')=='b64201aa3f6a51c385ca8f6934ad20c07ea3853b5a08766e2ae174f5da229547', 'accepted plan binding')
return build
def _frozen_files():
freeze = read(WORK / 'freeze.json')
require(freeze.get('status') == 'READY_FOR_REVIEW' and isinstance(freeze.get('files'), dict), 'G smoke freeze missing')
files = freeze['files']
require(files == expected_files(), 'G freeze file set changed')
validate_file_map(files)
return freeze
def expected_files():
"""Complete inputs: C2 closure plus G sources/build/patch and fixture code."""
c2 = read(LAB / 'horizontal/freeze.json')
rows = dict(c2['files'])
# Reused runtime modules and their sealed generator/input closure.
for path in [HERE/'adapter.py', HERE/'gate.py', HERE/'dispatch.py', HERE/'tests.py',
HERE/'README.md', HERE/'__init__.py',
OVERNIGHT/'horizontal_adapter.py', OVERNIGHT/'horizontal_gate.py',
OVERNIGHT/'ledger.py', BASE/'run.py', BASE/'client-contract.json',
*(BASE/n for n in ('model.py','observations.py','ipc_predicates.py','pixels.py','predicates.py','lifecycle.py','transition.py','support.py')),
WORK/'runner.py', WORK/'results/prepare-tests.json']:
rows[str(path)] = sha(path)
# Bind every source input of the G SceneFX and Mosaic trees.
for root in (GROOT/'scene-source', GROOT/'mosaic-source'):
for path in root.rglob('*'):
if path.is_file() and '.git' not in path.parts:
rows[str(path)] = sha(path)
fixed = [
REVIEW, LAB/'reviews/release-patch-review.md',
GROOT/'release-removal.patch', GROOT/'release-manifest.json',
LAB/'parent/release-plan.json', LAB/'reviews/release-plan-review.json',
LAB/'reviews/release-plan-review.md', BUILD_JSON, GROOT/'REPORT-BUILD.md',
GROOT/'results/static-stage-manifest.json', GROOT/'results/binary-elf.json',
GROOT/'raw/phase-g-commands.json',
GROOT/'scene-build/meson-info/intro-buildoptions.json',
GROOT/'mosaic-build/meson-info/intro-buildoptions.json',
GROOT/'mosaic-build/meson-info/intro-targets.json',
LAB/'horizontal/identity.json', LAB/'horizontal/compile.json',
LAB/'horizontal/protocol-scanner.json', LAB/'horizontal/offline-tests-repair-v1.json',
LAB/'horizontal/C2/runner.py',
Path('/usr/lib/libwlroots-0.20.so').resolve(), BINARY,
]
# The offline completion-predicate test consumes this immutable, completed
# C2 evidence. Freeze the actual events and the records that validate them.
c2work=LAB/'horizontal/C2'
c2_evidence=[c2work/'actions.json',c2work/'run/events.jsonl',
c2work/'results/captures.json',c2work/'results/clicks.json',c2work/'results/runtime.json']
c2_evidence.extend(c2work/'results'/name for name in (
'grab-serials.json','xdg-protocol-order.json','popup-root-geometry.json',
'popup-nested-geometry.json','applied-clips.json','mosaic-pixels.json',
'standard-return.json','standard-popup-input.json','final-restoration.json',
'cleanup.json','terminal-exit.json'))
c2_evidence.extend(c2work/'run'/name for name in (
'H2-reference-1.png','H2-reference-2.png','E1-top.png','E1-A.png',
'E1-mosaic-H1.png','E1-mosaic-H2.png','E1-dismissed-H2.png','E2-top.png',
'E2-A.png','E2-mosaic-H1.png','E2-mosaic-H2.png','standard-H1.png',
'standard-H2.png','standard-A-H1.png','standard-A-H2.png','final-H2.png','final-H1.png'))
fixed.extend(c2_evidence)
fixed.extend(path for info in (GROOT/'scene-build/meson-info',GROOT/'mosaic-build/meson-info') for path in info.iterdir() if path.is_file())
for path in fixed:
require(path.is_file(), 'required G freeze input missing: ' + str(path))
rows[str(path)] = sha(path)
# Complete DESTDIR identity, including the four separate future notices.
for path in (GROOT/'stage-static').rglob('*'):
if path.is_file(): rows[str(path)] = sha(path)
# Exact C17 fixture binaries and generated protocol sources are also present through C2 closure.
return dict(sorted(rows.items()))
def verify_review():
freeze = _frozen_files()
freeze_sha = sha(WORK/'freeze.json')
review_path = GROOT/'reviews/release-smoke-review.json'
require(review_path.is_file(), 'independent G smoke byte review missing')
review = read(review_path)
require(review.get('status') == 'ACCEPTED' and review.get('scope') == 'OVERNIGHT_G_SMOKE_FINAL_BYTES', 'G smoke review scope/status')
require(review.get('independent') is True and review.get('read_only') is True and review.get('blockers') == [], 'G smoke review independence/blockers')
require(review.get('freeze_sha256') == freeze_sha and review.get('files') == freeze['files'], 'G smoke review bindings')
require(Path(review['report_file']).is_file() and sha(review['report_file']) == review['report_sha256'], 'G smoke report bytes')
return review
def preflight(require_review=True):
require(sys.flags.optimize == 0, 'assertions disabled')
require(LAB.is_dir() and LAB.stat().st_uid == os.getuid() and stat.S_IMODE(LAB.stat().st_mode) == 0o700, 'registered owned LAB mode')
require(WORK.is_dir() and WORK.stat().st_uid == os.getuid(), 'G smoke workspace owner')
validate_one_shot(WORK)
require((WORK/'results').is_dir(), 'G results directory')
require((WORK/'results/clients').is_symlink() and (WORK/'results/clients').resolve()==(LAB/'horizontal/clients').resolve(), 'results client link changed')
for name in ('nested-client','pointer-daemon','static-reference'):
p=WORK/name
require(p.is_symlink() and p.resolve()==(LAB/'horizontal/clients'/name).resolve(), 'C2 client symlink changed: '+name)
for proc in Path('/proc').iterdir():
if proc.name.isdigit():
try: exe=(proc/'exe').resolve(strict=True)
except (FileNotFoundError, PermissionError, ProcessLookupError): continue
require(exe != BINARY.resolve(), 'G binary already running')
build = _check_build_identity()
sys.path.insert(0, str(OVERNIGHT)); import ledger
validate_budget_room(read(LAB/'budget.json'))
freeze = _frozen_files()
# Refuse inherited A bytes and any other ELF even if it is executable.
require(freeze['files'].get(str(BINARY)) == BINARY_SHA, 'G ELF absent from freeze')
require(all(sha(path)==digest for path,digest in TRACE_SOURCES.items()), 'G runtime sources changed')
from adapter import generate_g
require((WORK/'runner.py').read_text() == generate_g(), 'generated G C2 runner changed')
if require_review: verify_review()
return dict(binary=str(BINARY), binary_sha256=BINARY_SHA, build_id=BUILD_ID,
wlroots_sha256=WLROOTS_SHA, trace_sources=TRACE_SOURCES,
pristine=dict(path=str(LAB/'stage-static/usr/bin/swayfx-mosaic'), sha256='979dcd95ab43fa1e465d6e9d262485b73d186cedacd0f957059dd24f482ce6f4'))
def begin_run():
auth = preflight(require_review=True)
token = WORK/'consumed.json'
exclusive(token, dict(case=CASE, freeze_sha256=sha(WORK/'freeze.json'), consumed_at_ns=time.time_ns()))
# The durable unique token comes before the locked global ledger reservation.
sys.path.insert(0, str(OVERNIGHT))
import ledger
ledger.reserve(LAB/'budget.json', 'run', campaign='G', run_id=CASE, duration_s=150)
exclusive(WORK/'actions.json', dict(planned=PLANS, reserved=dict.fromkeys(PLANS,0), events=[]))
return LAB, CASE, WORK, auth
def debit(lab, case, work, *kinds):
require(Path(lab).resolve()==LAB and case==CASE and Path(work).resolve()==WORK, 'G action binding')
require(kinds and all(k in PLANS for k in kinds), 'unknown G action')
lockpath=WORK/'actions.lock'
with lockpath.open('a+') as lock:
fcntl.flock(lock, fcntl.LOCK_EX)
state=read(WORK/'actions.json')
require(state.get('planned')==PLANS and set(state.get('reserved',{}))==set(PLANS), 'G action ledger schema')
require(len(state.get('events',[]))==sum(state['reserved'].values()), 'G action ledger count mismatch')
require(dict(Counter(e.get('kind') for e in state['events']))=={k:v for k,v in state['reserved'].items() if v}, 'G action event/count mismatch')
for i,event in enumerate(state['events']):
require(event.get('sequence')==i and event.get('kind') in PLANS and type(event.get('ns')) is int, 'G action event schema')
require(all(state['reserved'][k]+kinds.count(k)<=PLANS[k] for k in kinds), 'G local action quota')
for kind in kinds:
if kind=='capture':
sys.path.insert(0,str(OVERNIGHT));import ledger
ledger.reserve(LAB/'budget.json','capture',run_id=CASE)
state['reserved'][kind]+=1
state['events'].append(dict(sequence=len(state['events']),kind=kind,ns=time.monotonic_ns()))
durable(WORK/'actions.json',state)
def binding_sources(auth):
require(auth['binary']==str(BINARY) and auth['binary_sha256']==BINARY_SHA, 'G binary source binding')
return source_bindings(auth)
def finish_predicate(state, events, captures, clicks, runtime, capture_ok, window_ok):
"""Evaluate the real completion gate over recorded action and runtime evidence."""
counts=dict(Counter(e.get('kind') for e in state.get('events',[])))
ledger_ok=(state.get('planned')==PLANS and set(state.get('reserved',{}))==set(PLANS)
and counts=={k:v for k,v in state.get('reserved',{}).items() if v}
and all(type(v) is int and 0<=v<=PLANS.get(k,-1) for k,v in state.get('reserved',{}).items()))
event_capture_ok=sum(e.get('kind')=='capture' for e in events)==PLANS['capture']
injections=[e for e in events if e.get('kind')=='injection']
click_actions=[e for e in injections if e.get('action') in ('left','right')]
standalone_moves=[e for e in injections if e.get('action')=='move']
injection_ok=(len(injections)==PLANS['click']+2
and len(click_actions)==PLANS['click']
and len(standalone_moves)==2
and len(click_actions)+len(standalone_moves)==len(injections))
grabs=sum(e.get('grab') is True for e in injections)
complete=(ledger_ok and state.get('reserved')==PLANS and capture_ok and window_ok
and event_capture_ok and injection_ok and grabs==PLANS['grab']
and len(clicks)==PLANS['click'] and runtime.get('status')=='PASS')
return dict(complete=complete, action_ledger_valid=ledger_ok,
event_capture_count_valid=event_capture_ok, injections=len(injections),
click_injections=len(click_actions), standalone_move_injections=len(standalone_moves),
pointer_counts_valid=injection_ok, grabs=grabs)
def finish_run(lab, case, work):
verify_review()
require(Path(lab).resolve()==LAB and case==CASE and Path(work).resolve()==WORK, 'G finish binding')
state=read(WORK/'actions.json')
events_path=WORK/'run/events.jsonl'
events=[json.loads(line) for line in events_path.read_text().splitlines()] if events_path.is_file() else []
captures=read(WORK/'results/captures.json') if (WORK/'results/captures.json').is_file() else []
clicks=read(WORK/'results/clicks.json') if (WORK/'results/clicks.json').is_file() else []
runtime=read(WORK/'results/runtime.json') if (WORK/'results/runtime.json').is_file() else {'status':'INCONCLUSIVE','failure':'runtime result missing'}
try: validate_capture_batch(WORK,captures);capture_ok=True
except Exception: capture_ok=False
try: validate_window_results(WORK);window_ok=True
except Exception: window_ok=False
predicate=finish_predicate(state,events,captures,clicks,runtime,capture_ok,window_ok)
observed=dict(status='PASS' if predicate['complete'] else 'INCONCLUSIVE', runtime=runtime,
planned=PLANS, reserved=state.get('reserved'), action_ledger_valid=predicate['action_ledger_valid'],
captures=len(captures), capture_bytes_valid=capture_ok, event_capture_count_valid=predicate['event_capture_count_valid'],
clicks=len(clicks), injections=predicate['injections'], click_injections=predicate['click_injections'],
standalone_move_injections=predicate['standalone_move_injections'], grabs=predicate['grabs'],
pointer_counts_valid=predicate['pointer_counts_valid'],
window_evidence_complete=window_ok,
measurement_event=any(e.get('kind')=='measurements-completed' and e.get('status')=='PASS' for e in events))
durable(WORK/'finish.json',observed)
return observed

View file

@ -0,0 +1,127 @@
"""Offline fail-closed tests for the G smoke fixture; never launch compositor."""
import argparse
import hashlib
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
HERE=Path(__file__).resolve().parent
sys.path[:0]=[str(HERE),str(HERE.parent)]
import adapter,gate
from horizontal_adapter import replace_one
class GOffline(unittest.TestCase):
def test_c2_transform_unique_and_compiles(self):
text=adapter.generate_g();compile(text,'G-smoke/runner.py','exec')
self.assertEqual(text.count(adapter.G_IMPORT),1)
self.assertIn("open_chain('E1')",text);self.assertIn('fresh_chain(',text)
self.assertIn('dismissal(log',text);self.assertIn('final_restoration(baseline,final)',text)
self.assertIn('BINARY.resolve() and sha(BINARY)==BINARY_SHA',text)
def test_transform_rejects_missing_and_duplicate(self):
for source in ('missing','x x'):
with self.assertRaises(ValueError):replace_one(source,'x','y')
def test_old_A_and_non_G_ELF_rejected(self):
old_a=gate.LAB/'stage-static/usr/bin/swayfx-mosaic'
with self.assertRaises(ValueError):gate.validate_binary_identity(old_a,'979dcd95ab43fa1e465d6e9d262485b73d186cedacd0f957059dd24f482ce6f4')
with self.assertRaises(ValueError):gate.validate_binary_identity('/home/fy59/swayfx-mosaic-overnight-mcolx0gq/matrix/D4-mosaic/stage/usr/bin/swayfx-mosaic','0'*64)
with self.assertRaises(ValueError):gate.validate_binary_identity('/usr/bin/sway','0'*64)
def test_old_D_or_other_source_set_rejected(self):
rows=dict(gate.TRACE_SOURCES);rows[next(iter(rows))]='0'*64
with self.assertRaises(ValueError):gate.source_bindings({'trace_sources':rows})
def test_wrong_wlroots_and_mutated_freeze_rejected(self):
with self.assertRaises(ValueError):gate.validate_wlroots(gate.WLROOTS,'0'*64)
with tempfile.TemporaryDirectory() as d:
p=Path(d)/'input';p.write_bytes(b'changed')
with self.assertRaises(ValueError):gate.validate_file_map({str(p):hashlib.sha256(b'original').hexdigest()})
def test_quota_and_no_retry_rejected(self):
state={'limits':{'scene_build':2,'mosaic_build':2,'stage':2,'run':18,'capture':220},
'campaign_limits':{'C':3,'D1':2,'D2':2,'D3':2,'D4':2,'E1':1,'E2':1,'E3':2,'F':2,'G':1},
'used':dict.fromkeys(('scene_build','mosaic_build','stage','run','capture'),0),
'campaign_used':dict.fromkeys(('C','D1','D2','D3','D4','E1','E2','E3','F','G'),0),
'runs':{},'events':[]}
state['used']['capture']=204
with patch('ledger._validate'):
with self.assertRaises(ValueError):gate.validate_budget_room(state)
with tempfile.TemporaryDirectory() as d:
work=Path(d);(work/'consumed.json').write_text('{}')
with self.assertRaises(ValueError):gate.validate_one_shot(work)
def test_incomplete_frame_and_window_evidence_rejected(self):
with tempfile.TemporaryDirectory() as d:
w=Path(d);(w/'results').mkdir();(w/'run').mkdir()
with self.assertRaises(ValueError):gate.validate_capture_batch(w,[])
with self.assertRaises(ValueError):gate.validate_window_results(w)
def test_real_c2_events_satisfy_finish_predicate_and_mutations_fail(self):
c2=gate.LAB/'horizontal/C2'
evidence=(c2/'run/events.jsonl',c2/'actions.json',c2/'results/captures.json',
c2/'results/clicks.json',c2/'results/runtime.json')
for path in evidence:
self.assertTrue(path.is_file(),str(path))
self.assertRegex(gate.sha(path),r'^[0-9a-f]{64}$')
if (gate.WORK/'freeze.json').is_file():
frozen=gate.expected_files()
for path in evidence:self.assertEqual(frozen[str(path)],gate.sha(path),str(path))
events=[json.loads(line) for line in evidence[0].read_text().splitlines()]
state=json.loads(evidence[1].read_text())
captures=json.loads(evidence[2].read_text())
clicks=json.loads(evidence[3].read_text())
runtime=json.loads(evidence[4].read_text())
gate.validate_capture_batch(c2,captures)
gate.validate_window_results(c2)
valid=gate.finish_predicate(state,events,captures,clicks,runtime,True,True)
self.assertTrue(valid['complete'],valid)
self.assertEqual((valid['injections'],valid['click_injections'],
valid['standalone_move_injections'],valid['grabs']),(10,8,2,4))
injections=[i for i,e in enumerate(events) if e.get('kind')=='injection']
self.assertEqual(len(injections),10)
missing=events[:injections[-1]]+events[injections[-1]+1:]
extra=events+[dict(events[injections[0]])]
wrong_action=[dict(e) for e in events]
wrong_action[injections[1]]['action']='move'
unknown_action=[dict(e) for e in events]
unknown_action[injections[1]]['action']='unknown'
for mutation in (missing,extra,wrong_action,unknown_action):
with self.subTest(mutation=len(mutation)):
self.assertFalse(gate.finish_predicate(
state,mutation,captures,clicks,runtime,True,True)['complete'])
def test_preflight_refuses_absent_or_rejected_review_without_side_effects(self):
if not (gate.WORK/'freeze.json').is_file():
self.skipTest('review-gated preflight is exercised after the fresh freeze exists')
real_root=gate.GROOT
with tempfile.TemporaryDirectory() as d:
review_dir=Path(d)/'reviews';review_dir.mkdir()
absent=review_dir/'release-smoke-review.json'
rejected=review_dir/'rejected.json'
rejected.write_text(json.dumps({'status':'CORRECTION_REQUIRED'}))
class RootWithIsolatedReview:
def __truediv__(self, other):
if str(other)=='reviews/release-smoke-review.json':
return absent
return real_root/other
budget=(gate.LAB/'budget.json').read_bytes()
for path,expected in ((absent,'independent G smoke byte review missing'),
(rejected,'G smoke review scope/status')):
if path==absent:
self.assertFalse(absent.exists())
else:
rejected.rename(absent)
with patch.object(gate,'GROOT',RootWithIsolatedReview()):
with self.assertRaisesRegex(ValueError,expected):
gate.preflight(require_review=True)
self.assertEqual((gate.LAB/'budget.json').read_bytes(),budget)
self.assertFalse((gate.WORK/'consumed.json').exists())
self.assertFalse((gate.WORK/'run').exists())
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--output',type=Path,required=True);args=p.parse_args()
result=unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromTestCase(GOffline))
data={'status':'PASS' if result.wasSuccessful() else 'FAIL','executed':result.testsRun,
'skipped':[(str(t),reason) for t,reason in result.skipped],
'failures':[(str(t),s) for t,s in result.failures], 'errors':[(str(t),s) for t,s in result.errors],
'runtime':'NOT RUN','build':'NOT RUN','stage':'NOT RUN','review_acceptance_created':False}
args.output.write_text(json.dumps(data,indent=2)+'\n')
raise SystemExit(not result.wasSuccessful())

View file

@ -0,0 +1,102 @@
"""WHY: requalify annotated identity without inheriting historical graphics.
CONTRACT: pinned replay gate before reserved one-shot builds.
INVARIANT: historical LAB read only; no semantic source edit or build retry.
"""
import sys, json, hashlib, shutil, subprocess, tarfile, re, time, os, argparse
from pathlib import Path
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('lab',type=Path)
parser.add_argument('--historical',type=Path,required=True)
args=parser.parse_args()
LAB=args.lab.resolve(); D=args.historical.resolve()
REPO=Path(__file__).resolve().parents[3]
assert LAB != D and not LAB.is_relative_to(REPO)
LAB.mkdir(exist_ok=True)
sys.path.insert(0,str(REPO/'tooling/probes/step20c-overnight'))
from ledger import initialize,reserve as debit
for name in ('scripts','raw','results','patches'): (LAB/name).mkdir(exist_ok=True)
initialize(LAB/'budget.json')
for name in ('support.py','audit-d.py','qualify-d.py','stage-d.py'):
shutil.copy2(D/'scripts'/name,LAB/'scripts'/name)
p=LAB/'scripts/support.py';txt=p.read_text();start=txt.index('def reserve(');end=txt.index('\ndef environment()',start)
txt=txt[:start]+"def reserve(lab, kind):\n import sys\n sys.path.insert(0, '"+str(REPO/'tooling/probes/step20c-overnight')+"')\n from ledger import reserve as debit\n return debit(Path(lab)/'budget.json', kind)\n"+txt[end:]
# Every command records wall time/date/return code, including failed diagnostics.
txt=txt.replace("lab=Path(lab); path=", "started=time.time(); lab=Path(lab); path=")
txt=txt.replace("'env':extra or {},'exit':proc.returncode", "'env':extra or {},'exit':proc.returncode,'started_unix':started,'duration_s':time.time()-started")
p.write_text(txt)
sys.path.insert(0,str(LAB/'scripts'))
from support import command,save,sha,reserve
def treehash(root):
entries=[{'path':str(p.relative_to(root)),'sha256':sha(p),'mode':oct(p.stat().st_mode&0o777)} for p in sorted(root.rglob('*')) if p.is_file()]
return hashlib.sha256(json.dumps(entries,sort_keys=True,separators=(',',':')).encode()).hexdigest()
def tokens(text):
# Strings/chars are consumed before comment syntax; comments inside literals survive.
expr=r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|/\*.*?\*/|//[^\n]*|[A-Za-z_]\w*|(?:\d[\w.]*)|[^\s]'
return [m.group(0) for m in re.finditer(expr,text,re.S) if not m.group(0).startswith(('/*','//'))]
state={'status':'RUNNING','source_gate':'NOT RUN','graphics':'NOT TESTED','historical':str(D)}
def checkpoint(): (LAB/'phase-a.json').write_text(json.dumps(state,indent=2)+'\n')
try:
versions=json.loads((D/'results/source-versions.json').read_text())
components=json.loads((D/'results/component-patches.json').read_text())
for name in ('mosaic-source','swayfx-source','scene-source','wlroots-source'):
arc=LAB/(name+'.tar');shutil.copy2(D/(name+'.tar'),arc)
assert sha(arc)==versions['archives'][arc.name]
if name in ('mosaic-source','swayfx-source'):
rev=versions['pins']['Mosaic' if name=='mosaic-source' else 'SwayFX']
with (LAB/(name+'-git-check.tar')).open('wb') as out:subprocess.run(['git','-C',str(REPO),'archive',rev],stdout=out,check=True)
assert sha(LAB/(name+'-git-check.tar'))==sha(arc)
(LAB/name).mkdir()
with tarfile.open(arc) as tf:tf.extractall(LAB/name,filter='data')
state['components']=[]
for item in components['ordered_patches']:
tree=LAB/item['tree'];src=Path(item['source']);patch=LAB/'patches'/src.name
assert sha(src)==sha(Path(item['candidate_path']))==item['sha256']
shutil.copy2(src,patch)
assert treehash(tree)==item['parent_tree_sha256']
command(LAB,'replay-'+src.stem+'-check',['git','apply','--check',patch],tree)
command(LAB,'replay-'+src.stem+'-apply',['git','apply',patch],tree)
assert treehash(tree)==item['result_tree_sha256']
state['components'].append({**item,'replayed_path':str(patch),'verified':True})
for name in ('mosaic-source','scene-source'):
assert treehash(LAB/name)==treehash(D/name)
manifest=json.loads((D/'comment-repair/repair-manifest.json').read_text())
before={row['path']:(LAB/row['path']).read_text() for row in manifest['files']}
for row in manifest['files']:assert sha(LAB/row['path'])==row['parent_sha256']
for name,digest in manifest['patches'].items():
patch=LAB/'patches'/name;shutil.copy2(D/'comment-repair'/name,patch);assert sha(patch)==digest
command(LAB,'comment-'+name+'-check',['git','apply','--check',patch],LAB)
command(LAB,'comment-'+name+'-apply',['git','apply',patch],LAB)
for row in manifest['files']:
assert sha(LAB/row['path'])==row['result_sha256']
assert tokens(before[row['path']])==tokens((LAB/row['path']).read_text())
state.update(source_gate='PASS',comments=manifest,source_trees={name:treehash(LAB/name) for name in ('scene-source','mosaic-source')})
assert sha(Path('/usr/lib/libwlroots-0.20.so').resolve())=='7edca9a7d65855addda68e29ba5db94d57c803eb5fa7d0f86db3136b90ca9837'
save(LAB,'source-versions',versions);checkpoint();print('SOURCE_GATE_PASS',flush=True)
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','--prefix=/usr','-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)
state['builds']='PASS';checkpoint();print('BUILDS_PASS',flush=True)
command(LAB,'mosaic-model',['meson','test','-C',LAB/'mosaic-build','--no-rebuild','mosaic-model','--print-errorlogs'])
save(LAB,'static-build',{'status':'PASS','flags':flags,'options':json.loads((LAB/'mosaic-build/meson-info/intro-buildoptions.json').read_text()),'archive_sha256':sha(LAB/'scene-build/libscenefx-0.5.a'),'binary_sha256':sha(LAB/'mosaic-build/sway/swayfx-mosaic')})
reserve(LAB,'stage')
command(LAB,'meson-stage',['meson','install','-C',LAB/'mosaic-build','--no-rebuild','--destdir',LAB/'stage-static'])
for script,args in [('audit-d.py',[LAB]),('qualify-d.py',[]),('stage-d.py',[LAB])]:
command(LAB,script,['python',LAB/'scripts'/script,*args],timeout=600)
state['binary']=json.loads((LAB/'results/binary-elf.json').read_text())
state['stage']=json.loads((LAB/'results/static-stage-manifest.json').read_text())
state['ABI']='PASS local layout/headers/85-symbol characterization; semantic maintenance NOT ESTABLISHED'
state['status']='PASS'
except Exception as exc:
state['status']='FAIL';state['error']=repr(exc);raise
finally:
state['budget']=json.loads((LAB/'budget.json').read_text());checkpoint()

View file

@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""One explicit reviewed C run; no retries, no inherited live desktop socket."""
import argparse
from pathlib import Path
import sys
from horizontal_adapter import BASE,generate
from horizontal_gate import preflight,sha
p=argparse.ArgumentParser();p.add_argument('lab',type=Path);p.add_argument('case',choices=['C0','C1','C2']);a=p.parse_args()
lab=a.lab.resolve();preflight(lab,a.case)
runner=lab/'horizontal'/a.case/'runner.py'
if runner.read_text()!=generate(a.case):raise ValueError('generated reviewed runner changed')
# Preserve overnight gate first, with only the identified validator dependency directory next.
sys.path.insert(1,str(BASE))
exec(compile(runner.read_text(),str(runner),'exec'),{'__name__':'__main__','__file__':str(runner)})

View file

@ -0,0 +1,49 @@
"""WHY: validate irreversible debits and concurrent reservation bounds offline."""
import concurrent.futures
import json
from pathlib import Path
import tempfile
import unittest
from ledger import initialize, reserve
class LedgerTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.path = Path(self.tmp.name) / 'budget.json'
initialize(self.path)
def test_no_reset_and_no_refund(self):
reserve(self.path, 'scene_build')
with self.assertRaises(FileExistsError): initialize(self.path)
self.assertEqual(json.loads(self.path.read_text())['used']['scene_build'], 1)
def test_concurrent_exhaustion(self):
def attempt(_):
try: reserve(self.path, 'mosaic_build'); return True
except RuntimeError: return False
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as pool:
self.assertEqual(sum(pool.map(attempt, range(16))), 2)
def test_run_and_capture_bounds(self):
reserve(self.path, 'run', campaign='F', run_id='f1')
reserve(self.path, 'capture', run_id='f1', amount=12)
with self.assertRaises(RuntimeError): reserve(self.path, 'capture', run_id='f1')
reserve(self.path, 'run', campaign='F', run_id='f2')
with self.assertRaises(RuntimeError): reserve(self.path, 'run', campaign='F', run_id='f3')
def test_invalid_does_not_debit(self):
for kwargs in ({'campaign':'C','run_id':'c','duration_s':151}, {'campaign':'C','run_id':'c','amount':2}):
with self.assertRaises(ValueError): reserve(self.path, 'run', **kwargs)
with self.assertRaises(ValueError): reserve(self.path, 'capture', run_id='missing')
self.assertEqual(json.loads(self.path.read_text())['events'], [])
def test_corrupted_persistence_fails_closed(self):
original = self.path.read_text()
for mutate in (
lambda s: s['used'].update(scene_build=-1),
lambda s: s['used'].update(scene_build=True),
lambda s: s['used'].update(scene_build=1),
lambda s: s['events'].append({'sequence':0,'kind':'run','amount':1,'campaign':'C','run_id':'x','reserved_at_ns':1}),
lambda s: s['runs'].update(x={'campaign':'C','captures':0,'duration_s':150}),
):
state = json.loads(original); mutate(state)
corrupted = json.dumps(state); self.path.write_text(corrupted)
with self.assertRaises(RuntimeError): reserve(self.path, 'stage')
self.assertEqual(self.path.read_text(), corrupted)
if __name__ == '__main__': unittest.main()