Context
#15664's root cause was a five-month-silent dead configuration: test/playwright/playwright.config.e2e.mjs shipped --use-gl=desktop on 2026-02-19 (6ca0d13aa2, "GPU-accelerated E2E benchmark infrastructure") — a GL selector modern Chrome's ANGLE-only allowlist rejects, so it resolved to gl=none,angle=none from the day it landed. The GPU process died at every window birth; --disable-software-rasterizer removed the swiftshader fallback that would have masked it; Chromium's GPU-crash threshold then terminated the whole headed browser mid-suite (GPU process isn't usable. Goodbye. → SIGTRAP), which we investigated for days as a vessel/heap-join defect. The "GPU-accelerated" benchmark suite plausibly never ran a single accelerated frame — and stayed green throughout, because no source-shaped check can see this class: the config's text never rotted, the environment rotted under it.
Evidence chain: https://github.com/neomjs/neo/issues/15664#issuecomment-5069969520
Live latest-open sweep: checked latest 20 open issues at 2026-07-24T12:59Z; no equivalent found. A2A in-flight sweep: no overlapping claim (my own [discussion-signal] at 12:58Z announced this filing).
The Problem
The defect class is environment rot under a valid artifact ("Face C" on D#15812): a launch flag, once correct or plausibly correct, silently loses its meaning when the runtime evolves (Chrome retired desktop GL; the next major can retire another flag). Review, lint, and a green suite are all form-shaped instruments — they verify the text, not the effect. The #15664 fix removed the two poison flags, but nothing now prevents the next flag from rotting the same way. The class needs an effect-shaped check: assert the intended runtime state, not the configuration's spelling.
The Architectural Reality
test/playwright/playwright.config.e2e.mjs — carries GPU-intent launch args (--ignore-gpu-blocklist, --enable-gpu-rasterization, --enable-zero-copy, --enable-accelerated-2d-canvas) whose intent is hardware GL. Its in-place comment (added by the #15664 fix) documents the constraint but is prose, not enforcement.
test/playwright/e2e/globalSetup.mjs — the existing suite-boot surface (sibling precedent for placement; Agent OS structure-map gate: N/A, test substrate with direct sibling).
- The e2e suite has no CI lane — local runs are the only enforcement point, so the probe must live in the suite boot itself, not in a workflow.
- Two candidate observation surfaces, both from inside the launched browser:
- WebGL renderer-string probe (simplest): open one page,
canvas.getContext('webgl') → WEBGL_debug_renderer_info UNMASKED_RENDERER_WEBGL. Healthy macOS = ANGLE (… Metal …); the #15664 failure state = context creation fails or software renderer while GPU flags are configured.
- Browser-level CDP
SystemInfo.getInfo via browser.newBrowserCDPSession() (browser-level domains verified reachable this way in the #15664 diagnosis — Target.setDiscoverTargets worked; SystemInfo availability itself is the falsifier to verify first).
The Fix
Add a fail-loud GL effect-probe to the e2e suite boot:
- At suite boot (globalSetup or a worker-scoped fixture — implementer verifies which runs inside the launched project browser, since globalSetup may run before browser launch; a first-project setup spec is the fallback shape), read the live GL/renderer state via surface (1) (fallback (2)).
- If the config carries GPU-intent flags AND the live state is
none/software-fallback, fail the run immediately with a message naming the flag-rot class and pointing at #15664's evidence chain.
- Keep the probe's own failure honest: if the observation surface itself is unavailable (no
WEBGL_debug_renderer_info, no SystemInfo), report inability to observe distinctly from observed-healthy — a diagnostic that cannot report inability cannot come back negative (the D#15812 lesson).
Acceptance Criteria
Cost note (why the AC moved)
A boot gate that catches flag-rot before the benchmarks it protects has to run in its own browser: sharing the suite's browser would mean the first benchmark has already launched by the time the probe fires. The setup-project placement gives that guarantee and costs one browser launch (~1.6–2.1s once per suite). An in-browser probe would cost ~1s but could only observe after the browser the benchmarks use is already up — too late to be a gate. The estimate assumed the cheaper shape; the correct shape is the dearer one.
Out of Scope
- The general detection mechanisms under divergence on D#15812 (CodeQL dataflow, Semgrep, per-file teardown gates) — this ticket is the narrow, incident-anchored regression guard for the config face only. If D#15812 graduates a broader effect-probe substrate (its Option 5/6 rows), this implementation should align with or be absorbed by it — noted there by the author.
- Face A (production reachability) and Face B (test teardown) of the D#15812 class.
- Any change to the launch flags themselves (done in #15664's fix PR).
Related
- #15664 — the incident this guards against regressing (fix PR removes the dead flags).
- D#15812 — the general "artifact that cannot fail" class discussion; this ticket is referenced there as an incident-anchored follow-up proceeding regardless of graduation outcome (relationship note, not a graduation dependency).
- #15648 — adjacent FM vessel-boot observability lane (benefits from a healthy-GPU baseline).
Decision Record impact: none.
Origin Session ID: 6b95191e-b5bf-487b-9672-96a76060a92b
Retrieval Hint: "GL effect probe suite boot gl=none GPU crash threshold #15664"
Context
#15664's root cause was a five-month-silent dead configuration:
test/playwright/playwright.config.e2e.mjsshipped--use-gl=desktopon 2026-02-19 (6ca0d13aa2, "GPU-accelerated E2E benchmark infrastructure") — a GL selector modern Chrome's ANGLE-only allowlist rejects, so it resolved togl=none,angle=nonefrom the day it landed. The GPU process died at every window birth;--disable-software-rasterizerremoved the swiftshader fallback that would have masked it; Chromium's GPU-crash threshold then terminated the whole headed browser mid-suite (GPU process isn't usable. Goodbye.→ SIGTRAP), which we investigated for days as a vessel/heap-join defect. The "GPU-accelerated" benchmark suite plausibly never ran a single accelerated frame — and stayed green throughout, because no source-shaped check can see this class: the config's text never rotted, the environment rotted under it.Evidence chain: https://github.com/neomjs/neo/issues/15664#issuecomment-5069969520 Live latest-open sweep: checked latest 20 open issues at 2026-07-24T12:59Z; no equivalent found. A2A in-flight sweep: no overlapping claim (my own
[discussion-signal]at 12:58Z announced this filing).The Problem
The defect class is environment rot under a valid artifact ("Face C" on D#15812): a launch flag, once correct or plausibly correct, silently loses its meaning when the runtime evolves (Chrome retired desktop GL; the next major can retire another flag). Review, lint, and a green suite are all form-shaped instruments — they verify the text, not the effect. The #15664 fix removed the two poison flags, but nothing now prevents the next flag from rotting the same way. The class needs an effect-shaped check: assert the intended runtime state, not the configuration's spelling.
The Architectural Reality
test/playwright/playwright.config.e2e.mjs— carries GPU-intent launch args (--ignore-gpu-blocklist,--enable-gpu-rasterization,--enable-zero-copy,--enable-accelerated-2d-canvas) whose intent is hardware GL. Its in-place comment (added by the #15664 fix) documents the constraint but is prose, not enforcement.test/playwright/e2e/globalSetup.mjs— the existing suite-boot surface (sibling precedent for placement; Agent OS structure-map gate: N/A, test substrate with direct sibling).canvas.getContext('webgl')→WEBGL_debug_renderer_infoUNMASKED_RENDERER_WEBGL. Healthy macOS =ANGLE (… Metal …); the #15664 failure state = context creation fails or software renderer while GPU flags are configured.SystemInfo.getInfoviabrowser.newBrowserCDPSession()(browser-level domains verified reachable this way in the #15664 diagnosis —Target.setDiscoverTargetsworked;SystemInfoavailability itself is the falsifier to verify first).The Fix
Add a fail-loud GL effect-probe to the e2e suite boot:
none/software-fallback, fail the run immediately with a message naming the flag-rot class and pointing at #15664's evidence chain.WEBGL_debug_renderer_info, noSystemInfo), report inability to observe distinctly from observed-healthy — a diagnostic that cannot report inability cannot come back negative (the D#15812 lesson).Acceptance Criteria
≤ ~1sestimate predated the placement decision and assumed an in-browser probe. The chosen placement — a setup project, per @neo-gpt's review and thechroma-setupprecedent — pays one full browser launch, measured at +1.6–2.1s incremental (independently by @neo-gpt on PR #15816). That is the price of the boot-gate guarantee: the probe must fail before any benchmark runs, which requires its own browser rather than a shared one. Accepted as the correct trade, not a regression against the estimate.--use-gl=desktop+--disable-software-rasterizeron a scratch branch turns the suite red at boot with the class-naming message (today's silent state becomes loud). Evidence: the failing output pasted in the PR.Cost note (why the AC moved)
A boot gate that catches flag-rot before the benchmarks it protects has to run in its own browser: sharing the suite's browser would mean the first benchmark has already launched by the time the probe fires. The setup-project placement gives that guarantee and costs one browser launch (~1.6–2.1s once per suite). An in-browser probe would cost ~1s but could only observe after the browser the benchmarks use is already up — too late to be a gate. The estimate assumed the cheaper shape; the correct shape is the dearer one.
Out of Scope
Related
Decision Record impact: none.
Origin Session ID: 6b95191e-b5bf-487b-9672-96a76060a92b Retrieval Hint: "GL effect probe suite boot gl=none GPU crash threshold #15664"