You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
UI E2E is flaky: serial debug server latency exceeds the 15s per-check window #862
UI E2E has started failing intermittently on main and on unrelated PRs:
main @ 126a104 (run 39409313917): 3 early checks failed because the rendered statements were read before the selected rule's content flushed (conditions render with lookup names, actions render with lookup display names — only Set Global Variable(score, 0) present — block bodies indent).
PR run 39411166202 (package-store branch): timed out at openRule(4, 'after') waiting for a Wait statement while rules 0–3 had already resolved.
A stacked superset branch (feat/lint-package-commands, run 39411419983) ran the same test green on the same code.
Root cause hypothesis
wright ui serves requests serially (for request in server.incoming_requests() in crates/wright-cli/src/ui.rs). The UI fires the rule-content fetches in parallel (Promise.all in ui/src/App.svelte), so the fixture's six rules?rule&content calls plus the bulk/list fallbacks queue behind each other on a single-threaded debug-build server. When the tail request lands later than a check's 15s waitFor window (ui/scripts/e2e.mjs), the assertion reads either a still-loading state or a previous rule's stale body.
This makes the job timing-sensitive rather than correctness-sensitive, and #847 added a small per-request cost (disk_fingerprint) that may have tightened the margin.
Options
Raise the per-check waitFor timeout in e2e.mjs (cheapest; hides a real latency cliff if one exists).
Bound request latency in the harness itself, e.g. warm the six content requests before starting checks so no check window covers cold queueing.
Parallelize or pipeline the tiny_http server (an ADR-0032 design decision, not a test fix).
Filing for owner triage; both failing runs were re-triggered as reruns.
Root cause found and fixed in #864. Reproduced all three failure signatures locally with in-page request/state traces:
"Loading rule…" hang — respond() added Connection: close to every answer. Chrome may already have dispatched the next POST /api onto a socket it still believed reusable; the close then dropped that request, and since POSTs are not retried at the network layer the fetch hung forever. The comment motivating close (a parked keep-alive/preconnect socket stalling the loop) is unfounded: tiny_http runs a reader task per connection inside an unbounded worker pool, so an idle socket only parks its own worker — the serial dispatch loop is unaffected. Verified directly that sequential requests on one connection now work (Re-using existing connection → 200, 200).
Wrong-content / missing-marker failures — load()'s per-rule fallback ended with selected = rules[0].summary.id after await Promise.all(...). A click that landed while per-rule fetches were still in flight was reverted to rule 0 the moment the last fetch settled, so the pane rendered off's statements for whichever rule was opened (traces show all fetches resolving correctly while selected flipped back to 0). This produced both the stale-assertion failures and, when the revert landed inside one poll interval, the title-timeout variant.
ENOTEMPTY exits — Chrome keeps writing profile files briefly after SIGKILL; rmSync now retries instead of failing a green run.
Verification: 31 consecutive npm run test:e2e runs on Google Chrome with zero failures (the unfixed tree failed ~30 % of runs); cargo test -p wright-cli --all-features, clippy, fmt, npm test, and svelte-check all clean.
Activity
e54-bot commentedon Oct 20, 2026
Root cause found and fixed in #864. Reproduced all three failure signatures locally with in-page request/state traces:
"Loading rule…" hang —
respond()addedConnection: closeto every answer. Chrome may already have dispatched the nextPOST /apionto a socket it still believed reusable; the close then dropped that request, and since POSTs are not retried at the network layer the fetch hung forever. The comment motivatingclose(a parked keep-alive/preconnect socket stalling the loop) is unfounded: tiny_http runs a reader task per connection inside an unbounded worker pool, so an idle socket only parks its own worker — the serial dispatch loop is unaffected. Verified directly that sequential requests on one connection now work (Re-using existing connection→ 200, 200).Wrong-content / missing-marker failures —
load()'s per-rule fallback ended withselected = rules[0].summary.idafterawait Promise.all(...). A click that landed while per-rule fetches were still in flight was reverted to rule 0 the moment the last fetch settled, so the pane renderedoff's statements for whichever rule was opened (traces show all fetches resolving correctly whileselectedflipped back to0). This produced both the stale-assertion failures and, when the revert landed inside one poll interval, the title-timeout variant.ENOTEMPTYexits — Chrome keeps writing profile files briefly after SIGKILL;rmSyncnow retries instead of failing a green run.Verification: 31 consecutive
npm run test:e2eruns on Google Chrome with zero failures (the unfixed tree failed ~30 % of runs);cargo test -p wright-cli --all-features, clippy, fmt,npm test, andsvelte-checkall clean.fix(ui): keep the selected rule and wait for the initial load in E2E
fix(ui): keep the selected rule and wait for the initial load in E2E (#…
fix(ui): keep connections alive in `wright ui`
chore(ui): remove the local browser E2E (#870)