LearnNewsExamplesServices
Frontmatter
id15813
titleE2E boot probe: fail loud when GPU-intent flags resolve to no GL
stateClosed
labels
enhancementaitesting
assigneesneo-opus-ada
createdAtJul 24, 2026, 3:01 PM
updatedAtJul 24, 2026, 7:07 PM
githubUrlhttps://github.com/neomjs/neo/issues/15813
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 24, 2026, 7:07 PM

E2E boot probe: fail loud when GPU-intent flags resolve to no GL

Closed Backlog/active-chunk-9 enhancementaitesting
neo-opus-vega
neo-opus-vega commented on Jul 24, 2026, 3:01 PM

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:
    1. 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.
    2. 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:

  1. 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)).
  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.
  3. 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

  • Probe runs once per e2e suite boot (not per spec). Cost AC amended 2026-07-24 (see Cost note): the ≤ ~1s estimate predated the placement decision and assumed an in-browser probe. The chosen placement — a setup project, per @neo-gpt's review and the chroma-setup precedent — 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.
  • Red-state acceptance test: re-adding --use-gl=desktop + --disable-software-rasterizer on 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.
  • Green-state test: current config passes the probe headed AND headless on a macOS seat; renderer string logged as info either way.
  • Inability-to-observe is a distinct, explicit outcome (neither pass nor the red-state message).
  • No new dependencies; probe implemented with Playwright + browser APIs already in the repo.

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"

tobiu referenced in commit 90a8426 - "feat(test): fail the E2E suite loud when GPU-intent flags resolve to no GL (#15813) (#15816) on Jul 24, 2026, 7:07 PM
tobiu closed this issue on Jul 24, 2026, 7:07 PM
tobiu referenced in commit 5acc564 - "fix(ai): pass argv, not a shell string, from the file-system MCP server (#15818) (#15819) on Jul 24, 2026, 7:35 PM