MAPLE SDK ISSUE: CORE BUNDLE (maple-issue-2026-10-08) ===================================================== Every text file from the maple-issue-2026-10-08 repository (bug report, standalone reproduction project, SDK source excerpts and evidence), concatenated into one file. This is the CORE bundle: report, repro code, SDK excerpts and evidence summaries/console logs. The raw per-run evidence JSON/JSONL files cited by the report are only in the COMPLETE bundle. There are no binaries or images in the repository. node_modules, build output and .git are excluded. Run `bun install` inside repro/ to fetch dependencies. FORMAT ------ Each file appears as: @@@@@@@@@@ FILE: ( bytes) @@@@@@@@@@ END FILE: (If a file does not end with a newline, one newline is inserted before the END marker; the byte count in the header is the authoritative length.) RECONSTRUCT THE ORIGINAL FOLDER, byte-for-byte (python3, run in an empty dir): python3 - THIS_FILE.txt <<'PY' import os, re, sys data = open(sys.argv[1], "rb").read() hdr = re.compile(rb"^@@@@@@@@@@ FILE: (.+?) \((\d+) bytes\)\n", re.M) pos = 0 while (m := hdr.search(data, pos)): path, n = m.group(1).decode(), int(m.group(2)) body = data[m.end():m.end() + n] os.makedirs(os.path.dirname(path) or ".", exist_ok=True) open(path, "wb").write(body) pos = m.end() + n PY TABLE OF CONTENTS (44 files) ----------------- README.md (2458 bytes) MAPLE_BUG_REPORT.txt (212121 bytes) repro/README.md (8750 bytes) repro/package.json (379 bytes) repro/.gitignore (153 bytes) repro/build.ts (1913 bytes) repro/bun.lock (4821 bytes) repro/client/app.ts (9255 bytes) repro/client/probe.ts (2686 bytes) repro/lib.ts (7349 bytes) repro/public/index.html (629 bytes) repro/public/probe.html (293 bytes) repro/run-scenarios.ts (8382 bytes) repro/scenarios/b-probe.ts (9438 bytes) repro/scenarios/sdk.ts (34500 bytes) repro/server.ts (13999 bytes) repro/variants/sdk-0.10.0/bun.lock (798 bytes) repro/variants/sdk-0.10.0/package.json (415 bytes) sdk-excerpts/01-flush-core-cooldown-serialization.txt (7677 bytes) sdk-excerpts/02-buffers-cap-restore.txt (6299 bytes) sdk-excerpts/03-mapleflush-make-client.txt (14768 bytes) sdk-excerpts/04-replay-transport-keepalive-budget.txt (6957 bytes) sdk-excerpts/05-session-event-sink.txt (4230 bytes) sdk-excerpts/06-session-lifecycle-unload.txt (10642 bytes) sdk-excerpts/07-client-session-startup.txt (9123 bytes) sdk-excerpts/08-replay-record-final-chunk.txt (13459 bytes) sdk-excerpts/09-maple-layer-client.txt (6922 bytes) sdk-excerpts/10-effect-otlp-exporter.txt (8426 bytes) sdk-excerpts/11-readme.txt (5172 bytes) sdk-excerpts/12-v0.10.0-what-changed-and-what-did-not.txt (10614 bytes) evidence/headless-new/runner-console.txt (29525 bytes) evidence/headless-new/summary-new-2026-10-07T23-50-02-862Z.txt (13911 bytes) evidence/runner-console-A.txt (5750 bytes) evidence/runner-console-F-rerun.txt (900 bytes) evidence/runner-console-W.txt (2528 bytes) evidence/runner-console-main.txt (102949 bytes) evidence/sdk-0.10.0/runner-console-A.txt (5863 bytes) evidence/sdk-0.10.0/runner-console-main.txt (49901 bytes) evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-41-29-113Z.txt (3149 bytes) evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-43-48-971Z.txt (22139 bytes) evidence/summary-shell-2026-10-07T23-39-09-001Z.txt (46826 bytes) evidence/summary-shell-2026-10-07T23-39-09-004Z.txt (3061 bytes) evidence/summary-shell-2026-10-07T23-58-29-444Z.txt (1271 bytes) evidence/webkit-firefox-launch.txt (1867 bytes) @@@@@@@@@@ FILE: README.md (2458 bytes) # maple-issue-2026-10-08 Bug report and standalone reproduction for telemetry loss in the Maple browser SDK (`@maple-dev/effect-sdk` 0.8.2, `@maple-dev/effect-sdk/client`, mainly `MapleFlush.make`). Most findings still reproduce on 0.10.0. - **[`MAPLE_BUG_REPORT.txt`](MAPLE_BUG_REPORT.txt):** the full report (~2,250 lines). - TL;DR table in section 1. - Per-bug root cause, evidence, reproduction steps and suggested fixes in section 4. - An index of every file in Appendix C. - **[`repro/`](repro/):** self-contained Bun + Playwright project with a mock ingest server. See [`repro/README.md`](repro/README.md). ```sh cd repro && bun install && bun run scenarios # all scenarios bun run scenarios A0 A # e.g. just the oversized-batch scenarios ``` - **[`sdk-excerpts/`](sdk-excerpts/):** verbatim SDK (and Effect) source excerpts with file names and line numbers. - **[`evidence/`](evidence/):** raw output of every run cited in the report (one JSON per run, plus server request logs). ## Summary | ID | Severity | Finding | |---|---|---| | BUG-1 | High | One flush whose keepalive body exceeds Chromium's 64 KiB in-flight budget (~163 spans) is rejected. It is then restored and cooled down for 60s, so the batch only grows and trace export stops for the rest of the page's life. | | BUG-2 | Medium | traces, logs and metrics are POSTed concurrently with `keepalive` and share that budget. | | BUG-3 | Medium | During the 60s cooldown the unload (`pagehide`) flush is skipped too. | | BUG-4 | Medium | The unload flush is serialized behind any in-flight flush. With a slow ingest the tail is lost, and one slow POST stalls all signals. | | BUG-5 | Medium | At unload, the OTLP posts and the session/replay posts compete for the same budget without shared accounting. | | BUG-6 | Medium | The final replay chunk is never uploaded on unload (`MapleFlush.make` and `Maple.layer`). | | BUG-7 | Low | "flush failed; cooldown 60s" / "POST failed" console errors at unload are mostly false alarms: the server received the requests and returned 200. | | BUG-8 | Low | Non-keepalive session posts aborted by navigation are dropped, despite the "will retry on next chunk" message. | | BUG-9 | Info | `Maple.layer` has no unload flush (documented). A small fix using Effect's own flusher is demonstrated. | Environment: Chromium 143 (Playwright 1.57.0), Bun 1.4.2, Linux x86_64. WebKit and Firefox were not tested. @@@@@@@@@@ END FILE: README.md @@@@@@@@@@ FILE: MAPLE_BUG_REPORT.txt (212121 bytes) MAPLE BUG REPORT @maple-dev/effect-sdk 0.8.2, browser entry (@maple-dev/effect-sdk/client): MapleFlush.make (and Maple.layer for comparison) Telemetry gets lost or stops completely around fetch(keepalive), the 60 s cooldown, the serialized flush and page unload. There are also misleading console errors at unload, and Playwright reports requests as aborted when they actually arrived. Date of investigation: 2026-10-07 Reporter: a Maple customer, a React + Effect v4 single-page app (product details withheld) Folder layout and index: see APPENDIX C at the end of this file. How to read this report ----------------------- * All empirical claims come from runs of the self-contained project in ./repro against a mock ingest in headless Chromium. The raw output of every run is in ./evidence (one JSON per run, plus the server's JSONL request log). Each finding cites the evidence files and scenario names. * Each statement is labelled: [VERIFIED-EMPIRICAL] reproduced in ./repro; evidence file(s) cited [VERIFIED-CODE] follows directly from the quoted SDK source; not separately run [HYPOTHESIS] plausible but not proven here [DISPROVEN] we tested it and it was not true (kept so nobody re-investigates) * SDK line numbers refer to the installed npm package (node_modules/@maple-dev/effect-sdk/dist/*.mjs, version 0.8.2). The verbatim excerpts are in ./sdk-excerpts (file names are given inline). * "budget" = Chromium's per-document limit on the total body bytes of all keepalive fetches that are in flight at the same time (measured below: 65,536 bytes). * Throughout this report, "ingest" means the endpoint the SDK posts to. In our production setup that is a same-origin proxy (details in section 3). In the repro it is the mock server in ./repro/server.ts. ============================================================================== 1. TL;DR / SUMMARY OF FINDINGS ============================================================================== Severity is our judgement of user impact: how much telemetry is lost and how silently. ID Sev. Title Status ------ --------- ------------------------------------------------------------- -------------------- BUG-1 HIGH One oversized flush permanently stops trace (or log) export VERIFIED-EMPIRICAL for the rest of the page's life BUG-2 MEDIUM traces + logs + metrics keepalive POSTs are fired VERIFIED-EMPIRICAL concurrently and share one 64 KiB budget, so one signal is rejected although each fits on its own BUG-3 MEDIUM The unload flush is skipped while a signal is in its 60 s VERIFIED-EMPIRICAL cooldown, so the failed batch plus everything since is lost BUG-4 MEDIUM The unload flush is queued behind an in-flight flush VERIFIED-EMPIRICAL (makeSerializedFlush). With a slow ingest it only runs after teardown has rejected the in-flight POST, which sets the cooldown, so the unload flush is skipped and the tail is lost. One slow POST also stalls ALL later exports (traces AND logs) BUG-5 MEDIUM At unload, MapleFlush's OTLP POSTs and the session/replay VERIFIED-EMPIRICAL POSTs (meta "ended" row, final events) compete for the same 64 KiB budget with no shared accounting; whichever pagehide listener runs first wins BUG-6 MEDIUM The final replay chunk is never uploaded on unload (its VERIFIED-EMPIRICAL sequence number is consumed, then an async gzip runs, and the document dies before the POST). Affects MapleFlush.make AND Maple.layer BUG-7 LOW (but False "failed" reports at every unload: keepalive requests VERIFIED-EMPIRICAL expensive that the server answered 200 reject with "TypeError: Failed to triage) to fetch" in the unloading document. The SDK prints "traces/ logs/metrics flush failed; cooldown 60s", "... flush skipped (cooldown 60000ms remaining)", and "[maple] session replay metadata/events POST failed (dropping; will retry on next chunk)". Playwright reports net::ERR_ABORTED BUG-8 LOW Non-keepalive session posts (5 s events flush, "active" VERIFIED-EMPIRICAL metadata row, replay chunks) in flight during a navigation (abort + message) are aborted. The batch is dropped, and the console message VERIFIED-CODE (drop) claims "will retry on next chunk", which nothing does BUG-9 INFO / Maple.layer has no unload flush (documented). A small fix is VERIFIED-EMPIRICAL FEATURE possible with Effect's own OtlpExporter.Flusher + FetchHttpClient.RequestInit {keepalive:true}; we verified a user-land version works MISC LOW/INFO Smaller items: section 6 (10k buffer cap keeps the oldest and mixed, each labelled drops the newest silently; keepaliveFor() counts UTF-16 code units instead of bytes; transport never reads or cancels the response body; Effect console logs are duplicated into session events; "session replay" wording when replay is disabled; the SDK sets a "user-agent" header; multi-argument log bodies are sent as an OTLP arrayValue; ...) What this means for our production observations (details in section 7): * The pair "[MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch" followed immediately by "[MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)" around a reload (also for "logs") is reproduced deterministically (scenario K: 3/3 runs replay on, 3/3 replay off). It is printed by the OLD document after pagehide, for requests the server answered 200. In that pattern it is a false alarm (BUG-7). It only appears when something was still buffered at pagehide, which explains why it looks rare and random. The same message can also mean real loss (BUG-1/2/5), and the SDK cannot tell the two cases apart. * "a span that ended ~5 s before a reload never arrived while its earlier sibling did": reproduced by BUG-4 (one slow ingest round-trip, 7 s here, is enough; 3/3 runs). We cannot prove this was the production cause. It is our leading explanation because the alternatives would have printed console errors. * "[maple] session replay events/metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch" around navigations with both presets: mostly BUG-7 (data delivered; reproduced at every reload with pending session data). Real loss can come from BUG-8 (aborted non-keepalive posts) and BUG-5 (budget). * "Playwright reports keepalive requests as requestfailed net::ERR_ABORTED although the data arrived": reproduced for every keepalive request still in flight when its document is torn down (G, K, C-control). In steady state with no navigation Playwright reports them as finished. This is an observability artifact of unload, not a delivery failure (section 5). Newer SDK versions: 0.10.0 was published on 2026-10-03, after the version we run. We read its source and ran the main scenarios against it (section 8). It adds a shared keepalive budget for the session/replay POSTs and counts bytes correctly there. The OTLP flush path (BUG-1, -2, -3, -4) and the final replay chunk (BUG-6) look unchanged. See section 8 for the empirical results. ============================================================================== 2. ENVIRONMENT AND VERSIONS ============================================================================== Item Value ------------------------- ---------------------------------------------------------- SDK under test @maple-dev/effect-sdk 0.8.2 (npm), entry "@maple-dev/effect-sdk/client" Effect effect 4.0.0-rc.112 (the SDK's peer; also what our app uses) Also checked @maple-dev/effect-sdk 0.10.0 + effect 4.0.0 (section 8) Browser Chromium 143.0.7499.4 (Playwright build 1200) - default mode: chromium-headless-shell - cross-check: full Chromium in "new headless" (channel "chromium") UA: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36 Playwright 1.57.0 Bun 1.4.2 (bundler: Bun.build target=browser, ESM, code splitting) OS Linux 6.18 x86_64 (Amazon Linux 2023 container), 8 vCPU, 16 GB Not tested WebKit: Playwright's WebKit cannot launch on this host (missing system libraries; see evidence/webkit-firefox-launch.txt). Firefox: not installed. All browser results below are Chromium-only. Safari/Firefox keepalive limits and unload behaviour differ and should be checked separately. Network Everything on localhost: real network latency is NOT modelled except where the mock server injects it (latencyMs rules). On a real network, aborted requests are more likely to lose their body than on localhost. Exact environment record of each run: evidence/environment-*.json. ============================================================================== 3. HOW WE USE THE SDK (generic) ============================================================================== * React + Effect v4 single-page app. All browser telemetry is produced by Effect (Effect.withSpan, Effect.log*, a few metrics) running on a ManagedRuntime that is provided the SDK's layer. * Until recently: Maple.layer({ serviceName, environment, endpoint, ingestKey }). Now: MapleFlush.make({ autoFlushInterval: 1000, ingestKey: "~", // placeholder; see below endpoint: "/otlp", // same-origin ingest proxy serviceName, environment, }) Replay and session settings are left at their defaults (replay enabled, sampleRate 1, emitSessionMeta true). * The endpoint is a same-origin proxy. The browser posts to /otlp/v1/* and the proxy forwards to Maple ingest, adding the real ingest key on the server. The browser passes the placeholder ingestKey "~" only because the SDK switches itself off without a key. The proxy adds one extra network hop, so its latency varies; this matters for BUG-4. * We test with Playwright-driven Chromium against deployed environments, which is where we made the observations in section 7. * The repro page (repro/client/app.ts) mirrors this: same options, same placeholder key, endpoint = /otlp/ (the tag only lets the mock server attribute requests to a run), and an optional cross-origin endpoint. ============================================================================== 4. FINDINGS IN DETAIL ============================================================================== ------------------------------------------------------------------------------ 4.0 Ground truth: Chromium's fetch(keepalive) limit (scenario B-keepalive-probe) ------------------------------------------------------------------------------ We measured the browser behaviour first, independently of the SDK, because every finding below depends on it. The probe page (repro/public/probe.html + repro/client/probe.ts) has no Maple code on it. The mock sink can hold its response for N ms so that requests stay in flight. Every sub-test starts from a freshly loaded document. Evidence: evidence/B-keepalive-probe/probe-run0.json and probe-run1.json (identical results in both runs). Sub-test Result (Chromium 143) ------------------------------------------------------------- --------------------------------------- B1 single keepalive POST, nothing else in flight 1024, 49152, 65535, 65536 B: ok 65537, 70000, 131072 B: REJECTED (immediately, 2-7 ms, "TypeError: Failed to fetch") B1b same with content-type: application/json 65536 ok, 65537 REJECTED B2 concurrent, server holds responses 2 s: 2 x 40000 B 1st ok, 2nd REJECTED 2 x 30000 B both ok 30000 + 30000 + 10000 B 3rd REJECTED 4 x 20000 B 1st-3rd ok, 4th REJECTED B3 hold 60000 B for 2 s; 10000 B at +500 ms; 10000 B after held: ok; during: REJECTED; after: ok B3b 8 sequential 40000 B POSTs, each awaited: response body NOT read (what the SDK transport does) "ok X X X X X X X" response body read (await res.text()) all ok body not read, 50 ms gap between POSTs "ok ok X ok ok X ok ok" (run 0) B4 cross-origin with CORS preflight (application/json): 1000 B ok (keepalive + preflight works) 65537 B REJECTED 2 x 40000 B concurrent 2nd REJECTED B5 200000 B WITHOUT keepalive ok (no limit) B6 2-byte UTF-8 body ("é"): bytes / JS string length: 60000 / 30000 ok 65536 / 32768 ok 80000 / 40000 REJECTED 98304 / 49152 REJECTED B7 tab 1 holds 60000 B; then: tab 1, second 10000 B REJECTED tab 2 (same context), 60000 B ok tab 3 (other context), 60000 B ok B8 keepalive POSTs fired from a pagehide handler, then reload: 2 x 20000 2/2 arrived 2 x 40000 1/2 arrived 3 x 20000 3/3 arrived 4 x 20000 3/4 arrived 1 x 70000 0/1 arrived B9 previous document still has 60000 B in flight; new ok (the budget is per document) document sends 60000 B B10 50000 B keepalive in flight (held 4 s); pagehide fires a only the 50000 B one arrived; the 20000 B keepalive, then reload pagehide one was rejected Conclusions [VERIFIED-EMPIRICAL]: * The limit is 65,536 bytes of UTF-8 body, summed over all keepalive requests of ONE document that are in flight at the same time. A request that would cross it is rejected synchronously with the generic "TypeError: Failed to fetch". There is no distinct error type or message, and the request never reaches the network. * A request keeps its share of the budget until its response body has finished loading, not just until fetch() resolves. If the body is never read, the bytes are released a few ms to tens of ms later (B3b). * The budget is per document: other tabs/contexts are unaffected, and a new document after a navigation starts with a fresh budget even while the previous document's keepalive requests are still in flight (B7, B9). * Keepalive requests in flight from earlier (for example a periodic flush to a slow ingest) reduce what is left for the unload flush (B10). * Cross-origin keepalive with preflight works in Chromium 143 (B4). The "preflight + keepalive unsupported" concern is [DISPROVEN] for current Chromium. We did not check other browsers. ------------------------------------------------------------------------------ 4.1 BUG-1 (HIGH): one oversized flush permanently stops trace export for the rest of the page's life ------------------------------------------------------------------------------ Symptoms (exact console output, Chromium 143, from evidence/A-oversized-batch/*): [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-.js:71:27) <- replay engine's fetch wrapper at Object.post (http://localhost:4173/dist/app.js:...) <- keepaliveTransport.post ... This repeats every ~60 s for as long as the tab stays open. In between, the auto-flush timer prints this every autoFlushInterval: [MapleClientSDK] traces flush skipped (cooldown NNNNNms remaining) No span reaches ingest again, including tiny spans created long after the burst. Other signals (logs) keep flowing, so dashboards look partly alive. Root cause [VERIFIED-CODE], see sdk-excerpts/03-mapleflush-make-client.txt, sdk-excerpts/01-flush-core-cooldown-serialization.txt and sdk-excerpts/02-buffers-cap-restore.txt: (a) The client transport ALWAYS uses keepalive, whatever the body size (client-BYIixzTb.mjs L1989-1998): /** `fetch(keepalive)` transport — see file header for why not `sendBeacon`. */ const keepaliveTransport = { post: async (url, headers, body) => { const res = await fetch(url, { method: "POST", headers, body: JSON.stringify(body), keepalive: true }); if (!res.ok) throw new Error(`OTLP ${res.status} ${res.statusText}`); } }; It is used for EVERY flush, including the periodic ones in steady state (L2070-2096), not only at unload. (b) flushSignal posts the WHOLE drained buffer as ONE request, with no chunking and no size check (version-B225m7RW.mjs L79-97): const batch = buffer.drain(); if (batch.length === 0) return; const posted = await Effect.runPromise(Effect.result(Effect.tryPromise({ try: () => transport.post(url, headers, body(batch)), ... }))); if (Result.isFailure(posted)) { buffer.restore(batch); state.disabledUntil = Date.now() + COOLDOWN_MS; // 6e4 console.error(`${logPrefix} ${signal} flush failed; cooldown 60s:`, posted.failure); } (c) restore() puts the failed batch back IN FRONT of everything buffered since (version-B225m7RW.mjs L483-486, spans; L311-314, logs): restore: (items) => { if (disabled || items.length === 0) return; buffer = [...items, ...buffer].slice(0, MAX_BUFFER); }, The next attempt (60 s later) is therefore at least as large as the one that failed, plus 60 s of new spans. Once a batch exceeds 65,536 bytes it can never succeed again; the failing batch only grows. (d) At MAX_BUFFER = 1e4 (L422/L443) new spans are dropped silently in exportFn (`if (buffer.length >= MAX_BUFFER) return;`), and restore's slice(0, MAX) keeps the OLDEST items and drops the newest. Neither prints a warning or exposes a counter. [VERIFIED-CODE; not run to 10k] (e) The README states "keepalive carries the header and still survives unload for small bodies" (sdk-excerpts/11-readme.txt, README L277), but nothing keeps the bodies small. Empirical evidence [VERIFIED-EMPIRICAL]: * Size per span and the exact threshold (scenario A0-threshold, autoFlushInterval false, one manual flush; evidence/A0-threshold/n*-run0.json). Body sizes are from Playwright's request.postDataBuffer(). spans in one flush traces POST body result 100 40,252 B delivered 150 60,052 B delivered 160 64,012 B delivered 163 65,200 B delivered 164 65,596 B REJECTED ("traces flush failed; cooldown 60s") 165 65,992 B REJECTED 170 67,972 B REJECTED 200 79,852 B REJECTED That is ~396 B per span with no custom attributes (it already includes session.id and the resource), plus ~650 B of envelope. With the default autoFlushInterval 5000, a page that ends more than ~163 plain spans in 5 s, or fewer spans with larger attributes (error stack traces, URLs, payload summaries), hits this. * Long runs (scenario A-oversized-batch; each run in its own browser process; after the burst, one small "tick" span every 2 s and one log every 10 s; observed for 130 s): variant runs spans created delivered "flush failed" at (ms) burst 200 x 500 B, interval 5000 2/2 265 0 ~5400, ~70400 burst 200 x 500 B, interval 1000 2/2 265 0 ~2240, ~63240, ~124240 steady 40 spans/s for 10 s, int. 5000 2/2 438 152 (the ~10500, ~75500 first batch only; 0/38 later ticks) steady 30 spans/s for 10 s, int. 5000 2/2 338 338 none (~150 spans ≈ 60 KB per batch, under the limit) In every reproducing run, logs (created alongside) were all delivered ("logs delivered 13" / "8"). Only the traces signal dies. Evidence: evidence/A-oversized-batch/*.json, evidence/runner-console-A.txt. * Flakiness: none. 100% deterministic for a given payload size. Per-variant verdict counts: burst200x500B-interval1000 -> [REPRODUCED (no span ever delivered): 2/2]; burst200x500B-interval5000(default) -> [REPRODUCED (no span ever delivered): 2/2]; steady30spansPerSec-10s-interval5000(default) -> [NOT REPRODUCED: 2/2]; steady40spansPerSec-10s-interval5000(default) -> [PARTIAL LOSS: 2/2] Expected: telemetry keeps flowing after a burst. At worst the burst itself is dropped or delayed, with a visible warning. Actual: trace export from that tab is dead until the page is reloaded. Every later span is lost. A console error appears once per minute (easy to miss in production, where nobody reads the console). Reproduction: cd repro && bun install && bun run build.ts bun run run-scenarios.ts A0 A # A takes ~2.5 min (runs in parallel) or manually: bun run start, open http://localhost:4173/?tag=manual&variant=flush, then in DevTools: await T.spans(200, 500, "burst"); then T.span("later") every few seconds; watch repro/server-requests.jsonl (no /v1/traces POST ever arrives). Suggested fixes: 1. Split every flush into chunks below a per-request byte limit (for example <= 48 KiB including the envelope), computed from the serialized size in UTF-8 bytes. 2. Do not use keepalive for steady-state (visible-page) flushes. keepalive only matters while the document unloads. Use keepalive only from the pagehide/visibilitychange(hidden) path, and there respect a page-wide budget shared with the replay/session posts (see BUG-5). 3. If a keepalive POST is rejected (TypeError before any response), retry immediately WITHOUT keepalive and/or with smaller chunks, instead of restoring and cooling down. TypeError from a keepalive request is almost always "too big", not "collector broken". 4. Do not restore unboundedly. Cap what is restored, drop or split oversized batches, and keep the newest data rather than the oldest (or make this configurable). 5. Make drops observable: an internal counter/metric (dropped spans/logs by reason), a one-time console warning when MAX_BUFFER is hit, and maybe an onDrop callback. Workaround available to users today [VERIFIED-EMPIRICAL, scenario W]: * Install a window.fetch wrapper BEFORE MapleFlush.make() that turns keepalive:true into keepalive:false when the UTF-8 body is larger than ~60 KiB (repro/client/app.ts, "?shim=1"). Steady-state export then recovers; only oversized flushes during unload remain at risk. without shim: [LOSS (burst 0/200, ticks 0/5): 2/2] run 0: LOSS (burst 0/200, ticks 0/5) | shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 run 1: LOSS (burst 0/200, ticks 0/5) | shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 with shim: [all delivered: 2/2] run 0: all delivered | shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 run 1: all delivered | shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 Evidence: evidence/W-workaround-keepalive-downgrade-shim/*.json * Keep autoFlushInterval small (we use 1000) and keep span attributes small. This lowers the chance but cannot rule it out. ------------------------------------------------------------------------------ 4.2 BUG-2 (MEDIUM): traces, logs and metrics are POSTed concurrently, so they compete for the same 64 KiB ------------------------------------------------------------------------------ Symptoms: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch (or "traces"/"metrics", depending on which request is issued last) when the SUM of the three bodies exceeds 65,536 B even though each fits on its own. That signal then enters BUG-1/BUG-3 territory (restore + 60 s cooldown). Root cause [VERIFIED-CODE] (version-B225m7RW.mjs L132-173, runFlush): await Promise.all([ flushSignal({... traces ...}), flushSignal({... logs ...}), flushSignal({... metrics ...}) ]); Each flushSignal issues its own keepalive fetch, so all three are in flight together. There is no accounting across them, and none with the replay engine's keepalive posts. Empirical evidence [VERIFIED-EMPIRICAL] (scenario B-sdk-concurrent-signals; 40 spans x 500 B attribute + 30 log lines x 800 B + 1 counter update; replay on): trigger runs request bodies issued (Playwright) result manual T.flush() 3/3 traces 38,472 B, logs 32,135 B, metrics 931 spans 40/40, logs 0/30 "logs flush failed; cooldown 60s" reload (pagehide flush) 3/3 same + session meta 2,524 B, events spans 40/40, logs 0/30, 40,248 B session events batch lost, metrics delivered Evidence: evidence/B-sdk-concurrent-signals/manual-flush-run{0,1,2}.json, reload-run{0,1,2}.json. Flakiness: none (6/6). In the reload case the console shows "metrics flush failed" and "traces flush failed" although both arrived (the BUG-7 artifact), while the lost logs and events print nothing reliable. During unload the console is not a usable signal. Expected: three small requests all succeed. Actual: whichever request is issued last and crosses 65,536 B is rejected. Here that is the logs POST. Suggested fixes: send the signals sequentially when using keepalive, or combine them under one budget/byte accounting (and share it with the replay posts, see BUG-5); apply BUG-1 fixes 1-3. ------------------------------------------------------------------------------ 4.3 BUG-3 (MEDIUM): the unload flush is skipped during the 60 s cooldown ------------------------------------------------------------------------------ Symptoms (exact, from evidence/C-unload-during-cooldown/inject503-then-reload-run0.json): [MapleClientSDK] traces flush failed; cooldown 60s: Error: OTLP 503 Service Unavailable [MapleClientSDK] traces flush skipped (cooldown 59xxxms remaining) <- auto-flush ticks ... reload ... [MapleClientSDK] traces flush skipped (cooldown 5xxxxms remaining) <- pagehide flush [MapleClientSDK] traces flush skipped (cooldown 5xxxxms remaining) <- visibilitychange flush Root cause [VERIFIED-CODE] (version-B225m7RW.mjs L79-84): if (state.disabledUntil && Date.now() < state.disabledUntil) { console.warn(`${logPrefix} ${signal} flush skipped (cooldown ...ms remaining)`); return; } The pagehide/visibilitychange handlers (client-BYIixzTb.mjs L2141-2151) call the same flush() as the timer, so the cooldown applies to the LAST chance to send. Everything buffered goes down with the page: the restored failed batch plus every span created during the cooldown. Empirical evidence [VERIFIED-EMPIRICAL] (autoFlushInterval 1000; the mock returns 503 for exactly ONE traces POST; span "C-first" is created, its POST gets the 503, then 1.5 s later "C-before-unload" is created and the page is unloaded 300 ms after that): unload method runs C-first C-before-unload page.reload() 3/3 LOST LOST navigate to another same-origin page 3/3 LOST LOST page.close({runBeforeUnload:true}) 3/3 LOST LOST control: no injected failure, reload 3/3 delivered delivered One transient 5xx (or one network blip, or one BUG-1/BUG-2 rejection) within the last 60 s of a page's life loses that page's tail. With autoFlushInterval 1000, a single failure skips 60 flushes. Evidence: evidence/C-unload-during-cooldown/*.json. Flakiness: none (9/9). Expected: the unload flush always tries to send, because there is no later chance. Actual: it is skipped; the data is lost and only a console.warn records it. Suggested fixes: never apply the cooldown to the unload path (pagehide / visibilitychange hidden / dispose). Better, replace the fixed 60 s cooldown with exponential backoff that resets on success, and distinguish "collector rejected (4xx/5xx)" from "keepalive rejected (TypeError, no response)". ------------------------------------------------------------------------------ 4.4 BUG-4 (MEDIUM): the unload flush is serialized behind an in-flight flush; one slow request stalls all signals ------------------------------------------------------------------------------ Root cause [VERIFIED-CODE] (version-B225m7RW.mjs L115-123): const makeSerializedFlush = (run) => { let tail = Promise.resolve(); return (...args) => { const next = tail.then(() => run(...args), () => run(...args)); tail = next.catch(() => void 0); return next; }; }; All flush() calls (timer, pagehide, visibilitychange, manual) go through this chain (client-BYIixzTb.mjs L2070). If a flush is waiting on a slow POST when pagehide fires, the pagehide flush can only start after that POST settles. The keepalive transport has no timeout either, so one hung request stalls the chain indefinitely, and setInterval keeps adding a closure per tick. What actually happens in Chromium [VERIFIED-EMPIRICAL] (page journal of all 12 D runs on 0.8.2 and all 8 on 0.10.0): 1. The slow POST is still in flight when pagehide fires. The pagehide and visibilitychange flushes, plus every timer flush queued while waiting, sit in the chain. 2. During teardown Chromium rejects the in-flight POST's promise with "TypeError: Failed to fetch". The request itself continues: the server later answers 200 and the early span IS delivered. flushSignal treats the rejection as a failure: restore + 60 s cooldown + "[MapleClientSDK] traces flush failed; cooldown 60s". 3. The chain advances, and every queued flush, including the pagehide one, runs within the same millisecond and is SKIPPED: "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)" (twice in the 4 s variants, 7 times in the 7 s variant). So the tail is lost through the combination of serialization (BUG-4), the teardown false failure (BUG-7) and the cooldown at unload (BUG-3). [HYPOTHESIS] Removing only the cooldown on that path might let the queued flushes send in time, since JS was evidently still running. Removing the serialization for the unload flush is the robust fix. runFlush awaits traces+logs+metrics together (Promise.all), so a slow traces POST also blocks the logs and metrics exports. Empirical evidence [VERIFIED-EMPIRICAL]: * Scenario D-pagehide-behind-inflight-flush. The mock holds only the FIRST traces POST for N ms (models one slow round-trip through a proxy). The test waits until that POST has reached the server, creates "D-tail", and reloads 150 ms later: (a) first POST held 4000 ms, autoFlushInterval 1000, D-tail ends 150 ms before reload [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5125ms); D-tail delivered=false; reload at t=1401ms run 1: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5107ms); D-tail delivered=false; reload at t=1382ms run 2: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5108ms); D-tail delivered=false; reload at t=1375ms (b) first POST held 4000 ms, autoFlushInterval 5000, D-tail ends 150 ms before reload [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9124ms); D-tail delivered=false; reload at t=5383ms run 1: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9115ms); D-tail delivered=false; reload at t=5367ms run 2: REPRODUCED (tail span lost) | first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9114ms); D-tail delivered=false; reload at t=5373ms (c) first POST held 7000 ms, autoFlushInterval 1000, D-tail ends 5000 ms (!) before reload [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms run 1: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms run 2: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms (d) control: no latency, autoFlushInterval 1000 [CONTROL OK: 3/3] run 0: CONTROL OK | first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1405ms); reload at t=1393ms run 1: CONTROL OK | first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1412ms); reload at t=1406ms run 2: CONTROL OK | first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1123ms); D-tail delivered=true (received t=1401ms); reload at t=1394ms In variant (c) the span ended 5 s before the reload and was still lost, because the 5 queued timer flushes and the pagehide flush were all waiting behind the one slow POST. This matches our production observation (section 7, obs. 1b). * Scenario D-hang-head-of-line-blocking (first traces POST held 15 s; one span and one Effect log created every second; no navigation): [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: REPRODUCED (exports stalled behind one slow request) | first traces POST held 15000 ms (from t=1153ms): span export delay max 14990 ms / min 909 ms; log export delay max 14991 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17143:1sp/1157B 18143:1sp/1157B 19144:1sp/1157B] run 1: REPRODUCED (exports stalled behind one slow request) | first traces POST held 15000 ms (from t=1137ms): span export delay max 14977 ms / min 905 ms; log export delay max 14977 ms (logs endpoint itself is fast); traces POSTs at server t=[1116:1sp/1160B 16120:15sp/6677B 17115:1sp/1157B 18115:1sp/1157B 19115:1sp/1157B] * Probabilistic version (scenario Dr-random-reload-latency: a span every 100 ms, reload at a random time 2-6 s in, every traces POST delayed by the given latency, 10 runs each): latency150-interval1000 runs with loss 1/10; spans lost 1/426; runs where a traces POST was in flight at reload: 1/10 latency 150 ms, interval 1000: created 40, lost 1 (created 100 ms before reload); trace flush in flight at reload: true latency600-interval1000 runs with loss 3/10; spans lost 11/423; runs where a traces POST was in flight at reload: 5/10 latency 600 ms, interval 1000: created 54, lost 5 (created 513,411,305,202,100 ms before reload); trace flush in flight at reload: true latency 600 ms, interval 1000: created 22, lost 2 (created 202,100 ms before reload); trace flush in flight at reload: true latency 600 ms, interval 1000: created 34, lost 4 (created 406,304,202,100 ms before reload); trace flush in flight at reload: true latency600-interval5000 runs with loss 0/10; spans lost 0/370; runs where a traces POST was in flight at reload: 2/10 control-latency0-interval1000 runs with loss 0/10; spans lost 0/415; runs where a traces POST was in flight at reload: 0/10 Loss happened only in runs where a traces POST was in flight at the reload AND spans had ended after that POST started. The two latency600-interval5000 runs flagged "in flight" lost nothing because the in-flight POST already carried every span (the reload came right after the 5 s tick; see their serverRecords). With ~150 ms latency and autoFlushInterval 1000 we saw 1 of 10 reloads lose the last ~100 ms of spans. With 600 ms, 3 of 10 lost up to the last ~0.5 s. Production latency through a proxy is often in this range. Evidence: evidence/D-pagehide-behind-inflight-flush/*.json, evidence/D-hang-head-of-line-blocking/*.json, evidence/Dr-random-reload-latency/*.json. Expected: the unload flush sends whatever is buffered at pagehide, whatever any earlier request is doing. An unresponsive request does not block other signals. Actual: the unload flush waits for the earlier request and the tail is lost; a slow traces POST also delays logs. Suggested fixes: * The unload flush should bypass the serialization. It can drain and send immediately, concurrently with any in-flight flush. Draining is synchronous, so there is no double-send risk. To keep the unload flush from being rejected, reserve budget for it (see BUG-5). * Add a timeout (AbortSignal.timeout) to steady-state (non-keepalive) POSTs. Restore and retry on timeout. * Do not let one signal's slow POST hold back the others (flush signals independently). * Collapse queued timer flushes: if a flush is already queued, a new tick should not enqueue another one. (0.10.0 adds coalesceSameArguments, but only for Workers; section 8.) Workaround available today: make the ingest proxy acknowledge immediately (for example 202 before forwarding upstream) so client round-trips stay short. Not tested in this repro, but it follows from the mechanism. ------------------------------------------------------------------------------ 4.5 BUG-5 (MEDIUM): MapleFlush OTLP posts and session/replay unload posts share one budget with no shared accounting ------------------------------------------------------------------------------ On pagehide, two independent parts of the SDK fire keepalive requests: (1) MapleFlush.make's own pagehide/visibilitychange handlers -> flush() -> traces/logs/metrics, keepalive:true, any size (client-BYIixzTb.mjs L2141-2151). (2) The session lifecycle's pagehide handler -> endRun({flush:true, keepalive:true}) -> metadata "ended" row + final session-events batch (+ the final replay chunk, see BUG-6). Each uses keepalive only if keepaliveFor(true, body.length) (client-BYIixzTb.mjs L603-607, L1531-1573, L1599-1610; replay-DBMTP4cB.mjs L10762-10771). The SDK's own comment states the constraint (client-BYIixzTb.mjs L594-603): * The Fetch spec caps the *combined* inflight keepalive body at 64 KiB, and on * the way out we issue up to three of these at once (metadata row, final events * batch, last replay chunk). ... const MAX_KEEPALIVE_BYTES = 49152; function keepaliveFor(requested, bytes) { return requested && bytes <= MAX_KEEPALIVE_BYTES; } but the check is per request: three 48 KiB bodies pass it and total 144 KiB. It also ignores the OTLP posts in (1) and anything still in flight. It also measures body.length (UTF-16 code units) where Chromium counts UTF-8 bytes (B6). The order in which the listeners fire decides who gets the budget: * replay enabled (default): the replay engine loads through a dynamic import, so its pagehide listener is registered AFTER MapleFlush's. OTLP goes first, and the session rows lose. * replay disabled / not sampled: startClientSession() starts the metadata-only lifecycle synchronously inside MapleFlush.make(), BEFORE MapleFlush registers its own pagehide listener. The session rows go first, and the TRACES lose. (Verified by request order in evidence: E2 vs E3, and K replay-on vs replay-off.) Empirical evidence [VERIFIED-EMPIRICAL] (scenario E-combined-unload-budget; the page loads, the first replay chunk is shipped, then a payload is created and the page is reloaded ~300 ms later): E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 3/3] run 0: LOST: meta(ended), final sessionEvents | spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) run 1: LOST: meta(ended), final sessionEvents | spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) run 2: LOST: meta(ended), final sessionEvents | spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) E3-replay-disabled-spans150(~60kB) [LOST: traces: 3/3] run 0: LOST: traces | spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) run 1: LOST: traces | spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) run 2: LOST: traces | spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) | spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) Also scenario B-sdk-concurrent-signals/reload (4.2): sessionEvents 40,248 B and logs 32,135 B were rejected in favour of traces 38,472 B + metrics + meta (3/3). Evidence: evidence/E-combined-unload-budget/*.json. Expected: the SDK's unload writes are coordinated so the important ones (spans, "ended" row) fit, and anything that does not fit falls back to a normal request. Actual: the first to fire wins; the others are rejected and silently lost (the console says "failed" both for these and for successfully delivered requests; see BUG-7). Suggested fixes: one page-wide keepalive byte budget (on globalThis, shared by both SDK copies) that every keepalive request reserves from and releases when the response body ends (0.10.0 does this for session posts only; the OTLP transport must use it too). Measure bytes with TextEncoder. At unload, prioritise (spans > ended row > events > replay chunk) and send what does not fit without keepalive. ------------------------------------------------------------------------------ 4.6 BUG-6 (MEDIUM): the final replay chunk is never uploaded on unload ------------------------------------------------------------------------------ Root cause [VERIFIED-CODE] (replay-DBMTP4cB.mjs L10627-10653): const flush = async (keepalive = false) => { if (stopped || exhausted || parts.length === 0) return; const body = `[${parts.join(",")}]`; ... const seq = nextChunkSeq(); // persisted to sessionStorage immediately resetBuffer(); let gzipped; try { gzipped = await gzip(new TextEncoder().encode(body)); } // CompressionStream: async ... if (await postSessionBlob(config, {...}, gzipped, keepalive) === "exhausted" ... On pagehide (onSuspend, L10762-10771) this runs with keepalive=true, but the POST is only issued after the CompressionStream round-trip finishes. The document is torn down before that, so the request is never created. The chunk sequence number has already been consumed and persisted, so the backend sees a permanent gap. The last seconds before every navigation/close (often the most interesting part of a replay) are never recorded. Empirical evidence [VERIFIED-EMPIRICAL] (scenario E: after the first chunk (seq 0) has been uploaded, mutate the DOM, reload, wait for the new page's first chunk): E1-spans45-plus-60kB-DOM-mutation: run 0: replay chunkSeqs at server [0,2]; run 1: replay chunkSeqs at server [0,2]; run 2: replay chunkSeqs at server [0,2] E2-spans150(~60kB)-plus-30-console-lines: run 0: replay chunkSeqs at server [0,1]; run 1: replay chunkSeqs at server [0,1]; run 2: replay chunkSeqs at server [0,1] E4-no-spans-small-DOM-mutation: run 0: replay chunkSeqs at server [0,2]; run 1: replay chunkSeqs at server [0,2]; run 2: replay chunkSeqs at server [0,2] E5-Maple.layer-small-DOM-mutation: run 0: replay chunkSeqs at server [0,2]; run 1: replay chunkSeqs at server [0,2]; run 2: replay chunkSeqs at server [0,2] Playwright saw no request at all for the old page's final blob, so it is not a budget rejection: the request is never created. Expected: the final chunk is sent (keepalive if it fits, otherwise a normal POST). Actual: it is silently never sent, and the chunk sequence has a hole. Suggested fixes: compress incrementally while recording, so a ready-to-send gzip buffer exists at pagehide; or on unload send the chunk uncompressed (or with a synchronous compressor) inside the pagehide task; or keep the last chunk in sessionStorage/IndexedDB and upload it from the next page load of the same session. Claim the sequence number only when the POST is actually issued. ------------------------------------------------------------------------------ 4.7 BUG-7 (LOW severity, HIGH triage cost): false failure reports at every unload ------------------------------------------------------------------------------ Symptoms (exact, scenario K: span + Effect log, reload 150 ms later, autoFlushInterval 1000; evidence/K-reload-150ms-after-action/*): [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining) and Playwright: requestfailed net::ERR_ABORTED for /v1/traces, /v1/logs, /v1/sessionReplays/meta, /v1/sessionEvents. Meanwhile the mock ingest received all four requests and answered 200 (server timestamps 2-10 ms BEFORE the browser reported the failure). Which document and which flush? [VERIFIED-EMPIRICAL] The page mirrors every [MapleClientSDK]/[maple] console message, synchronously, into a localStorage journal tagged with a per-document id and a flag "pagehide already fired". Every one of these messages carries the OLD document's id, afterPagehide=true and visibilityState "hidden" (example: evidence/K-reload-150ms-after-action/ replay-on(default)-run0.json -> pageJournal). The order is: pagehide -> visibilitychange:hidden -> "... flush failed" -> "... flush skipped" -> new document script-start. So: * "flush failed" belongs to the pagehide flush. Its keepalive POSTs were issued, delivered and answered 200, but the fetch() promises of the unloading document reject with TypeError: Failed to fetch, because the document can no longer receive the responses. The keepalive request itself completes in the browser process. * "flush skipped (cooldown 60000ms remaining)" is the visibilitychange(hidden) flush, which ran within the same millisecond and found the cooldown that the previous line had just set. The value 60000/59999 always identifies this case. * Nothing fails in the NEW document. * The messages only appear if something was buffered at pagehide (otherwise flushSignal returns before fetch), which explains why they look intermittent. Results (scenario K): replay on (default): [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] | reload at t=1795; requests issued around the reload (Playwright): traces(1165B)@1807 logs(1014B)@1807 sessionReplays/meta(1164B)@1807 sessionEvents(1021B)@1807 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1801 logs@1802 sessionReplays/meta@1802 sessionEvents@1804 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1808 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1808 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1808 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1808 run 1: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] | reload at t=1810; requests issued around the reload (Playwright): traces(1165B)@1824 logs(1014B)@1824 sessionReplays/meta(1164B)@1824 sessionEvents(1021B)@1824 sessionReplays/meta(1060B)@1891; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1817 sessionReplays/meta@1818 logs@1818 sessionEvents@1820 sessionReplays/meta@1890; console: [MapleClientSDK] logs flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1825 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1825 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1825 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1825 run 2: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] | reload at t=1803; requests issued around the reload (Playwright): traces(1165B)@1815 logs(1014B)@1816 sessionReplays/meta(1164B)@1816 sessionEvents(1021B)@1816 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1809 logs@1810 sessionReplays/meta@1813 sessionEvents@1814 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1816 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1816 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1816 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1816 replay off: [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] | reload at t=1770; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1781 sessionEvents(499B)@1781 traces(1165B)@1782 logs(1014B)@1782 sessionReplays/meta(1070B)@1814; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1777 traces@1778 sessionEvents@1779 logs@1781 sessionReplays/meta@1811; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1787 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1787 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1788 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1782 run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] | reload at t=1771; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1783 sessionEvents(499B)@1783 traces(1165B)@1784 logs(1014B)@1784 sessionReplays/meta(1070B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1778 sessionEvents@1779 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1785 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1785 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1785 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 run 2: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] | reload at t=1769; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1780 sessionEvents(499B)@1780 traces(1165B)@1781 logs(1014B)@1781 sessionReplays/meta(1070B)@1813; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1776 traces@1777 sessionEvents@1778 logs@1780 sessionReplays/meta@1810; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1782 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1782 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1782 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1781 The same pattern appears in every control run that reloads with pending data (C-control 3/3, G-with-final-reload, E-*). Why it matters: the SDK cannot tell "rejected by the budget, data lost" (BUG-1/2/5) from "delivered, document gone" (this item). Both produce the same TypeError and the same console line. Users chasing real loss get flooded with false alarms at every navigation; users seeing false alarms learn to ignore the line that also signals real loss. The "dropping; will retry on next chunk" wording is also wrong (BUG-8). Suggested fixes: record the phase (set a flag on pagehide). For requests issued during unload, do not log, do not cool down, do not restore (the page is going away). Optionally log once at debug level: "N bytes handed to the browser at unload". Classify errors: a TypeError raised synchronously at fetch() time is a budget rejection (real loss); a TypeError after unload is not actionable. ------------------------------------------------------------------------------ 4.8 BUG-8 (LOW): non-keepalive session posts are aborted by navigation and dropped; the console message promises a retry that never happens ------------------------------------------------------------------------------ Root cause [VERIFIED-CODE]: * The session event sink flushes every 5 s with keepalive=false (client-BYIixzTb.mjs L1165-1171, L1198). The lifecycle posts its "active" row at start and its heartbeat with keepalive=false (L1512-1530). Replay chunks are flushed every 5 s or at 100 kB with keepalive=false (replay-DBMTP4cB.mjs L10681, L10689-10696). These are all aborted if a navigation happens while they are in flight. * On failure the batch has already been taken out of the buffer (`const batch = buffer; buffer = []`) and is not restored. warnDropped (L588-593) then prints "(dropping; will retry on next chunk)". Nothing retries the dropped batch; only future data gets sent. The message is also throttled to once per 30 s per kind, so repeated losses are under-reported. Empirical evidence [VERIFIED-EMPIRICAL] (scenarios F and Fr; evidence/F-*/, evidence/Fr-*/). F "periodic-events": a console line is created; the mock holds the 5 s periodic (non-keepalive) sessionEvents POST for 3 s; the page reloads while that POST is in flight. F "active-meta": the mock holds every sessionReplays/meta POST for 3 s; the page reloads ~300 ms after load while the new page's "active" row is in flight. Fr: 300 ms latency on every ingest path, 8 reloads at random 0.5-7 s intervals. Counts come from the per-document page journal. Playwright's console capture showed the same lines, but during unload it is not guaranteed to. F-replay-posts-aborted-by-navigation / flush-active-meta-POST-in-flight run 0: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 2, 'replay metadata POST (after pagehide)': 2} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 4, 'sessionEvents[net::ERR_ABORTED]': 2} server POSTs answered (path,status): {('sessionReplays/meta', 200): 5, ('sessionEvents', 200): 2} evidence: evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run0.json F-replay-posts-aborted-by-navigation / flush-active-meta-POST-in-flight run 1: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 2, 'replay metadata POST (after pagehide)': 2} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 4, 'sessionEvents[net::ERR_ABORTED]': 2} server POSTs answered (path,status): {('sessionReplays/meta', 200): 5, ('sessionEvents', 200): 2} evidence: evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / flush-periodic-events-POST-in-flight run 0: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 1, 'replay events POST (after pagehide)': 1} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 1, 'sessionEvents[net::ERR_ABORTED]': 1} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1, ('sessionReplays/blob', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run0.json F-replay-posts-aborted-by-navigation / flush-periodic-events-POST-in-flight run 1: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 1, 'replay events POST (after pagehide)': 1} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 1, 'sessionEvents[net::ERR_ABORTED]': 1} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1, ('sessionReplays/blob', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / layer-active-meta-POST-in-flight run 0: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 1, 'replay metadata POST (after pagehide)': 1} Playwright requestfailed: {'sessionEvents[net::ERR_ABORTED]': 1, 'sessionReplays/meta[net::ERR_ABORTED]': 2} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run0.json F-replay-posts-aborted-by-navigation / layer-active-meta-POST-in-flight run 1: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 1, 'replay metadata POST (after pagehide)': 1} Playwright requestfailed: {'sessionEvents[net::ERR_ABORTED]': 1, 'sessionReplays/meta[net::ERR_ABORTED]': 2} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / layer-periodic-events-POST-in-flight run 0: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 1, 'replay events POST (after pagehide)': 1} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 1, 'sessionEvents[net::ERR_ABORTED]': 1} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1, ('sessionReplays/blob', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run0.json F-replay-posts-aborted-by-navigation / layer-periodic-events-POST-in-flight run 1: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 1, 'replay events POST (after pagehide)': 1} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 1, 'sessionEvents[net::ERR_ABORTED]': 1} server POSTs answered (path,status): {('sessionReplays/meta', 200): 3, ('sessionEvents', 200): 1, ('sessionReplays/blob', 200): 1} evidence: evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run1.json Fr-random-reloads / flush-8-random-reloads-latency300 run 0: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 7, 'replay metadata POST (after pagehide)': 8, 'sdk traces flush (after pagehide)': 2} Playwright requestfailed: {'sessionEvents[net::ERR_ABORTED]': 7, 'traces[net::ERR_ABORTED]': 1, 'sessionReplays/meta[net::ERR_ABORTED]': 8} server POSTs answered (path,status): {('sessionReplays/meta', 200): 17, ('traces', 200): 8, ('sessionEvents', 200): 8, ('sessionReplays/blob', 200): 1} evidence: evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run0.json Fr-random-reloads / flush-8-random-reloads-latency300 run 1: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 8, 'replay events POST (after pagehide)': 5, 'replay blob PUT (after pagehide)': 1, 'sdk traces flush (after pagehide)': 2} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 8, 'sessionEvents[net::ERR_ABORTED]': 5, 'sessionReplays/blob[net::ERR_ABORTED]': 1, 'traces[net::ERR_ABORTED]': 1} server POSTs answered (path,status): {('sessionReplays/meta', 200): 17, ('traces', 200): 8, ('sessionEvents', 200): 8, ('sessionReplays/blob', 200): 4} evidence: evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run1.json Fr-random-reloads / layer-8-random-reloads-latency300 run 0: journal (SDK console lines, per document): {'replay events POST (after pagehide)': 7, 'replay metadata POST (after pagehide)': 8} Playwright requestfailed: {'sessionEvents[net::ERR_ABORTED]': 7, 'sessionReplays/meta[net::ERR_ABORTED]': 8} server POSTs answered (path,status): {('sessionReplays/meta', 200): 17, ('sessionEvents', 200): 8, ('traces', 200): 1, ('sessionReplays/blob', 200): 1} evidence: evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run0.json Fr-random-reloads / layer-8-random-reloads-latency300 run 1: journal (SDK console lines, per document): {'replay metadata POST (after pagehide)': 8, 'replay events POST (after pagehide)': 6} Playwright requestfailed: {'sessionReplays/meta[net::ERR_ABORTED]': 8, 'sessionEvents[net::ERR_ABORTED]': 6} server POSTs answered (path,status): {('sessionReplays/meta', 200): 17, ('sessionEvents', 200): 8, ('traces', 200): 2, ('sessionReplays/blob', 200): 2} evidence: evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run1.json Notes on these results: * In every F run the held, in-flight non-keepalive POST (the periodic sessionEvents POST, or the new page's "active" meta row) shows up in Playwright as requestfailed net::ERR_ABORTED, and the page prints the matching "[maple] session replay events/metadata POST failed (dropping; ...)" line, with both presets. Every run also has a second line from the keepalive "ended" row, which was delivered (BUG-7). * The server answered 200 for every one of these requests, because on localhost the body had already arrived before the abort. On a real network an abort during upload loses the batch, and the SDK never re-sends it (code above). * (Harness note: the first run of the Maple.layer "periodic-events" variant did not meet its precondition. Maple.layer starts the replay engine later, so the console lines were logged before the console capture existed. The scenario now waits for the first "active" row; the runs shown were re-run with that fix: evidence/runner-console-F-rerun.txt.) * In the Fr runs with the MapleFlush variant, each run also shows one "[MapleClientSDK] traces flush failed" + "skipped (cooldown 60000ms remaining)" pair after pagehide (BUG-7). Caveat: on localhost the mock server had usually already read the request body before the abort, so the data arrived anyway. On a real network, an abort during upload loses it. What we verified is the abort, the message and the code path that drops; we did not measure the loss rate on a real network. Suggested fixes: restore the batch on failure (bounded) and retry; use keepalive (within the shared budget) for posts that start after pagehide or while hidden; fix the message wording; count drops. ------------------------------------------------------------------------------ 4.9 BUG-9 / feature gap (INFO): Maple.layer has no unload flush (documented) ------------------------------------------------------------------------------ Documented in the README ("Manual flush", sdk-excerpts/11-readme.txt, L237). Included for completeness and because a fix is cheap. Mechanism [VERIFIED-CODE]: Maple.layer = Effect Otlp.layerJson with 5 s export intervals (client-BYIixzTb.mjs L2286-2330). Effect's exporter (effect OtlpExporter.js, sdk-excerpts/10-effect-otlp-exporter.txt) also DROPS the batch on failure and disables export for 60 s (`buffer = []; disabledUntil = now + 60_000`). Its fetches are not keepalive, so an export in flight at navigation is aborted and its batch lost. Empirical [VERIFIED-EMPIRICAL] (scenario H; span "H-early" created 7.5 s before the reload, "H-tail" 1 s before; evidence/H-maple-layer-unload/*): layer [tail LOST: 3/3] run 0: tail LOST | layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false run 1: tail LOST | layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false run 2: tail LOST | layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false layer-flusher [tail delivered: 3/3] run 0: tail delivered | layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true run 1: tail delivered | layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true run 2: tail delivered | layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true flush [tail delivered: 3/3] run 0: tail delivered | flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true run 1: tail delivered | flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true run 2: tail delivered | flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true The "layer-flusher" variant is a few lines of user code (repro/client/app.ts): Maple.layer(cfg).pipe(Layer.provide(Layer.succeed(FetchHttpClient.RequestInit, { keepalive: true }))) + on pagehide / visibilitychange(hidden): runtime.runPromise(Effect.flatMap(OtlpExporter.Flusher, f => f.flush)) Effect 4.0.0-rc.112's Otlp layers already provide OtlpExporter.Flusher (OtlpTracer.js L94 `Layer.provideMerge(Exporter.layerFlusher)`), so the README statement "there's no way to force an export" no longer holds with current Effect. Maple.layer could ship this built in. Caveat: with keepalive on every export it inherits BUG-1/2 unless it chunks and uses keepalive only at unload. ============================================================================== 5. NON-BUGS / ARTIFACTS / DOCUMENTED LIMITATIONS (do not chase these) ============================================================================== 5.1 Playwright "requestfailed net::ERR_ABORTED" for keepalive requests [VERIFIED-EMPIRICAL] (scenario G + K + every unload scenario) steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events | reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 run 1: no requestfailed events | reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) | reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) | reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 * Steady state (no navigation): Playwright reports every SDK keepalive request as finished, and the server agrees. * Any request still in flight when its document is torn down, which includes EVERYTHING the pagehide flush sends, is reported as requestfailed net::ERR_ABORTED although the server received it and answered 200 (the server timestamp precedes the failure event by a few ms). In the browser the same requests reject with TypeError (BUG-7). * This affects Playwright's view only; delivery is fine. Use server-side receipts as the source of truth. The ERR_ABORTED that a real-app dev setup reports on "essentially every" blob/traces request is consistent with this whenever those requests overlap a navigation (with a dev server, e.g. HMR full reloads, navigations are frequent). We could NOT reproduce "every request fails even without navigation" in either headless mode. Cross-check in full Chromium "new headless": full Chromium 'new headless' (evidence/headless-new/): steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events | reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 run 1: no requestfailed events | reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) | reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) | reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [HYPOTHESIS] Possible reasons in a real-app dev setup: frequent full-page reloads or navigations (HMR, auth redirects) so most requests overlap an unload; a page whose requests are mostly sent from the pagehide flush; or differences in headed Chromium or in Playwright's request tracking when service workers or other frames are involved. None of these was tested. 5.2 Pagehide flush works in the simple case [VERIFIED-EMPIRICAL] Span created ~150 ms before reload with nothing pending/in flight: delivered (C-control 3/3, K 6/6 spans and logs, H "flush" 3/3, I-cross-origin). The hypothesis "the pagehide flush never fires / Effect schedules the fetch too late" is [DISPROVEN] for Chromium 143. [HYPOTHESIS, not observed] The stack traces show the fetch is issued from Effect's MixedScheduler ("at MixedScheduler."). If the Effect scheduler ever deferred that work to a macrotask (setTimeout) during pagehide, the request would never be issued. Issuing the unload POSTs synchronously in the event handler would remove that dependency. 5.3 Cross-origin endpoints [VERIFIED-EMPIRICAL] (scenario I-cross-origin) Keepalive + CORS preflight works in Chromium 143 (preflight per request with our max-age 0). The preflight asks for "authorization,content-type,x-maple-sdk". The SDK also sets a "user-agent" header (version-B225m7RW.mjs L23-27), which Chromium silently ignores: the server sees the browser UA. [HYPOTHESIS, untested] Browsers that let pages set User-Agent on fetch (Firefox has allowed it for years) would add "user-agent" to Access-Control-Request-Headers, so an ingest CORS policy that does not list it would block every browser OTLP request in those browsers. Consider not sending "user-agent" from the browser entry, since x-maple-sdk already carries the same value. Runs: same-origin [tail delivered: 2/2] run 0: tail delivered | same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 run 1: tail delivered | same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 cross-origin [tail delivered: 2/2] run 0: tail delivered | cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 run 1: tail delivered | cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 5.4 Session metadata "ended" rows and trace-id lists are capped (200 ids, client L1629). We saw "ended" rows of 1.1-6.4 kB. Not a bug; listed because it feeds BUG-5. ============================================================================== 6. MISCELLANEOUS SMALLER FINDINGS ============================================================================== M-1 [VERIFIED-CODE] restore() keeps the OLDEST items when over MAX_BUFFER (slice(0, 1e4)) and exportFn drops NEW spans/logs silently at 1e4; no warning, no metric (version L301, L313, L443, L485). The type docs mention the 10k cap only for autoFlushInterval:false. M-2 [VERIFIED-EMPIRICAL + CODE] keepaliveFor(requested, body.length) compares a JS string length (UTF-16 code units) with a byte budget. A 40,000-character body of 2-byte characters is 80,000 bytes and is rejected (B6). Non-ASCII console messages, URLs or user names in session events can trip this. (0.10.0 fixes this for session posts with TextEncoder.) M-3 [VERIFIED-EMPIRICAL] The keepalive transport never reads or cancels the response body. Chromium keeps counting the request's bytes against the budget until the body finishes loading (B3b: back-to-back keepalive POSTs fail unless the body is read). Small effect with the "{}" bodies our mock returns, but larger or slower response bodies hold budget longer. (0.10.0's postToIngest cancels the body; the OTLP transport does not.) M-4 [VERIFIED-EMPIRICAL] With replay sampled, console.* is wrapped and every console call becomes a session event. Effect's default logger (kept by Logger.layer([...], {mergeWithExisting:true}), version L305) prints every Effect log to the console, so every Effect log is exported twice: as an OTLP log and as a "console.log" session event. Evidence: the sessionEvents consoleMessages in evidence/D-hang-head-of-line-blocking/*.json contain the "INFO (#n): Dhlog-..." lines. This inflates the events payload that competes for the unload budget (BUG-5). The SDK's own warnings/errors are captured as session events too. M-5 [VERIFIED-EMPIRICAL] With replay DISABLED, the metadata-only session still prints "[maple] session replay events POST failed ..." (K replay-off). The wording is confusing for users who turned replay off. M-6 [VERIFIED-EMPIRICAL] Multi-argument Effect log messages: Effect.logError("... Failed to connect", new Error("ClientError: boom"), "Unknown Error", {code:42,...}) is POSTed by the SDK with logRecord.body = {"arrayValue":{"values":[{"stringValue":"L Failed to connect"}, {"stringValue":"Error: ClientError: boom"},{"stringValue":"Unknown Error"}, {"stringValue":"{\"code\":42,\"nested\":{\"a\":1}}"}]}} (scenario L, evidence/L-multi-arg-log-body/logError-4-args-run0.json). That is valid, structured OTLP (version L338-345 via Effect's OtlpResource.unknownToAttributeValue). Our production observation that such logs show up in Maple as a JSON-encoded array string (`["Failed to connect","ClientError: ...","Unknown Error","{...}"]`) must therefore come from how ingest/storage/UI renders an arrayValue body, not from the SDK. Ergonomics suggestion: join multiple string parts with " " (as Effect's console logger does) or render arrayValue bodies as joined text in the UI, with structured args kept as attributes. M-7 [VERIFIED-CODE] MapleFlush.make() has side effects at construction: startClientSession() (L2027-2036) mints the session/visitor ids, writes cookies/localStorage, starts the event sink (5 s timer, patched history API, click/error listeners) and dynamically imports the replay engine, all before (and whether or not) telemetry.layer is ever provided. Calling make() twice (e.g. StrictMode/HMR) is guarded by readSessionSink()/lease counting. Not a bug, but surprising for a "make" function; worth documenting. M-8 [VERIFIED-CODE] dispose() awaits flush() before clientSession.stop(), so it inherits BUG-3/BUG-4: during cooldown or behind a hung POST, dispose skips or stalls. M-9 [VERIFIED-CODE, investigated, not a bug] Metric buffer: drains a cumulative snapshot only when something changed; restore() re-marks dirty. Fine. guardFlush: only logs failures around runFlush; fine. M-10 [HYPOTHESIS] Mobile Safari/Chrome often kill backgrounded tabs without a pagehide, after visibilitychange(hidden). There the visibilitychange flush is the last chance, and BUG-3/4/5/6 apply equally. Not testable here (no mobile browser). ============================================================================== 7. MAPPING TO OUR PRODUCTION OBSERVATIONS ============================================================================== Obs. 1a: after switching to MapleFlush.make (autoFlushInterval 1000), one of ~6 Playwright runs logged "[MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch" then "[MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)" around a reload. A later run printed both traces AND logs variants around a reload ~150 ms after an action (3/3 runs that day), and the span created ~150 ms before the reload still arrived. -> Reproduced as scenario K: 6/6 runs show the traces and logs variants; span + log delivered in 6/6. Attributed to the OLD document's pagehide flush (failed) and visibilitychange flush (skipped, "60000ms remaining"); the data was delivered (BUG-7). It is intermittent in production because it needs something buffered at pagehide: with autoFlushInterval 1000 the buffer is usually empty unless the action happened within ~1 s of the reload. Concurrent budget (BUG-2/5) was the leading hypothesis for this pattern. [DISPROVEN] for these small payloads: the server got every request. It remains possible with large payloads, which print the same line. Obs. 1b: in one run, a span that ended ~5 s before a reload never arrived (its sibling that ended earlier did); not reproducible on rerun. -> Most consistent with BUG-4: one slow ingest-proxy round-trip (several seconds) blocks every later flush, including the pagehide flush. Reproduced with a 7 s round-trip and a span ending 5 s before the reload ([REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms run 1: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms run 2: REPRODUCED (tail span lost) | first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms). Other ways to lose it (BUG-1/2/3/5) would have printed "flush failed" BEFORE the reload. In the BUG-4 case the console at the reload shows one "traces flush failed; cooldown 60s: TypeError: Failed to fetch" followed by one or more "traces flush skipped (cooldown 5999xms remaining)" lines; the number of "skipped" lines is the number of flushes that were queued. If the production console of that run showed more than one "skipped" line at the reload, that points to BUG-4. [HYPOTHESIS for the production case.] Obs. 2: with BOTH presets, "[maple] session replay events/metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch" around navigations; Playwright showed ERR_ABORTED for sessionEvents and sessionReplays/meta at those moments. -> Mostly BUG-7 (delivered, document gone); reproduced at every reload with pending session data (K, C-control, E, F). Real loss is possible through BUG-8 (aborted non-keepalive periodic posts) and BUG-5 (budget). The final replay chunk is lost at every unload anyway (BUG-6), with no message at all. Obs. 3: Playwright reports essentially every keepalive request (traces, sessionReplays/blob) as requestfailed ERR_ABORTED although the data arrived. -> Section 5.1: reproduced for every request in flight when its document goes away. We did NOT see it for steady-state requests in headless-shell or new headless Chromium 143 without navigation. Obs. 4: steady state without navigation: everything arrives; send -> 150 ms -> reload delivered the final span. -> Consistent (G steady, C-control, K, H flush). ============================================================================== 8. CHECK AGAINST THE LATEST SDK (0.10.0, published 2026-10-03) ============================================================================== Code reading [VERIFIED-CODE] (sdk-excerpts/12-v0.10.0-what-changed-and-what-did-not.txt): * NEW: a shared page-wide keepalive budget for session/replay posts (KEEPALIVE_BUDGET_BYTES = 49152 on globalThis, reserve/release, TextEncoder byte length, response body cancelled before release). This addresses part of BUG-5 (meta/events/blob among themselves) and M-2/M-3 for those posts. * UNCHANGED: MapleFlush keepaliveTransport (always keepalive:true, no reservation, no size check), flushSignal (whole batch, restore, 60 s cooldown, cooldown also at unload), runFlush (Promise.all over three signals), and makeSerializedFlush for the browser (the new coalesceSameArguments option is documented as a Workers option). So BUG-1/2/3/4 remain. The new comment says the session budget "stops short of 64 KiB because the OTLP trace exporter spends from the same page-wide allowance under its own accounting", but the OTLP transport has no accounting; an OTLP body above ~16 KiB at unload still starves the session posts, or the reverse (BUG-5 remains). * UNCHANGED: the replay uploadChunk() claims the chunk seq, then awaits gzip, then POSTs (BUG-6 remains). * UNCHANGED: warnDropped wording (BUG-8). Empirical (same scenarios, SDK=0.10.0 build, effect 4.0.0; evidence/sdk-0.10.0/): A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] A0-threshold / n100 [DELIVERED: 2/2] A0-threshold / n150 [DELIVERED: 2/2] A0-threshold / n160 [DELIVERED: 2/2] A0-threshold / n163 [DELIVERED: 2/2] A0-threshold / n164 [REJECTED: 2/2] A0-threshold / n165 [REJECTED: 2/2] A0-threshold / n170 [REJECTED: 2/2] A0-threshold / n200 [REJECTED: 2/2] B-probe / probe [MEASURED: 2/2] B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 2/2] C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 2/2] C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 2/2] C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 2/2] D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 2/2] D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 2/2] D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 2/2] D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 2/2] E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 2/2] (per-run details: evidence/sdk-0.10.0/summary-*.txt and the per-run JSON files) ============================================================================== 9. SUGGESTED FIXES (consolidated, roughly by priority) ============================================================================== 1. OTLP transport: chunk by serialized UTF-8 size (<= 48 KiB per request); steady-state POSTs without keepalive (with a timeout); keepalive only on the unload path. (BUG-1, BUG-2, BUG-4) 2. One page-wide keepalive budget object shared by OTLP + session/replay posts and by multiple SDK copies; reserve on send, release when the response body ends; fall back to non-keepalive when a reservation fails; prioritise at unload. (BUG-2, BUG-5, M-2, M-3) 3. Unload path: synchronous drain + send inside the pagehide/visibilitychange task, never blocked by the serialized chain, never skipped by the cooldown, and no cooldown/restore/console.error for failures after pagehide. (BUG-3, BUG-4, BUG-7) 4. On a keepalive TypeError at send time: retry without keepalive or with smaller chunks; do not restore an unsendable batch forever; keep the newest data when trimming. (BUG-1, M-1) 5. Replay: have the final chunk ready synchronously at pagehide (incremental gzip or uncompressed fallback) or persist it for the next load; claim the seq only when sending. (BUG-6) 6. Session posts: restore + retry instead of drop, fix the "will retry" wording, throttle less aggressively or count drops. (BUG-8, M-5) 7. Observability of the SDK itself: counters for dropped spans/logs/events by reason (buffer cap, oversized, keepalive rejected, cooldown at unload), exposed via an API or as a metric. Today the only signal is a console line that is also printed for successful requests. 8. Optional sendBeacon fallback for the unload path if ingest accepted auth via query parameter or body. Beacons have the same 64 KiB budget in Chromium but are queued even when the document dies mid-flush. Not needed for same-origin-proxy setups like ours. 9. Maple.layer: register a pagehide/visibilitychange hook that calls Effect's OtlpExporter.Flusher with keepalive (with chunking), as in 4.9. 10. Docs: state the 64 KiB keepalive budget, the per-flush span limit (~160 plain spans), and that a single failure skips the unload flush for 60 s (until fixed). ============================================================================== APPENDIX A. FULL SCENARIO TABLE (Chromium 143 headless-shell, SDK 0.8.2) ============================================================================== (Generated from evidence/summary-*.txt; each line = scenario / variant [verdict counts].) --- SDK 0.8.2, chromium-headless-shell (evidence/) --- B-probe / probe [MEASURED: 2/2] run 0: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/B-keepalive-probe/probe-run0.json run 1: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 3/3] run 0: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/manual-flush-run0.json run 1: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/manual-flush-run1.json run 2: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/manual-flush-run2.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 3/3] run 0: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/reload-run0.json run 1: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/reload-run1.json run 2: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/B-sdk-concurrent-signals/reload-run2.json A0-threshold / n100 [DELIVERED: 1/1] run 0: 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: evidence/A0-threshold/n100-run0.json A0-threshold / n150 [DELIVERED: 1/1] run 0: 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: evidence/A0-threshold/n150-run0.json A0-threshold / n160 [DELIVERED: 1/1] run 0: 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: evidence/A0-threshold/n160-run0.json A0-threshold / n163 [DELIVERED: 1/1] run 0: 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: evidence/A0-threshold/n163-run0.json A0-threshold / n164 [REJECTED: 1/1] run 0: 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/A0-threshold/n164-run0.json A0-threshold / n165 [REJECTED: 1/1] run 0: 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/A0-threshold/n165-run0.json A0-threshold / n170 [REJECTED: 1/1] run 0: 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/A0-threshold/n170-run0.json A0-threshold / n200 [REJECTED: 1/1] run 0: 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/A0-threshold/n200-run0.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 3/3] run 0: inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2937,t=2942 evidence: evidence/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2947,t=2956 evidence: evidence/C-unload-during-cooldown/inject503-then-reload-run1.json run 2: inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2122,t=2952,t=2958 evidence: evidence/C-unload-during-cooldown/inject503-then-reload-run2.json A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 3/3] run 0: inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2937,t=2941 evidence: evidence/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2108,t=2937,t=2943 evidence: evidence/C-unload-during-cooldown/inject503-then-navigate-run1.json run 2: inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2147,t=2960,t=2965 evidence: evidence/C-unload-during-cooldown/inject503-then-navigate-run2.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 3/3] run 0: inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2938,t=2942 evidence: evidence/C-unload-during-cooldown/inject503-then-close-run0.json run 1: inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2930,t=2935 evidence: evidence/C-unload-during-cooldown/inject503-then-close-run1.json run 2: inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2939,t=2945 evidence: evidence/C-unload-during-cooldown/inject503-then-close-run2.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2565,63567,124567ms evidence: evidence/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2548,63547,124547ms evidence: evidence/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 0: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5491,70488,135493ms evidence: evidence/A-oversized-batch/burst200x500B-interval5000(default)-run0.json run 1: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5483,70483,135481ms evidence: evidence/A-oversized-batch/burst200x500B-interval5000(default)-run1.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 3/3] run 0: inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2972 evidence: evidence/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2949 evidence: evidence/C-unload-during-cooldown/control-no-failure-reload-run1.json run 2: inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2970 evidence: evidence/C-unload-during-cooldown/control-no-failure-reload-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 3/3] run 0: first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5125ms); D-tail delivered=false; reload at t=1401ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5107ms); D-tail delivered=false; reload at t=1382ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json run 2: first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5108ms); D-tail delivered=false; reload at t=1375ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 3/3] run 0: first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9124ms); D-tail delivered=false; reload at t=5383ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9115ms); D-tail delivered=false; reload at t=5367ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json run 2: first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9114ms); D-tail delivered=false; reload at t=5373ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run2.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 3/3] run 0: first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json run 2: first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms evidence: evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run2.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 3/3] run 0: first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1405ms); reload at t=1393ms evidence: evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1412ms); reload at t=1406ms evidence: evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json run 2: first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1123ms); D-tail delivered=true (received t=1401ms); reload at t=1394ms evidence: evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run2.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: first traces POST held 15000 ms (from t=1153ms): span export delay max 14990 ms / min 909 ms; log export delay max 14991 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17143:1sp/1157B 18143:1sp/1157B 19144:1sp/1157B] evidence: evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: first traces POST held 15000 ms (from t=1137ms): span export delay max 14977 ms / min 905 ms; log export delay max 14977 ms (logs endpoint itself is fast); traces POSTs at server t=[1116:1sp/1160B 16120:15sp/6677B 17115:1sp/1157B 18115:1sp/1157B 19115:1sp/1157B] evidence: evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json Dr-random-reload-latency / latency150-interval1000 [no loss: 9/10; LOSS (1 spans): 1/10] run 0: latency 150 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run0.json run 1: latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run1.json run 2: latency 150 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run2.json run 3: latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run3.json run 4: latency 150 ms, interval 1000: created 38, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run4.json run 5: latency 150 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run5.json run 6: latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run6.json run 7: latency 150 ms, interval 1000: created 40, lost 1 (created 100 ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run7.json run 8: latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run8.json run 9: latency 150 ms, interval 1000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency150-interval1000-run9.json Dr-random-reload-latency / latency600-interval1000 [no loss: 7/10; LOSS (5 spans): 1/10; LOSS (2 spans): 1/10; LOSS (4 spans): 1/10] run 0: latency 600 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run0.json run 1: latency 600 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run1.json run 2: latency 600 ms, interval 1000: created 54, lost 5 (created 513,411,305,202,100 ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run2.json run 3: latency 600 ms, interval 1000: created 22, lost 2 (created 202,100 ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run3.json run 4: latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run4.json run 5: latency 600 ms, interval 1000: created 57, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run5.json run 6: latency 600 ms, interval 1000: created 39, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run6.json run 7: latency 600 ms, interval 1000: created 34, lost 4 (created 406,304,202,100 ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run7.json run 8: latency 600 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run8.json run 9: latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval1000-run9.json Dr-random-reload-latency / latency600-interval5000 [no loss: 10/10] run 0: latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run0.json run 1: latency 600 ms, interval 5000: created 41, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run1.json run 2: latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run2.json run 3: latency 600 ms, interval 5000: created 22, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run3.json run 4: latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run4.json run 5: latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run5.json run 6: latency 600 ms, interval 5000: created 35, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run6.json run 7: latency 600 ms, interval 5000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run7.json run 8: latency 600 ms, interval 5000: created 21, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run8.json run 9: latency 600 ms, interval 5000: created 25, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/latency600-interval5000-run9.json Dr-random-reload-latency / control-latency0-interval1000 [no loss: 10/10] run 0: latency 0 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run0.json run 1: latency 0 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run1.json run 2: latency 0 ms, interval 1000: created 43, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run2.json run 3: latency 0 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run3.json run 4: latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run4.json run 5: latency 0 ms, interval 1000: created 31, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run5.json run 6: latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run6.json run 7: latency 0 ms, interval 1000: created 33, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run7.json run 8: latency 0 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run8.json run 9: latency 0 ms, interval 1000: created 23, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: evidence/Dr-random-reload-latency/control-latency0-interval1000-run9.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json run 2: spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run2.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 3/3] run 0: spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json run 2: spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run2.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 3/3] run 0: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json run 2: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run2.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json run 2: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run2.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json run 2: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run2.json F-replay-posts-aborted-by-navigation / flush-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run0.json run 1: flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run1.json Fr-random-reloads / flush-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: flush, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"traces[net::ERR_ABORTED]":1,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run0.json run 1: flush, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST; [maple] session replay blob PUT], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":5,"sessionReplays/blob[net::ERR_ABORTED]":1,"traces[net::ERR_ABORTED]":1} evidence: evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run1.json F-replay-posts-aborted-by-navigation / layer-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run0.json run 1: layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run1.json Fr-random-reloads / layer-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: layer, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run0.json run 1: layer, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":6} evidence: evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/G-playwright-keepalive-artifact/with-final-reload-run1.json H-maple-layer-unload / layer [tail LOST: 3/3] run 0: layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: evidence/H-maple-layer-unload/layer-run0.json run 1: layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: evidence/H-maple-layer-unload/layer-run1.json run 2: layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: evidence/H-maple-layer-unload/layer-run2.json H-maple-layer-unload / layer-flusher [tail delivered: 3/3] run 0: layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/layer-flusher-run0.json run 1: layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/layer-flusher-run1.json run 2: layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/layer-flusher-run2.json H-maple-layer-unload / flush [tail delivered: 3/3] run 0: flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/flush-run0.json run 1: flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/flush-run1.json run 2: flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: evidence/H-maple-layer-unload/flush-run2.json I-cross-origin / same-origin [tail delivered: 2/2] run 0: same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: evidence/I-cross-origin/same-origin-run0.json run 1: same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: evidence/I-cross-origin/same-origin-run1.json I-cross-origin / cross-origin [tail delivered: 2/2] run 0: cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: evidence/I-cross-origin/cross-origin-run0.json run 1: cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: evidence/I-cross-origin/cross-origin-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 3/3] run 0: reload at t=1795; requests issued around the reload (Playwright): traces(1165B)@1807 logs(1014B)@1807 sessionReplays/meta(1164B)@1807 sessionEvents(1021B)@1807 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1801 logs@1802 sessionReplays/meta@1802 sessionEvents@1804 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flu evidence: evidence/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: reload at t=1810; requests issued around the reload (Playwright): traces(1165B)@1824 logs(1014B)@1824 sessionReplays/meta(1164B)@1824 sessionEvents(1021B)@1824 sessionReplays/meta(1060B)@1891; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1817 sessionReplays/meta@1818 logs@1818 sessionEvents@1820 sessionReplays/meta@1890; console: [MapleClientSDK] logs flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flu evidence: evidence/K-reload-150ms-after-action/replay-on(default)-run1.json run 2: reload at t=1803; requests issued around the reload (Playwright): traces(1165B)@1815 logs(1014B)@1816 sessionReplays/meta(1164B)@1816 sessionEvents(1021B)@1816 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1809 logs@1810 sessionReplays/meta@1813 sessionEvents@1814 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flu evidence: evidence/K-reload-150ms-after-action/replay-on(default)-run2.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 3/3] run 0: reload at t=1770; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1781 sessionEvents(499B)@1781 traces(1165B)@1782 logs(1014B)@1782 sessionReplays/meta(1070B)@1814; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1777 traces@1778 sessionEvents@1779 logs@1781 sessionReplays/meta@1811; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1787 | [MapleClientSDK] traces flus evidence: evidence/K-reload-150ms-after-action/replay-off-run0.json run 1: reload at t=1771; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1783 sessionEvents(499B)@1783 traces(1165B)@1784 logs(1014B)@1784 sessionReplays/meta(1070B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1778 sessionEvents@1779 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1785 | [MapleClientSDK] traces flus evidence: evidence/K-reload-150ms-after-action/replay-off-run1.json run 2: reload at t=1769; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1780 sessionEvents(499B)@1780 traces(1165B)@1781 logs(1014B)@1781 sessionReplays/meta(1070B)@1813; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1776 traces@1777 sessionEvents@1778 logs@1780 sessionReplays/meta@1810; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1782 | [MapleClientSDK] traces flus evidence: evidence/K-reload-150ms-after-action/replay-off-run2.json L-multi-arg-log-body / logError-4-args [body is an OTLP arrayValue (structured), not a JSON string: 1/1] run 0: OTLP logRecord.body as POSTed by the SDK: {"arrayValue":{"values":[{"stringValue":"L Failed to connect"},{"stringValue":"Error: ClientError: boom"},{"stringValue":"Unknown Error"},{"stringValue":"{\"code\":42,\"nested\":{\"a\":1}}"}]}} evidence: evidence/L-multi-arg-log-body/logError-4-args-run0.json W-workaround-keepalive-downgrade-shim / without-shim [LOSS (burst 0/200, ticks 0/5): 2/2] run 0: shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: evidence/W-workaround-keepalive-downgrade-shim/without-shim-run0.json run 1: shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: evidence/W-workaround-keepalive-downgrade-shim/without-shim-run1.json W-workaround-keepalive-downgrade-shim / with-keepalive-downgrade-shim [all delivered: 2/2] run 0: shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run0.json run 1: shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run1.json F-replay-posts-aborted-by-navigation / layer-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run0.json run 1: layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / flush-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run0.json run 1: flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run1.json --- SDK 0.10.0 + effect 4.0.0, chromium-headless-shell (evidence/sdk-0.10.0/) --- A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10460,75460ms evidence: evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10481,75482ms evidence: evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2447,63447,124447ms evidence: evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2513,63514,124513ms evidence: evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 1: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5451,70449,135456ms evidence: evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run1.json run 0: created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5488,70485,135492ms evidence: evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run0.json B-probe / probe [MEASURED: 2/2] run 0: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/sdk-0.10.0/B-keepalive-probe/probe-run0.json run 1: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/sdk-0.10.0/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run0.json run 1: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run0.json run 1: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run1.json A0-threshold / n100 [DELIVERED: 2/2] run 0: 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n100-run0.json run 1: 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n100-run1.json A0-threshold / n150 [DELIVERED: 2/2] run 0: 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n150-run0.json run 1: 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n150-run1.json A0-threshold / n160 [DELIVERED: 2/2] run 0: 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n160-run0.json run 1: 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n160-run1.json A0-threshold / n163 [DELIVERED: 2/2] run 0: 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n163-run0.json run 1: 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: evidence/sdk-0.10.0/A0-threshold/n163-run1.json A0-threshold / n164 [REJECTED: 2/2] run 0: 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n164-run0.json run 1: 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n164-run1.json A0-threshold / n165 [REJECTED: 2/2] run 0: 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n165-run0.json run 1: 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n165-run1.json A0-threshold / n170 [REJECTED: 2/2] run 0: 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n170-run0.json run 1: 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n170-run1.json A0-threshold / n200 [REJECTED: 2/2] run 0: 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n200-run0.json run 1: 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/sdk-0.10.0/A0-threshold/n200-run1.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 2/2] run 0: inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2115,t=2950,t=2956 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2949,t=2953 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run1.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 2/2] run 0: inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2119,t=2954,t=2960 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2956,t=2962 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run1.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 2/2] run 0: inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2931,t=2936 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run0.json run 1: inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2134,t=2949,t=2959 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run1.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 2/2] run 0: inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2960 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2954 evidence: evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 2/2] run 0: first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5114ms); D-tail delivered=false; reload at t=1383ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5124ms); D-tail delivered=false; reload at t=1380ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 2/2] run 0: first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9123ms); D-tail delivered=false; reload at t=5383ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9116ms); D-tail delivered=false; reload at t=5370ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 2/2] run 0: first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8113ms); D-tail delivered=false; reload at t=6231ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6241ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 2/2] run 0: first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1145ms); D-tail delivered=true (received t=1408ms); reload at t=1401ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1115ms); D-tail delivered=true (received t=1396ms); reload at t=1390ms evidence: evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: first traces POST held 15000 ms (from t=1161ms): span export delay max 14983 ms / min 886 ms; log export delay max 14983 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17144:1sp/1157B 18144:1sp/1157B 19144:1sp/1157B] evidence: evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: first traces POST held 15000 ms (from t=1131ms): span export delay max 14992 ms / min 922 ms; log export delay max 14992 ms (logs endpoint itself is fast); traces POSTs at server t=[1123:1sp/1160B 16128:15sp/6677B 17123:1sp/1157B 18123:1sp/1157B 19122:1sp/1157B] evidence: evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 2/2] run 0: reload at t=1777; requests issued around the reload (Playwright): traces(1165B)@1789 logs(1014B)@1790 sessionReplays/meta(1194B)@1790 sessionEvents(1043B)@1790 sessionReplays/meta(1090B)@1850; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1783 sessionReplays/meta@1785 logs@1785 sessionEvents@1787 sessionReplays/meta@1849; console: [MapleClientSDK] logs flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush failed; cooldown evidence: evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: reload at t=1807; requests issued around the reload (Playwright): traces(1165B)@1819 logs(1014B)@1819 sessionReplays/meta(1194B)@1819 sessionEvents(1043B)@1819 sessionReplays/meta(1090B)@1882; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1813 logs@1814 sessionReplays/meta@1815 sessionEvents@1816 sessionReplays/meta@1882; console: [MapleClientSDK] logs flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush failed; cooldown evidence: evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: reload at t=1783; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1792 sessionEvents(510B)@1793 traces(1165B)@1793 logs(1014B)@1793 sessionReplays/meta(1100B)@1825; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1789 sessionEvents@1790 traces@1790 logs@1791 sessionReplays/meta@1822; console: [MapleClientSDK] traces flush failed; cooldown 60s@1793 | [MapleClientSDK] logs flush failed; cooldown 6 evidence: evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run0.json run 1: reload at t=1772; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1782 sessionEvents(510B)@1783 traces(1165B)@1783 logs(1014B)@1784 sessionReplays/meta(1100B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1779 sessionEvents@1780 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 6 evidence: evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run1.json --- SDK 0.8.2, full Chromium new headless (evidence/headless-new/) --- B-probe / probe [MEASURED: 2/2] run 0: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/headless-new/B-keepalive-probe/probe-run0.json run 1: single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: evidence/headless-new/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run0.json run 1: trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/headless-new/B-sdk-concurrent-signals/reload-run0.json run 1: trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: evidence/headless-new/B-sdk-concurrent-signals/reload-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 1/2; span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 1/2] run 0: reload at t=1856; requests issued around the reload (Playwright): traces(1162B)@1871 logs(1011B)@1871 sessionReplays/meta(1160B)@1872 sessionEvents(1019B)@1872 sessionReplays/meta(1056B)@1931; Playwright failed: sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1865 logs@1867 sessionReplays/meta@1867 sessionEvents@1868 sessionReplays/meta@1930; console: [MapleClientSDK] logs flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flu evidence: evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: reload at t=1880; requests issued around the reload (Playwright): traces(1162B)@1899 logs(1011B)@1899 sessionReplays/meta(1160B)@1900 sessionEvents(1019B)@1900 sessionReplays/meta(1056B)@1964; Playwright failed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: traces@1890 logs@1891 sessionReplays/meta@1893 sessionEvents@1895 sessionReplays/meta@1963; console: [MapleClientSDK] traces flush failed; cooldown 60s@1901 | [MapleClientSDK] logs flush failed; cooldown 60s@1901 | [MapleClientSDK] traces flu evidence: evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: reload at t=1833; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1846 sessionEvents(498B)@1847 traces(1162B)@1847 logs(1011B)@1847 sessionReplays/meta(1066B)@1888; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1842 traces@1844 sessionEvents@1844 logs@1845 sessionReplays/meta@1886; console: [MapleClientSDK] traces flush failed; cooldown 60s@1852 | [MapleClientSDK] logs flush failed; cooldown 60s@1852 | [MapleClientSDK] traces flus evidence: evidence/headless-new/K-reload-150ms-after-action/replay-off-run0.json run 1: reload at t=1819; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1831 sessionEvents(498B)@1831 traces(1162B)@1831 logs(1011B)@1831 sessionReplays/meta(1066B)@1868; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1827 sessionEvents@1828 traces@1828 logs@1830 sessionReplays/meta@1867; console: [MapleClientSDK] traces flush failed; cooldown 60s@1832 | [MapleClientSDK] logs flush failed; cooldown 60s@1832 | [MapleClientSDK] traces flus evidence: evidence/headless-new/K-reload-150ms-after-action/replay-off-run1.json ============================================================================== APPENDIX B. RAW LOG SNIPPETS ============================================================================== Extracted verbatim from the evidence JSON files (t values = ms since the start of that run; 'server recv/resp' = when the mock ingest got the request / sent its response; console lines truncated to their first line, full text including stack traces in the JSON). ### BUG-1: burst of 200 spans x 500 B, autoFlushInterval 1000 (no /v1/traces POST ever reaches the server; logs do) file: evidence/A-oversized-batch/burst200x500B-interval1000-run0.json marks: goto@272, ready@651, burst-created@1674 console t= 2548 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch console t= 63547 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch console t=124547 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch ### BUG-1: 164 plain spans in one flush (65,596 B) -> rejected file: evidence/A0-threshold/n164-run0.json marks: goto@39, ready@212, spans-created@230, flush-returned@246 console t= 246 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch playwright t= 212 request POST sessionReplays/meta body=1045B playwright t= 245 request POST traces body=65596B playwright t= 263 finished POST sessionReplays/meta status=200 server recv= 210 resp=210 POST sessionReplays/meta 1045B status=200 rows=[('active', 1)] ### BUG-2: traces 38,472 B + logs 32,135 B + metrics 931 B in one flush file: evidence/B-sdk-concurrent-signals/manual-flush-run0.json marks: goto@30, ready@168, data-created@190, flush-returned@202 console t= 202 error [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch playwright t= 167 request POST sessionReplays/meta body=1057B playwright t= 199 request POST traces body=38472B playwright t= 200 request POST logs body=32135B playwright t= 201 request POST metrics body=931B playwright t= 219 finished POST sessionReplays/meta status=200 playwright t= 253 finished POST traces status=200 playwright t= 253 finished POST metrics status=200 server recv= 166 resp=167 POST sessionReplays/meta 1057B status=200 rows=[('active', 1)] server recv= 195 resp=195 POST traces 38472B status=200 spans=40 ['Bsdk-span-0', 'Bsdk-span-1', 'Bsdk-span-2', 'Bsdk-span-3']... server recv= 197 resp=197 POST metrics 931B status=200 ### BUG-3: one 503, then reload 1.8 s later file: evidence/C-unload-during-cooldown/inject503-then-reload-run0.json marks: goto@33, ready@158, C-first created@162, C-first POST received by server@1127, C-before-unload created@2630, reload-start@2930, reload-load-event@2951, reload-ready@2989 console t= 1126 error [MapleClientSDK] traces flush failed; cooldown 60s: Error: OTLP 503 Service Unavailable console t= 2120 warning [MapleClientSDK] traces flush skipped (cooldown 59005ms remaining) console t= 2937 warning [MapleClientSDK] traces flush skipped (cooldown 58188ms remaining) console t= 2942 warning [MapleClientSDK] traces flush skipped (cooldown 58184ms remaining) playwright t= 158 request POST sessionReplays/meta body=1056B playwright t= 209 finished POST sessionReplays/meta status=200 playwright t= 1123 request POST traces body=1159B playwright t= 1175 finished POST traces status=503 playwright t= 2941 request POST sessionReplays/meta body=1195B playwright t= 2942 request POST sessionEvents body=2167B playwright t= 2943 failed POST sessionEvents [net::ERR_ABORTED] playwright t= 2943 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 3012 request POST sessionReplays/meta body=1056B playwright t= 3064 finished POST sessionReplays/meta status=200 server recv= 157 resp=157 POST sessionReplays/meta 1056B status=200 rows=[('active', 1)] server recv= 1122 resp=1123 POST traces 1159B status=503 spans=1 ['C-first'] server recv= 2938 resp=2938 POST sessionReplays/meta 1195B status=200 rows=[('ended', 2)] server recv= 2940 resp=2940 POST sessionEvents 2167B status=200 rows=4 seqs=[0, 1, 2, 3] server recv= 3012 resp=3012 POST sessionReplays/meta 1056B status=200 rows=[('active', 3)] ### BUG-4: first traces POST held 7 s; D-tail ends 5 s before reload; never sent file: evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json marks: goto@28, ready@110, D-early created@135, D-early POST received by server (response held for latencyMs)@1121, D-tail created@1226, reload-start@6226, reload-load-event@6244, reload-ready@6270 console t= 6236 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch console t= 6236 warning [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) console t= 6236 warning [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) console t= 6236 warning [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) console t= 6236 warning [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) console t= 6236 warning [MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) console t= 6237 warning [MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) console t= 6237 warning [MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) journal t= 83 script-start {"docId": "vv82k0", "variant": "flush", "origin": "same", "autoFlushInterval": 1000, "replayEnabled": true} journal t= 106 runtime-ready {} journal t= 134 span-created {"name": "D-early"} journal t= 1224 span-created {"name": "D-tail"} journal t= 6231 pagehide(page listener, registered before SDK) {"docId": "vv82k0", "persisted": false} journal t= 6232 visibilitychange:hidden {"docId": "vv82k0"} journal t= 6233 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: F journal t= 6234 console.error {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch"} journal t= 6235 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) "} journal t= 6235 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) "} journal t= 6235 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) "} journal t= 6235 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) "} journal t= 6236 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) "} journal t= 6236 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) "} journal t= 6236 console.warn {"docId": "vv82k0", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59998ms remaining) "} journal t= 6266 script-start {"docId": "s6rfd3", "variant": "flush", "origin": "same", "autoFlushInterval": 1000, "replayEnabled": true} journal t= 6268 runtime-ready {} playwright t= 133 request POST sessionReplays/meta body=1064B playwright t= 186 finished POST sessionReplays/meta status=200 playwright t= 1107 request POST traces body=1159B playwright t= 5088 request POST sessionEvents body=494B playwright t= 5134 request POST sessionReplays/blob body=623B playwright t= 5140 finished POST sessionEvents status=200 playwright t= 5186 finished POST sessionReplays/blob status=200 playwright t= 6235 request POST sessionReplays/meta body=1203B playwright t= 6235 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 6235 failed POST traces [net::ERR_ABORTED] playwright t= 6295 request POST sessionReplays/meta body=1064B playwright t= 6346 finished POST sessionReplays/meta status=200 playwright t= 11269 request POST sessionEvents body=494B playwright t= 11295 request POST sessionReplays/blob body=623B playwright t= 11320 finished POST sessionEvents status=200 playwright t= 11346 finished POST sessionReplays/blob status=200 server recv= 133 resp=133 POST sessionReplays/meta 1064B status=200 rows=[('active', 1)] server recv= 1106 resp=8107 POST traces 1159B status=200 spans=1 ['D-early'] server recv= 5088 resp=5088 POST sessionEvents 494B status=200 rows=1 seqs=[0] server recv= 5133 resp=5133 POST sessionReplays/blob 623B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 6232 resp=6233 POST sessionReplays/meta 1203B status=200 rows=[('ended', 2)] server recv= 6294 resp=6294 POST sessionReplays/meta 1064B status=200 rows=[('active', 3)] server recv= 11268 resp=11268 POST sessionEvents 494B status=200 rows=1 seqs=[0] server recv= 11294 resp=11295 POST sessionReplays/blob 623B status=200 chunkSeq=1 checkpoint=1 events=2 ### BUG-5: ~60 kB of spans pending at reload; meta(ended) + events rejected file: evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json marks: goto@27, ready@137, first replay blob received (or replay disabled)@5140, payload created@5359, reload-start@5659, reload-load-event@5680, reload-ready@5703 console t= 5676 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch console t= 5676 warning [MapleClientSDK] traces flush skipped (cooldown 59995ms remaining) journal t= 83 script-start {"docId": "moitk4", "variant": "flush", "origin": "same", "autoFlushInterval": 60000, "replayEnabled": true} journal t= 109 runtime-ready {} journal t= 5358 spans-created {"n": 150, "attrBytes": 0, "prefix": "E-span"} journal t= 5663 pagehide(page listener, registered before SDK) {"docId": "moitk4", "persisted": false} journal t= 5668 visibilitychange:hidden {"docId": "moitk4"} journal t= 5669 console.error {"docId": "moitk4", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch"} journal t= 5674 console.warn {"docId": "moitk4", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59995ms remaining) "} journal t= 5700 script-start {"docId": "kknzhk", "variant": "flush", "origin": "same", "autoFlushInterval": 60000, "replayEnabled": true} journal t= 5702 runtime-ready {} playwright t= 136 request POST sessionReplays/meta body=1058B playwright t= 188 finished POST sessionReplays/meta status=200 playwright t= 5088 request POST sessionEvents body=488B playwright t= 5137 request POST sessionReplays/blob body=620B playwright t= 5139 finished POST sessionEvents status=200 playwright t= 5189 finished POST sessionReplays/blob status=200 playwright t= 5675 request POST traces body=60652B playwright t= 5675 request POST sessionReplays/meta body=6377B playwright t= 5676 request POST sessionEvents body=21041B playwright t= 5676 failed POST traces [net::ERR_ABORTED] playwright t= 5726 request POST sessionReplays/meta body=1058B playwright t= 5777 finished POST sessionReplays/meta status=200 playwright t= 10703 request POST sessionEvents body=488B playwright t= 10727 request POST sessionReplays/blob body=620B playwright t= 10754 finished POST sessionEvents status=200 playwright t= 10778 finished POST sessionReplays/blob status=200 server recv= 136 resp=136 POST sessionReplays/meta 1058B status=200 rows=[('active', 1)] server recv= 5088 resp=5088 POST sessionEvents 488B status=200 rows=1 seqs=[0] server recv= 5137 resp=5137 POST sessionReplays/blob 620B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 5667 resp=5668 POST traces 60652B status=200 spans=150 ['E-span-0', 'E-span-1', 'E-span-2', 'E-span-3']... server recv= 5726 resp=5726 POST sessionReplays/meta 1058B status=200 rows=[('active', 3)] server recv= 10702 resp=10702 POST sessionEvents 488B status=200 rows=1 seqs=[0] server recv= 10726 resp=10726 POST sessionReplays/blob 620B status=200 chunkSeq=1 checkpoint=1 events=2 ### BUG-5: same with replay disabled; session rows go first, traces rejected file: evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json marks: goto@26, ready@109, first replay blob received (or replay disabled)@109, payload created@331, reload-start@632, reload-load-event@650, reload-ready@675 console t= 646 warning [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch journal t= 76 script-start {"docId": "x881i5", "variant": "flush", "origin": "same", "autoFlushInterval": 60000, "replayEnabled": false} journal t= 105 runtime-ready {} journal t= 330 spans-created {"n": 150, "attrBytes": 0, "prefix": "E-span"} journal t= 636 pagehide(page listener, registered before SDK) {"docId": "x881i5", "persisted": false} journal t= 640 visibilitychange:hidden {"docId": "x881i5"} journal t= 641 console.warn {"docId": "x881i5", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Fai journal t= 669 script-start {"docId": "pep5s3", "variant": "flush", "origin": "same", "autoFlushInterval": 60000, "replayEnabled": false} journal t= 671 runtime-ready {} playwright t= 106 request POST sessionReplays/meta body=1068B playwright t= 159 finished POST sessionReplays/meta status=200 playwright t= 642 request POST sessionReplays/meta body=6386B playwright t= 642 request POST sessionEvents body=497B playwright t= 646 request POST traces body=60652B playwright t= 646 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 646 failed POST sessionEvents [net::ERR_ABORTED] playwright t= 672 request POST sessionReplays/meta body=1068B playwright t= 724 finished POST sessionReplays/meta status=200 server recv= 83 resp=83 POST sessionReplays/meta 1068B status=200 rows=[('active', 1)] server recv= 637 resp=638 POST sessionReplays/meta 6386B status=200 rows=[('ended', 2)] server recv= 638 resp=638 POST sessionEvents 497B status=200 rows=1 seqs=[0] server recv= 671 resp=671 POST sessionReplays/meta 1068B status=200 rows=[('active', 3)] ### BUG-6: replay chunk seq 1 consumed at unload, never POSTed (server sees 0 then 2) file: evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json marks: goto@26, ready@125, first replay blob received (or replay disabled)@5135, payload created@5344, reload-start@5644, reload-load-event@5663, reload-ready@5689 console t= 5656 warning [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch console t= 5656 warning [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch playwright t= 125 request POST sessionReplays/meta body=1058B playwright t= 176 finished POST sessionReplays/meta status=200 playwright t= 5080 request POST sessionEvents body=488B playwright t= 5125 request POST sessionReplays/blob body=620B playwright t= 5132 finished POST sessionEvents status=200 playwright t= 5177 finished POST sessionReplays/blob status=200 playwright t= 5655 request POST sessionReplays/meta body=1128B playwright t= 5656 request POST sessionEvents body=2100B playwright t= 5656 failed POST sessionEvents [net::ERR_ABORTED] playwright t= 5656 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 5713 request POST sessionReplays/meta body=1058B playwright t= 5765 finished POST sessionReplays/meta status=200 playwright t= 10688 request POST sessionEvents body=488B playwright t= 10714 request POST sessionReplays/blob body=619B playwright t= 10740 finished POST sessionEvents status=200 playwright t= 10765 finished POST sessionReplays/blob status=200 server recv= 124 resp=125 POST sessionReplays/meta 1058B status=200 rows=[('active', 1)] server recv= 5080 resp=5080 POST sessionEvents 488B status=200 rows=1 seqs=[0] server recv= 5125 resp=5125 POST sessionReplays/blob 620B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 5652 resp=5652 POST sessionReplays/meta 1128B status=200 rows=[('ended', 2)] server recv= 5653 resp=5653 POST sessionEvents 2100B status=200 rows=3 seqs=[1, 2, 3] server recv= 5713 resp=5713 POST sessionReplays/meta 1058B status=200 rows=[('active', 3)] server recv= 10688 resp=10688 POST sessionEvents 488B status=200 rows=1 seqs=[0] server recv= 10713 resp=10713 POST sessionReplays/blob 619B status=200 chunkSeq=2 checkpoint=1 events=2 ### BUG-7: span + log, reload 150 ms later: delivered, yet 'failed' in the old document file: evidence/K-reload-150ms-after-action/replay-on(default)-run0.json marks: goto@28, ready@136, action (span + log) done@1644, reload-start@1795, reload-load-event@1815, reload-ready@1842 console t= 1808 warning [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch console t= 1808 warning [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch console t= 1808 error [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch console t= 1808 error [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch console t= 1808 warning [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) console t= 1808 warning [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining) journal t= 84 script-start {"docId": "5eo9a3", "variant": "flush", "origin": "same", "autoFlushInterval": 1000, "replayEnabled": true} journal t= 108 runtime-ready {} journal t= 1640 span-created {"name": "K-action-span"} journal t= 1644 logs-created {"n": 1, "bytes": 0, "prefix": "K-action-log"} journal t= 1799 pagehide(page listener, registered before SDK) {"docId": "5eo9a3", "persisted": false} journal t= 1804 visibilitychange:hidden {"docId": "5eo9a3"} journal t= 1806 console.warn {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: F journal t= 1806 console.warn {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Fai journal t= 1807 console.error {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch"} journal t= 1807 console.error {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch"} journal t= 1808 console.warn {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] traces flush skipped (cooldown 59999ms remaining) "} journal t= 1808 console.warn {"docId": "5eo9a3", "afterPagehide": true, "visibility": "hidden", "text": "[MapleClientSDK] logs flush skipped (cooldown 59999ms remaining) "} journal t= 1838 script-start {"docId": "spoowt", "variant": "flush", "origin": "same", "autoFlushInterval": 1000, "replayEnabled": true} journal t= 1840 runtime-ready {} playwright t= 136 request POST sessionReplays/meta body=1060B playwright t= 187 finished POST sessionReplays/meta status=200 playwright t= 1807 request POST traces body=1165B playwright t= 1807 request POST logs body=1014B playwright t= 1807 request POST sessionReplays/meta body=1164B playwright t= 1807 request POST sessionEvents body=1021B playwright t= 1807 failed POST logs [net::ERR_ABORTED] playwright t= 1807 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 1807 failed POST sessionEvents [net::ERR_ABORTED] playwright t= 1807 failed POST traces [net::ERR_ABORTED] playwright t= 1871 request POST sessionReplays/meta body=1060B playwright t= 1922 finished POST sessionReplays/meta status=200 server recv= 136 resp=136 POST sessionReplays/meta 1060B status=200 rows=[('active', 1)] server recv= 1801 resp=1801 POST traces 1165B status=200 spans=1 ['K-action-span'] server recv= 1802 resp=1802 POST logs 1014B status=200 logs=1 ['K-action-log-0 '] server recv= 1802 resp=1802 POST sessionReplays/meta 1164B status=200 rows=[('ended', 2)] server recv= 1804 resp=1804 POST sessionEvents 1021B status=200 rows=2 seqs=[0, 1] server recv= 1870 resp=1871 POST sessionReplays/meta 1060B status=200 rows=[('active', 3)] ### BUG-8: periodic (non-keepalive) sessionEvents POST in flight at reload file: evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run0.json marks: goto@29, ready@140, periodic sessionEvents POST received by server; response held 3 s@5095, reload-start@5195, reload-load-event@5209, reload-ready@5243 console t= 5204 warning [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch console t= 5204 warning [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch journal t= 82 script-start {"docId": "gndnpy", "variant": "flush", "origin": "same", "replayEnabled": true} journal t= 108 runtime-ready {} journal t= 5199 pagehide(page listener, registered before SDK) {"docId": "gndnpy", "persisted": false} journal t= 5201 visibilitychange:hidden {"docId": "gndnpy"} journal t= 5202 console.warn {"docId": "gndnpy", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: F journal t= 5202 console.warn {"docId": "gndnpy", "afterPagehide": true, "visibility": "hidden", "text": "[maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Fai journal t= 5231 script-start {"docId": "qm3r0p", "variant": "flush", "origin": "same", "replayEnabled": true} journal t= 5233 runtime-ready {} playwright t= 139 request POST sessionReplays/meta body=1054B playwright t= 190 finished POST sessionReplays/meta status=200 playwright t= 5089 request POST sessionEvents body=2959B playwright t= 5139 request POST sessionReplays/blob body=619B playwright t= 5191 finished POST sessionReplays/blob status=200 playwright t= 5204 request POST sessionReplays/meta body=1124B playwright t= 5204 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 5204 failed POST sessionEvents [net::ERR_ABORTED] playwright t= 5258 request POST sessionReplays/meta body=1054B playwright t= 5310 finished POST sessionReplays/meta status=200 server recv= 138 resp=138 POST sessionReplays/meta 1054B status=200 rows=[('active', 1)] server recv= 5088 resp=8089 POST sessionEvents 2959B status=200 rows=6 seqs=[0, 1, 2, 3, 4, 5] server recv= 5139 resp=5139 POST sessionReplays/blob 619B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 5201 resp=5201 POST sessionReplays/meta 1124B status=200 rows=[('ended', 2)] server recv= 5258 resp=5258 POST sessionReplays/meta 1054B status=200 rows=[('active', 3)] ### BUG-9: Maple.layer, H-tail lost file: evidence/H-maple-layer-unload/layer-run0.json marks: goto@28, ready@117, H-early created@120, H-tail created@6623, reload-start@7623, reload-load-event@7635, reload-ready@7662 playwright t= 153 request POST sessionReplays/meta body=1039B playwright t= 204 finished POST sessionReplays/meta status=200 playwright t= 5112 request POST sessionEvents body=469B playwright t= 5118 request POST traces body=1066B playwright t= 5153 request POST sessionReplays/blob body=603B playwright t= 5164 finished POST sessionEvents status=200 playwright t= 5169 finished POST traces status=200 playwright t= 5205 finished POST sessionReplays/blob status=200 playwright t= 7631 request POST sessionReplays/meta body=1178B playwright t= 7631 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 7686 request POST sessionReplays/meta body=1039B playwright t= 7737 finished POST sessionReplays/meta status=200 server recv= 152 resp=152 POST sessionReplays/meta 1039B status=200 rows=[('active', 1)] server recv= 5112 resp=5112 POST sessionEvents 469B status=200 rows=1 seqs=[0] server recv= 5118 resp=5118 POST traces 1066B status=200 spans=1 ['H-early'] server recv= 5153 resp=5153 POST sessionReplays/blob 603B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 7629 resp=7629 POST sessionReplays/meta 1178B status=200 rows=[('ended', 2)] server recv= 7685 resp=7685 POST sessionReplays/meta 1039B status=200 rows=[('active', 3)] ### Artifact: Playwright requestfailed vs server 200 file: evidence/G-playwright-keepalive-artifact/with-final-reload-run0.json marks: goto@30, ready@112, reload-start@8309, reload-load-event@8325, reload-ready@8347 playwright t= 140 request POST sessionReplays/meta body=1064B playwright t= 191 finished POST sessionReplays/meta status=200 playwright t= 1110 request POST traces body=1941B playwright t= 1163 finished POST traces status=200 playwright t= 2109 request POST traces body=1548B playwright t= 2162 finished POST traces status=200 playwright t= 3110 request POST traces body=1941B playwright t= 3161 finished POST traces status=200 playwright t= 4109 request POST traces body=1548B playwright t= 4161 finished POST traces status=200 playwright t= 5091 request POST sessionEvents body=494B playwright t= 5109 request POST traces body=1944B playwright t= 5140 request POST sessionReplays/blob body=620B playwright t= 5142 finished POST sessionEvents status=200 playwright t= 5161 finished POST traces status=200 playwright t= 5192 finished POST sessionReplays/blob status=200 playwright t= 6110 request POST traces body=1550B playwright t= 6161 finished POST traces status=200 playwright t= 7110 request POST traces body=1944B playwright t= 7162 finished POST traces status=200 playwright t= 8110 request POST traces body=1550B playwright t= 8161 finished POST traces status=200 playwright t= 8319 request POST traces body=1158B playwright t= 8319 request POST sessionReplays/meta body=1868B playwright t= 8319 failed POST sessionReplays/meta [net::ERR_ABORTED] playwright t= 8319 failed POST traces [net::ERR_ABORTED] playwright t= 8370 request POST sessionReplays/meta body=1064B playwright t= 8422 finished POST sessionReplays/meta status=200 server recv= 139 resp=139 POST sessionReplays/meta 1064B status=200 rows=[('active', 1)] server recv= 1111 resp=1111 POST traces 1941B status=200 spans=3 ['G-0', 'G-1', 'G-2'] server recv= 2109 resp=2109 POST traces 1548B status=200 spans=2 ['G-3', 'G-4'] server recv= 3109 resp=3109 POST traces 1941B status=200 spans=3 ['G-5', 'G-6', 'G-7'] server recv= 4109 resp=4109 POST traces 1548B status=200 spans=2 ['G-8', 'G-9'] server recv= 5090 resp=5091 POST sessionEvents 494B status=200 rows=1 seqs=[0] server recv= 5109 resp=5109 POST traces 1944B status=200 spans=3 ['G-10', 'G-11', 'G-12'] server recv= 5140 resp=5140 POST sessionReplays/blob 620B status=200 chunkSeq=0 checkpoint=1 events=2 server recv= 6109 resp=6109 POST traces 1550B status=200 spans=2 ['G-13', 'G-14'] server recv= 7109 resp=7109 POST traces 1944B status=200 spans=3 ['G-15', 'G-16', 'G-17'] server recv= 8109 resp=8109 POST traces 1550B status=200 spans=2 ['G-18', 'G-19'] server recv= 8316 resp=8316 POST traces 1158B status=200 spans=1 ['G-tail'] server recv= 8317 resp=8317 POST sessionReplays/meta 1868B status=200 rows=[('ended', 2)] server recv= 8370 resp=8370 POST sessionReplays/meta 1064B status=200 rows=[('active', 3)] ============================================================================== APPENDIX C. FILES IN THIS FOLDER ============================================================================== MAPLE_BUG_REPORT.txt -- this report evidence/ -- raw outputs of the runs cited in this report environment-shell-A.json environment-shell-B_A0_C_D_Dr_E_F_G_H_I_K_L.json environment-shell-W.json runner-console-A.txt runner-console-F-rerun.txt runner-console-W.txt runner-console-main.txt server-requests-all.jsonl summary-shell-2026-10-07T23-39-09-001Z.txt summary-shell-2026-10-07T23-39-09-004Z.txt summary-shell-2026-10-07T23-58-29-444Z.txt summary.jsonl webkit-firefox-launch.txt A-oversized-batch/ (8 run files: -run.json) A0-threshold/ (8 run files: -run.json) B-keepalive-probe/ (2 run files: -run.json) B-sdk-concurrent-signals/ (6 run files: -run.json) C-unload-during-cooldown/ (12 run files: -run.json) D-hang-head-of-line-blocking/ (2 run files: -run.json) D-pagehide-behind-inflight-flush/ (12 run files: -run.json) Dr-random-reload-latency/ (40 run files: -run.json) E-combined-unload-budget/ (15 run files: -run.json) F-replay-posts-aborted-by-navigation/ (8 run files: -run.json) Fr-random-reloads/ (4 run files: -run.json) G-playwright-keepalive-artifact/ (4 run files: -run.json) H-maple-layer-unload/ (9 run files: -run.json) I-cross-origin/ (4 run files: -run.json) K-reload-150ms-after-action/ (6 run files: -run.json) L-multi-arg-log-body/ (1 run files: -run.json) W-workaround-keepalive-downgrade-shim/ (4 run files: -run.json) headless-new/ environment-new-B_G_K_E.json runner-console.txt server-requests-all.jsonl summary-new-2026-10-07T23-50-02-862Z.txt summary.jsonl B-keepalive-probe/ (2 run files: -run.json) B-sdk-concurrent-signals/ (4 run files: -run.json) E-combined-unload-budget/ (10 run files: -run.json) G-playwright-keepalive-artifact/ (4 run files: -run.json) K-reload-150ms-after-action/ (4 run files: -run.json) sdk-0.10.0/ environment-shell-sdk0.10.0-A.json environment-shell-sdk0.10.0-A0_B_C_D_E_K.json runner-console-A.txt runner-console-main.txt server-requests-all.jsonl summary-shell-sdk0.10.0-2026-10-07T23-41-29-113Z.txt summary-shell-sdk0.10.0-2026-10-07T23-43-48-971Z.txt summary.jsonl A-oversized-batch/ (8 run files: -run.json) A0-threshold/ (16 run files: -run.json) B-keepalive-probe/ (2 run files: -run.json) B-sdk-concurrent-signals/ (4 run files: -run.json) C-unload-during-cooldown/ (8 run files: -run.json) D-hang-head-of-line-blocking/ (2 run files: -run.json) D-pagehide-behind-inflight-flush/ (8 run files: -run.json) E-combined-unload-budget/ (10 run files: -run.json) K-reload-150ms-after-action/ (4 run files: -run.json) repro/ -- standalone Bun + Playwright reproduction project (see repro/README.md) .gitignore README.md -- how to run; what each scenario does and what to expect build.ts -- Bun.build of the browser bundles (+ optional 0.10.0 variant) bun.lock -- lockfile lib.ts -- Playwright harness (capture console, network events, server records, evidence writing) package.json -- pins @maple-dev/effect-sdk 0.8.2, effect 4.0.0-rc.112, playwright 1.57.0 run-scenarios.ts -- scenario runner + summary server.ts -- mock Maple/OTLP ingest + static server; request logging; latency/failure injection client/ app.ts -- test page using MapleFlush.make / Maple.layer (window.T API) probe.ts -- SDK-free keepalive probe (window.P API) public/ index.html -- test page shell probe.html -- probe page shell scenarios/ b-probe.ts -- scenario B part 1: Chromium keepalive limits without the SDK sdk.ts -- all SDK scenarios (A0, A, Bsdk, C, D, Dhang, Dr, E, F, Fr, G, H, I, K, L, W) variants/ sdk-0.10.0/ bun.lock package.json -- optional: same page against @maple-dev/effect-sdk 0.10.0 + effect 4.0.0 sdk-excerpts/ -- verbatim SDK (and Effect) source excerpts with file names + line numbers 01-flush-core-cooldown-serialization.txt 02-buffers-cap-restore.txt 03-mapleflush-make-client.txt 04-replay-transport-keepalive-budget.txt 05-session-event-sink.txt 06-session-lifecycle-unload.txt 07-client-session-startup.txt 08-replay-record-final-chunk.txt 09-maple-layer-client.txt 10-effect-otlp-exporter.txt 11-readme.txt 12-v0.10.0-what-changed-and-what-did-not.txt Evidence conventions: evidence//-run.json one run: config, result, marks (timeline), full browser console, Playwright request/requestfinished/requestfailed for ingest URLs, every server record of that run (t* = ms since run start), and the page's localStorage journal (lifecycle + SDK console lines tagged per document) evidence/server-requests-all.jsonl every ingest request of the 0.8.2 runs (absolute epoch ms) evidence/summary.jsonl per-run verdict lines (authoritative; Appendix A is generated from it) evidence/summary-*.txt snapshot written at the end of each runner invocation (the main one predates the F periodic-events re-run and scenario W: see runner-console-F-rerun.txt and runner-console-W.txt) evidence/runner-console-*.txt raw stdout of the runner (includes full stack traces) evidence/environment-*.json browser/Playwright/Bun versions + UA per runner invocation evidence/sdk-0.10.0/ same layout, runs against @maple-dev/effect-sdk 0.10.0 + effect 4.0.0 evidence/headless-new/ same layout, SDK 0.8.2 in full Chromium "new headless" evidence/webkit-firefox-launch.txt why WebKit/Firefox were not tested @@@@@@@@@@ END FILE: MAPLE_BUG_REPORT.txt @@@@@@@@@@ FILE: repro/README.md (8750 bytes) # Repro: @maple-dev/effect-sdk 0.8.2 browser flush / keepalive data loss A standalone project (no dependency on anything outside this folder) that reproduces the issues described in `../MAPLE_BUG_REPORT.txt` in headless Chromium, against a mock Maple/OTLP ingest that records every request it receives. Pinned versions: `@maple-dev/effect-sdk@0.8.2`, `effect@4.0.0-rc.112` (the SDK's Effect peer), `playwright@1.57.0` (Chromium build 1200 = Chromium 143.0.7499.4). Bun 1.4.2 was used. ## Quick start ```sh cd repro bun install bunx playwright install chromium # only if Chromium build 1200 is not already in ~/.cache/ms-playwright bun run start # builds the bundles and serves http://localhost:4173/ (manual poking) bun run scenarios # builds, then runs every scenario in headless Chromium (~40 min; group A alone ~2.5 min in parallel browsers) ``` Run a subset / change settings: ```sh bun run build.ts && bun run run-scenarios.ts B C D # only groups B, C and D REPEAT=1 bun run run-scenarios.ts E # 1 run per variant instead of the default HEADLESS=new bun run run-scenarios.ts G # full Chromium "new headless" (channel "chromium") EVIDENCE_DIR=../evidence bun run run-scenarios.ts # where evidence JSON goes (default ./evidence-out) PAGE_PORT=4183 CROSS_PORT=4184 bun run run-scenarios.ts A # other ports (lets two runners run side by side) ``` Optional — the same page against the newest published SDK (0.10.0, Effect 4.0.0): ```sh (cd variants/sdk-0.10.0 && bun install) bun run build.ts # now also emits public/dist-0.10.0/ SDK=0.10.0 bun run run-scenarios.ts A0 B C D E K # or open http://localhost:4173/?sdk=0.10.0&tag=x ``` ## Layout | file | what | | --- | --- | | `server.ts` | Bun server. Port 4173: test pages + same-origin ingest under `/otlp//v1/*` + `/probe/sink`. Port 4174: the same ingest cross-origin (CORS, preflight with `access-control-max-age: 0`). Logs every request (timestamps for headers-received / body-read / response-sent, byte size, headers of interest, parsed span names / log bodies / metric names / session-event types+seqs / replay chunk headers) to memory and to a JSONL file. Latency / failure injection via rules (`latencyMs`, `latencyCount`, `failStatus`, `failCount`, scoped per run tag). Control API: `POST /__control {rules}`, `GET /__log?tag=`, `POST /__reset`. | | `client/app.ts` | Test page. Query string: `tag`, `variant=flush` (MapleFlush.make) / `layer` (Maple.layer) / `layer-flusher` (Maple.layer + user-land pagehide flush via Effect's `OtlpExporter.Flusher` and `FetchHttpClient.RequestInit {keepalive:true}`), `origin=same/cross`, `interval=/false` (autoFlushInterval), `replay=1/0`. Uses `ingestKey: "~"` like a same-origin proxy that injects the real key server-side. Exposes `window.T` (`spans(n, attrBytes, prefix)`, `span(name)`, `logs(n, bytes, prefix)`, `logMulti()`, `metric()`, `flush()`, `mutate(chars, chunks)`, `journal()`). Journals lifecycle events and every `[MapleClientSDK]`/`[maple]` console warning/error to localStorage tagged with a per-document id, so messages printed during unload can be attributed to the document that printed them. | | `client/probe.ts` + `public/probe.html` | SDK-free page for measuring Chromium's `fetch(keepalive)` limits (`window.P.send(bytes, {delay, keepalive, cross, contentType, consume, utf8TwoByte})`, `P.armUnloadBurst(sizes)`). | | `build.ts` | `Bun.build` (target browser, ESM, code splitting so the SDK's replay engine is a lazy chunk, like in a real app). | | `lib.ts` | Harness: launches Chromium, captures browser console + Playwright `request`/`requestfinished`/`requestfailed`, joins with server records, writes evidence JSON per run. | | `scenarios/b-probe.ts` | Scenario B part 1 (keepalive limits, no SDK). | | `scenarios/sdk.ts` | All SDK scenarios. | | `run-scenarios.ts` | Runs groups, prints + writes a summary. | Every run writes `//-run.json` with: config, result, marks (timeline), the full browser console, every Playwright network event for ingest URLs, every server record for that run (all times in ms relative to run start), and the page's localStorage journal. `/server-requests-all.jsonl` has every ingest request (absolute epoch ms), `summary-*.txt` the aggregated verdicts. ## Scenarios (groups) and what to expect with 0.8.2 | group | scenario | expected / observed with 0.8.2 | | --- | --- | --- | | B | `B-keepalive-probe` (no SDK) | Chromium rejects a keepalive request immediately with `TypeError: Failed to fetch` when the *sum* of in-flight keepalive bodies of the document would exceed 65,536 bytes (UTF-8 bytes). Per document (other tabs and the next document after navigation have their own budget). Released when the response body has finished loading. Cross-origin keepalive + preflight works. | | B | `B-sdk-concurrent-signals` | 40 spans (≈38 KB) + 30 logs (≈32 KB): each fits, but traces/logs/metrics are POSTed concurrently, so the logs POST is rejected → `[MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch`. On reload the logs (and the replay events batch) are lost. | | A0 | `A0-threshold` | ≈ 396 B per plain span → a single flush of ≥ 164 spans (> 65,536 B body) is rejected. | | A | `A-oversized-batch` | 200 spans × 500 B in one flush interval: the batch is rejected, restored, retried after 60 s with even more spans, rejected again… **no span is ever delivered for the rest of the page's life**, while logs keep flowing. | | C | `C-unload-during-cooldown` | One 503 from ingest → 60 s cooldown → the pagehide/visibilitychange flush is skipped (`traces flush skipped (cooldown …)`) → the failed batch and every span created since are lost on reload / navigation / close. | | D | `D-pagehide-behind-inflight-flush`, `D-hang-head-of-line-blocking` | If one traces POST is slow (held 4–7 s by the mock), the pagehide flush is queued behind it by `makeSerializedFlush` and never runs before the document dies → tail spans lost (also a span that ended 5 s before the reload). One slow POST also delays *all* later traces **and logs** exports. | | Dr | `Dr-random-reload-latency` | Same as D, probabilistic: steady spans, reload at a random time, 150–600 ms ingest latency. Counts spans lost. | | E | `E-combined-unload-budget` | At unload the MapleFlush OTLP posts and the replay/session posts share one 64 KiB budget; whichever listener fires first wins. ≈ 60 KB of spans → the session `ended` metadata row and final events batch are rejected; with replay disabled the metadata-only session fires first and the *traces* POST is rejected. Separately, the final replay chunk is never posted on unload at all (its chunk sequence number is consumed, gzip is async) — also with `Maple.layer`. | | F | `F-replay-posts-aborted-by-navigation`, `Fr-random-reloads` | Non-keepalive session posts (5 s events flush, `active` metadata row) in flight during a navigation are aborted → `[maple] session replay … POST failed (dropping; will retry on next chunk)`; nothing is retried. | | G | `G-playwright-keepalive-artifact` | Steady state: Playwright reports keepalive requests as finished. Any request still in flight when the document unloads is reported as `requestfailed net::ERR_ABORTED` even though the server answered 200. | | H | `H-maple-layer-unload` | `Maple.layer`: span created 1 s before reload is lost (documented). `layer-flusher` workaround delivers it. MapleFlush delivers it. | | I | `I-cross-origin` | Cross-origin endpoint works (preflight requests `authorization,content-type,x-maple-sdk`; Chromium ignores the SDK's `user-agent` header). | | K | `K-reload-150ms-after-action` | 1 span + 1 Effect log, reload 150 ms later: both are delivered (server 200), yet the *old* document prints `traces flush failed`, `logs flush failed`, `traces/logs flush skipped (cooldown 60000ms remaining)`, `[maple] session replay events/metadata POST failed` — false alarms caused by the document being torn down while its keepalive requests complete. | | W | `W-workaround-keepalive-downgrade-shim` | A-style burst (200 × 500 B) without / with a user-land `window.fetch` wrapper (`?shim=1`, see `client/app.ts`) that sends keepalive POSTs above ~60 KiB as normal requests. Without: 0/200 burst spans and 0/5 later spans delivered. With: 200/200 and 5/5. | | L | `L-multi-arg-log-body` | `Effect.logError(a, b, c, obj)` is exported as an OTLP `arrayValue` body (structured), not a JSON string. | WebKit could not be launched on the machine used for the report (missing system libraries), and Firefox was not available, so only Chromium results exist. @@@@@@@@@@ END FILE: repro/README.md @@@@@@@@@@ FILE: repro/package.json (379 bytes) { "name": "maple-effect-sdk-keepalive-repro", "private": true, "type": "module", "scripts": { "build": "bun run build.ts", "start": "bun run build.ts && bun run server.ts", "scenarios": "bun run build.ts && bun run run-scenarios.ts" }, "dependencies": { "@maple-dev/effect-sdk": "0.8.2", "effect": "4.0.0-rc.112", "playwright": "1.57.0" } } @@@@@@@@@@ END FILE: repro/package.json @@@@@@@@@@ FILE: repro/.gitignore (153 bytes) node_modules/ public/dist/ variants/sdk-0.10.0/app.ts variants/sdk-0.10.0/node_modules/ public/dist-0.10.0/ scratch/ server-requests.jsonl evidence-out/ @@@@@@@@@@ END FILE: repro/.gitignore @@@@@@@@@@ FILE: repro/build.ts (1913 bytes) // Bundle the browser entry points with Bun (code splitting on, so the SDK's // dynamic import of the replay engine becomes its own chunk, as in a real app). // // Main build: client/app.ts against @maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112 -> public/dist/ // Optional build: if variants/sdk-0.10.0/node_modules exists (cd variants/sdk-0.10.0 && bun install), // the same app.ts is also built against @maple-dev/effect-sdk@0.10.0 + effect@4.0.0 // -> public/dist-0.10.0/ (load the page with ?sdk=0.10.0). Effect 4.0.0 renamed // effect/unstable/{http,observability} to effect/{http,observability}; the copy is // rewritten accordingly. Nothing else differs. import { existsSync, readFileSync, writeFileSync } from "node:fs" const build = async (entrypoints: string[], outdir: string) => { const result = await Bun.build({ entrypoints, outdir, target: "browser", format: "esm", splitting: true, minify: false, naming: { entry: "[name].js", chunk: "chunk-[hash].js" }, }) if (!result.success) { for (const log of result.logs) console.error(log) process.exit(1) } for (const o of result.outputs) console.log(`built ${o.path.replace(process.cwd() + "/", "")} (${o.size} B)`) } await build(["./client/app.ts", "./client/probe.ts"], "./public/dist") if (existsSync("./variants/sdk-0.10.0/node_modules/@maple-dev/effect-sdk")) { const src = readFileSync("./client/app.ts", "utf8").replaceAll("effect/unstable/http", "effect/http").replaceAll("effect/unstable/observability", "effect/observability") writeFileSync("./variants/sdk-0.10.0/app.ts", `// GENERATED by build.ts from client/app.ts — do not edit\n${src}`) await build(["./variants/sdk-0.10.0/app.ts"], "./public/dist-0.10.0") } else { console.log("(variants/sdk-0.10.0 not installed — skipping the optional 0.10.0 build)") } @@@@@@@@@@ END FILE: repro/build.ts @@@@@@@@@@ FILE: repro/bun.lock (4821 bytes) { "lockfileVersion": 2, "configVersion": 1, "workspaces": { "": { "name": "maple-effect-sdk-keepalive-repro", "dependencies": { "@maple-dev/effect-sdk": "0.8.2", "effect": "4.0.0-rc.112", "playwright": "1.57.0", }, }, }, "packages": { "@maple-dev/effect-sdk": ["@maple-dev/effect-sdk@0.8.2", "", { "dependencies": { "std-env": "^4.0.0" }, "peerDependencies": { "effect": ">=4.0.0-rc.111" } }, "sha512-bbkmSWI/RVcLWyFL1czayCw+wCCq3v0vYM3n08/O0rT63dwvHepYy+2cV0rXHo0FPMplvi5FUV0lKAQobJ0DvQ=="], "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="], "@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w=="], "@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw=="], "@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw=="], "@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ=="], "@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "effect": ["effect@4.0.0-rc.112", "", { "dependencies": { "fast-check": "^4.9.0", "msgpackr": "^2.0.5" } }, "sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A=="], "fast-check": ["fast-check@4.10.2", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw=="], "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "msgpackr": ["msgpackr@2.1.0", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ=="], "msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="], "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], "playwright": ["playwright@1.57.0", "", { "dependencies": { "playwright-core": "1.57.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw=="], "playwright-core": ["playwright-core@1.57.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ=="], "pure-rand": ["pure-rand@8.4.2", "", {}, "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng=="], "std-env": ["std-env@4.3.0", "", {}, "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag=="], } } @@@@@@@@@@ END FILE: repro/bun.lock @@@@@@@@@@ FILE: repro/client/app.ts (9255 bytes) /** * Test page client. Configured entirely through the query string: * * ?tag= required; becomes part of the ingest endpoint path * &variant=flush|layer|layer-flusher * flush -> MapleFlush.make(...) (the configuration under test) * layer -> Maple.layer(...) (baseline, documented "no unload flush") * layer-flusher -> Maple.layer(...) + user-land pagehide hook calling Effect's * OtlpExporter.Flusher, with FetchHttpClient.RequestInit {keepalive:true} * (a possible workaround, NOT something the SDK does) * &origin=same|cross same: endpoint = /otlp/ (like a same-origin ingest proxy) * cross: endpoint = http://127.0.0.1:/otlp/ * &interval=|false MapleFlush autoFlushInterval (default: SDK default 5000) * &replay=1|0 replay.enabled (default 1 = SDK default) * &crossPort=4174 * * Exposes `window.T` for the Playwright runner (and for manual poking in DevTools). */ import { Maple, MapleFlush } from "@maple-dev/effect-sdk/client" import { Effect, Layer, ManagedRuntime, Metric } from "effect" import { FetchHttpClient } from "effect/unstable/http" import { OtlpExporter } from "effect/unstable/observability" const params = new URLSearchParams(location.search) const tag = params.get("tag") ?? "manual" const variant = params.get("variant") ?? "flush" const origin = params.get("origin") ?? "same" const crossPort = params.get("crossPort") ?? "4174" const intervalParam = params.get("interval") const autoFlushInterval = intervalParam === null ? undefined : intervalParam === "false" ? (false as const) : Number(intervalParam) const replayEnabled = (params.get("replay") ?? "1") === "1" const endpoint = origin === "cross" ? `http://127.0.0.1:${crossPort}/otlp/${tag}` : `${location.origin}/otlp/${tag}` // --- lifecycle journal (survives reloads via localStorage so the runner can read it afterwards) const journalKey = `repro.journal.${tag}` const journal = (event: string, extra: Record = {}) => { try { const list = JSON.parse(localStorage.getItem(journalKey) ?? "[]") list.push({ t: Date.now(), event, href: location.pathname + location.search, ...extra }) localStorage.setItem(journalKey, JSON.stringify(list)) } catch {} } // One id per loaded document, so every journal entry (and every SDK console message, see below) // can be attributed to the document that produced it — even messages printed while it unloads. const docId = Math.random().toString(36).slice(2, 8) let pagehideFired = false journal("script-start", { docId, variant, origin, autoFlushInterval, replayEnabled }) addEventListener("pagehide", (e) => { pagehideFired = true journal("pagehide(page listener, registered before SDK)", { docId, persisted: (e as PageTransitionEvent).persisted }) }) document.addEventListener("visibilitychange", () => journal(`visibilitychange:${document.visibilityState}`, { docId })) // Mirror SDK console warnings/errors into the localStorage journal synchronously, tagged with the // document id and whether pagehide already fired in this document. (Installed before the SDK, so // the replay engine's own console wrapper later wraps this one; behaviour is unchanged.) for (const level of ["warn", "error"] as const) { const orig = console[level] console[level] = (...args: unknown[]) => { const first = typeof args[0] === "string" ? args[0] : "" if (first.startsWith("[MapleClientSDK]") || first.startsWith("[maple]")) journal(`console.${level}`, { docId, afterPagehide: pagehideFired, visibility: document.visibilityState, text: `${first} ${args.slice(1).map((a) => String(a)).join(" ")}`.slice(0, 300) }) orig.apply(console, args) } } // Optional user-land workaround (?shim=1): installed BEFORE the SDK. Any keepalive POST whose body // would not fit Chromium's 64 KiB keepalive budget is sent as a normal (non-keepalive) request instead // of being rejected outright. This does not help during unload, but it stops one oversized batch from // disabling export for the rest of the page's life. (NOT part of the SDK.) if (params.get("shim") === "1") { const origFetch = window.fetch.bind(window) const LIMIT = 60 * 1024 window.fetch = (input: RequestInfo | URL, init?: RequestInit) => { if (init?.keepalive && typeof init.body === "string" && new TextEncoder().encode(init.body).byteLength > LIMIT) { journal("shim: keepalive downgraded", { bytes: new TextEncoder().encode(init.body).byteLength }) return origFetch(input, { ...init, keepalive: false }) } return origFetch(input, init) } } const common = { serviceName: "repro-web", environment: "repro", endpoint, ingestKey: "~", // placeholder, exactly like a same-origin proxy that injects the real key server-side replay: { enabled: replayEnabled }, } let flushFn: (() => Promise) | undefined let layer: Layer.Layer if (variant === "flush") { const telemetry = MapleFlush.make({ ...common, ...(autoFlushInterval !== undefined ? { autoFlushInterval } : {}) }) flushFn = telemetry.flush layer = telemetry.layer } else if (variant === "layer") { layer = Maple.layer(common) } else if (variant === "layer-flusher") { layer = Maple.layer(common).pipe(Layer.provide(Layer.succeed(FetchHttpClient.RequestInit, { keepalive: true }))) } else { throw new Error(`unknown variant ${variant}`) } const runtime = ManagedRuntime.make(layer) if (variant === "layer-flusher") { const flushAll = Effect.gen(function* () { const flusher = yield* OtlpExporter.Flusher yield* flusher.flush }) flushFn = () => runtime.runPromise(flushAll as Effect.Effect) addEventListener("pagehide", () => void flushFn!()) document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") void flushFn!() }) } const counter = Metric.counter("repro_counter") const randomText = (n: number) => { // random (incompressible-ish) text, so gzip'd replay chunks stay large const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" let s = "" const buf = new Uint8Array(n) crypto.getRandomValues(buf) for (let i = 0; i < n; i++) s += alphabet[buf[i]! % alphabet.length] return s } const T = { tag, docId, variant, endpoint, ready: false, /** Create `n` sibling spans named `${prefix}-${i}`, each with a `pad` attribute of `attrBytes` chars. */ spans: async (n: number, attrBytes = 0, prefix = "span") => { const pad = "x".repeat(attrBytes) await runtime.runPromise( Effect.forEach( Array.from({ length: n }, (_, i) => i), (i) => Effect.void.pipe(Effect.withSpan(`${prefix}-${i}`, { attributes: attrBytes > 0 ? { pad } : {} })), { discard: true }, ), ) journal("spans-created", { n, attrBytes, prefix }) return Date.now() }, /** One span with an explicit name. */ span: async (name: string, attrBytes = 0) => { await runtime.runPromise(Effect.void.pipe(Effect.withSpan(name, { attributes: attrBytes > 0 ? { pad: "x".repeat(attrBytes) } : {} }))) journal("span-created", { name }) return Date.now() }, logs: async (n: number, bytes = 0, prefix = "log") => { const pad = "y".repeat(bytes) await runtime.runPromise( Effect.forEach( Array.from({ length: n }, (_, i) => i), (i) => Effect.logInfo(`${prefix}-${i} ${pad}`), { discard: true }, ), ) journal("logs-created", { n, bytes, prefix }) return Date.now() }, /** Effect log with several message arguments (Effect.logError(a, b, c, obj)). */ logMulti: async (prefix = "multi") => { await runtime.runPromise(Effect.logError(`${prefix} Failed to connect`, new Error("ClientError: boom"), "Unknown Error", { code: 42, nested: { a: 1 } })) return Date.now() }, metric: async () => { await runtime.runPromise(Metric.update(counter, 1)) return Date.now() }, flush: async () => { if (!flushFn) return "no-flush-fn" await flushFn() return "flushed" }, /** Append `bytes` of random text to the DOM (drives rrweb incremental-snapshot size). */ mutate: (bytes: number, chunks = 1) => { const host = document.getElementById("host")! for (let c = 0; c < chunks; c++) { const div = document.createElement("div") div.textContent = randomText(Math.floor(bytes / chunks)) host.appendChild(div) } return Date.now() }, journal: () => JSON.parse(localStorage.getItem(journalKey) ?? "[]"), clearJournal: () => localStorage.removeItem(journalKey), } ;(globalThis as any).T = T // Build the runtime eagerly so the tracer/logger are installed before the runner acts. runtime.runPromise(Effect.void).then(() => { T.ready = true journal("runtime-ready") document.getElementById("status")!.textContent = `ready: variant=${variant} endpoint=${endpoint}` }) document.getElementById("btn-spans")?.addEventListener("click", () => void T.spans(50, 500, "click")) document.getElementById("btn-reload")?.addEventListener("click", () => location.reload()) @@@@@@@@@@ END FILE: repro/client/app.ts @@@@@@@@@@ FILE: repro/client/probe.ts (2686 bytes) /** * SDK-independent probe of Chromium's fetch(keepalive) limits. * Exposes `window.P`. Every request goes to the mock server's /probe/sink, * which records arrival + byte size and can delay its response (?delay=ms) so a * request stays "in flight" for as long as we want. */ const params = new URLSearchParams(location.search) const tag = params.get("tag") ?? "probe" const crossPort = params.get("crossPort") ?? "4174" export interface SendOptions { readonly delay?: number readonly keepalive?: boolean readonly cross?: boolean readonly contentType?: string readonly id?: string /** Use a body made of a 2-byte UTF-8 character ("é") instead of ASCII, so JS string.length = bytes / 2. */ readonly utf8TwoByte?: boolean /** Read the response body (await res.text()) before resolving. The SDK's keepalive transport does NOT do this. */ readonly consume?: boolean } const send = async (bytes: number, opts: SendOptions = {}) => { const id = opts.id ?? `p${Math.random().toString(36).slice(2, 8)}` const base = opts.cross ? `http://127.0.0.1:${crossPort}` : location.origin const url = `${base}/probe/sink?tag=${encodeURIComponent(tag)}&id=${id}&delay=${opts.delay ?? 0}` const body = opts.utf8TwoByte ? "é".repeat(Math.floor(bytes / 2)) : "x".repeat(bytes) const t0 = performance.now() try { const res = await fetch(url, { method: "POST", keepalive: opts.keepalive ?? true, headers: opts.contentType ? { "content-type": opts.contentType } : undefined, body, }) if (opts.consume) await res.text() return { id, bytes, jsStringLength: body.length, ok: res.ok, status: res.status, consumed: !!opts.consume, ms: Math.round(performance.now() - t0) } } catch (error) { return { id, bytes, jsStringLength: body.length, ok: false, error: String(error), ms: Math.round(performance.now() - t0) } } } const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) /** Arm a pagehide handler that fires the given keepalive posts (no await possible during unload). */ const armUnloadBurst = (sizes: number[], opts: SendOptions = {}) => { addEventListener("pagehide", () => { sizes.forEach((bytes, i) => { void send(bytes, { ...opts, id: `${opts.id ?? "unload"}-${i}` }).then((r) => { try { const prev = JSON.parse(localStorage.getItem(`probe.unload.${tag}`) ?? "[]") prev.push(r) localStorage.setItem(`probe.unload.${tag}`, JSON.stringify(prev)) } catch {} }) }) }) return "armed" } ;(globalThis as any).P = { tag, send, sleep, armUnloadBurst, ready: true } document.getElementById("status")!.textContent = `probe ready (tag=${tag})` @@@@@@@@@@ END FILE: repro/client/probe.ts @@@@@@@@@@ FILE: repro/lib.ts (7349 bytes) /** * Shared harness for run-scenarios.ts: browser launch, per-run capture of * browser console + Playwright network events + server-side ingest records, * and evidence writing. */ import { chromium, type Browser, type BrowserContext, type Page } from "playwright" import { mkdirSync, writeFileSync, appendFileSync } from "node:fs" import { join } from "node:path" import { startServer, type IngestRecord, type Rule } from "./server.ts" export const PAGE_PORT = Number(process.env.PAGE_PORT ?? 4173) export const CROSS_PORT = Number(process.env.CROSS_PORT ?? 4174) export const EVIDENCE = process.env.EVIDENCE_DIR ?? "./evidence-out" /** "shell" = Playwright default (chromium-headless-shell); "new" = full Chromium in new headless mode (channel "chromium"). */ export const HEADLESS = process.env.HEADLESS ?? "shell" mkdirSync(EVIDENCE, { recursive: true }) export const server = startServer({ pagePort: PAGE_PORT, crossPort: CROSS_PORT, logFile: join(EVIDENCE, "server-requests-all.jsonl") }) export const launch = (mode: string = HEADLESS): Promise => chromium.launch(mode === "new" ? { channel: "chromium" } : {}) export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) let tagCounter = 0 export const newTag = (scenario: string) => `${scenario}-${Date.now().toString(36)}-${(tagCounter++).toString(36)}` export interface ConsoleEntry { t: number type: string text: string page: string } export interface PwEntry { t: number ev: "request" | "finished" | "failed" method: string url: string postBytes?: number failure?: string | null status?: number } const shortUrl = (u: string) => u.replace(`http://localhost:${PAGE_PORT}`, "").replace(`http://127.0.0.1:${CROSS_PORT}`, "[cross]") export class Run { readonly t0 = Date.now() readonly console: ConsoleEntry[] = [] readonly pw: PwEntry[] = [] readonly marks: { t: number; name: string; [k: string]: unknown }[] = [] constructor( readonly scenario: string, readonly label: string, readonly index: number, readonly tag: string, readonly config: Record = {}, ) {} rel(t: number = Date.now()) { return t - this.t0 } mark(name: string, extra: Record = {}) { this.marks.push({ t: this.rel(), name, ...extra }) } attach(page: Page) { page.on("console", (m) => this.console.push({ t: this.rel(), type: m.type(), text: m.text(), page: shortUrl(page.url()) })) page.on("pageerror", (e) => this.console.push({ t: this.rel(), type: "pageerror", text: String(e), page: shortUrl(page.url()) })) page.on("request", (r) => { if (!/\/otlp\/|\/probe\//.test(r.url())) return let postBytes: number | undefined try { postBytes = r.postDataBuffer()?.length } catch {} this.pw.push({ t: this.rel(), ev: "request", method: r.method(), url: shortUrl(r.url()), postBytes }) }) page.on("requestfinished", async (r) => { if (!/\/otlp\/|\/probe\//.test(r.url())) return const t = this.rel() let status: number | undefined try { status = (await r.response())?.status() } catch {} this.pw.push({ t, ev: "finished", method: r.method(), url: shortUrl(r.url()), status }) }) page.on("requestfailed", (r) => { if (!/\/otlp\/|\/probe\//.test(r.url())) return this.pw.push({ t: this.rel(), ev: "failed", method: r.method(), url: shortUrl(r.url()), failure: r.failure()?.errorText ?? null }) }) } records(): IngestRecord[] { return server.records.filter((r) => r.tag === this.tag) } /** Every span name that reached the ingest for this run (POSTs answered 200 only). */ arrivedSpans(): Map { const out = new Map() for (const r of this.records()) { const s = r.summary as any if (r.method !== "POST" || s?.kind !== "traces" || r.status !== 200) continue for (const n of s.spanNames) out.set(n, this.rel(r.tRecv)) } return out } arrivedLogs(): Map { const out = new Map() for (const r of this.records()) { const s = r.summary as any if (r.method !== "POST" || s?.kind !== "logs" || r.status !== 200) continue for (const b of s.logBodies) out.set(String(b).split(" ")[0]!, this.rel(r.tRecv)) } return out } consoleMatching(re: RegExp) { return this.console.filter((c) => re.test(c.text)) } async waitForRecord(pred: (r: IngestRecord) => boolean, timeoutMs = 15_000): Promise { const end = Date.now() + timeoutMs while (Date.now() < end) { const hit = this.records().find(pred) if (hit) return hit await sleep(25) } return undefined } save(result: Record, extra: Record = {}) { const dir = join(EVIDENCE, this.scenario) mkdirSync(dir, { recursive: true }) const file = join(dir, `${this.label}-run${this.index}.json`) const records = this.records().map((r) => ({ ...r, tRecv: this.rel(r.tRecv), tBody: r.tBody !== undefined ? this.rel(r.tBody) : undefined, tResp: r.tResp !== undefined ? this.rel(r.tResp) : undefined, })) writeFileSync( file, JSON.stringify( { scenario: this.scenario, label: this.label, run: this.index, tag: this.tag, note: "all t* values are milliseconds relative to the start of this run", config: this.config, result, marks: this.marks, browserConsole: this.console, playwrightNetwork: this.pw, serverRecords: records, ...extra, }, null, 2, ), ) return file } } export const appUrl = (tag: string, q: Record) => { const p = new URLSearchParams({ tag, crossPort: String(CROSS_PORT) }) for (const [k, v] of Object.entries(q)) p.set(k, String(v)) return `http://localhost:${PAGE_PORT}/?${p}` } export const openApp = async (ctx: BrowserContext, run: Run, q: Record) => { const page = await ctx.newPage() run.attach(page) run.mark("goto") await page.goto(appUrl(run.tag, q)) await page.waitForFunction(() => (globalThis as any).T?.ready === true, undefined, { timeout: 15_000 }) run.mark("ready") return page } export const waitReady = async (page: Page) => page.waitForFunction(() => (globalThis as any).T?.ready === true, undefined, { timeout: 15_000 }) /** Add failure/latency rules scoped to one run's tag (so runs can execute in parallel). */ export const addRulesFor = (run: Run, rules: Omit[]) => { server.addRules(rules.map((r) => ({ ...r, tag: run.tag }))) run.config.rules = [...((run.config.rules as unknown[]) ?? []), ...rules] } export const clearRulesFor = (run: Run) => server.clearRulesForTag(run.tag) export interface SummaryRow { scenario: string label: string run: number verdict: string details: string evidence: string } export const summary: SummaryRow[] = [] export const addSummary = (row: SummaryRow) => { summary.push(row) appendFileSync(join(EVIDENCE, "summary.jsonl"), `${JSON.stringify(row)}\n`) console.log(`[${row.scenario}/${row.label}#${row.run}] ${row.verdict} — ${row.details}`) } @@@@@@@@@@ END FILE: repro/lib.ts @@@@@@@@@@ FILE: repro/public/index.html (629 bytes) maple effect-sdk keepalive repro

maple effect-sdk keepalive repro

loading…

@@@@@@@@@@ END FILE: repro/public/index.html @@@@@@@@@@ FILE: repro/public/probe.html (293 bytes) keepalive probe (no SDK)

fetch(keepalive) probe — no SDK on this page

loading…

@@@@@@@@@@ END FILE: repro/public/probe.html @@@@@@@@@@ FILE: repro/run-scenarios.ts (8382 bytes) /** * Playwright runner for all scenarios. * * bun run run-scenarios.ts # everything (≈ 25 min) * bun run run-scenarios.ts B C D # only some groups * REPEAT=1 bun run run-scenarios.ts E # override repeat count for every group * HEADLESS=new bun run run-scenarios.ts G # full Chromium "new headless" instead of headless-shell * EVIDENCE_DIR=../evidence bun run run-scenarios.ts * * Groups: B (probe + SDK concurrency), A0, A, C, D, Dr, E, F, G, H, I, K, L, W */ import { writeFileSync } from "node:fs" import { join } from "node:path" import { chromium } from "playwright" import { EVIDENCE, HEADLESS, launch, server, summary } from "./lib.ts" import { scenarioBProbe } from "./scenarios/b-probe.ts" import * as S from "./scenarios/sdk.ts" const args = process.argv.slice(2) const groups = args.length > 0 ? new Set(args) : new Set(["B", "A0", "A", "C", "D", "Dr", "E", "F", "G", "H", "I", "K", "L", "W"]) // SDK=0.10.0 runs the same scenarios against the optional latest-SDK build (see build.ts) if (process.env.SDK) S.setExtraQuery({ sdk: process.env.SDK }) const REPEAT = process.env.REPEAT ? Number(process.env.REPEAT) : undefined const rep = (n: number) => REPEAT ?? n const startedAt = new Date() const browser = await launch() const env = { sdkUnderTest: process.env.SDK ? `@maple-dev/effect-sdk@${process.env.SDK} (optional variant build)` : "@maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112", startedAt: startedAt.toISOString(), headlessMode: HEADLESS === "new" ? "new headless (full Chromium, channel 'chromium')" : "chromium-headless-shell (Playwright default)", chromiumVersion: browser.version(), playwrightVersion: (await import("playwright/package.json", { with: { type: "json" } })).default.version, bunVersion: Bun.version, platform: `${process.platform} ${process.arch}`, userAgent: await (async () => { const p = await browser.newPage() const ua = await p.evaluate(() => navigator.userAgent) await p.close() return ua })(), groups: [...groups], } console.log("environment:", env) writeFileSync(join(EVIDENCE, `environment-${HEADLESS}${process.env.SDK ? `-sdk${process.env.SDK}` : ""}-${args.join("_") || "all"}.json`), JSON.stringify(env, null, 2)) const times = async (n: number, fn: (i: number) => Promise) => { for (let i = 0; i < n; i++) await fn(i) } if (groups.has("B")) { await times(rep(2), (i) => scenarioBProbe(browser, i)) await times(rep(3), (i) => S.Bsdk(browser, i, "manual-flush", "flush")) await times(rep(3), (i) => S.Bsdk(browser, i, "reload", "reload")) } if (groups.has("A0")) { for (const n of [100, 150, 160, 163, 164, 165, 170, 200]) await times(rep(1), (i) => S.A0(browser, i, n)) } if (groups.has("A")) { // long-running (≈ 2.5 min each); every run gets its own browser process and they run in parallel const variants: [string, Record, Parameters[4]][] = [ ["burst200x500B-interval5000(default)", {}, { burstSpans: 200, burstAttrBytes: 500, observeMs: 130_000 }], ["burst200x500B-interval1000", { interval: 1000 }, { burstSpans: 200, burstAttrBytes: 500, observeMs: 130_000 }], ["steady30spansPerSec-10s-interval5000(default)", {}, { burstSpans: 0, burstAttrBytes: 0, steadyPerSec: 30, steadySecs: 10, observeMs: 75_000 }], ["steady40spansPerSec-10s-interval5000(default)", {}, { burstSpans: 0, burstAttrBytes: 0, steadyPerSec: 40, steadySecs: 10, observeMs: 75_000 }], ] const jobs: Promise[] = [] for (const [label, q, opts] of variants) for (let i = 0; i < rep(2); i++) jobs.push( (async () => { const b = await launch() try { await S.A(b, i, label, q, opts) } finally { await b.close() } })(), ) await Promise.all(jobs) } if (groups.has("C")) { await times(rep(3), (i) => S.C(browser, i, "inject503-then-reload", "reload", true)) await times(rep(3), (i) => S.C(browser, i, "inject503-then-navigate", "navigate", true)) await times(rep(3), (i) => S.C(browser, i, "inject503-then-close", "close", true)) await times(rep(3), (i) => S.C(browser, i, "control-no-failure-reload", "reload", false)) } if (groups.has("D")) { await times(rep(3), (i) => S.D(browser, i, "first-POST-4000ms-interval1000", 4000, 1000)) await times(rep(3), (i) => S.D(browser, i, "first-POST-4000ms-interval5000", 4000, 5000)) await times(rep(3), (i) => S.D(browser, i, "first-POST-7000ms-interval1000-tail-5s-before-reload", 7000, 1000, 5000)) await times(rep(3), (i) => S.D(browser, i, "control-no-latency-interval1000", 0, 1000)) await times(rep(2), (i) => S.Dhang(browser, i, "first-POST-held-15s", 15000)) } if (groups.has("Dr")) { await times(rep(10), (i) => S.Dr(browser, i, "latency150-interval1000", 150, 1000)) await times(rep(10), (i) => S.Dr(browser, i, "latency600-interval1000", 600, 1000)) await times(rep(10), (i) => S.Dr(browser, i, "latency600-interval5000", 600, 5000)) await times(rep(10), (i) => S.Dr(browser, i, "control-latency0-interval1000", 0, 1000)) } if (groups.has("E")) { await times(rep(3), (i) => S.E(browser, i, "E1-spans45-plus-60kB-DOM-mutation", {}, { spans: 45, spanAttr: 0, mutateChars: 60000, consoleLines: 0 })) await times(rep(3), (i) => S.E(browser, i, "E2-spans150(~60kB)-plus-30-console-lines", {}, { spans: 150, spanAttr: 0, mutateChars: 0, consoleLines: 30 })) await times(rep(3), (i) => S.E(browser, i, "E3-replay-disabled-spans150(~60kB)", { replay: 0 }, { spans: 150, spanAttr: 0, mutateChars: 0, consoleLines: 0 })) await times(rep(3), (i) => S.E(browser, i, "E4-no-spans-small-DOM-mutation", {}, { spans: 0, spanAttr: 0, mutateChars: 2000, consoleLines: 3 })) await times(rep(3), (i) => S.E(browser, i, "E5-Maple.layer-small-DOM-mutation", { variant: "layer" }, { spans: 0, spanAttr: 0, mutateChars: 2000, consoleLines: 3 })) } if (groups.has("F")) { for (const v of ["flush", "layer"]) { await times(rep(2), (i) => S.F(browser, i, `${v}-periodic-events-POST-in-flight`, v, "events")) await times(rep(2), (i) => S.F(browser, i, `${v}-active-meta-POST-in-flight`, v, "meta")) await times(rep(2), (i) => S.Fr(browser, i, `${v}-8-random-reloads-latency300`, v, 300)) } } if (groups.has("G")) { await times(rep(2), (i) => S.G(browser, i, "steady-state-no-navigation", false)) await times(rep(2), (i) => S.G(browser, i, "with-final-reload", true)) } if (groups.has("H")) { for (const v of ["layer", "layer-flusher", "flush"]) await times(rep(3), (i) => S.H(browser, i, v, v)) } if (groups.has("I")) { await times(rep(2), (i) => S.Icross(browser, i, "same-origin", "same")) await times(rep(2), (i) => S.Icross(browser, i, "cross-origin", "cross")) } if (groups.has("K")) { await times(rep(3), (i) => S.K(browser, i, "replay-on(default)", {})) await times(rep(3), (i) => S.K(browser, i, "replay-off", { replay: 0 })) } if (groups.has("L")) await times(rep(1), (i) => S.L(browser, i)) if (groups.has("W")) { await times(rep(2), (i) => S.W(browser, i, "without-shim", false)) await times(rep(2), (i) => S.W(browser, i, "with-keepalive-downgrade-shim", true)) } await browser.close() server.stop() // ---- summary const grouped = new Map; details: string[] }>() for (const row of summary) { const key = `${row.scenario} / ${row.label}` const g = grouped.get(key) ?? { verdicts: new Map(), details: [] } g.verdicts.set(row.verdict, (g.verdicts.get(row.verdict) ?? 0) + 1) g.details.push(` run ${row.run}: ${row.verdict} — ${row.details.split("\n")[0]}\n evidence: ${row.evidence}`) grouped.set(key, g) } let out = `Scenario summary (${env.headlessMode}, Chromium ${env.chromiumVersion}, Playwright ${env.playwrightVersion}, Bun ${env.bunVersion})\nstarted ${env.startedAt}, finished ${new Date().toISOString()}\n\n` for (const [key, g] of grouped) { const total = [...g.verdicts.values()].reduce((a, b) => a + b, 0) out += `${key} [${[...g.verdicts.entries()].map(([v, n]) => `${v}: ${n}/${total}`).join("; ")}]\n${g.details.join("\n")}\n\n` } const file = join(EVIDENCE, `summary-${HEADLESS}${process.env.SDK ? `-sdk${process.env.SDK}` : ""}-${startedAt.toISOString().replace(/[:.]/g, "-")}.txt`) writeFileSync(file, out) console.log(`\n${out}\nsummary written to ${file}`) process.exit(0) @@@@@@@@@@ END FILE: repro/run-scenarios.ts @@@@@@@@@@ FILE: repro/scenarios/b-probe.ts (9438 bytes) /** * Scenario B (part 1): SDK-independent measurement of Chromium's fetch(keepalive) * body limits, using /probe.html (no Maple SDK on the page). * * Every sub-test starts from a freshly loaded document so that budget held by a * previous sub-test cannot leak into the next one. */ import type { Browser, Page } from "playwright" import { PAGE_PORT, Run, addSummary, newTag, sleep } from "../lib.ts" const probeUrl = (tag: string) => `http://localhost:${PAGE_PORT}/probe.html?tag=${tag}` const arrivedIds = (run: Run, prefix: string) => run .records() .filter((r) => r.method === "POST" && r.query.includes(`id=${prefix}`)) .map((r) => ({ id: new URLSearchParams(r.query).get("id"), bytes: r.bytes, t: run.rel(r.tRecv) })) const ev = (page: Page, fn: string): Promise => page.evaluate(`(async () => { const P = globalThis.P; ${fn} })()`) as Promise export const scenarioBProbe = async (browser: Browser, index: number) => { const tag = newTag("B-probe") const run = new Run("B-keepalive-probe", "probe", index, tag, { page: "/probe.html (no SDK)" }) const ctx = await browser.newContext() const page = await ctx.newPage() run.attach(page) const fresh = async (p: Page = page) => { await p.goto(probeUrl(tag)) await p.waitForFunction(() => (globalThis as any).P?.ready) } const result: Record = {} const ok = (r: any) => (r.ok ? "ok" : "REJECTED") // B1: single request, nothing else in flight (fresh document per size) const b1: unknown[] = [] for (const size of [1024, 49152, 65535, 65536, 65537, 70000, 131072]) { await fresh() b1.push(await ev(page, `return P.send(${size}, { id: "B1-${size}", consume: true })`)) } result.B1_single = b1 await fresh() result.B1b_single_json_65536_then_65537 = await ev(page, `return [await P.send(65536, { id: "B1b-65536", contentType: "application/json", consume: true }), await P.send(65537, { id: "B1b-65537", contentType: "application/json", consume: true })]`) // B2: concurrent requests whose SUM exceeds 64 KiB although each is small; the sink // holds each response 2 s so they are all in flight together. await fresh() const b2a = await ev(page, `return Promise.all([P.send(40000, { delay: 2000, id: "B2a-0" }), P.send(40000, { delay: 2000, id: "B2a-1" })])`) await fresh() const b2b = await ev(page, `return Promise.all([P.send(30000, { delay: 2000, id: "B2b-0" }), P.send(30000, { delay: 2000, id: "B2b-1" })])`) await fresh() const b2c = await ev(page, `return Promise.all([P.send(30000, { delay: 2000, id: "B2c-0" }), P.send(30000, { delay: 2000, id: "B2c-1" }), P.send(10000, { delay: 2000, id: "B2c-2" })])`) await fresh() const b2d = await ev(page, `return Promise.all([P.send(20000, { delay: 2000, id: "B2d-0" }), P.send(20000, { delay: 2000, id: "B2d-1" }), P.send(20000, { delay: 2000, id: "B2d-2" }), P.send(20000, { delay: 2000, id: "B2d-3" })])`) result.B2_concurrent = { two_x_40000: b2a, two_x_30000: b2b, three_30000_30000_10000: b2c, four_x_20000: b2d } // B3: release timing. Hold 60 kB for 2 s; 10 kB at +500 ms; 10 kB after the hold completes. await fresh() result.B3_release_timing = await ev(page, ` const hold = P.send(60000, { delay: 2000, id: "B3-hold60000", consume: true }) await P.sleep(500) const during = await P.send(10000, { id: "B3-during", consume: true }) const held = await hold const after = await P.send(10000, { id: "B3-after", consume: true }) return { held, during_plus500ms: during, after_hold_completed: after }`) // B3b: budget is held until the response BODY finishes loading, not until fetch() resolves. // 8 sequential 40000 B keepalive POSTs, each awaited; variants: body consumed or not, gap between. const seq = async (opts: string, gapMs: number) => { await fresh() return ev(page, `const out = []; for (let i = 0; i < 8; i++) { const r = await P.send(40000, ${opts}); out.push(r.ok ? "ok" : "X"); if (${gapMs}) await P.sleep(${gapMs}) } return out.join(" ")`) } result.B3b_sequential_8x40000 = { "response body NOT read, no gap (what the SDK transport does)": await seq(`{}`, 0), "response body read (await res.text()), no gap": await seq(`{ consume: true }`, 0), "response body NOT read, 50 ms gap": await seq(`{}`, 50), } // B4: cross-origin (CORS preflight, content-type: application/json) await fresh() const b4single = await ev(page, `return P.send(1000, { cross: true, contentType: "application/json", id: "B4-single", consume: true })`) await fresh() const b4over = await ev(page, `return P.send(65537, { cross: true, contentType: "application/json", id: "B4-over" })`) await fresh() const b4conc = await ev(page, `return Promise.all([P.send(40000, { cross: true, contentType: "application/json", delay: 2000, id: "B4c-0" }), P.send(40000, { cross: true, contentType: "application/json", delay: 2000, id: "B4c-1" })])`) result.B4_cross_origin = { single_1000_json: b4single, single_65537_json: b4over, two_x_40000_json_concurrent: b4conc } // B5: control — without keepalive there is no size limit await fresh() result.B5_no_keepalive_200000 = await ev(page, `return P.send(200000, { keepalive: false, id: "B5", consume: true })`) // B6: the limit is in UTF-8 BYTES, not JS string length const b6: Record = {} for (const bytes of [60000, 65536, 80000, 98304]) { await fresh() b6[`utf8_bytes_${bytes}_strlen_${bytes / 2}`] = await ev(page, `return P.send(${bytes}, { utf8TwoByte: true, id: "B6-${bytes}", consume: true })`) } result.B6_utf8 = b6 // B7: scope of the budget — other tab (same context) / other context while tab 1 holds 60 kB await fresh() const page2 = await ctx.newPage() run.attach(page2) await fresh(page2) const otherCtx = await browser.newContext() const page3 = await otherCtx.newPage() run.attach(page3) await fresh(page3) const hold = ev(page, `return P.send(60000, { delay: 3000, id: "B7-tab1-hold60000", consume: true })`) await sleep(300) const sameTabSecond = await ev(page, `return P.send(10000, { id: "B7-tab1-second-10000", consume: true })`) const tab2 = await ev(page2, `return P.send(60000, { id: "B7-tab2-60000", consume: true })`) const tab3 = await ev(page3, `return P.send(60000, { id: "B7-otherContext-60000", consume: true })`) result.B7_scope = { tab1_hold_60000: await hold, tab1_second_10000_while_holding: sameTabSecond, tab2_same_context_60000_while_tab1_holds: tab2, tab3_other_context_60000_while_tab1_holds: tab3 } await page2.close() await otherCtx.close() // B8: keepalive posts fired from a pagehide handler during a reload (fresh doc each) const unloadCase = async (name: string, sizes: number[]) => { await fresh() await page.evaluate(([sizes, name]) => (globalThis as any).P.armUnloadBurst(sizes, { id: name }), [sizes, name] as const) run.mark(`B8 reload (${name})`) await page.reload() await page.waitForFunction(() => (globalThis as any).P?.ready) await sleep(1500) const arrived = arrivedIds(run, `${name}-`) return { fired: sizes, firedTotal: sizes.reduce((a, b) => a + b, 0), arrivedAtServer: arrived, arrivedCount: arrived.length } } result.B8_unload_burst = { two_x_20000: await unloadCase("B8a", [20000, 20000]), two_x_40000: await unloadCase("B8b", [40000, 40000]), three_x_20000: await unloadCase("B8c", [20000, 20000, 20000]), four_x_20000: await unloadCase("B8d", [20000, 20000, 20000, 20000]), one_x_70000: await unloadCase("B8e", [70000]), } // B9: an in-flight keepalive from the PREVIOUS document does not count against the new document await fresh() await page.evaluate(() => void (globalThis as any).P.send(60000, { delay: 4000, id: "B9-prevdoc-hold60000" })) await sleep(200) run.mark("B9 reload while 60000 B keepalive in flight") await page.reload() await page.waitForFunction(() => (globalThis as any).P?.ready) result.B9_new_document_60000 = await ev(page, `return P.send(60000, { id: "B9-newdoc-60000", consume: true })`) await sleep(4500) result.B9_arrived = arrivedIds(run, "B9-") // B10: an in-flight keepalive in the SAME document blocks the pagehide keepalive await fresh() await page.evaluate(() => { const P = (globalThis as any).P void P.send(50000, { delay: 4000, id: "B10-periodic-hold50000" }) P.armUnloadBurst([20000], { id: "B10-unload" }) }) await sleep(200) run.mark("B10 reload while 50000 B keepalive in flight, pagehide fires 20000 B") await page.reload() await page.waitForFunction(() => (globalThis as any).P?.ready) await sleep(4500) result.B10_arrived = arrivedIds(run, "B10-") await ctx.close() const file = run.save(result, { browserVersion: browser.version() }) const single = (result.B1_single as any[]).map((r) => `${r.bytes}:${ok(r)}`).join(" ") addSummary({ scenario: "B-probe", label: "probe", run: index, verdict: "MEASURED", details: `single: ${single} | 2x40000 concurrent: ${b2a.map(ok).join("/")} | 4x20000 concurrent: ${b2d.map(ok).join("/")} | unload 2x40000 arrived ${(result.B8_unload_burst as any).two_x_40000.arrivedCount}/2, 4x20000 arrived ${(result.B8_unload_burst as any).four_x_20000.arrivedCount}/4 | B10 unload-while-inflight arrived ${(result.B10_arrived as any[]).map((a) => a.id).join(",")}`, evidence: file, }) return result } @@@@@@@@@@ END FILE: repro/scenarios/b-probe.ts @@@@@@@@@@ FILE: repro/scenarios/sdk.ts (34500 bytes) /** * SDK-level scenarios (A, A0, Bsdk, C, D, Dr, E, F, G, H, I). Each scenario * function performs ONE run in its own browser context and returns a verdict. * See README.md for what each scenario tests. */ import type { Browser, Page } from "playwright" import { Run, addRulesFor, addSummary, clearRulesFor, newTag, openApp, sleep, waitReady, PAGE_PORT } from "../lib.ts" type Q = Record const FAIL_RE = /\[MapleClientSDK\] (\w+) flush failed; cooldown 60s/ const SKIP_RE = /\[MapleClientSDK\] (\w+) flush skipped \(cooldown/ const REPLAY_RE = /\[maple\] session replay (.+?) failed/ const reload = async (page: Page, run: Run) => { run.mark("reload-start") await page.reload() run.mark("reload-load-event") await waitReady(page) run.mark("reload-ready") } const consoleCounts = (run: Run) => ({ flushFailed: run.consoleMatching(FAIL_RE).map((c) => ({ t: c.t, text: c.text })), flushSkipped: run.consoleMatching(SKIP_RE).map((c) => ({ t: c.t, text: c.text })), replayPostFailed: run.consoleMatching(REPLAY_RE).map((c) => ({ t: c.t, text: c.text })), }) /** Extra query parameters added to every app URL (e.g. { sdk: "0.10.0" } for the optional latest-SDK build). */ let extraQuery: Q = {} export const setExtraQuery = (q: Q) => { extraQuery = q } /** Read the page's localStorage journal (lifecycle events + SDK console messages tagged per document). */ const readJournal = async (ctx: import("playwright").BrowserContext, run: Run) => { try { const p = await ctx.newPage() await p.goto(`http://localhost:${PAGE_PORT}/probe.html?tag=${run.tag}-journal`) const j = await p.evaluate((key) => JSON.parse(localStorage.getItem(key) ?? "[]"), `repro.journal.${run.tag}`) await p.close() return (j as any[]).map((e) => ({ ...e, t: run.rel(e.t) })) } catch (error) { return [{ error: String(error) }] } } const withRun = async ( browser: Browser, scenario: string, label: string, index: number, q: Q, body: (page: Page, run: Run) => Promise<{ verdict: string; details: string; result: Record }>, ) => { q = { ...q, ...extraQuery } const run = new Run(scenario, label, index, newTag(scenario), { query: q, browserVersion: browser.version() }) const ctx = await browser.newContext() try { const page = await openApp(ctx, run, q) const out = await body(page, run) const journal = await readJournal(ctx, run) const file = run.save({ ...out.result, console: consoleCounts(run) }, { pageJournal: journal }) addSummary({ scenario, label, run: index, verdict: out.verdict, details: out.details, evidence: file }) return out } catch (error) { const file = run.save({ harnessError: String(error) }) addSummary({ scenario, label, run: index, verdict: "HARNESS-ERROR", details: String(error), evidence: file }) return { verdict: "HARNESS-ERROR", details: String(error), result: {} } } finally { clearRulesFor(run) await ctx.close() } } /** Wait until a traces POST containing `spanName` has been received (headers+body) by the server. */ const waitSpanReceived = (run: Run, spanName: string, timeoutMs = 15_000) => run.waitForRecord((r) => (r.summary as any)?.kind === "traces" && (r.summary as any).spanNames.includes(spanName) && r.tBody !== undefined, timeoutMs) // --------------------------------------------------------------------------- // A0: threshold — how many plain spans fit in one flush before the keepalive POST is rejected? export const A0 = (browser: Browser, index: number, spans: number) => withRun(browser, "A0-threshold", `n${spans}`, index, { variant: "flush", interval: "false" }, async (page, run) => { await page.evaluate((n) => (globalThis as any).T.spans(n, 0, "A0"), spans) run.mark("spans-created") await page.evaluate(() => (globalThis as any).T.flush()) run.mark("flush-returned") await sleep(500) const arrived = [...run.arrivedSpans().keys()].filter((n) => n.startsWith("A0-")).length const traceReqs = run.pw.filter((p) => p.ev === "request" && p.url.includes("/v1/traces")) const failed = run.consoleMatching(FAIL_RE) return { verdict: arrived === spans ? "DELIVERED" : "REJECTED", details: `${spans} spans in one flush: arrived ${arrived}; traces POST body bytes (Playwright) ${traceReqs.map((r) => r.postBytes).join(",") || "n/a"}; console: ${failed.map((f) => f.text).join(" | ") || "none"}`, result: { spans, arrived, traceRequestBodyBytes: traceReqs.map((r) => r.postBytes) }, } }) // --------------------------------------------------------------------------- // A: an oversized batch poisons trace export for the lifetime of the page. export const A = (browser: Browser, index: number, label: string, q: Q, opts: { burstSpans: number; burstAttrBytes: number; steadyPerSec?: number; steadySecs?: number; observeMs: number }) => withRun(browser, "A-oversized-batch", label, index, { variant: "flush", ...q }, async (page, run) => { await sleep(1000) const created: string[] = [] if (opts.burstSpans > 0) { await page.evaluate(([n, b]) => (globalThis as any).T.spans(n, b, "A-burst"), [opts.burstSpans, opts.burstAttrBytes] as const) for (let i = 0; i < opts.burstSpans; i++) created.push(`A-burst-${i}`) run.mark("burst-created", { spans: opts.burstSpans, attrBytes: opts.burstAttrBytes }) } if (opts.steadyPerSec) { // steady stream, created in 100 ms slices const slices = (opts.steadySecs ?? 10) * 10 const perSlice = opts.steadyPerSec / 10 for (let s = 0; s < slices; s++) { await page.evaluate(([n, p]) => (globalThis as any).T.spans(n, 0, p), [perSlice, `A-steady-${s}`] as const) for (let i = 0; i < perSlice; i++) created.push(`A-steady-${s}-${i}`) await sleep(100) } run.mark("steady-done") } const start = Date.now() let k = 0 while (Date.now() - start < opts.observeMs) { await page.evaluate((name) => (globalThis as any).T.span(name), `A-tick-${k}`) created.push(`A-tick-${k}`) if (k % 5 === 0) await page.evaluate((p) => (globalThis as any).T.logs(1, 0, p), `A-log-${k}`) k++ await sleep(2000) } // allow the last regular flush to happen before judging await sleep(Number(q.interval ?? 5000) + 2000) const arrived = run.arrivedSpans() const arrivedCreated = created.filter((n) => arrived.has(n)) const ticks = created.filter((n) => n.startsWith("A-tick-")) const ticksArrived = ticks.filter((n) => arrived.has(n)) const logsArrived = [...run.arrivedLogs().keys()].filter((n) => n.startsWith("A-log-")).length const c = consoleCounts(run) const tracePosts = run.records().filter((r) => r.method === "POST" && r.path.endsWith("/v1/traces")) return { verdict: arrivedCreated.length === 0 && c.flushFailed.length > 0 ? "REPRODUCED (no span ever delivered)" : arrivedCreated.length < created.length ? "PARTIAL LOSS" : "NOT REPRODUCED", details: `created ${created.length} spans (${ticks.length} small 'tick' spans after the burst); delivered ${arrivedCreated.length} (ticks ${ticksArrived.length}/${ticks.length}); traces POSTs reaching server: ${tracePosts.length}; logs delivered ${logsArrived}; console 'flush failed' at t=${c.flushFailed.map((f) => f.t).join(",")}ms`, result: { createdCount: created.length, deliveredCount: arrivedCreated.length, ticksCreated: ticks.length, ticksDelivered: ticksArrived.length, logsDelivered: logsArrived, tracePostsAtServer: tracePosts.length, flushFailedAt: c.flushFailed.map((f) => f.t), }, } }) // --------------------------------------------------------------------------- // Bsdk: traces + logs + metrics are POSTed concurrently (Promise.all) as separate keepalive // requests; each fits under 64 KiB but their sum does not. export const Bsdk = (browser: Browser, index: number, label: string, trigger: "flush" | "reload") => withRun(browser, "B-sdk-concurrent-signals", label, index, { variant: "flush", interval: trigger === "flush" ? "false" : 60000 }, async (page, run) => { await page.evaluate(() => (globalThis as any).T.spans(40, 500, "Bsdk-span")) await page.evaluate(() => (globalThis as any).T.logs(30, 800, "Bsdk-log")) await page.evaluate(() => (globalThis as any).T.metric()) run.mark("data-created") if (trigger === "flush") { await page.evaluate(() => (globalThis as any).T.flush()) run.mark("flush-returned") await sleep(500) } else { await reload(page, run) await sleep(2000) } const spans = [...run.arrivedSpans().keys()].filter((n) => n.startsWith("Bsdk-span")).length const logs = [...run.arrivedLogs().keys()].filter((n) => n.startsWith("Bsdk-log")).length const metrics = run.records().filter((r) => r.method === "POST" && r.path.endsWith("/v1/metrics") && r.status === 200).length const reqs = run.pw.filter((p) => p.ev === "request" && /\/v1\/(traces|logs|metrics)$/.test(p.url)).map((p) => `${p.url.split("/").pop()}:${p.postBytes}`) const c = consoleCounts(run) const lost = spans < 40 || logs < 30 return { verdict: lost ? "REPRODUCED (one signal rejected)" : "NOT REPRODUCED", details: `trigger=${trigger}; spans ${spans}/40, logs ${logs}/30, metrics POSTs ${metrics}; request bodies seen by Playwright [${reqs.join(" ")}]; console: ${[...c.flushFailed, ...c.flushSkipped].map((x) => x.text).join(" | ") || "none"}`, result: { spansDelivered: spans, logsDelivered: logs, metricsPosts: metrics, requestBodies: reqs }, } }) // --------------------------------------------------------------------------- // C: a single transient failure puts traces into a 60 s cooldown; the pagehide flush during // that cooldown is skipped, so everything buffered (including the failed batch) is lost. export const C = (browser: Browser, index: number, label: string, how: "reload" | "navigate" | "close", inject: boolean) => withRun(browser, "C-unload-during-cooldown", label, index, { variant: "flush", interval: 1000 }, async (page, run) => { if (inject) addRulesFor(run, [{ pathIncludes: "/v1/traces", failStatus: 503, failCount: 1 }]) await page.evaluate(() => (globalThis as any).T.span("C-first")) run.mark("C-first created") const firstPost = await waitSpanReceived(run, "C-first", 5000) run.mark("C-first POST received by server", { status: firstPost?.status }) await sleep(1500) await page.evaluate(() => (globalThis as any).T.span("C-before-unload")) run.mark("C-before-unload created") await sleep(300) if (how === "reload") await reload(page, run) else if (how === "navigate") { run.mark("navigate-start") await page.goto(`http://localhost:${PAGE_PORT}/probe.html?tag=${run.tag}`) run.mark("navigate-done") } else { run.mark("close-start") await page.close({ runBeforeUnload: true }) await sleep(200) run.mark("close-done") } await sleep(3000) const arrived = run.arrivedSpans() const c = consoleCounts(run) const first = arrived.has("C-first") const tail = arrived.has("C-before-unload") return { verdict: inject ? (!tail && !first ? "REPRODUCED (both spans lost)" : !tail ? "REPRODUCED (tail lost)" : "NOT REPRODUCED") : tail && first ? "CONTROL OK (both delivered)" : "CONTROL FAILED", details: `inject503=${inject} unload=${how}: C-first delivered=${first}, C-before-unload delivered=${tail}; console failed=${c.flushFailed.length} skipped=${c.flushSkipped.map((s) => `t=${s.t}`).join(",") || 0}`, result: { firstDelivered: first, tailDelivered: tail }, } }) // --------------------------------------------------------------------------- // D: the pagehide flush is serialized behind an in-flight periodic flush. export const D = (browser: Browser, index: number, label: string, latencyMs: number, interval: number, tailBeforeReloadMs = 150) => withRun(browser, "D-pagehide-behind-inflight-flush", label, index, { variant: "flush", interval }, async (page, run) => { // only the FIRST traces POST is slow (models one slow/hung round-trip through an ingest proxy) if (latencyMs > 0) addRulesFor(run, [{ pathIncludes: "/v1/traces", latencyMs, latencyCount: 1 }]) await page.evaluate(() => (globalThis as any).T.span("D-early")) run.mark("D-early created") const early = await waitSpanReceived(run, "D-early", interval + 3000) run.mark("D-early POST received by server (response held for latencyMs)", { latencyMs }) await sleep(100) await page.evaluate(() => (globalThis as any).T.span("D-tail")) run.mark("D-tail created", { msBeforeReload: tailBeforeReloadMs }) await sleep(tailBeforeReloadMs) await reload(page, run) await sleep(latencyMs + 3000) const arrived = run.arrivedSpans() const tail = arrived.has("D-tail") const tailRec = run.records().find((r) => (r.summary as any)?.spanNames?.includes("D-tail")) const earlyRec = run.records().find((r) => (r.summary as any)?.spanNames?.includes("D-early")) return { verdict: latencyMs > 0 ? (tail ? "NOT REPRODUCED" : "REPRODUCED (tail span lost)") : tail ? "CONTROL OK" : "CONTROL FAILED", details: `first traces POST held ${latencyMs} ms, autoFlushInterval ${interval}, D-tail ended ${tailBeforeReloadMs} ms before reload: D-early delivered=${arrived.has("D-early")}${earlyRec ? ` (server responded t=${earlyRec.tResp !== undefined ? run.rel(earlyRec.tResp) : "?"}ms)` : ""}; D-tail delivered=${tail}${tailRec ? ` (received t=${run.rel(tailRec.tRecv)}ms)` : ""}; reload at t=${run.marks.find((m) => m.name === "reload-start")?.t}ms`, result: { earlyReceived: !!early, tailDelivered: tail }, } }) // Dhang: head-of-line blocking — one slow traces POST stalls ALL later exports (traces, logs, metrics) export const Dhang = (browser: Browser, index: number, label: string, holdMs: number) => withRun(browser, "D-hang-head-of-line-blocking", label, index, { variant: "flush", interval: 1000 }, async (page, run) => { addRulesFor(run, [{ pathIncludes: "/v1/traces", latencyMs: holdMs, latencyCount: 1 }]) await page.evaluate(() => (globalThis as any).T.span("Dh-first")) await waitSpanReceived(run, "Dh-first", 4000) const tHold = run.rel() run.mark("first traces POST received; server holds it", { holdMs }) const created: { name: string; t: number }[] = [] for (let i = 0; i < Math.floor(holdMs / 1000) + 3; i++) { await page.evaluate((i) => (globalThis as any).T.span(`Dh-${i}`), i) await page.evaluate((i) => (globalThis as any).T.logs(1, 0, `Dhlog-${i}`), i) created.push({ name: `Dh-${i}`, t: run.rel() }) await sleep(1000) } await sleep(2000) const spans = run.arrivedSpans() const logs = run.arrivedLogs() const rows = created.map((c) => ({ name: c.name, createdAt: c.t, spanArrivedAt: spans.get(c.name), logArrivedAt: logs.get(`${c.name.replace("Dh-", "Dhlog-")}-0`) })) const delays = rows.filter((r) => r.spanArrivedAt !== undefined).map((r) => r.spanArrivedAt! - r.createdAt) const logDelays = rows.filter((r) => r.logArrivedAt !== undefined).map((r) => r.logArrivedAt! - r.createdAt) const traceReqs = run.records().filter((r) => r.method === "POST" && r.path.endsWith("/v1/traces")) return { verdict: Math.max(...delays) > holdMs / 2 ? "REPRODUCED (exports stalled behind one slow request)" : "NOT REPRODUCED", details: `first traces POST held ${holdMs} ms (from t=${tHold}ms): span export delay max ${Math.max(...delays)} ms / min ${Math.min(...delays)} ms; log export delay max ${Math.max(...logDelays)} ms (logs endpoint itself is fast); traces POSTs at server t=[${traceReqs.map((r) => `${run.rel(r.tRecv)}:${(r.summary as any).spanCount}sp/${r.bytes}B`).join(" ")}]`, result: { rows, holdMs }, } }) // Dr: probabilistic version — steady span stream, reload at a random moment, realistic latency. export const Dr = (browser: Browser, index: number, label: string, latencyMs: number, interval: number) => withRun(browser, "Dr-random-reload-latency", label, index, { variant: "flush", interval }, async (page, run) => { if (latencyMs > 0) addRulesFor(run, [{ pathIncludes: "/v1/traces", latencyMs }]) const created: { name: string; t: number }[] = [] const until = Date.now() + 2000 + Math.floor(Math.random() * 4000) let k = 0 while (Date.now() < until) { const name = `Dr-${k++}` await page.evaluate((n) => (globalThis as any).T.span(n), name) created.push({ name, t: run.rel() }) await sleep(100) } const reloadAt = run.rel() const inflight = run.records().some((r) => r.path.endsWith("/v1/traces") && r.tResp === undefined) run.mark("reload decided", { traceFlushInFlightAtServer: inflight }) await reload(page, run) await sleep(latencyMs + 3000) const arrived = run.arrivedSpans() const lost = created.filter((c) => !arrived.has(c.name)) return { verdict: lost.length > 0 ? `LOSS (${lost.length} spans)` : "no loss", details: `latency ${latencyMs} ms, interval ${interval}: created ${created.length}, lost ${lost.length} (created ${lost.map((l) => reloadAt - l.t).join(",")} ms before reload); trace flush in flight at reload: ${inflight}`, result: { created: created.length, lost: lost.map((l) => ({ ...l, msBeforeReload: reloadAt - l.t })), inflightAtReload: inflight }, } }) // --------------------------------------------------------------------------- // E: combined unload keepalive budget — MapleFlush OTLP posts + replay meta/events/blob. export const E = (browser: Browser, index: number, label: string, q: Q, opts: { spans: number; spanAttr: number; mutateChars: number; consoleLines: number }) => withRun(browser, "E-combined-unload-budget", label, index, { variant: "flush", interval: 60000, ...q }, async (page, run) => { // let the replay engine start and ship its first full snapshot (chunk 0) if (q.replay !== 0) await run.waitForRecord((r) => (r.summary as any)?.kind === "replayBlob", 12_000) run.mark("first replay blob received (or replay disabled)") await sleep(200) if (opts.mutateChars > 0) await page.evaluate(([n]) => (globalThis as any).T.mutate(n, 20), [opts.mutateChars] as const) if (opts.consoleLines > 0) await page.evaluate((n) => { for (let i = 0; i < n; i++) console.log(`E-console-${i} ${"z".repeat(200)}`) }, opts.consoleLines) if (opts.spans > 0) await page.evaluate(([n, b]) => (globalThis as any).T.spans(n, b, "E-span"), [opts.spans, opts.spanAttr] as const) run.mark("payload created") await sleep(300) const reloadStart = run.rel() await reload(page, run) // wait long enough for the NEW page's first replay chunk (5 s timer) so we can see the chunk-seq gap if (q.replay !== 0) await run.waitForRecord((r) => (r.summary as any)?.kind === "replayBlob" && run.rel(r.tRecv) > reloadStart, 9000) await sleep(500) const unloadWindow = run.records().filter((r) => run.rel(r.tRecv) >= reloadStart - 50 && run.rel(r.tRecv) <= reloadStart + 1500 && r.method === "POST") const oldPage = { traces: unloadWindow.filter((r) => (r.summary as any)?.kind === "traces" && (r.summary as any).spanNames.some((n: string) => n.startsWith("E-span"))).map((r) => r.bytes), metaEnded: unloadWindow.filter((r) => (r.summary as any)?.kind === "sessionMeta" && (r.summary as any).rows.some((x: any) => x.status === "ended")).map((r) => r.bytes), sessionEvents: unloadWindow.filter((r) => (r.summary as any)?.kind === "sessionEvents").map((r) => r.bytes), replayBlob: unloadWindow.filter((r) => (r.summary as any)?.kind === "replayBlob").map((r) => r.bytes), } const chunkSeqs = run.records().filter((r) => (r.summary as any)?.kind === "replayBlob").map((r) => (r.summary as any).chunkSeq as number) const missingSeqs: number[] = [] for (let i = 0; i <= Math.max(-1, ...chunkSeqs); i++) if (!chunkSeqs.includes(i)) missingSeqs.push(i) const pwUnload = run.pw.filter((p) => p.t >= reloadStart - 50 && p.t <= reloadStart + 1500) const c = consoleCounts(run) const spansDelivered = [...run.arrivedSpans().keys()].filter((n) => n.startsWith("E-span")).length const missing = [ opts.spans > 0 && spansDelivered < opts.spans ? "traces" : null, oldPage.metaEnded.length === 0 ? "meta(ended)" : null, opts.consoleLines > 0 && oldPage.sessionEvents.length === 0 ? "final sessionEvents" : null, q.replay !== 0 && missingSeqs.length > 0 ? `replay chunk(s) ${missingSeqs.join(",")} (seq consumed, never posted)` : null, ].filter(Boolean) return { verdict: missing.length ? `LOST: ${missing.join(", ")}` : "all delivered", details: `spans ${spansDelivered}/${opts.spans}; old-page unload arrivals (bytes): traces[${oldPage.traces}] meta-ended[${oldPage.metaEnded}] events[${oldPage.sessionEvents}] blob[${oldPage.replayBlob}]; replay chunkSeqs at server [${chunkSeqs.join(",")}]; Playwright during unload: ${pwUnload.filter((p) => p.ev === "request").map((p) => `${p.url.split("/v1/")[1]}(${p.postBytes}B)`).join(" ")}`, result: { spansDelivered, oldPageUnloadArrivals: oldPage, chunkSeqs, missingSeqs, playwrightDuringUnload: pwUnload }, } }) // --------------------------------------------------------------------------- // F: replay/session posts that are NOT keepalive are aborted by navigation. export const F = (browser: Browser, index: number, label: string, variant: string, target: "events" | "meta") => withRun(browser, "F-replay-posts-aborted-by-navigation", label, index, { variant }, async (page, run) => { if (target === "events") { addRulesFor(run, [{ pathIncludes: "/v1/sessionEvents", latencyMs: 3000 }]) // the replay engine (which installs the console capture) loads asynchronously, later with // Maple.layer than with MapleFlush.make; wait for its first "active" row before logging await run.waitForRecord((r) => (r.summary as any)?.kind === "sessionMeta", 5000) await sleep(1000) await page.evaluate(() => { for (let i = 0; i < 5; i++) console.log(`F-console-${i}`) }) // the event sink flushes every 5 s with keepalive=false; wait until that POST is at the server const rec = await run.waitForRecord((r) => (r.summary as any)?.kind === "sessionEvents" && (r.summary as any).consoleMessages.some((m: string) => m.startsWith("F-console")), 8000) run.mark("periodic sessionEvents POST received by server; response held 3 s", { found: !!rec }) await sleep(100) await reload(page, run) } else { addRulesFor(run, [{ pathIncludes: "/v1/sessionReplays/meta", latencyMs: 3000 }]) // the new page posts an "active" meta row (keepalive=false) right at startup; reload while it is in flight await reload(page, run) await run.waitForRecord((r) => (r.summary as any)?.kind === "sessionMeta" && run.rel(r.tRecv) > (run.marks.find((m) => m.name === "reload-start")?.t ?? 0), 5000) await sleep(300) await reload(page, run) } await sleep(3500) const c = consoleCounts(run) const failedPw = run.pw.filter((p) => p.ev === "failed") return { verdict: target === "events" && !run.marks.find((m) => m.name.startsWith("periodic sessionEvents POST received"))?.found ? "PRECONDITION NOT MET (no in-flight events POST at reload)" : c.replayPostFailed.length > 0 ? "REPRODUCED" : "NOT REPRODUCED", details: `${variant}/${target}: console: ${c.replayPostFailed.map((x) => x.text).join(" | ") || "none"}; Playwright requestfailed: ${failedPw.map((p) => `${p.url.split("/v1/")[1]}[${p.failure}]`).join(" ") || "none"}`, result: { replayPostFailed: c.replayPostFailed, playwrightFailed: failedPw }, } }) // Fr: realistic — modest latency on every ingest path, several reloads at random moments. export const Fr = (browser: Browser, index: number, label: string, variant: string, latencyMs: number) => withRun(browser, "Fr-random-reloads", label, index, { variant, interval: 1000 }, async (page, run) => { addRulesFor(run, [{ pathIncludes: "/otlp/", latencyMs }]) for (let i = 0; i < 8; i++) { await page.evaluate((i) => { console.log(`Fr-console-${i}`) document.getElementById("btn-spans")?.dispatchEvent(new MouseEvent("click", { bubbles: true })) }, i) await sleep(500 + Math.floor(Math.random() * 6500)) await reload(page, run) } await sleep(latencyMs + 3000) const c = consoleCounts(run) const failedPw = run.pw.filter((p) => p.ev === "failed") const byPath: Record = {} for (const p of failedPw) byPath[`${p.url.split("/v1/")[1]}[${p.failure}]`] = (byPath[`${p.url.split("/v1/")[1]}[${p.failure}]`] ?? 0) + 1 return { verdict: c.replayPostFailed.length > 0 || c.flushFailed.length > 0 ? "REPRODUCED" : "NOT REPRODUCED", details: `${variant}, latency ${latencyMs} ms, 8 reloads: replay-post console errors ${c.replayPostFailed.length} [${[...new Set(c.replayPostFailed.map((x) => x.text.split(" failed")[0]))].join("; ")}], SDK flush failed ${c.flushFailed.length}, skipped ${c.flushSkipped.length}; Playwright requestfailed by path: ${JSON.stringify(byPath)}`, result: { replayPostFailed: c.replayPostFailed, flushFailed: c.flushFailed, flushSkipped: c.flushSkipped, playwrightFailedByPath: byPath }, } }) // --------------------------------------------------------------------------- // G: is Playwright's requestfailed/ERR_ABORTED trustworthy for keepalive requests? export const G = (browser: Browser, index: number, label: string, withReload: boolean) => withRun(browser, "G-playwright-keepalive-artifact", label, index, { variant: "flush", interval: 1000 }, async (page, run) => { for (let i = 0; i < 20; i++) { await page.evaluate((i) => (globalThis as any).T.span(`G-${i}`), i) await sleep(400) } if (withReload) { await page.evaluate(() => (globalThis as any).T.span("G-tail")) await sleep(150) await reload(page, run) } await sleep(3000) // join Playwright's view with the server's view, per request const posts = run.pw.filter((p) => p.ev === "request" && p.method === "POST") const fin = run.pw.filter((p) => p.ev === "finished" && p.method === "POST") const fail = run.pw.filter((p) => p.ev === "failed" && p.method === "POST") const server200 = run.records().filter((r) => r.method === "POST" && r.status === 200) const kinds = ["traces", "sessionReplays/meta", "sessionEvents", "sessionReplays/blob", "logs", "metrics"] const table: Record = {} for (const k of kinds) { const m = (u: string) => u.endsWith(`/v1/${k}`) table[k] = { pwRequest: posts.filter((p) => m(p.url)).length, pwFinished: fin.filter((p) => m(p.url)).length, pwFailed: fail.filter((p) => m(p.url)).length, failures: [...new Set(fail.filter((p) => m(p.url)).map((p) => p.failure ?? ""))], server200: server200.filter((r) => m(r.path)).length, } } const tracesFailedButDelivered = table.traces!.pwFailed > 0 && table.traces!.server200 >= table.traces!.pwRequest return { verdict: fail.length > 0 && server200.length >= posts.length ? "ARTIFACT (Playwright reports failures for requests the server answered 200)" : fail.length > 0 ? "MIXED" : "no requestfailed events", details: `reload=${withReload}: ${Object.entries(table).map(([k, v]) => `${k}: pw req ${v.pwRequest}/fin ${v.pwFinished}/fail ${v.pwFailed}${v.failures.length ? `[${v.failures.join(",")}]` : ""} vs server200 ${v.server200}`).join("; ")}`, result: { table, tracesFailedButDelivered }, } }) // --------------------------------------------------------------------------- // H: Maple.layer has no unload flush (documented) — baseline + a user-land workaround. export const H = (browser: Browser, index: number, label: string, variant: string) => withRun(browser, "H-maple-layer-unload", label, index, { variant }, async (page, run) => { await page.evaluate(() => (globalThis as any).T.span("H-early")) run.mark("H-early created") await sleep(6500) await page.evaluate(() => (globalThis as any).T.span("H-tail")) run.mark("H-tail created") await sleep(1000) await reload(page, run) await sleep(3000) const arrived = run.arrivedSpans() return { verdict: arrived.has("H-tail") ? "tail delivered" : "tail LOST", details: `${variant}: H-early (created 7.5 s before reload) delivered=${arrived.has("H-early")}; H-tail (created 1 s before reload) delivered=${arrived.has("H-tail")}`, result: { early: arrived.has("H-early"), tail: arrived.has("H-tail") }, } }) // --------------------------------------------------------------------------- // I-cross: cross-origin endpoint (CORS preflight) + pagehide flush; also records request headers. export const Icross = (browser: Browser, index: number, label: string, origin: "same" | "cross") => withRun(browser, "I-cross-origin", label, index, { variant: "flush", origin, interval: 5000 }, async (page, run) => { await page.evaluate(() => (globalThis as any).T.span("I-tail")) await sleep(150) await reload(page, run) await sleep(2500) const arrived = run.arrivedSpans() const pre = run.records().filter((r) => r.method === "OPTIONS") const tracePost = run.records().find((r) => r.method === "POST" && r.path.endsWith("/v1/traces")) return { verdict: arrived.has("I-tail") ? "tail delivered" : "tail LOST", details: `${origin}-origin: I-tail delivered=${arrived.has("I-tail")}; preflights ${pre.length} (access-control-request-headers: ${[...new Set(pre.map((p) => p.headers["access-control-request-headers"]))].join(" | ") || "n/a"}); traces POST user-agent header: ${tracePost?.headers["user-agent"] ?? "n/a"}, x-maple-sdk: ${tracePost?.headers["x-maple-sdk"] ?? "n/a"}`, result: { tail: arrived.has("I-tail"), preflightRequestHeaders: pre.map((p) => p.headers["access-control-request-headers"]), tracePostHeaders: tracePost?.headers }, } }) // --------------------------------------------------------------------------- // K: (follow-up to a real-app observation) action = 1 span + 1 Effect log, then reload ~150 ms later, // autoFlushInterval 1000. Which document / which flush prints "traces|logs flush failed", and was the // data actually delivered? export const K = (browser: Browser, index: number, label: string, q: Q) => withRun(browser, "K-reload-150ms-after-action", label, index, { variant: "flush", interval: 1000, ...q }, async (page, run) => { await sleep(1500) await page.evaluate(() => (globalThis as any).T.span("K-action-span")) await page.evaluate(() => (globalThis as any).T.logs(1, 0, "K-action-log")) run.mark("action (span + log) done") await sleep(150) await reload(page, run) await sleep(3000) const span = run.arrivedSpans().has("K-action-span") const log = run.arrivedLogs().has("K-action-log-0") const c = consoleCounts(run) const reloadAt = run.marks.find((m) => m.name === "reload-start")!.t const pw = run.pw.filter((p) => p.t >= reloadAt - 20 && p.t <= reloadAt + 500) const srv = run.records().filter((r) => r.method === "POST" && run.rel(r.tRecv) >= reloadAt - 20 && run.rel(r.tRecv) <= reloadAt + 500) return { verdict: `span ${span ? "delivered" : "LOST"}, log ${log ? "delivered" : "LOST"}; console flush-failed: [${c.flushFailed.map((f) => f.text.split(";")[0]!.replace("[MapleClientSDK] ", "")).join(", ")}]`, details: `reload at t=${reloadAt}; requests issued around the reload (Playwright): ${pw.filter((p) => p.ev === "request").map((p) => `${p.url.split("/v1/")[1]}(${p.postBytes}B)@${p.t}`).join(" ")}; Playwright failed: ${pw.filter((p) => p.ev === "failed").map((p) => `${p.url.split("/v1/")[1]}[${p.failure}]`).join(" ") || "none"}; server 200 in same window: ${srv.map((r) => `${r.path.split("/v1/")[1]}@${run.rel(r.tRecv)}`).join(" ")}; console: ${[...c.flushFailed, ...c.flushSkipped, ...c.replayPostFailed].map((x) => `${x.text.split(":")[0]}@${x.t}`).join(" | ")}`, result: { spanDelivered: span, logDelivered: log, playwrightAroundReload: pw }, } }) // L: multi-argument Effect log -> OTLP log body encoding export const L = (browser: Browser, index: number) => withRun(browser, "L-multi-arg-log-body", "logError-4-args", index, { variant: "flush", interval: 1000 }, async (page, run) => { await page.evaluate(() => (globalThis as any).T.logMulti("L")) await sleep(2500) const rec = run.records().find((r) => (r.summary as any)?.kind === "logs") const raw = (rec?.summary as any)?.firstRawBodies?.[0] return { verdict: raw?.arrayValue ? "body is an OTLP arrayValue (structured), not a JSON string" : raw?.stringValue ? "body is a stringValue" : "no log arrived", details: `OTLP logRecord.body as POSTed by the SDK: ${JSON.stringify(raw)}`, result: { rawBody: raw }, } }) // W: user-land workaround check — A-style burst with the ?shim=1 fetch wrapper (downgrade oversized keepalive POSTs) export const W = (browser: Browser, index: number, label: string, shim: boolean) => withRun(browser, "W-workaround-keepalive-downgrade-shim", label, index, { variant: "flush", interval: 1000, ...(shim ? { shim: 1 } : {}) }, async (page, run) => { await sleep(1000) await page.evaluate(() => (globalThis as any).T.spans(200, 500, "W-burst")) for (let k = 0; k < 5; k++) { await sleep(1000) await page.evaluate((k) => (globalThis as any).T.span(`W-tick-${k}`), k) } await sleep(2500) const arrived = [...run.arrivedSpans().keys()] const burst = arrived.filter((n) => n.startsWith("W-burst")).length const ticks = arrived.filter((n) => n.startsWith("W-tick")).length const c = consoleCounts(run) return { verdict: burst === 200 && ticks === 5 ? "all delivered" : `LOSS (burst ${burst}/200, ticks ${ticks}/5)`, details: `shim=${shim}: burst spans delivered ${burst}/200, later spans ${ticks}/5; console flush-failed ${c.flushFailed.length}, skipped ${c.flushSkipped.length}`, result: { burst, ticks }, } }) @@@@@@@@@@ END FILE: repro/scenarios/sdk.ts @@@@@@@@@@ FILE: repro/server.ts (13999 bytes) /** * Mock Maple / OTLP ingest + static file server for the keepalive repro. * * Two listeners: * - PAGE_PORT (default 4173): serves the test pages (/ , /probe.html, /dist/*) * AND a same-origin ingest under /otlp/* * - CROSS_PORT (default 4174): serves ONLY the ingest under /otlp/* (cross-origin * from the page, so the browser must CORS-preflight). * * Every ingest request (including OPTIONS preflights) is recorded in memory and * appended to a JSONL log. Each record carries: wall-clock timestamps for * "request headers received", "body fully read" and "response sent", the path, * body byte size, interesting headers, and a parsed summary of the payload * (span names, log bodies, metric names, session-event types/seqs, replay * chunk headers). * * Failure / latency injection is driven by "rules" (see `Rule` below), set via * POST /__control (JSON) or programmatically through `startServer().setRules()`. * * Run standalone: bun run server.ts (then open http://localhost:4173/) */ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs" import { dirname, join } from "node:path" export interface Rule { /** Substring match on the request path, e.g. "/v1/traces". */ readonly pathIncludes: string /** Optional run tag; rule only applies to requests of that run (tag = path segment after /otlp/). */ readonly tag?: string /** Delay before responding (ms). The body is read first, then we wait. */ readonly latencyMs?: number /** Apply `latencyMs` only to the first N matching requests (default: all). */ readonly latencyCount?: number /** Respond with this status instead of 200 ... */ readonly failStatus?: number /** ... for the first N matching requests only (default: all). */ readonly failCount?: number } export interface IngestRecord { readonly seq: number readonly port: number readonly method: string readonly path: string readonly query: string readonly tag: string | null readonly origin: string | null readonly tRecv: number tBody?: number tResp?: number bytes?: number status?: number clientAborted?: boolean readonly headers: Record summary?: unknown latencyMs?: number injectedFailure?: boolean } const ROOT = dirname(new URL(import.meta.url).pathname) const PUBLIC = join(ROOT, "public") const INTERESTING_HEADERS = [ "content-type", "content-length", "authorization", "x-maple-sdk", "user-agent", "origin", "referer", "sec-fetch-mode", "sec-fetch-dest", "sec-fetch-site", "access-control-request-method", "access-control-request-headers", "x-maple-session-id", "x-maple-chunk-seq", "x-maple-is-checkpoint", "x-maple-event-count", "x-maple-duration-ms", ] const pickHeaders = (req: Request): Record => { const out: Record = {} for (const name of INTERESTING_HEADERS) { const v = req.headers.get(name) if (v === null) continue // never log real credentials; the repro only ever sends the "~" placeholder out[name] = name === "authorization" ? (v === "Bearer ~" ? v : "Bearer ") : name === "user-agent" ? v.slice(0, 60) : v } return out } const tagFrom = (req: Request, url: URL): string | null => { // The page configures the SDK endpoint as `/otlp/`, so every SDK // request path is `/otlp//v1/...` — this works cross-origin too, where the // Referer is reduced to the bare origin. const m = /^\/otlp\/([^/]+)\/v1\//.exec(url.pathname) if (m) return decodeURIComponent(m[1]!) const direct = url.searchParams.get("tag") if (direct) return direct const ref = req.headers.get("referer") if (!ref) return null try { return new URL(ref).searchParams.get("tag") } catch { return null } } const anyValueToString = (v: any): string => { if (!v || typeof v !== "object") return String(v) if ("stringValue" in v) return v.stringValue if ("intValue" in v) return String(v.intValue) if ("doubleValue" in v) return String(v.doubleValue) if ("boolValue" in v) return String(v.boolValue) return JSON.stringify(v).slice(0, 200) } const summarize = (path: string, req: Request, body: Uint8Array): unknown => { try { if (path.endsWith("/v1/traces")) { const json = JSON.parse(new TextDecoder().decode(body)) const names: string[] = [] for (const rs of json.resourceSpans ?? []) for (const ss of rs.scopeSpans ?? []) for (const s of ss.spans ?? []) names.push(s.name) return { kind: "traces", spanCount: names.length, spanNames: names } } if (path.endsWith("/v1/logs")) { const json = JSON.parse(new TextDecoder().decode(body)) const bodies: string[] = [] const rawBodies: unknown[] = [] for (const rl of json.resourceLogs ?? []) for (const sl of rl.scopeLogs ?? []) for (const r of sl.logRecords ?? []) { bodies.push(anyValueToString(r.body).slice(0, 80)) if (rawBodies.length < 3) rawBodies.push(r.body) } return { kind: "logs", logCount: bodies.length, logBodies: bodies, firstRawBodies: rawBodies } } if (path.endsWith("/v1/metrics")) { const json = JSON.parse(new TextDecoder().decode(body)) const names: string[] = [] for (const rm of json.resourceMetrics ?? []) for (const sm of rm.scopeMetrics ?? []) for (const m of sm.metrics ?? []) names.push(m.name) return { kind: "metrics", metricCount: names.length, metricNames: names } } if (path.endsWith("/v1/sessionEvents")) { const rows = new TextDecoder().decode(body).split("\n").filter(Boolean).map((l) => JSON.parse(l)) return { kind: "sessionEvents", rowCount: rows.length, sessionId: rows[0]?.session_id, seqs: rows.map((r) => r.seq), types: rows.map((r) => (r.type === "console" ? `console.${r.level}` : r.type)), consoleMessages: rows.filter((r) => r.type === "console").map((r) => String(r.message).slice(0, 160)), } } if (path.endsWith("/v1/sessionReplays/meta")) { const rows = new TextDecoder().decode(body).split("\n").filter(Boolean).map((l) => JSON.parse(l)) return { kind: "sessionMeta", rows: rows.map((r) => ({ session_id: r.session_id, status: r.status, version: r.version, trace_ids: r.trace_ids?.length })), } } if (path.endsWith("/v1/sessionReplays/blob")) { return { kind: "replayBlob", sessionId: req.headers.get("x-maple-session-id"), chunkSeq: Number(req.headers.get("x-maple-chunk-seq")), isCheckpoint: req.headers.get("x-maple-is-checkpoint"), eventCount: Number(req.headers.get("x-maple-event-count")), gzippedBytes: body.byteLength, } } if (path.startsWith("/probe/")) { return { kind: "probe", bytes: body.byteLength } } return { kind: "unknown", bytes: body.byteLength } } catch (error) { return { kind: "unparseable", error: String(error) } } } const corsHeaders = (req: Request): Record => { const origin = req.headers.get("origin") const h: Record = { "access-control-allow-methods": "POST, OPTIONS", "access-control-max-age": "0", // force a preflight for every cross-origin request (worst case) vary: "origin", } if (origin) { h["access-control-allow-origin"] = origin h["access-control-allow-credentials"] = "true" } const requested = req.headers.get("access-control-request-headers") if (requested) h["access-control-allow-headers"] = requested return h } const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) const contentTypeOf = (p: string) => p.endsWith(".html") ? "text/html; charset=utf-8" : p.endsWith(".js") ? "text/javascript; charset=utf-8" : p.endsWith(".map") ? "application/json" : "application/octet-stream" export interface ServerHandle { readonly pagePort: number readonly crossPort: number readonly records: IngestRecord[] setRules(rules: Rule[]): void addRules(rules: Rule[]): void clearRulesForTag(tag: string): void reset(): void stop(): void } export const startServer = (opts: { pagePort?: number; crossPort?: number; logFile?: string } = {}): ServerHandle => { const pagePort = opts.pagePort ?? Number(process.env.PAGE_PORT ?? 4173) const crossPort = opts.crossPort ?? Number(process.env.CROSS_PORT ?? 4174) const logFile = opts.logFile if (logFile) mkdirSync(dirname(logFile), { recursive: true }) const records: IngestRecord[] = [] let rules: Rule[] = [] const ruleHits = new Map() const latencyHits = new Map() let seq = 0 const persist = (rec: IngestRecord) => { if (logFile) appendFileSync(logFile, `${JSON.stringify(rec)}\n`) } const handleIngest = async (req: Request, url: URL, port: number): Promise => { const rec: IngestRecord = { seq: seq++, port, method: req.method, path: url.pathname, query: url.search, tag: tagFrom(req, url), origin: req.headers.get("origin"), tRecv: Date.now(), headers: pickHeaders(req), } records.push(rec) const cors = corsHeaders(req) if (req.method === "OPTIONS") { rec.status = 204 rec.tResp = Date.now() persist(rec) return new Response(null, { status: 204, headers: cors }) } let body: Uint8Array try { body = new Uint8Array(await req.arrayBuffer()) } catch (error) { rec.clientAborted = true rec.summary = { kind: "body-read-failed", error: String(error) } rec.tResp = Date.now() persist(rec) return new Response(null, { status: 499, headers: cors }) } rec.tBody = Date.now() rec.bytes = body.byteLength rec.summary = summarize(url.pathname, req, body) // probe endpoints take their latency from the query string let latency = Number(url.searchParams.get("delay") ?? 0) let failStatus: number | undefined for (const rule of rules) { if (!url.pathname.includes(rule.pathIncludes)) continue if (rule.tag !== undefined && rule.tag !== rec.tag) continue if (rule.latencyMs) { const hits = latencyHits.get(rule) ?? 0 if (rule.latencyCount === undefined || hits < rule.latencyCount) { latencyHits.set(rule, hits + 1) latency = Math.max(latency, rule.latencyMs) } } if (rule.failStatus) { const hits = ruleHits.get(rule) ?? 0 if (rule.failCount === undefined || hits < rule.failCount) { ruleHits.set(rule, hits + 1) failStatus = rule.failStatus } } } if (latency > 0) { rec.latencyMs = latency let aborted = false req.signal?.addEventListener?.("abort", () => { aborted = true }) await sleep(latency) if (aborted) rec.clientAborted = true } const status = failStatus ?? 200 rec.injectedFailure = failStatus !== undefined rec.status = status rec.tResp = Date.now() persist(rec) return new Response(status === 200 ? "{}" : `injected ${status}`, { status, headers: { ...cors, "content-type": "application/json" }, }) } const handleControl = async (req: Request, url: URL): Promise => { if (url.pathname === "/__control" && req.method === "POST") { const json = (await req.json()) as { rules?: Rule[] } rules = json.rules ?? [] ruleHits.clear() return Response.json({ ok: true, rules }) } if (url.pathname === "/__reset") { records.length = 0 rules = [] ruleHits.clear() return Response.json({ ok: true }) } if (url.pathname === "/__log") { const since = Number(url.searchParams.get("since") ?? 0) const tag = url.searchParams.get("tag") return Response.json(records.filter((r) => r.seq >= since && (tag === null || r.tag === tag))) } return new Response("not found", { status: 404 }) } const serveStatic = (url: URL): Response => { let p = url.pathname if (p === "/" || p === "/index.html") p = "/index.html" const file = join(PUBLIC, p) if (!file.startsWith(PUBLIC) || !existsSync(file)) return new Response("not found", { status: 404 }) return new Response(readFileSync(file), { headers: { "content-type": contentTypeOf(p), "cache-control": "no-store" }, }) } const page = Bun.serve({ port: pagePort, idleTimeout: 60, async fetch(req) { const url = new URL(req.url) if (url.pathname.startsWith("/otlp/") || url.pathname.startsWith("/probe/")) return handleIngest(req, url, pagePort) if (url.pathname.startsWith("/__")) return handleControl(req, url) return serveStatic(url) }, }) const cross = Bun.serve({ port: crossPort, idleTimeout: 60, async fetch(req) { const url = new URL(req.url) if (url.pathname.startsWith("/otlp/") || url.pathname.startsWith("/probe/")) return handleIngest(req, url, crossPort) return new Response("not found", { status: 404, headers: corsHeaders(req) }) }, }) return { pagePort, crossPort, records, setRules(next) { rules = next ruleHits.clear() }, addRules(next) { rules = [...rules, ...next] }, clearRulesForTag(tag) { rules = rules.filter((r) => r.tag !== tag) }, reset() { records.length = 0 rules = [] ruleHits.clear() }, stop() { page.stop(true) cross.stop(true) }, } } if (import.meta.main) { const logFile = join(ROOT, "server-requests.jsonl") const s = startServer({ logFile }) console.log(`page + same-origin ingest: http://localhost:${s.pagePort}/`) console.log(`cross-origin ingest: http://127.0.0.1:${s.crossPort}/otlp`) console.log(`request log: ${logFile}`) console.log(`try: http://localhost:${s.pagePort}/?variant=flush&interval=1000&tag=manual`) } @@@@@@@@@@ END FILE: repro/server.ts @@@@@@@@@@ FILE: repro/variants/sdk-0.10.0/bun.lock (798 bytes) { "lockfileVersion": 2, "configVersion": 1, "workspaces": { "": { "name": "maple-repro-variant-sdk-0-10-0", "dependencies": { "@maple-dev/effect-sdk": "0.10.0", "effect": "4.0.0", }, }, }, "packages": { "@maple-dev/effect-sdk": ["@maple-dev/effect-sdk@0.10.0", "", { "dependencies": { "std-env": "^4.0.0" }, "peerDependencies": { "effect": "^4.0.0" } }, "sha512-3MWyBqWPyiizAZItDk7Uk+vi69csziIJxCcaqW10aowVV1Tlcwao1EW6xLccAzvfkz43Y8WAiLzhf/Kr4oRkdQ=="], "effect": ["effect@4.0.0", "", {}, "sha512-ooc1TG5t+FfzgYnFz2ff6BBKyZ7EwBRVXC7c4RhQUAD6/TZ2gTXXMeb4WX7a19ozQo4J73/QW+S00YAIresoMQ=="], "std-env": ["std-env@4.3.0", "", {}, "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag=="], } } @@@@@@@@@@ END FILE: repro/variants/sdk-0.10.0/bun.lock @@@@@@@@@@ FILE: repro/variants/sdk-0.10.0/package.json (415 bytes) { "name": "maple-repro-variant-sdk-0-10-0", "private": true, "type": "module", "description": "Optional: the same test page built against the latest published SDK (0.10.0) and its Effect peer (4.0.0), to check which findings persist. Built by `bun run build.ts` in the parent folder when this folder has node_modules.", "dependencies": { "@maple-dev/effect-sdk": "0.10.0", "effect": "4.0.0" } } @@@@@@@@@@ END FILE: repro/variants/sdk-0.10.0/package.json @@@@@@@@@@ FILE: sdk-excerpts/01-flush-core-cooldown-serialization.txt (7677 bytes) Flush core: cooldown, flushSignal, guardFlush, makeSerializedFlush, runFlush ============================================================================ Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/version-B225m7RW.mjs lines 1-173 (src/shared/flush-core.ts region) --- 1 import { Array as Array$1, Cause, Context, Effect, Exit, Layer, Logger, Metric, Option, Predicate, Redacted, References, Result, Tracer } from "effect"; 2 import * as OtlpResource from "effect/unstable/observability/OtlpResource"; 3 import * as ErrorReporter from "effect/ErrorReporter"; 4 //#region src/shared/try-sync.ts 5 /** Run a throwing synchronous call, `None` if it threw. */ 6 const trySync = (thunk) => Effect.runSync(Effect.option(Effect.try(thunk))); 7 /** Run a throwing synchronous call, `undefined` if it threw. */ 8 const trySyncOrUndefined = (thunk) => Option.getOrUndefined(trySync(thunk)); 9 //#endregion 10 //#region src/shared/flush-core.ts 11 /** Disable a signal for this long after a failed POST so a broken collector isn't hammered. */ 12 const COOLDOWN_MS = 6e4; 13 /** 14 * Turn a resolved resource into ready-to-POST URLs + headers. Shared by all 15 * presets; `userAgent` is the only per-preset difference. 16 */ 17 const buildResolved = (r, opts) => { 18 const base = r.endpoint ?? "https://ingest.maple.dev"; 19 const baseUrl = base.endsWith("/") ? base.slice(0, -1) : base; 20 const tracesUrl = `${baseUrl}${opts.tracesPath ?? "/v1/traces"}`; 21 const logsUrl = `${baseUrl}${opts.logsPath ?? "/v1/logs"}`; 22 const metricsUrl = `${baseUrl}${opts.metricsPath ?? "/v1/metrics"}`; 23 const headers = { 24 "content-type": "application/json", 25 "user-agent": opts.userAgent, 26 "x-maple-sdk": opts.userAgent 27 }; 28 if (r.ingestKey) headers.authorization = `Bearer ${Redacted.value(r.ingestKey)}`; 29 return { 30 tracesUrl, 31 logsUrl, 32 metricsUrl, 33 resource: makeOtlpResource(r.resource), 34 scope: { name: r.resource.serviceName }, 35 headers, 36 noOp: r.ingestKey === void 0 37 }; 38 }; 39 const makeOtlpResource = (resource) => { 40 const attrs = []; 41 for (const [key, value] of Object.entries(resource.attributes)) attrs.push({ 42 key, 43 value: anyValue(value) 44 }); 45 attrs.push({ 46 key: "service.name", 47 value: { stringValue: resource.serviceName } 48 }); 49 if (resource.serviceVersion) attrs.push({ 50 key: "service.version", 51 value: { stringValue: resource.serviceVersion } 52 }); 53 return { 54 attributes: attrs, 55 droppedAttributesCount: 0 56 }; 57 }; 58 const anyValue = (value) => { 59 if (Array.isArray(value)) return { arrayValue: { values: value.map(anyValue) } }; 60 switch (typeof value) { 61 case "string": return { stringValue: value }; 62 case "boolean": return { boolValue: value }; 63 case "number": return Number.isInteger(value) ? { intValue: value } : { doubleValue: value }; 64 case "bigint": return { intValue: Number(value) }; 65 default: return { stringValue: String(value) }; 66 } 67 }; 68 /** Plain `fetch` POST. Throws on non-2xx so {@link flushSignal} records a cooldown. */ 69 const post = async (url, headers, body) => { 70 const res = await fetch(url, { 71 method: "POST", 72 headers, 73 body: JSON.stringify(body) 74 }); 75 if (!res.ok) throw new Error(`OTLP ${res.status} ${res.statusText}`); 76 }; 77 /** Default transport: plain `fetch` (server + Cloudflare). */ 78 const fetchTransport = { post }; 79 const flushSignal = async (args) => { 80 const { url, headers, buffer, body, state, signal, transport, logPrefix } = args; 81 if (state.disabledUntil && Date.now() < state.disabledUntil) { 82 console.warn(`${logPrefix} ${signal} flush skipped (cooldown ${state.disabledUntil - Date.now()}ms remaining)`); 83 return; 84 } 85 state.disabledUntil = 0; 86 const batch = buffer.drain(); 87 if (batch.length === 0) return; 88 const posted = await Effect.runPromise(Effect.result(Effect.tryPromise({ 89 try: () => transport.post(url, headers, body(batch)), 90 catch: (cause) => cause 91 }))); 92 if (Result.isFailure(posted)) { 93 buffer.restore(batch); 94 state.disabledUntil = Date.now() + COOLDOWN_MS; 95 console.error(`${logPrefix} ${signal} flush failed; cooldown 60s:`, posted.failure); 96 } 97 }; 98 /** 99 * Wrap a flush body so it logs its failure rather than rejecting. 100 * 101 * Every flush is documented never to reject: they are fired as `void flush()` 102 * from auto-flush timers, `pagehide`/`visibilitychange` handlers, and 103 * `ctx.waitUntil`, where a rejection is an unhandled rejection — fatal under 104 * `--unhandled-rejections=strict` — caused purely by telemetry. `runFlush` 105 * already absorbs per-signal transport errors; this covers everything around it, 106 * chiefly resource resolution. 107 */ 108 const guardFlush = (logPrefix, run) => async (...args) => { 109 const outcome = await Effect.runPromise(Effect.result(Effect.tryPromise({ 110 try: () => run(...args), 111 catch: (cause) => cause 112 }))); 113 if (Result.isFailure(outcome)) console.error(`${logPrefix} flush failed:`, outcome.failure); 114 }; 115 /** Serialize flush calls so concurrent timers/manual hooks cannot drain overlapping batches. */ 116 const makeSerializedFlush = (run) => { 117 let tail = Promise.resolve(); 118 return (...args) => { 119 const next = tail.then(() => run(...args), () => run(...args)); 120 tail = next.catch(() => void 0); 121 return next; 122 }; 123 }; 124 /** 125 * Drain the span + log buffers and POST them. Errors are swallowed per signal 126 * (see {@link flushSignal}); the returned promise never rejects. 127 * 128 * - `noOp`: drain so the buffers don't grow unbounded, fire `onNoOp` (one-shot 129 * "telemetry disabled" notice), never POST. 130 * - empty buffers: short-circuit without a request. 131 */ 132 const runFlush = async (args) => { 133 const { resolved: r, spans, logs, metrics, tracesState, logsState, metricsState, transport, logPrefix, onNoOp } = args; 134 if (r.noOp) { 135 spans.drain(); 136 logs.drain(); 137 metrics.drain(); 138 onNoOp(); 139 return; 140 } 141 await Promise.all([ 142 flushSignal({ 143 url: r.tracesUrl, 144 headers: r.headers, 145 buffer: spans, 146 body: (items) => makeTracesBody(items, r), 147 state: tracesState, 148 signal: "traces", 149 transport, 150 logPrefix 151 }), 152 flushSignal({ 153 url: r.logsUrl, 154 headers: r.headers, 155 buffer: logs, 156 body: (items) => makeLogsBody(items, r), 157 state: logsState, 158 signal: "logs", 159 transport, 160 logPrefix 161 }), 162 flushSignal({ 163 url: r.metricsUrl, 164 headers: r.headers, 165 buffer: metrics, 166 body: (items) => makeMetricsBody(items, r), 167 state: metricsState, 168 signal: "metrics", 169 transport, 170 logPrefix 171 }) 172 ]); 173 }; @@@@@@@@@@ END FILE: sdk-excerpts/01-flush-core-cooldown-serialization.txt @@@@@@@@@@ FILE: sdk-excerpts/02-buffers-cap-restore.txt (6299 bytes) Buffers: log/metric/span buffers, 10k cap, restore() ==================================================== Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/version-B225m7RW.mjs lines 293-321 (src/shared/flushable-logger.ts: makeLogBuffer) --- 293 //#region src/shared/flushable-logger.ts 294 const MAX_BUFFER$1 = 1e4; 295 const makeLogBuffer = (options = {}) => { 296 let buffer = []; 297 let disabled = false; 298 const excludeLogSpans = options.excludeLogSpans ?? false; 299 const logger = Logger.make((logOptions) => { 300 if (disabled) return; 301 if (buffer.length >= MAX_BUFFER$1) return; 302 buffer.push(makeLogRecord(logOptions, excludeLogSpans)); 303 }); 304 return { 305 loggerLayer: Logger.layer([logger], { mergeWithExisting: true }), 306 drain: () => { 307 const items = buffer; 308 buffer = []; 309 return items; 310 }, 311 restore: (items) => { 312 if (disabled || items.length === 0) return; 313 buffer = [...items, ...buffer].slice(0, MAX_BUFFER$1); 314 }, 315 setDisabled: (value) => { 316 disabled = value; 317 if (value) buffer = []; 318 }, 319 size: () => buffer.length 320 }; 321 }; --- @maple-dev/effect-sdk/dist/version-B225m7RW.mjs lines 367-419 (src/shared/flushable-metrics.ts: makeMetricBuffer) --- 367 //#region src/shared/flushable-metrics.ts 368 /** 369 * Isolated cumulative registry. Drains only after changes, and `restore` 370 * re-marks failed exports as pending. 371 */ 372 const makeMetricBuffer = () => { 373 let disabled = false; 374 let captured = false; 375 let dirty = false; 376 /** 377 * Revoking after capture disables metrics for the page lifetime. Cumulative 378 * state cannot forget prior samples, and clearing the registry breaks cached 379 * Metric hooks. A late first grant still works because `captured` is false. 380 */ 381 let revoked = false; 382 const registry = /* @__PURE__ */ new Map(); 383 const set = registry.set.bind(registry); 384 registry.set = (key, metadata) => { 385 const hooks = metadata.hooks; 386 metadata.hooks = { 387 get: hooks.get, 388 update: (input, context) => { 389 if (disabled) return; 390 captured = true; 391 hooks.update(input, context); 392 dirty = true; 393 }, 394 modify: (input, context) => { 395 if (disabled) return; 396 captured = true; 397 hooks.modify(input, context); 398 dirty = true; 399 } 400 }; 401 return set(key, metadata); 402 }; 403 const snapshotContext = Context.make(Metric.MetricRegistry, registry); 404 return { 405 layer: Layer.succeed(Metric.MetricRegistry, registry), 406 drain: () => { 407 if (disabled || revoked || !dirty) return []; 408 dirty = false; 409 return [...Metric.snapshotUnsafe(snapshotContext)]; 410 }, 411 restore: (items) => { 412 if (items.length > 0) dirty = true; 413 }, 414 setDisabled: (value) => { 415 if (value && captured) revoked = true; 416 disabled = value; 417 } 418 }; 419 }; --- @maple-dev/effect-sdk/dist/version-B225m7RW.mjs lines 421-493 (src/shared/flushable-tracer.ts: makeSpanBuffer) --- 421 //#region src/shared/flushable-tracer.ts 422 const MAX_BUFFER = 1e4; 423 const isIgnoredFailure = (error) => Predicate.hasProperty(error, ErrorReporter.ignore) && error[ErrorReporter.ignore] === true; 424 const isIgnoredSpan = (span) => { 425 const status = span.status; 426 if (status._tag !== "Ended") return false; 427 const exit = status.exit; 428 if (exit._tag !== "Failure") return false; 429 if (exit.cause.reasons.some(Cause.isDieReason)) return false; 430 const failures = exit.cause.reasons.filter(Cause.isFailReason); 431 return failures.length > 0 && failures.every((reason) => isIgnoredFailure(reason.error)); 432 }; 433 const makeSpanBuffer = (options = {}) => { 434 let buffer = []; 435 let disabled = false; 436 const dropSpan = options.dropSpan; 437 const anticipatedErrorIdentifiers = options.anticipatedErrorIdentifiers ?? options.anticipatedErrorTags; 438 const exportFn = (span) => { 439 if (disabled) return; 440 if (!span.sampled) return; 441 if (dropSpan !== void 0 && dropSpan(span.name)) return; 442 if (isIgnoredSpan(span)) return; 443 if (buffer.length >= MAX_BUFFER) return; 444 buffer.push(makeOtlpSpan(span, anticipatedErrorIdentifiers)); 445 }; 446 const tracer = Tracer.make({ span(spanOptions) { 447 return makeSpan({ 448 ...spanOptions, 449 status: { 450 _tag: "Started", 451 startTime: spanOptions.startTime 452 }, 453 attributes: /* @__PURE__ */ new Map(), 454 export: exportFn 455 }); 456 } }); 457 const captureException = (error, captureOptions = {}) => { 458 if (disabled) return; 459 const now = BigInt(Date.now()) * 1000000n; 460 makeSpan({ 461 name: captureOptions.name ?? "exception", 462 parent: Option.none(), 463 annotations: Context.empty(), 464 status: { 465 _tag: "Started", 466 startTime: now 467 }, 468 attributes: new Map(Object.entries(captureOptions.attributes ?? {})), 469 links: [], 470 sampled: true, 471 kind: "internal", 472 export: exportFn 473 }).end(now, Exit.failCause(Cause.die(error))); 474 }; 475 return { 476 tracerLayer: Layer.succeed(Tracer.Tracer, tracer), 477 captureException, 478 drain: () => { 479 const items = buffer; 480 buffer = []; 481 return items; 482 }, 483 restore: (items) => { 484 if (disabled || items.length === 0) return; 485 buffer = [...items, ...buffer].slice(0, MAX_BUFFER); 486 }, 487 setDisabled: (value) => { 488 disabled = value; 489 if (value) buffer = []; 490 }, 491 size: () => buffer.length 492 }; 493 }; @@@@@@@@@@ END FILE: sdk-excerpts/02-buffers-cap-restore.txt @@@@@@@@@@ FILE: sdk-excerpts/03-mapleflush-make-client.txt (14768 bytes) MapleFlush.make (client): keepalive transport, auto-flush timer, pagehide/visibilitychange flush, dispose ========================================================================================================= Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 1984-2175 (src/client/flushable.ts) --- 1984 //#region src/client/flushable.ts 1985 var flushable_exports = /* @__PURE__ */ __exportAll({ make: () => make }); 1986 /** Default auto-flush cadence (ms), matching `Otlp.layerJson`'s 5s export interval. */ 1987 const DEFAULT_AUTO_FLUSH_MS = 5e3; 1988 const browserInstanceId = globalThis.crypto?.randomUUID?.() ?? `browser-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; 1989 /** `fetch(keepalive)` transport — see file header for why not `sendBeacon`. */ 1990 const keepaliveTransport = { post: async (url, headers, body) => { 1991 const res = await fetch(url, { 1992 method: "POST", 1993 headers, 1994 body: JSON.stringify(body), 1995 keepalive: true 1996 }); 1997 if (!res.ok) throw new Error(`OTLP ${res.status} ${res.statusText}`); 1998 } }; 1999 const buildBrowserAttributes = (config) => { 2000 const attributes = { 2001 "maple.sdk.type": "client", 2002 "service.instance.id": browserInstanceId 2003 }; 2004 const nav = browserNavigator(); 2005 if (nav) { 2006 if (nav.userAgent) attributes["user_agent.original"] = nav.userAgent; 2007 if (nav.language) attributes["browser.language"] = nav.language; 2008 } 2009 if (typeof Intl !== "undefined") { 2010 const timezone = trySyncOrUndefined(() => Intl.DateTimeFormat().resolvedOptions().timeZone); 2011 if (timezone) attributes["browser.timezone"] = timezone; 2012 } 2013 if (config.environment) { 2014 attributes["deployment.environment"] = config.environment; 2015 attributes["deployment.environment.name"] = config.environment; 2016 } 2017 if (config.serviceVersion && /^[0-9a-f]{7,40}$/i.test(config.serviceVersion)) attributes["vcs.ref.head.revision"] = config.serviceVersion; 2018 if (config.serviceNamespace) attributes["service.namespace"] = config.serviceNamespace; 2019 if (config.attributes) Object.assign(attributes, config.attributes); 2020 return attributes; 2021 }; 2022 const make = (config) => { 2023 const dropPrefixes = config.dropSpanNames; 2024 const dropSpan = dropPrefixes !== void 0 && dropPrefixes.length > 0 ? (name) => dropPrefixes.some((prefix) => name.startsWith(prefix)) : void 0; 2025 const anticipatedErrorIdentifiers = [...config.anticipatedErrorIdentifiers ?? [], ...config.anticipatedErrorTags ?? []]; 2026 const anticipatedIdentifiers = anticipatedErrorIdentifiers.length > 0 ? new Set(anticipatedErrorIdentifiers) : void 0; 2027 const clientSession = startClientSession({ 2028 endpoint: config.endpoint, 2029 ingestKey: config.ingestKey, 2030 serviceName: config.serviceName, 2031 environment: config.environment, 2032 serviceVersion: config.serviceVersion, 2033 replay: config.replay, 2034 emitSessionMeta: config.emitSessionMeta, 2035 privacy: config.privacy 2036 }); 2037 const spans = makeSpanBuffer({ 2038 dropSpan, 2039 anticipatedErrorIdentifiers: anticipatedIdentifiers 2040 }); 2041 const logs = makeLogBuffer({ excludeLogSpans: config.excludeLogSpans }); 2042 const metrics = makeMetricBuffer(); 2043 const setCaptureAllowed = (allowed) => { 2044 spans.setDisabled(!allowed); 2045 logs.setDisabled(!allowed); 2046 metrics.setDisabled(!allowed); 2047 }; 2048 setCaptureAllowed(hasConsent()); 2049 const stopConsentListener = config.privacy?.requireConsent ? onConsentChange(setCaptureAllowed) : () => {}; 2050 const layer = withSessionLink(Layer.mergeAll(spans.tracerLayer, logs.loggerLayer, metrics.layer)); 2051 const resource = { 2052 endpoint: config.endpoint, 2053 ingestKey: config.ingestKey ? Redacted.make(config.ingestKey) : void 0, 2054 resource: { 2055 serviceName: config.serviceName, 2056 serviceVersion: config.serviceVersion, 2057 attributes: buildBrowserAttributes(config) 2058 } 2059 }; 2060 const resolved = buildResolved(resource, { 2061 tracesPath: config.tracesPath, 2062 logsPath: config.logsPath, 2063 metricsPath: config.metricsPath, 2064 userAgent: `maple-effect-sdk-client/${SDK_VERSION}` 2065 }); 2066 const tracesState = { disabledUntil: 0 }; 2067 const logsState = { disabledUntil: 0 }; 2068 const metricsState = { disabledUntil: 0 }; 2069 const noOpNotice = makeNoOpNotice("[MapleClientSDK]", "pass `ingestKey` to enable"); 2070 const flush = makeSerializedFlush(guardFlush("[MapleClientSDK]", async () => { 2071 if (!hasConsent()) { 2072 spans.drain(); 2073 logs.drain(); 2074 metrics.drain(); 2075 return; 2076 } 2077 await runFlush({ 2078 resolved, 2079 spans, 2080 logs, 2081 metrics, 2082 tracesState, 2083 logsState, 2084 metricsState, 2085 transport: keepaliveTransport, 2086 logPrefix: "[MapleClientSDK]", 2087 onNoOp: noOpNotice 2088 }); 2089 })); 2090 const intervalMs = config.autoFlushInterval === void 0 ? DEFAULT_AUTO_FLUSH_MS : config.autoFlushInterval === false ? 0 : config.autoFlushInterval; 2091 let timer; 2092 if (intervalMs > 0) { 2093 timer = setInterval(() => { 2094 flush(); 2095 }, intervalMs); 2096 timer.unref?.(); 2097 } 2098 /** 2099 * One error reaching two paths must still be one issue. React rethrows a 2100 * boundary-caught error in development, so a boundary that reports it *and* 2101 * `window.onerror` would otherwise fingerprint the same crash twice. 2102 */ 2103 const reported = /* @__PURE__ */ new WeakSet(); 2104 const captureException = (error, options = {}) => { 2105 if (typeof error === "object" && error !== null) { 2106 if (reported.has(error)) return; 2107 reported.add(error); 2108 } 2109 const page = globalThis.location?.href; 2110 spans.captureException(error, { 2111 ...options, 2112 attributes: { 2113 ...page !== void 0 ? { "url.full": page } : void 0, 2114 ...options.attributes 2115 } 2116 }); 2117 }; 2118 const onWindowError = (event) => { 2119 const error = event.error ?? (event.message && event.filename ? new Error(event.message) : void 0); 2120 if (error === void 0) return; 2121 captureException(error, { 2122 name: "browser.uncaught_error", 2123 attributes: { 2124 "maple.exception.source": "window.onerror", 2125 ...event.filename ? { "code.file.path": event.filename } : void 0, 2126 ...event.lineno ? { "code.line.number": event.lineno } : void 0 2127 } 2128 }); 2129 }; 2130 const onUnhandledRejection = (event) => { 2131 captureException(event.reason, { 2132 name: "browser.unhandled_rejection", 2133 attributes: { "maple.exception.source": "unhandledrejection" } 2134 }); 2135 }; 2136 const canCaptureGlobals = (config.captureGlobalErrors ?? true) && typeof globalThis.addEventListener === "function"; 2137 if (canCaptureGlobals) { 2138 globalThis.addEventListener("error", onWindowError); 2139 globalThis.addEventListener("unhandledrejection", onUnhandledRejection); 2140 } 2141 const onPageHide = () => { 2142 flush(); 2143 }; 2144 const onVisibilityChange = () => { 2145 if (browserDocument()?.visibilityState === "hidden") flush(); 2146 }; 2147 const canListen = (config.flushOnUnload ?? true) && typeof globalThis.addEventListener === "function"; 2148 if (canListen) { 2149 globalThis.addEventListener("pagehide", onPageHide); 2150 globalThis.addEventListener("visibilitychange", onVisibilityChange); 2151 } 2152 const dispose = async () => { 2153 if (timer !== void 0) { 2154 clearInterval(timer); 2155 timer = void 0; 2156 } 2157 if (canListen) { 2158 globalThis.removeEventListener("pagehide", onPageHide); 2159 globalThis.removeEventListener("visibilitychange", onVisibilityChange); 2160 } 2161 if (canCaptureGlobals) { 2162 globalThis.removeEventListener("error", onWindowError); 2163 globalThis.removeEventListener("unhandledrejection", onUnhandledRejection); 2164 } 2165 stopConsentListener(); 2166 await flush(); 2167 await clientSession.stop(); 2168 }; 2169 return { 2170 layer, 2171 captureException, 2172 flush, 2173 dispose 2174 }; 2175 }; --- @maple-dev/effect-sdk/dist/index-CM1baBqK.d.mts lines 123-225 (type declarations: MapleClientFlushableConfig / FlushableTelemetry) --- 123 interface MapleClientFlushableConfig { 124 /** Service name reported in traces, logs, and metrics. */ 125 readonly serviceName: string; 126 /** Maple ingest endpoint URL. */ 127 readonly endpoint: string; 128 /** Maple ingest key. When unset, the preset runs in no-op mode. */ 129 readonly ingestKey?: string | undefined; 130 /** Service version or commit SHA. */ 131 readonly serviceVersion?: string | undefined; 132 /** 133 * Logical group this service belongs to, emitted as the OTel 134 * `service.namespace` resource attribute. Optional — only stamped when set. 135 */ 136 readonly serviceNamespace?: string | undefined; 137 /** Deployment environment (e.g. "production", "staging"). */ 138 readonly environment?: string | undefined; 139 /** Additional resource attributes (highest precedence). */ 140 readonly attributes?: Record | undefined; 141 /** Skip Effect log spans in OTLP log attributes. Default `false`. */ 142 readonly excludeLogSpans?: boolean | undefined; 143 /** Span name prefixes to drop before OTLP export. */ 144 readonly dropSpanNames?: ReadonlyArray | undefined; 145 /** 146 * Stable `_tag` / `Error.name` identifiers of anticipated 4xx failures. Spans 147 * failing entirely with these export as status `Ok` (no `exception` event), 148 * so they stay visible but never count as errors. 149 */ 150 readonly anticipatedErrorIdentifiers?: ReadonlyArray | undefined; 151 /** @deprecated Use `anticipatedErrorIdentifiers`. */ 152 readonly anticipatedErrorTags?: ReadonlyArray | undefined; 153 /** OTLP traces path appended to `endpoint`. Default `/v1/traces`. */ 154 readonly tracesPath?: string | undefined; 155 /** OTLP logs path appended to `endpoint`. Default `/v1/logs`. */ 156 readonly logsPath?: string | undefined; 157 /** OTLP metrics path appended to `endpoint`. Default `/v1/metrics`. */ 158 readonly metricsPath?: string | undefined; 159 /** 160 * Background auto-flush cadence in milliseconds. Default `5000`. Set to `0` 161 * or `false` to disable and flush purely on demand (note: the in-memory 162 * buffer caps at 10k items, so a long-lived tab that never flushes will 163 * eventually drop new spans). 164 */ 165 readonly autoFlushInterval?: number | false | undefined; 166 /** 167 * Register `pagehide` + `visibilitychange→hidden` listeners that flush the 168 * buffer before the tab goes away. Default `true`. No-ops when there's no 169 * `addEventListener` (SSR / non-DOM runtime). 170 */ 171 readonly flushOnUnload?: boolean | undefined; 172 /** 173 * Post session metadata rows for the standalone session so it appears in 174 * Maple's Sessions UI (list entry + linked traces, no replay recording). 175 * Default `true`; no-ops when `@maple-dev/browser` is on the page (it owns 176 * the session rows), without a browser DOM, or without an ingest key. 177 */ 178 readonly emitSessionMeta?: boolean | undefined; 179 /** 180 * rrweb session replay for this app, recorded by the shared Maple replay 181 * engine (loaded lazily in a code-split chunk). Default enabled with 182 * sampleRate 1 and inputs masked — set `{ enabled: false }` to opt out. 183 */ 184 readonly replay?: ClientReplayConfig | undefined; 185 /** 186 * Capture uncaught errors and unhandled promise rejections from the page and 187 * record them as error spans. Default `true`. 188 * 189 * Without this the SDK only ever sees failures that happened *inside* an 190 * Effect span, which in a browser is the minority of them — a React render 191 * crash, a throw in an event handler and a floating rejected promise all 192 * bypass Effect entirely and would otherwise never reach Maple. 193 */ 194 readonly captureGlobalErrors?: boolean | undefined; 195 /** 196 * Consent gating, persistent-visitor-id storage, and whether `identify()`'s 197 * email reaches the warehouse. Defaults capture everything except where a 198 * browser signals otherwise: Global Privacy Control suppresses the 199 * persistent visitor id (capture itself continues, anonymously). 200 */ 201 readonly privacy?: PrivacyOptions | undefined; 202 } 203 interface FlushableTelemetry { 204 /** 205 * Effect Layer installing the buffer-backed OTLP tracer (with replay-session 206 * linking) + Effect logger. Must live in the same runtime as your 207 * instrumented code. 208 */ 209 readonly layer: Layer.Layer; 210 /** 211 * Record an error that never passed through an Effect span — the escape 212 * hatch for the places a browser throws outside Effect. The canonical caller 213 * is a React error boundary, which catches the error and, unless it reports 214 * it here, is the reason nobody ever hears about the crash. 215 * 216 * BOUNDARY: a thrown value is unparsed by definition — JavaScript can throw 217 * anything. It is narrowed on the way into the exception event. 218 */ 219 readonly captureException: (error: unknown, options?: CaptureExceptionOptions) => void; 220 /** Drain the buffers and POST them now (keepalive). Never rejects. */ 221 readonly flush: () => Promise; 222 /** Remove unload listeners, stop the auto-flush timer, then do one final flush. */ 223 readonly dispose: () => Promise; 224 } 225 declare const make: (config: MapleClientFlushableConfig) => FlushableTelemetry; @@@@@@@@@@ END FILE: sdk-excerpts/03-mapleflush-make-client.txt @@@@@@@@@@ FILE: sdk-excerpts/04-replay-transport-keepalive-budget.txt (6957 bytes) Session/replay transport: MAX_KEEPALIVE_BYTES, keepaliveFor(), warnDropped(), postSessionMeta/Events/Blob ========================================================================================================= Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 569-687 (../browser-session/src/platform/transport.ts) --- 569 //#region ../browser-session/src/platform/transport.ts 570 /** 571 * Header stamped on every request to ingest: `/`, e.g. 572 * `maple-browser/0.3.0`. Browsers refuse to let a page set `user-agent`, so 573 * without this a rejected request carried nothing that said which SDK build 574 * produced it. Ingest records it as `maple.sdk` on the request span. Every 575 * gateway that receives browser traffic must allow it in CORS (`apps/ingest` 576 * and the CLI's local listener do) — a header the SDK always sends and 577 * preflight refuses blocks the whole SDK, not just the header. 578 */ 579 const SDK_HINT_HEADER = "x-maple-sdk"; 580 /** Auth + identity headers shared by every ingest request. */ 581 function ingestHeaders(config) { 582 return { 583 Authorization: `Bearer ${config.ingestKey}`, 584 [SDK_HINT_HEADER]: config.sdk 585 }; 586 } 587 const lastWarnAt = /* @__PURE__ */ new Map(); 588 function warnDropped(what, error) { 589 const now = Date.now(); 590 if (now - (lastWarnAt.get(what) ?? 0) < 3e4) return; 591 lastWarnAt.set(what, now); 592 console.warn(`[maple] session replay ${what} failed (dropping; will retry on next chunk):`, error); 593 } 594 /** 595 * Largest body we will hand to a `keepalive` fetch. 596 * 597 * The Fetch spec caps the *combined* inflight keepalive body at 64 KiB, and on 598 * the way out we issue up to three of these at once (metadata row, final events 599 * batch, last replay chunk). Over the budget the browser rejects the request 600 * outright, so a normal request — which the page may or may not survive long 601 * enough to finish — is strictly the better bet than a guaranteed rejection. 602 */ 603 const MAX_KEEPALIVE_BYTES = 49152; 604 /** Whether a body of `bytes` may still ride the keepalive budget. */ 605 function keepaliveFor(requested, bytes) { 606 return requested && bytes <= MAX_KEEPALIVE_BYTES; 607 } 608 /** 609 * Smallest well-formed gzip stream: a 10-byte header plus an 8-byte trailer. 610 * Anything shorter cannot carry a complete member, whatever it contains. 611 */ 612 const GZIP_MIN_BYTES = 18; 613 /** 614 * gzip a byte buffer using the native CompressionStream (no library). 615 * 616 * Throws rather than returning a partial stream. The write and close promises 617 * used to be discarded, so a page torn down mid-compression still resolved 618 * `arrayBuffer()` — with only the bytes already flushed — and the caller posted 619 * a 3-byte header stub as if it were a complete chunk. Ingest rejected those as 620 * corrupt gzip; they were 82% of all replay-blob failures, overwhelmingly from 621 * crawlers, which tear the page down on nearly every session. 622 */ 623 async function gzip(bytes) { 624 const stream = new CompressionStream("gzip"); 625 const writer = stream.writable.getWriter(); 626 const written = writer.write(bytes).then(() => writer.close()); 627 const [buffer] = await Promise.all([new Response(stream.readable).arrayBuffer(), written]); 628 const out = new Uint8Array(buffer); 629 if (out.length < GZIP_MIN_BYTES || out[0] !== 31 || out[1] !== 139 || out[2] !== 8) throw new Error(`gzip produced ${out.length} bytes, not a complete gzip stream`); 630 return out; 631 } 632 /** POST session metadata (NDJSON, single row). `keepalive` for the final unload write. */ 633 async function postSessionMeta(config, row, keepalive = false) { 634 const body = `${JSON.stringify(row)}\n`; 635 await fetch(`${config.endpoint}/v1/sessionReplays/meta`, { 636 method: "POST", 637 headers: { 638 ...ingestHeaders(config), 639 "content-type": "application/x-ndjson" 640 }, 641 body, 642 keepalive: keepaliveFor(keepalive, body.length) 643 }).catch((error) => { 644 warnDropped("metadata POST", error); 645 }); 646 } 647 /** POST distilled session events (NDJSON, one row per event). Best-effort. */ 648 async function postSessionEvents(config, rows, keepalive = false) { 649 if (rows.length === 0) return; 650 const body = `${rows.map((row) => JSON.stringify(row)).join("\n")}\n`; 651 await fetch(`${config.endpoint}/v1/sessionEvents`, { 652 method: "POST", 653 headers: { 654 ...ingestHeaders(config), 655 "content-type": "application/x-ndjson" 656 }, 657 body, 658 keepalive: keepaliveFor(keepalive, body.length) 659 }).catch((error) => { 660 warnDropped("events POST", error); 661 }); 662 } 663 const SESSION_EXHAUSTED_STATUS = 413; 664 /** POST a gzipped rrweb event chunk. */ 665 async function postSessionBlob(config, meta, gzipped, keepalive = false) { 666 try { 667 const response = await fetch(`${config.endpoint}/v1/sessionReplays/blob`, { 668 method: "POST", 669 headers: { 670 ...ingestHeaders(config), 671 "content-type": "application/octet-stream", 672 "x-maple-session-id": meta.sessionId, 673 "x-maple-chunk-seq": String(meta.chunkSeq), 674 "x-maple-is-checkpoint": meta.isCheckpoint ? "1" : "0", 675 "x-maple-event-count": String(meta.eventCount), 676 "x-maple-duration-ms": String(meta.durationMs) 677 }, 678 body: gzipped, 679 keepalive: keepaliveFor(keepalive, gzipped.byteLength) 680 }); 681 if (response.ok) return "accepted"; 682 return response.status === SESSION_EXHAUSTED_STATUS ? "exhausted" : "rejected"; 683 } catch (error) { 684 warnDropped("blob PUT", error); 685 return "failed"; 686 } 687 } --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 796-808 (../browser-session/src/events/meta-row.ts: postSessionMetaRow (metadata-only sessions)) --- 796 /** POST one session metadata row (NDJSON). Best-effort — never throws. */ 797 async function postSessionMetaRow(target, row, keepalive = false) { 798 const body = `${JSON.stringify(row)}\n`; 799 await fetch(`${target.endpoint.replace(/\/$/, "")}/v1/sessionReplays/meta`, { 800 method: "POST", 801 headers: { 802 ...ingestHeaders(target), 803 "content-type": "application/x-ndjson" 804 }, 805 body, 806 keepalive: keepaliveFor(keepalive, body.length) 807 }).catch(() => {}); 808 } @@@@@@@@@@ END FILE: sdk-excerpts/04-replay-transport-keepalive-budget.txt @@@@@@@@@@ FILE: sdk-excerpts/05-session-event-sink.txt (4230 bytes) Session event sink: 5 s non-keepalive flush, drop-on-failure ============================================================ Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 1123-1220 (../browser-session/src/events/events-sink.ts) --- 1123 //#region ../browser-session/src/events/events-sink.ts 1124 const FLUSH_INTERVAL_MS = 5e3; 1125 const FLUSH_BYTES = 65536; 1126 /** 1127 * The sink is a per-session singleton, published on `globalThis` rather than a 1128 * module-level variable. 1129 * 1130 * Two consumers can end up with separate copies of this module in one page (an 1131 * app bundling both `@maple-dev/browser` and the Effect SDK, or a 1132 * lazily-imported replay chunk). Two sinks would mean two `seq` counters 1133 * starting at 0, and `Seq` is part of the `session_events` sorting key — the 1134 * rows would collide. One global owner avoids that. 1135 */ 1136 const SINK_KEY = "__MAPLE_SESSION_EVENT_SINK__"; 1137 function holder() { 1138 return globalThis; 1139 } 1140 /** The live sink, if one has been started. */ 1141 function getActiveSink() { 1142 return holder()[SINK_KEY]?.sink; 1143 } 1144 /** 1145 * Start (or reuse) the distilled-event sink for a session. 1146 * 1147 * Runs on **every** page load, not just sampled-for-replay ones: page views and 1148 * `track()` calls are the analytics substrate, and gating them behind replay 1149 * sampling would make unique visitors and top pages a sample rather than a 1150 * count. The rrweb recorder stays sampled — it is the expensive part. 1151 */ 1152 function startEventSink(config, sessionId) { 1153 const existing = holder()[SINK_KEY]; 1154 if (existing && existing.sessionId === sessionId) return existing.sink; 1155 if (existing) { 1156 existing.sink.flush(); 1157 existing.sink.stop(); 1158 } 1159 let buffer = []; 1160 let bufferBytes = 0; 1161 let seq = 0; 1162 let pageViews = 0; 1163 let clickCount = 0; 1164 let errorCount = 0; 1165 const flush = async (keepalive = false) => { 1166 if (buffer.length === 0) return; 1167 const batch = buffer; 1168 buffer = []; 1169 bufferBytes = 0; 1170 await postSessionEvents(config, batch.map(({ ev, seq }) => toRow(config, sessionId, ev, seq)), keepalive); 1171 }; 1172 const emit = (ev) => { 1173 const session = markActivity(); 1174 if (session && session.id !== sessionId) { 1175 const nextSink = startEventSink(config, session.id); 1176 if (ev.type !== "navigation") nextSink.emit(ev); 1177 return; 1178 } 1179 if (ev.type === "navigation") { 1180 pageViews++; 1181 noteNavigation(ev.url ?? (typeof location !== "undefined" ? location.href : "")); 1182 } else if (ev.type === "click") clickCount++; 1183 else if (ev.type === "error" || ev.type === "console" && ev.level === "error") errorCount++; 1184 buffer.push({ 1185 ev, 1186 seq: seq++ 1187 }); 1188 bufferBytes += approximateSize(ev); 1189 if (bufferBytes >= FLUSH_BYTES) flush(); 1190 }; 1191 const stopNavigation = installNavigationObserver((url) => { 1192 emit({ 1193 type: "navigation", 1194 url 1195 }); 1196 }); 1197 const stopBaselineCapture = startBaselineCapture(emit, config.maskAllText); 1198 const flushTimer = setInterval(() => void flush(), FLUSH_INTERVAL_MS); 1199 const sink = { 1200 sessionId, 1201 emit, 1202 flush, 1203 stop: () => { 1204 clearInterval(flushTimer); 1205 stopNavigation(); 1206 stopBaselineCapture(); 1207 if (holder()[SINK_KEY]?.sink === sink) holder()[SINK_KEY] = void 0; 1208 }, 1209 getPageViews: () => pageViews, 1210 getClickCount: () => clickCount, 1211 getErrorCount: () => errorCount, 1212 ignoreUrl: (url) => url.startsWith(`${config.endpoint}/v1/`) 1213 }; 1214 holder()[SINK_KEY] = { 1215 sessionId, 1216 sink 1217 }; 1218 drainPending(sink); 1219 return sink; 1220 } @@@@@@@@@@ END FILE: sdk-excerpts/05-session-event-sink.txt @@@@@@@@@@ FILE: sdk-excerpts/06-session-lifecycle-unload.txt (10642 bytes) Session lifecycle: pagehide/visibilitychange 'ended' row (keepalive) + metadata-only session + trace-id cap comment =================================================================================================================== Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 1410-1611 (../browser-session/src/session/lifecycle.ts + metadata-session.ts) --- 1410 //#region ../browser-session/src/session/lifecycle.ts 1411 /** 1412 * How often a visible tab re-posts its `active` row. 1413 * 1414 * This is what makes exit page, page views and duration survive a tab killed 1415 * without an unload beacon — otherwise the only surviving row is the v1 row 1416 * posted at session start, whose counters are all zero and therefore read as a 1417 * bounce. 60s is the floor worth using: the table is a ReplacingMergeTree, so 1418 * every heartbeat is an unmerged part until the next merge. Billing meters only 1419 * `version == 1` rows, so heartbeats do not double-bill. 1420 */ 1421 const HEARTBEAT_INTERVAL_MS = 6e4; 1422 /** 1423 * Drive one session's metadata lifecycle. Returns undefined outside a browser; 1424 * sampling and consent are the caller's decisions. 1425 */ 1426 function startSessionLifecycle(options, hooks) { 1427 if (typeof window === "undefined") return void 0; 1428 let current = getSession(); 1429 let stopped = false; 1430 /** Whether a run is live: capture started, `active` posted, heartbeat armed. */ 1431 let running = false; 1432 let heartbeat; 1433 let clickCountBase = 0; 1434 let errorCountBase = 0; 1435 let sinkClickCountAtStart = 0; 1436 let sinkErrorCountAtStart = 0; 1437 /** 1438 * The persisted record of the session this lifecycle owns. 1439 * 1440 * `current` fixes only the identity of the run — counters, `lastUrl` and 1441 * `lastActivityAt` move underneath it as the page is used, so a row built 1442 * from the captured object alone reports the exit path and page views of the 1443 * session's *first* page forever. Reading storage back per row keeps them 1444 * live, and the id check keeps a rotation from attributing the incoming 1445 * session's context to the outgoing session's `ended` row. 1446 */ 1447 const liveRecord = () => { 1448 const stored = peekSession(); 1449 return stored?.id === current.id ? stored : current; 1450 }; 1451 /** 1452 * Re-baseline the cumulative counters against the persisted record. 1453 * 1454 * The record is what carries counts across a reload and across a hide/resume 1455 * cycle (each `ended` row writes them back via `noteCounts`), while live 1456 * capture restarts from zero every run — so a run's row is the persisted base 1457 * plus what capture has seen since this run began. 1458 */ 1459 const rebaseCounts = (record) => { 1460 clickCountBase = record.clickCount ?? 0; 1461 errorCountBase = record.errorCount ?? 0; 1462 const sink = getActiveSink(); 1463 sinkClickCountAtStart = sink?.getClickCount() ?? 0; 1464 sinkErrorCountAtStart = sink?.getErrorCount() ?? 0; 1465 }; 1466 const countsFor = (record) => { 1467 const sink = getActiveSink(); 1468 const sinkMatches = sink !== void 0 && sink.sessionId === record.id; 1469 const clicks = hooks.clicksSinceStart?.() ?? (sinkMatches ? sink.getClickCount() - sinkClickCountAtStart : 0); 1470 const errors = sinkMatches ? sink.getErrorCount() - sinkErrorCountAtStart : 0; 1471 const counts = { 1472 clickCount: clickCountBase + Math.max(0, clicks), 1473 errorCount: errorCountBase + Math.max(0, errors), 1474 pageViews: record.pageViews ?? (sinkMatches ? sink.getPageViews() : 0) 1475 }; 1476 if (peekSession()?.id === record.id) noteCounts({ 1477 clickCount: counts.clickCount, 1478 errorCount: counts.errorCount 1479 }); 1480 return counts; 1481 }; 1482 const post = (status, keepalive) => { 1483 const record = liveRecord(); 1484 const counts = countsFor(record); 1485 hooks.post(buildSessionMetaRow({ 1486 sessionId: record.id, 1487 startedAt: new Date(record.startedAt), 1488 version: nextMetaVersion(), 1489 status, 1490 serviceName: options.serviceName, 1491 identity: options.getIdentity?.(), 1492 captureUserEmail: options.captureUserEmail, 1493 environment: options.environment, 1494 serviceVersion: options.serviceVersion, 1495 visitorId: getVisitorId(), 1496 visitorIsNew: record.visitorIsNew === true, 1497 visitorIdPersisted: isVisitorIdPersisted(), 1498 entry: entryContextOf(record), 1499 lastUrl: record.lastUrl, 1500 clickCount: counts.clickCount, 1501 pageViews: counts.pageViews, 1502 errorCount: counts.errorCount, 1503 traceIds: status === "ended" ? options.getTraceIds?.(record.id) : void 0, 1504 recorded: hooks.recorded 1505 }), keepalive); 1506 }; 1507 const stopHeartbeat = () => { 1508 if (heartbeat === void 0) return; 1509 clearInterval(heartbeat); 1510 heartbeat = void 0; 1511 }; 1512 const startRun = () => { 1513 if (stopped || running) return; 1514 running = true; 1515 const record = liveRecord(); 1516 rebaseCounts(record); 1517 hooks.onStart?.(record); 1518 post("active", false); 1519 heartbeat = setInterval(() => { 1520 if (typeof document !== "undefined" && document.visibilityState !== "visible") return; 1521 if (isSessionExpired(liveRecord())) { 1522 endRun({ 1523 flush: true, 1524 keepalive: false 1525 }); 1526 return; 1527 } 1528 post("active", false); 1529 }, HEARTBEAT_INTERVAL_MS); 1530 }; 1531 const endRun = (suspend) => { 1532 if (!running) return; 1533 running = false; 1534 stopHeartbeat(); 1535 if (suspend.flush) post("ended", suspend.keepalive); 1536 return hooks.onSuspend?.(suspend); 1537 }; 1538 const stopRotationListener = onSessionRotate((previous, next) => { 1539 if (stopped || previous.id !== current.id) return; 1540 endRun({ 1541 flush: true, 1542 keepalive: false 1543 }); 1544 current = next; 1545 hooks.onSessionChange?.(next.id); 1546 queueMicrotask(() => startRun()); 1547 }); 1548 const onPageHide = () => void endRun({ 1549 flush: true, 1550 keepalive: true 1551 }); 1552 const onVisibilityChange = () => { 1553 const doc = browserDocument$1(); 1554 if (!doc) return; 1555 if (doc.visibilityState === "hidden") { 1556 endRun({ 1557 flush: true, 1558 keepalive: true 1559 }); 1560 return; 1561 } 1562 if (isSessionExpired(liveRecord())) { 1563 getSession(); 1564 return; 1565 } 1566 startRun(); 1567 }; 1568 const visibilityTarget = typeof document !== "undefined" && typeof document.addEventListener === "function" ? document : typeof globalThis.addEventListener === "function" ? globalThis : void 0; 1569 const pageHideTarget = typeof globalThis.addEventListener === "function" ? globalThis : void 0; 1570 startRun(); 1571 visibilityTarget?.addEventListener("visibilitychange", onVisibilityChange); 1572 pageHideTarget?.addEventListener("pagehide", onPageHide); 1573 return { 1574 get sessionId() { 1575 return current.id; 1576 }, 1577 shutdown: async (shutdownOptions) => { 1578 if (stopped) return; 1579 stopped = true; 1580 stopRotationListener(); 1581 visibilityTarget?.removeEventListener("visibilitychange", onVisibilityChange); 1582 pageHideTarget?.removeEventListener("pagehide", onPageHide); 1583 const flush = shutdownOptions?.flush ?? true; 1584 await endRun({ 1585 flush, 1586 keepalive: flush 1587 }); 1588 } 1589 }; 1590 } 1591 //#endregion 1592 //#region ../browser-session/src/session/metadata-session.ts 1593 /** 1594 * Metadata-only session lifecycle used whenever rrweb is disabled or unsampled. 1595 * It is the shared lifecycle driver with no capture of its own: the distilled 1596 * event sink runs on every page load regardless, so this owner only has to post 1597 * rows and flush that sink on the way out. 1598 */ 1599 function startMetadataSession(options) { 1600 return startSessionLifecycle(options, { 1601 recorded: false, 1602 post: (row, keepalive) => { 1603 postSessionMetaRow(options, row, keepalive); 1604 }, 1605 onSuspend: ({ flush, keepalive }) => { 1606 if (flush && keepalive) getActiveSink()?.flush(true); 1607 }, 1608 onSessionChange: options.onSessionChange 1609 }); 1610 } 1611 //#endregion --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 1612-1640 (../browser-session/src/session/sink.ts (comment about the shared 64 KiB budget)) --- 1612 //#region ../browser-session/src/session/sink.ts 1613 const SESSION_SINK_KEY = "__MAPLE_BROWSER_SESSION__"; 1614 const observedTraceIdsBySession = /* @__PURE__ */ new Map(); 1615 /** 1616 * Ceiling on trace ids retained per session. 1617 * 1618 * A session lives up to 24h and every span feeds this, so it is otherwise 1619 * unbounded — and the whole set is serialized into the `ended` metadata row, 1620 * which is written with `keepalive` on the way out. The Fetch spec caps the 1621 * *combined* keepalive body across in-flight requests at 64 KiB, shared here 1622 * with the final events flush and the last replay chunk, so an app emitting a 1623 * span a second would silently lose its entire session row after ~30 minutes. 1624 * 1625 * Keep-first rather than keep-last: the ids are a join key for "show me this 1626 * session's traces", the UI paginates them anyway, and dropping the tail of a 1627 * long session is a smaller loss than dropping the row. 1628 */ 1629 const MAX_TRACE_IDS_PER_SESSION = 200; 1630 /** Record a trace id seen during the session. Idempotent per id. */ 1631 function recordTraceId(traceId, sessionId = readSessionSink()?.sessionId) { 1632 if (!sessionId) return; 1633 let ids = observedTraceIdsBySession.get(sessionId); 1634 if (!ids) { 1635 ids = /* @__PURE__ */ new Set(); 1636 observedTraceIdsBySession.set(sessionId, ids); 1637 } 1638 if (ids.size >= MAX_TRACE_IDS_PER_SESSION && !ids.has(traceId)) return; 1639 ids.add(traceId); 1640 } @@@@@@@@@@ END FILE: sdk-excerpts/06-session-lifecycle-unload.txt @@@@@@@@@@ FILE: sdk-excerpts/07-client-session-startup.txt (9123 bytes) startClientSession: side effects at MapleFlush.make() time, replay dynamic import, stop() ========================================================================================= Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 1750-1937 (src/client/standalone-session.ts + replay-loader.ts) --- 1750 //#endregion 1751 //#region src/client/standalone-session.ts 1752 /** Trace ids observed per standalone session — attached to its ended row. */ 1753 const observedBySession = /* @__PURE__ */ new Map(); 1754 let current; 1755 /** 1756 * The page-level metadata session, handed out as reference-counted leases. 1757 * 1758 * The refcount is load-bearing, not defensive scaffolding: `Maple.layer` starts 1759 * a client session per *runtime*, and two runtimes can overlap on one page — 1760 * `ManagedRuntime.dispose()` resolves asynchronously, so a React StrictMode 1761 * double-mount (or an HMR remount) builds the replacement runtime before the 1762 * outgoing one's release finalizer runs. Both then hold a lease on the same 1763 * singleton. Without the count, the outgoing runtime's `shutdown` posts the 1764 * `ended` row and clears `current` while the live runtime is still holding the 1765 * handle — the session dies mid-page and nothing restarts it, because 1766 * `setupStandaloneSession` only runs on runtime start. Two `MapleFlush.make()` 1767 * calls in one page reproduce the same overlap. 1768 * 1769 * Within a single `startClientSession` controller leases never overlap 1770 * (`stopRuntime` calls the outgoing `shutdown` synchronously, before any await, 1771 * and `startRuntime` refuses to run while a runtime exists), so the count only 1772 * ever exceeds 1 across controllers. The per-lease `released` flag keeps a 1773 * double `shutdown` of one lease from decrementing twice and stranding another 1774 * holder. Covered by the overlap cases in `standalone-session.test.ts`. 1775 */ 1776 const leaseCurrent = () => { 1777 if (!current) return void 0; 1778 current.references++; 1779 const owned = current.handle; 1780 let released = false; 1781 return { 1782 get sessionId() { 1783 return owned.sessionId; 1784 }, 1785 shutdown: async (options) => { 1786 if (released) return; 1787 released = true; 1788 if (!current || current.handle !== owned) return; 1789 current.references--; 1790 if (current.references > 0) return; 1791 current = void 0; 1792 await owned.shutdown(options); 1793 } 1794 }; 1795 }; 1796 /** Record a span against the session resolved by the shared span decorator. */ 1797 const noteStandaloneSpan = (sessionId, traceId) => { 1798 let ids = observedBySession.get(sessionId); 1799 if (!ids) { 1800 ids = /* @__PURE__ */ new Set(); 1801 observedBySession.set(sessionId, ids); 1802 } 1803 ids.add(traceId); 1804 }; 1805 /** 1806 * Start metadata-only emission when the browser SDK is not already the owner. 1807 * Idempotent per Effect client runtime. 1808 */ 1809 const setupStandaloneSession = (options) => { 1810 if (typeof window === "undefined" || typeof document === "undefined" || !options.ingestKey || readSessionSink()) return void 0; 1811 if (current) return leaseCurrent(); 1812 const handle = startMetadataSession({ 1813 endpoint: options.endpoint, 1814 ingestKey: options.ingestKey, 1815 sdk: CLIENT_SDK_HINT, 1816 serviceName: options.serviceName, 1817 environment: options.environment, 1818 serviceVersion: options.serviceVersion, 1819 captureUserEmail: options.captureUserEmail, 1820 getIdentity: getCurrentIdentity, 1821 getTraceIds: (sessionId) => Array.from(observedBySession.get(sessionId) ?? []), 1822 onSessionChange: (sessionId) => { 1823 for (const key of observedBySession.keys()) if (key !== sessionId) observedBySession.delete(key); 1824 } 1825 }); 1826 if (!handle) return void 0; 1827 current = { 1828 handle, 1829 references: 0 1830 }; 1831 return leaseCurrent(); 1832 }; 1833 //#endregion 1834 //#region src/client/replay-loader.ts 1835 const noOpHandle = { stop: () => Promise.resolve() }; 1836 /** 1837 * Own the complete client-session lifecycle. Consent can change after startup: 1838 * grant starts capture, revoke synchronously detaches producers and discards 1839 * buffers, and a later grant creates a clean runtime. 1840 */ 1841 const startClientSession = (config) => { 1842 configurePrivacy(config.privacy); 1843 if (!hasConsent()) clearPendingEvents(); 1844 const ingestKey = config.ingestKey; 1845 if (typeof window === "undefined" || typeof document === "undefined" || !ingestKey || readSessionSink()) return noOpHandle; 1846 const engineConfig = { 1847 endpoint: config.endpoint.replace(/\/$/, ""), 1848 ingestKey, 1849 sdk: CLIENT_SDK_HINT, 1850 maskAllInputs: config.replay?.maskAllInputs ?? true, 1851 maskAllText: config.replay?.maskAllText ?? false, 1852 getIdentity: getCurrentIdentity 1853 }; 1854 const sampled = (config.replay?.enabled ?? true) && typeof document !== "undefined" && Math.random() < (config.replay?.sampleRate ?? 1); 1855 let runtime; 1856 let stopped = false; 1857 let generation = 0; 1858 let rotateOnNextStart = false; 1859 const stopRuntime = async (flush) => { 1860 generation++; 1861 const previous = runtime; 1862 runtime = void 0; 1863 if (!previous) return; 1864 const replayShutdown = previous.replay?.shutdown({ flush }); 1865 const metadataShutdown = previous.metadata?.shutdown({ flush }); 1866 const currentSessionId = previous.replay?.sessionId ?? previous.metadata?.sessionId; 1867 const liveSink = getActiveSink(); 1868 const sink = liveSink && currentSessionId && liveSink.sessionId === currentSessionId ? liveSink : previous.sink; 1869 if (flush) await sink.flush(true); 1870 sink.stop(); 1871 if (sink !== previous.sink) previous.sink.stop(); 1872 if (currentSessionId) clearSessionSink(currentSessionId); 1873 await Promise.all([replayShutdown, metadataShutdown]); 1874 }; 1875 const startRuntime = () => { 1876 if (stopped || runtime || !hasConsent() || readSessionSink()) return; 1877 setVisitorTracking((config.privacy?.persistVisitorId ?? true) && mayPersistIdentifier()); 1878 const session = (rotateOnNextStart ? rotateSession() : void 0) ?? getSession(); 1879 rotateOnNextStart = false; 1880 const next = { sink: startEventSink(engineConfig, session.id) }; 1881 runtime = next; 1882 const ownGeneration = ++generation; 1883 if (sampled) { 1884 import("./replay-DBMTP4cB.mjs").then(({ startReplaySession }) => { 1885 if (stopped || !hasConsent() || generation !== ownGeneration || runtime !== next) return; 1886 next.replay = startReplaySession({ 1887 endpoint: config.endpoint, 1888 ingestKey, 1889 sdk: CLIENT_SDK_HINT, 1890 serviceName: config.serviceName, 1891 environment: config.environment, 1892 serviceVersion: config.serviceVersion, 1893 maskAllInputs: engineConfig.maskAllInputs, 1894 maskAllText: engineConfig.maskAllText, 1895 getIdentity: getCurrentIdentity, 1896 captureUserEmail: config.privacy?.captureUserEmail 1897 }); 1898 }).catch(() => { 1899 if ((config.emitSessionMeta ?? true) && !stopped && hasConsent() && generation === ownGeneration && runtime === next) next.metadata = setupStandaloneSession({ 1900 endpoint: config.endpoint, 1901 ingestKey: config.ingestKey, 1902 serviceName: config.serviceName, 1903 environment: config.environment, 1904 serviceVersion: config.serviceVersion, 1905 captureUserEmail: config.privacy?.captureUserEmail 1906 }); 1907 }); 1908 return; 1909 } 1910 if (config.emitSessionMeta ?? true) next.metadata = setupStandaloneSession({ 1911 endpoint: config.endpoint, 1912 ingestKey: config.ingestKey, 1913 serviceName: config.serviceName, 1914 environment: config.environment, 1915 serviceVersion: config.serviceVersion, 1916 captureUserEmail: config.privacy?.captureUserEmail 1917 }); 1918 }; 1919 startRuntime(); 1920 const stopConsentListener = config.privacy?.requireConsent ? onConsentChange((allowed) => { 1921 if (allowed) { 1922 startRuntime(); 1923 return; 1924 } 1925 rotateOnNextStart = runtime !== void 0; 1926 clearPendingEvents(); 1927 setVisitorTracking(false); 1928 stopRuntime(false); 1929 }) : () => {}; 1930 return { stop: async (options) => { 1931 if (stopped) return; 1932 stopped = true; 1933 stopConsentListener(); 1934 await stopRuntime(options?.flush ?? true); 1935 } }; 1936 }; 1937 //#endregion @@@@@@@@@@ END FILE: sdk-excerpts/07-client-session-startup.txt @@@@@@@@@@ FILE: sdk-excerpts/08-replay-record-final-chunk.txt (13459 bytes) Replay engine: console/fetch/XHR wrapping, recorder flush (async gzip before POST), onSuspend ============================================================================================= Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/replay-DBMTP4cB.mjs lines 1-145 (console + network capture + event capture) --- 1 import { _ as BLOCK_SELECTOR, c as publishSessionSink, d as markActivity, f as nextChunkSeq, g as warnDropped, h as postSessionMeta, l as startSessionLifecycle, m as postSessionBlob, p as gzip, s as getObservedTraceIds, u as startEventSink, v as safeEmit, y as activeTraceId } from "./client-BYIixzTb.mjs"; 2 //#region ../browser-session/src/replay/capture/console.ts 3 const LEVELS = [ 4 "log", 5 "info", 6 "warn", 7 "error", 8 "debug" 9 ]; 10 const MAX_MESSAGE = 2e3; 11 /** 12 * Capture `console.*` calls as session events. Wraps each method, emits a 13 * distilled record, then forwards to the original so the host app's console 14 * behaves normally. Never throws into the call site. 15 */ 16 function installConsoleCapture(emit) { 17 const original = {}; 18 for (const level of LEVELS) { 19 const orig = console[level]; 20 original[level] = orig; 21 console[level] = (...args) => { 22 safeEmit(emit, { 23 type: "console", 24 level, 25 message: formatArgs(args) 26 }); 27 orig.apply(console, args); 28 }; 29 } 30 return () => { 31 for (const level of LEVELS) { 32 const orig = original[level]; 33 if (orig) console[level] = orig; 34 } 35 }; 36 } 37 function formatArgs(args) { 38 const text = args.map((a) => { 39 if (typeof a === "string") return a; 40 if (a instanceof Error) return `${a.name}: ${a.message}`; 41 try { 42 return JSON.stringify(a); 43 } catch { 44 return String(a); 45 } 46 }).join(" "); 47 return text.length > MAX_MESSAGE ? `${text.slice(0, MAX_MESSAGE)}…` : text; 48 } 49 //#endregion 50 //#region ../browser-session/src/replay/capture/network.ts 51 /** 52 * Capture fetch + XHR requests as session events, tagged with the active trace 53 * id so each request links to its backend trace. `ignoreUrl` skips Maple's own 54 * ingest endpoints (otherwise capturing the session-events POST would loop). 55 */ 56 function installNetworkCapture(emit, ignoreUrl) { 57 const origFetch = typeof window !== "undefined" ? window.fetch : void 0; 58 if (origFetch) window.fetch = async (input, init) => { 59 const url = requestUrl(input); 60 const method = requestMethod(input, init); 61 const traceId = activeTraceId(); 62 const start = performance.now(); 63 try { 64 const res = await origFetch(input, init); 65 record(url, method, res.status, start, traceId); 66 return res; 67 } catch (error) { 68 record(url, method, 0, start, traceId, String(error)); 69 throw error; 70 } 71 }; 72 const record = (url, method, status, start, traceId, error) => { 73 if (ignoreUrl(url)) return; 74 safeEmit(emit, { 75 type: "network", 76 net: { 77 method, 78 url, 79 status, 80 durationMs: Math.round(performance.now() - start) 81 }, 82 traceId, 83 ...error ? { attrs: { error } } : void 0 84 }); 85 }; 86 const XHR = typeof window !== "undefined" ? window.XMLHttpRequest : void 0; 87 const origOpen = XHR?.prototype.open; 88 const origSend = XHR?.prototype.send; 89 if (XHR && origOpen && origSend) { 90 XHR.prototype.open = function(method, url, ...rest) { 91 this.__mapleMethod = String(method).toUpperCase(); 92 this.__mapleUrl = typeof url === "string" ? url : url.href; 93 return origOpen.apply(this, [ 94 method, 95 url, 96 ...rest 97 ]); 98 }; 99 XHR.prototype.send = function(...args) { 100 const meta = this; 101 const start = performance.now(); 102 const traceId = activeTraceId(); 103 this.addEventListener("loadend", () => { 104 record(meta.__mapleUrl ?? "", meta.__mapleMethod ?? "GET", this.status, start, traceId); 105 }); 106 return origSend.apply(this, args); 107 }; 108 } 109 return () => { 110 if (origFetch) window.fetch = origFetch; 111 if (XHR && origOpen) XHR.prototype.open = origOpen; 112 if (XHR && origSend) XHR.prototype.send = origSend; 113 }; 114 } 115 function requestUrl(input) { 116 if (typeof input === "string") return input; 117 if (input instanceof URL) return input.href; 118 return input.url; 119 } 120 function requestMethod(input, init) { 121 return (init?.method ?? (typeof input === "object" && "method" in input ? input.method : void 0) ?? "GET").toUpperCase(); 122 } 123 //#endregion 124 //#region ../browser-session/src/replay/events.ts 125 /** 126 * Install the capture modules that turn console output and network requests 127 * into distilled session events. 128 * 129 * Navigation, errors and interactions are deliberately absent: the sink 130 * installs those itself (see `startBaselineCapture`), because page views, error 131 * counts and click counts have to be counted even when replay is unsampled — 132 * and a second listener here would double-count every one of them. 133 */ 134 function startEventCapture(config, sessionId) { 135 const sink = startEventSink(config, sessionId); 136 const emit = sink.emit; 137 const uninstall = [installConsoleCapture(emit), installNetworkCapture(emit, sink.ignoreUrl)]; 138 return { 139 stop: () => { 140 for (const off of uninstall) off(); 141 }, 142 flush: sink.flush 143 }; 144 } 145 //#endregion --- @maple-dev/effect-sdk/dist/replay-DBMTP4cB.mjs lines 10591-10779 (../browser-session/src/replay/record.ts + session/replay-session.ts) --- 10591 //#region ../browser-session/src/replay/record.ts 10592 const FULL_SNAPSHOT = 2; 10593 const INCREMENTAL = 3; 10594 const SOURCE_MOUSE_INTERACTION = 2; 10595 const MOUSE_CLICK = 2; 10596 const FLUSH_INTERVAL_MS = 5e3; 10597 const FLUSH_BYTES = 102400; 10598 const CHECKOUT_EVERY_MS = 3e5; 10599 const MAX_BUFFER_BYTES = 4194304; 10600 function warnExhausted(sessionId) { 10601 console.warn(`[maple] session replay ${sessionId} reached its maximum recorded size; recording stopped for this session (metadata and events continue)`); 10602 } 10603 let lastDropWarnAt = 0; 10604 function warnBufferDropped(bytes) { 10605 const now = Date.now(); 10606 if (now - lastDropWarnAt < 3e4) return; 10607 lastDropWarnAt = now; 10608 console.warn(`[maple] session replay buffer exceeded ${MAX_BUFFER_BYTES} bytes (dropping ${bytes} buffered bytes; recording continues from the next full snapshot)`); 10609 } 10610 function startRecording(config, sessionId) { 10611 let parts = []; 10612 let bufferBytes = 0; 10613 let bufferHasCheckpoint = false; 10614 let firstTimestamp = 0; 10615 let lastTimestamp = 0; 10616 let droppedChunk = false; 10617 let clickCount = 0; 10618 let stopped = false; 10619 let exhausted = false; 10620 const resetBuffer = () => { 10621 parts = []; 10622 bufferBytes = 0; 10623 bufferHasCheckpoint = false; 10624 firstTimestamp = 0; 10625 lastTimestamp = 0; 10626 }; 10627 const flush = async (keepalive = false) => { 10628 if (stopped || exhausted || parts.length === 0) return; 10629 const body = `[${parts.join(",")}]`; 10630 const isCheckpoint = bufferHasCheckpoint; 10631 const eventCount = parts.length; 10632 const durationMs = Math.max(0, lastTimestamp - firstTimestamp); 10633 const seq = nextChunkSeq(); 10634 resetBuffer(); 10635 let gzipped; 10636 try { 10637 gzipped = await gzip(new TextEncoder().encode(body)); 10638 } catch (error) { 10639 warnDropped("chunk compression", error); 10640 return; 10641 } 10642 if (await postSessionBlob(config, { 10643 sessionId, 10644 chunkSeq: seq, 10645 isCheckpoint, 10646 eventCount, 10647 durationMs 10648 }, gzipped, keepalive) === "exhausted" && !exhausted) { 10649 exhausted = true; 10650 warnExhausted(sessionId); 10651 haltCapture(); 10652 } 10653 }; 10654 let haltCapture = () => {}; 10655 const stop = record({ 10656 emit: (event, isCheckpoint) => { 10657 const active = markActivity(); 10658 if (active && active.id !== sessionId) return; 10659 const e = event; 10660 const isFullSnapshot = isCheckpoint === true || e.type === FULL_SNAPSHOT; 10661 if (e.type === INCREMENTAL && e.data?.source === SOURCE_MOUSE_INTERACTION && e.data.type === MOUSE_CLICK) clickCount++; 10662 let json; 10663 try { 10664 json = JSON.stringify(e); 10665 } catch { 10666 return; 10667 } 10668 if (droppedChunk && !isFullSnapshot) return; 10669 droppedChunk = false; 10670 if (bufferBytes + json.length > MAX_BUFFER_BYTES) { 10671 warnBufferDropped(bufferBytes + json.length); 10672 resetBuffer(); 10673 droppedChunk = true; 10674 return; 10675 } 10676 if (isFullSnapshot) bufferHasCheckpoint = true; 10677 if (parts.length === 0) firstTimestamp = e.timestamp; 10678 lastTimestamp = e.timestamp; 10679 parts.push(json); 10680 bufferBytes += json.length; 10681 if (bufferBytes >= FLUSH_BYTES) flush(); 10682 }, 10683 maskAllInputs: config.maskAllInputs, 10684 blockSelector: BLOCK_SELECTOR, 10685 ...config.maskAllText ? { maskTextSelector: "*" } : void 0, 10686 checkoutEveryNms: CHECKOUT_EVERY_MS 10687 }); 10688 let idleHandle; 10689 const scheduleFlush = () => { 10690 if (typeof requestIdleCallback === "function") idleHandle = requestIdleCallback(() => { 10691 idleHandle = void 0; 10692 flush(); 10693 }, { timeout: 2e3 }); 10694 else flush(); 10695 }; 10696 const flushTimer = setInterval(scheduleFlush, FLUSH_INTERVAL_MS); 10697 let halted = false; 10698 haltCapture = () => { 10699 if (halted) return; 10700 halted = true; 10701 clearInterval(flushTimer); 10702 if (idleHandle !== void 0 && typeof cancelIdleCallback === "function") { 10703 cancelIdleCallback(idleHandle); 10704 idleHandle = void 0; 10705 } 10706 resetBuffer(); 10707 stop?.(); 10708 }; 10709 return { 10710 stop: () => { 10711 stopped = true; 10712 haltCapture(); 10713 }, 10714 flush, 10715 getClickCount: () => clickCount 10716 }; 10717 } 10718 //#endregion 10719 //#region ../browser-session/src/session/replay-session.ts 10720 /** 10721 * Start recording the current browser session. Publishes the session sink, 10722 * posts an `active` metadata row, and installs visibility handlers: 10723 * hidden → flush + `ended` row (with observed trace ids) + stop capture; 10724 * visible → re-resolve the session (rotating if idle-expired), republish the 10725 * sink, restart capture, post a fresh `active` row. Metadata versions are 10726 * monotonic per session, so the latest row always wins on the backend. 10727 * 10728 * Returns undefined outside a browser. Sampling is the caller's decision. 10729 */ 10730 function startReplaySession(options) { 10731 if (typeof window === "undefined") return void 0; 10732 const engineConfig = { 10733 endpoint: options.endpoint.replace(/\/$/, ""), 10734 ingestKey: options.ingestKey, 10735 sdk: options.sdk, 10736 maskAllInputs: options.maskAllInputs, 10737 maskAllText: options.maskAllText, 10738 getIdentity: options.getIdentity 10739 }; 10740 let recorder; 10741 let events; 10742 let publishedSessionId; 10743 const publish = (sessionId) => { 10744 if (publishedSessionId === sessionId) return; 10745 publishedSessionId = sessionId; 10746 publishSessionSink(sessionId); 10747 }; 10748 return startSessionLifecycle({ 10749 ...options, 10750 getTraceIds: getObservedTraceIds 10751 }, { 10752 recorded: true, 10753 post: (row, keepalive) => { 10754 postSessionMeta(engineConfig, row, keepalive); 10755 }, 10756 clicksSinceStart: () => recorder?.getClickCount() ?? 0, 10757 onStart: (record) => { 10758 publish(record.id); 10759 recorder = startRecording(engineConfig, record.id); 10760 events = startEventCapture(engineConfig, record.id); 10761 }, 10762 onSuspend: ({ flush, keepalive }) => { 10763 const stoppingRecorder = recorder; 10764 const stoppingEvents = events; 10765 recorder = void 0; 10766 events = void 0; 10767 const flushed = flush ? Promise.all([stoppingRecorder?.flush(keepalive), stoppingEvents?.flush(keepalive)]).then(() => {}) : void 0; 10768 stoppingRecorder?.stop(); 10769 stoppingEvents?.stop(); 10770 return flushed; 10771 }, 10772 onSessionChange: (sessionId) => { 10773 publish(sessionId); 10774 options.onSessionChange?.(sessionId); 10775 } 10776 }); 10777 } 10778 //#endregion 10779 export { startReplaySession }; @@@@@@@@@@ END FILE: sdk-excerpts/08-replay-record-final-chunk.txt @@@@@@@@@@ FILE: sdk-excerpts/09-maple-layer-client.txt (6922 bytes) Maple.layer (client): Otlp.layerJson + consent HTTP client; no unload flush =========================================================================== Verbatim excerpt from the published npm package @maple-dev/effect-sdk@0.8.2 (installed with `bun install` in ../repro). Line numbers are those of the installed dist file. Nothing has been edited. --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 2177-2228 (src/client/consent-http-client.ts) --- 2177 //#region src/client/consent-http-client.ts 2178 const after = (value, threshold) => trySyncOrUndefined(() => BigInt(String(value ?? 0)) >= threshold) ?? false; 2179 const pruneTraces = (body, threshold) => { 2180 const resourceSpans = (body.resourceSpans ?? []).map((resource) => ({ 2181 ...resource, 2182 scopeSpans: (resource.scopeSpans ?? []).map((scope) => ({ 2183 ...scope, 2184 spans: (scope.spans ?? []).filter((span) => after(span.startTimeUnixNano, threshold)) 2185 })).filter((scope) => scope.spans.length > 0) 2186 })).filter((resource) => resource.scopeSpans.length > 0); 2187 return resourceSpans.length > 0 ? { 2188 ...body, 2189 resourceSpans 2190 } : void 0; 2191 }; 2192 const pruneLogs = (body, threshold) => { 2193 const resourceLogs = (body.resourceLogs ?? []).map((resource) => ({ 2194 ...resource, 2195 scopeLogs: (resource.scopeLogs ?? []).map((scope) => ({ 2196 ...scope, 2197 logRecords: (scope.logRecords ?? []).filter((record) => after(record.timeUnixNano ?? record.observedTimeUnixNano, threshold)) 2198 })).filter((scope) => scope.logRecords.length > 0) 2199 })).filter((resource) => resource.scopeLogs.length > 0); 2200 return resourceLogs.length > 0 ? { 2201 ...body, 2202 resourceLogs 2203 } : void 0; 2204 }; 2205 /** 2206 * Keeps signals captured under the current grant. Cumulative Effect metrics 2207 * cannot be separated here, so explicit-consent installs suppress them. 2208 */ 2209 const filterOtlpRequestForConsent = (request, requireConsent) => { 2210 if (!hasConsent()) return void 0; 2211 if (!requireConsent) return request; 2212 if (request.url.endsWith("/v1/metrics")) return void 0; 2213 if (request.body._tag !== "Uint8Array") return void 0; 2214 const body = request.body; 2215 const since = consentAllowedSince(); 2216 if (!Number.isFinite(since)) return void 0; 2217 return trySyncOrUndefined(() => { 2218 const decoded = JSON.parse(new TextDecoder().decode(body.body)); 2219 const threshold = BigInt(Math.trunc(since)) * 1000000n; 2220 const filtered = request.url.endsWith("/v1/traces") ? pruneTraces(decoded, threshold) : request.url.endsWith("/v1/logs") ? pruneLogs(decoded, threshold) : void 0; 2221 return filtered ? HttpClientRequest.setBody(request, HttpBody.jsonUnsafe(filtered, body.contentType)) : void 0; 2222 }); 2223 }; 2224 const consentHttpClientLayer = (requireConsent) => Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (inner) => HttpClient.make((request) => { 2225 const filtered = filterOtlpRequestForConsent(request, requireConsent); 2226 return filtered ? inner.execute(filtered) : Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 }))); 2227 }))).pipe(Layer.provide(FetchHttpClient.layer)); 2228 //#endregion --- @maple-dev/effect-sdk/dist/client-BYIixzTb.mjs lines 2257-2330 (src/client/layer.ts) --- 2257 //#region src/client/layer.ts 2258 var layer_exports = /* @__PURE__ */ __exportAll({ 2259 clearIdentity: () => clearIdentity, 2260 identify: () => identify, 2261 layer: () => layer, 2262 track: () => track 2263 }); 2264 /** 2265 * Create an Effect Layer that provides OpenTelemetry traces, logs, and metrics 2266 * configured for Maple in browser environments. 2267 * 2268 * Unlike the server layer, all configuration must be provided programmatically 2269 * since browsers don't have access to environment variables. 2270 * 2271 * @example 2272 * ```typescript 2273 * import { Maple } from "@maple-dev/effect-sdk/client" 2274 * import { Effect } from "effect" 2275 * 2276 * const TracerLive = Maple.layer({ 2277 * serviceName: "my-frontend", 2278 * endpoint: "https://ingest.maple.dev", 2279 * ingestKey: "maple_pk_...", 2280 * }) 2281 * 2282 * const program = Effect.log("Hello!").pipe(Effect.withSpan("hello")) 2283 * Effect.runPromise(program.pipe(Effect.provide(TracerLive))) 2284 * ``` 2285 */ 2286 const layer = (config) => { 2287 const attributes = { "maple.sdk.type": "client" }; 2288 const nav = browserNavigator(); 2289 if (nav) { 2290 if (nav.userAgent) attributes["user_agent.original"] = nav.userAgent; 2291 if (nav.language) attributes["browser.language"] = nav.language; 2292 } 2293 if (typeof Intl !== "undefined") { 2294 const timezone = trySyncOrUndefined(() => Intl.DateTimeFormat().resolvedOptions().timeZone); 2295 if (timezone) attributes["browser.timezone"] = timezone; 2296 } 2297 if (config.environment) { 2298 attributes["deployment.environment"] = config.environment; 2299 attributes["deployment.environment.name"] = config.environment; 2300 } 2301 if (config.serviceVersion && /^[0-9a-f]{7,40}$/i.test(config.serviceVersion)) attributes["vcs.ref.head.revision"] = config.serviceVersion; 2302 if (config.serviceNamespace) attributes["service.namespace"] = config.serviceNamespace; 2303 if (config.attributes) Object.assign(attributes, config.attributes); 2304 const clientSessionConfig = { 2305 endpoint: config.endpoint, 2306 ingestKey: config.ingestKey, 2307 serviceName: config.serviceName, 2308 environment: config.environment, 2309 serviceVersion: config.serviceVersion, 2310 replay: config.replay, 2311 emitSessionMeta: config.emitSessionMeta, 2312 privacy: config.privacy 2313 }; 2314 const base = Otlp.layerJson({ 2315 baseUrl: config.endpoint, 2316 resource: { 2317 serviceName: config.serviceName, 2318 serviceVersion: config.serviceVersion, 2319 attributes 2320 }, 2321 headers: config.ingestKey ? { Authorization: `Bearer ${config.ingestKey}` } : void 0, 2322 maxBatchSize: config.maxBatchSize, 2323 loggerExportInterval: config.loggerExportInterval, 2324 metricsExportInterval: config.metricsExportInterval, 2325 tracerExportInterval: config.tracerExportInterval, 2326 shutdownTimeout: config.shutdownTimeout 2327 }).pipe(Layer.provide(consentHttpClientLayer(config.privacy?.requireConsent ?? false))); 2328 const lifecycle = Layer.effectDiscard(Effect.acquireRelease(Effect.sync(() => startClientSession(clientSessionConfig)), (clientSession) => Effect.promise(() => clientSession.stop().catch(() => void 0)))); 2329 return withSessionLink(Layer.merge(base, lifecycle)); 2330 }; @@@@@@@@@@ END FILE: sdk-excerpts/09-maple-layer-client.txt @@@@@@@@@@ FILE: sdk-excerpts/10-effect-otlp-exporter.txt (8426 bytes) Effect (NOT Maple) OtlpExporter used by Maple.layer: Flusher service, 60 s disable + drop on failure ==================================================================================================== Verbatim excerpt from effect@4.0.0-rc.112 (the SDK's Effect peer), installed in ../repro. Included because Maple.layer delegates export to it. --- effect/dist/unstable/observability/OtlpExporter.js lines 41-181 (effect@4.0.0-rc.112) --- 41 const policy = /*#__PURE__*/Schedule.forever.pipe(Schedule.passthrough, /*#__PURE__*/Schedule.addDelay(({ 42 output: error 43 }) => { 44 if (HttpClientError.isHttpClientError(error) && error.reason._tag === "StatusCodeError" && error.reason.response.status === 429) { 45 return retryAfterDelay(error.reason.response.headers["retry-after"]); 46 } 47 return Effect.succeed(Duration.seconds(1)); 48 })); 49 /** 50 * Registry of exporter flush operations, used to manually drain buffered 51 * telemetry before the surrounding scope closes. 52 * 53 * **Details** 54 * 55 * Every exporter created by `make` registers its export operation here, so a 56 * single `flush` drains all signals sharing the registry. `flush` returns only 57 * after the exports it initiated have settled, cannot fail, and respects each 58 * exporter's temporary-disable window. Wrap it with `Effect.timeoutOption` to 59 * bound its duration at the call site. 60 * 61 * @category services 62 * @since 4.0.0 63 */ 64 export class Flusher extends /*#__PURE__*/Context.Service()("effect/observability/OtlpExporter/Flusher") {} 65 /** 66 * Provides a `Flusher` backed by a fresh registry. 67 * 68 * **Details** 69 * 70 * This is intentionally a single module-level constant rather than a factory: 71 * layer memoization is keyed by layer instance, so every signal layer 72 * referencing this same constant shares one registry per layer build, and one 73 * `flush` drains traces, logs and metrics together. A factory returning a new 74 * layer per call would silently create one registry per signal. 75 * 76 * Registration is scoped — an exporter is removed from the registry when its 77 * own scope closes. Flushing with an empty registry is a no-op. 78 * 79 * Note that `flush` cannot await an export that was already in flight when it 80 * was called (for example one started by the export interval); it only waits 81 * for the exports it initiates. 82 * 83 * @category layers 84 * @since 4.0.0 85 */ 86 export const layerFlusher = /*#__PURE__*/Layer.sync(Flusher, () => { 87 const registry = new Set(); 88 return { 89 flush: Effect.suspend(() => { 90 if (registry.size === 0) { 91 return Effect.void; 92 } 93 return Effect.forEach(registry, identity, { 94 concurrency: "unbounded", 95 discard: true 96 }); 97 }), 98 register: run => Effect.flatMap(Scope.Scope, scope => { 99 registry.add(run); 100 return Scope.addFinalizer(scope, Effect.sync(() => registry.delete(run))); 101 }) 102 }; 103 }); 104 /** 105 * Creates a scoped OTLP batch exporter. 106 * 107 * **Details** 108 * 109 * The exporter buffers pushed data, periodically posts encoded batches to the 110 * configured URL, retries transient failures, temporarily disables exporting 111 * after unhandled failures, and flushes during scope finalization up to 112 * `shutdownTimeout`. 113 * 114 * @category constructors 115 * @since 4.0.0 116 */ 117 export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) { 118 const services = yield* Effect.context(); 119 const clock = Context.get(services, Clock); 120 const scope = Context.get(services, Scope.Scope); 121 const exportInterval = Duration.max(Duration.fromInputUnsafe(options.exportInterval), Duration.zero); 122 let disabledUntil = undefined; 123 const client = HttpClient.filterStatusOk(Context.get(services, HttpClient.HttpClient)).pipe(HttpClient.transformResponse(Effect.provideService(HttpClient.TracerPropagationEnabled, false)), HttpClient.retryTransient({ 124 schedule: policy, 125 times: 3 126 })); 127 let headers = Headers.fromRecordUnsafe({ 128 "user-agent": `effect-opentelemetry-${options.label}/0.0.0` 129 }); 130 if (options.headers) { 131 headers = Headers.merge(Headers.fromInput(options.headers), headers); 132 } 133 const request = HttpClientRequest.post(options.url, { 134 headers 135 }); 136 let buffer = []; 137 const runExport = Effect.suspend(() => { 138 if (disabledUntil !== undefined && clock.currentTimeMillisUnsafe() < disabledUntil) { 139 return Effect.void; 140 } else if (disabledUntil !== undefined) { 141 disabledUntil = undefined; 142 } 143 const items = buffer; 144 if (options.maxBatchSize !== "disabled") { 145 if (buffer.length === 0) { 146 return Effect.void; 147 } 148 buffer = []; 149 } 150 const [body, onSuccess] = options.body(items); 151 return client.execute(HttpClientRequest.setBody(request, body)).pipe(Effect.andThen(onSuccess), Effect.asVoid, Effect.withTracerEnabled(false)); 152 }).pipe(Effect.catchCause(cause => { 153 if (disabledUntil !== undefined) return Effect.void; 154 disabledUntil = clock.currentTimeMillisUnsafe() + 60_000; 155 buffer = []; 156 return Effect.logDebug("Disabling exporter for 60 seconds", cause); 157 }), Effect.annotateLogs({ 158 package: "@effect/opentelemetry", 159 module: options.label 160 })); 161 const exportFibers = yield* FiberSet.make(); 162 const runExportFork = yield* FiberSet.runtime(exportFibers)(); 163 const flusher = yield* Flusher; 164 yield* flusher.register(runExport); 165 yield* Scope.addFinalizer(scope, Effect.suspend(() => { 166 if (disabledUntil !== undefined) return Effect.void; 167 runExportFork(runExport); 168 return FiberSet.awaitEmpty(exportFibers); 169 }).pipe(Effect.ignore, Effect.interruptible, Effect.timeoutOption(options.shutdownTimeout))); 170 yield* Effect.sleep(exportInterval).pipe(Effect.andThen(FiberSet.run(exportFibers, runExport)), Effect.flatMap(Fiber.await), Effect.forever, Effect.forkIn(scope)); 171 return { 172 push(data) { 173 if (disabledUntil !== undefined) return; 174 buffer.push(data); 175 if (options.maxBatchSize !== "disabled" && buffer.length >= options.maxBatchSize) { 176 runExportFork(runExport); 177 } 178 } 179 }; 180 }); 181 //# sourceMappingURL=OtlpExporter.js.map --- effect/dist/unstable/observability/OtlpTracer.js lines 94-94 (OtlpTracer.layer provides layerFlusher) --- 94 export const layer = /*#__PURE__*/flow(make, /*#__PURE__*/Layer.effect(Tracer.Tracer), /*#__PURE__*/Layer.provideMerge(Exporter.layerFlusher)); --- effect/dist/unstable/http/FetchHttpClient.js lines 40-60 (FetchHttpClient.RequestInit service) --- 40 * 41 * Request-specific method, headers, body, and abort signal are supplied by the client when a request is executed. 42 * 43 * @category services 44 * @since 4.0.0 45 */ 46 export class RequestInit extends /*#__PURE__*/Context.Service()("effect/http/FetchHttpClient/RequestInit") {} 47 const fetch = /*#__PURE__*/HttpClient.make((request, url, signal, fiber) => { 48 const fetch = fiber.getRef(Fetch); 49 const options = Context.getOrUndefined(fiber.context, RequestInit) ?? {}; 50 let headers = options.headers ? Headers.merge(Headers.fromInput(options.headers), request.headers) : request.headers; 51 if (headers["content-length"]) { 52 headers = Headers.remove(headers, "content-length"); 53 } 54 const send = body => Effect.map(Effect.tryPromise({ 55 try: () => fetch(url, { 56 ...options, 57 method: request.method, 58 headers, 59 body, 60 duplex: request.body._tag === "Stream" ? "half" : undefined, @@@@@@@@@@ END FILE: sdk-excerpts/10-effect-otlp-exporter.txt @@@@@@@@@@ FILE: sdk-excerpts/11-readme.txt (5172 bytes) README.md of @maple-dev/effect-sdk@0.8.2: session replay + manual flush sections ================================================================================ Verbatim excerpt from the package README. --- @maple-dev/effect-sdk/README.md lines 156-177 (Session replay & sessions) --- 156 ### Session replay & sessions (built in) 157 158 The browser presets (`Maple.layer` and `MapleFlush.make`) record **rrweb session replays by default** — no separate browser SDK needed. Every span carries a `session.id`, the session appears in Maple's Sessions UI with its linked traces, and the recording is playable next to them. 159 160 ```typescript 161 const TracerLive = Maple.layer({ 162 serviceName: "my-frontend", 163 endpoint: "https://ingest.maple.dev", 164 ingestKey: "maple_pk_...", 165 replay: { 166 sampleRate: 0.1, // record 10% of sessions (default 1) 167 maskAllInputs: true, // default 168 maskAllText: false, // default 169 }, 170 }) 171 ``` 172 173 - **Bundle size:** the replay engine (rrweb included) loads through a dynamic import, so it lands in a code-split chunk (~360 kB) fetched only when replay is enabled _and_ the session is sampled. The base client bundle stays ~13 kB. 174 - **Vendored dependency:** rrweb (MIT) is bundled into that chunk rather than declared in `dependencies`, so it will not appear in your lockfile or in `npm ls` output. Note it directly if your license or supply-chain audit enumerates transitive packages. 175 - **Opt out** with `replay: { enabled: false }`. Unsampled or disabled sessions still appear in the Sessions UI (metadata rows + linked traces, no recording); turn that off too with `emitSessionMeta: false`. 176 - **Tab lifecycle:** recording suspends on `visibilitychange → hidden` (flushing the tail with `keepalive`) and resumes when the tab becomes visible again, rotating to a fresh session after 30 minutes of inactivity. 177 - **Identify users** at any point — the id is attached to the session's next-posted metadata row _and_ stamped as `user.id` on every span the client tracer creates from then on (traces become user-attributable, not just session-grouped). Spans created before you call it stay anonymous. Pass `null` or `undefined` after sign-out to make future telemetry anonymous again. --- @maple-dev/effect-sdk/README.md lines 235-277 (Manual flush) --- 235 ## Manual flush 236 237 `Maple.layer` (server + client) batches in the background and only exports on a timer, on batch overflow, or when its scope closes — there's no way to force an export. That's a problem in two places: a browser tab dropping the last few seconds of spans on unload, and a short-lived process exiting before the timer fires. 238 239 `MapleFlush.make()` (available from both `/server` and `/client`) swaps the background exporter for the same buffer-backed tracer/logger the Cloudflare preset uses, and returns an explicit `flush()`: 240 241 ```typescript 242 export interface FlushableTelemetry { 243 readonly layer: Layer.Layer 244 readonly flush: () => Promise // drain buffers → POST now (never rejects) 245 readonly dispose: () => Promise // stop the auto-flush timer/listeners + final flush 246 } 247 ``` 248 249 Both presets run a background auto-flush every 5s by default (configurable via `autoFlushInterval`, or `false` to flush purely on demand), so it's a safe drop-in for `Maple.layer` with manual flush layered on top. Effect metrics are cumulative, but the flushable presets only post a new snapshot after a metric changes; an unchanged registry does not generate a metrics request on every flush tick. 250 251 ### Server / Node 252 253 ```typescript 254 import { MapleFlush } from "@maple-dev/effect-sdk/server" 255 256 const telemetry = MapleFlush.make({ serviceName: "my-app" }) // same env auto-detect as Maple.layer 257 258 // ...provide telemetry.layer to your runtime... 259 await telemetry.flush() // force an export at a checkpoint 260 await telemetry.dispose() // before exit: stop the timer + final flush 261 ``` 262 263 ### Client / Browser 264 265 ```typescript 266 import { MapleFlush } from "@maple-dev/effect-sdk/client" 267 268 const telemetry = MapleFlush.make({ 269 serviceName: "my-frontend", 270 endpoint: "https://ingest.maple.dev", 271 ingestKey: "maple_pk_...", 272 // flushOnUnload: true (default) registers pagehide / visibilitychange→hidden handlers 273 }) 274 // telemetry.layer keeps the replay-session trace linking from Maple.layer. 275 ``` 276 277 By default the client preset flushes on `pagehide` and `visibilitychange→hidden` so the tail of a session isn't lost when the tab goes away. Flush uses `fetch(url, { keepalive: true })`, **not** `navigator.sendBeacon`: Maple's ingest authenticates via the `Authorization` header (no query-param auth) and sendBeacon can't set headers, so it would 401 whenever an ingest key is set. `keepalive` carries the header and still survives unload for small bodies. @@@@@@@@@@ END FILE: sdk-excerpts/11-readme.txt @@@@@@@@@@ FILE: sdk-excerpts/12-v0.10.0-what-changed-and-what-did-not.txt (10614 bytes) @maple-dev/effect-sdk@0.10.0 (latest at time of writing): shared keepalive budget for session posts; OTLP flush path unchanged ============================================================================================================================== Verbatim excerpt from @maple-dev/effect-sdk@0.10.0 (npm, published 2026-10-03), installed in ../repro/variants/sdk-0.10.0 with effect@4.0.0. Shown for comparison only; the report's findings were made against 0.8.2 and re-checked against 0.10.0 where stated. --- @maple-dev/effect-sdk/dist/version-B9ejEduZ.mjs lines 156-245 (flushSignal / guardFlush / makeSerializedFlush / runFlush - cooldown, whole-batch POST, serialization unchanged in substance) --- 156 const flushSignal = async (args) => { 157 const { url, headers, buffer, body, state, signal, transport, logPrefix } = args; 158 if (state.disabledUntil && Date.now() < state.disabledUntil) { 159 console.warn(`${logPrefix} ${signal} flush skipped (cooldown ${state.disabledUntil - Date.now()}ms remaining)`); 160 return; 161 } 162 state.disabledUntil = 0; 163 const batch = buffer.drain(); 164 if (batch.length === 0) return; 165 const posted = await Effect.runPromise(Effect.result(Effect.tryPromise({ 166 try: () => transport.post(url, headers, body(batch)), 167 catch: (cause) => cause 168 }))); 169 if (Result.isFailure(posted)) { 170 buffer.restore(batch); 171 state.disabledUntil = Date.now() + COOLDOWN_MS; 172 console.error(`${logPrefix} ${signal} flush failed; cooldown 60s:`, posted.failure); 173 } 174 }; 175 /** 176 * Wrap a flush body so it logs its failure rather than rejecting. 177 * 178 * Every flush is documented never to reject: they are fired as `void flush()` 179 * from auto-flush timers, `pagehide`/`visibilitychange` handlers, and 180 * `ctx.waitUntil`, where a rejection is an unhandled rejection — fatal under 181 * `--unhandled-rejections=strict` — caused purely by telemetry. `runFlush` 182 * already absorbs per-signal transport errors; this covers everything around it, 183 * chiefly resource resolution. 184 */ 185 const guardFlush = (logPrefix, run) => async (...args) => { 186 const outcome = await Effect.runPromise(Effect.result(Effect.tryPromise({ 187 try: () => run(...args), 188 catch: (cause) => cause 189 }))); 190 if (Result.isFailure(outcome)) console.error(`${logPrefix} flush failed:`, outcome.failure); 191 }; 192 /** 193 * Serialize drains. Workers may coalesce queued calls with identical arguments: 194 * every caller waits for that drain, and calls arriving DURING it queue a later 195 * drain so spans completed after the first snapshot cannot be stranded. 196 * Other presets retain one drain per call by default. 197 */ 198 const makeSerializedFlush = (run, options) => { 199 let tail = Promise.resolve(); 200 let queued; 201 return (...args) => { 202 const waiting = queued; 203 if (options?.coalesceSameArguments && waiting && args.length === waiting.args.length && args.every((value, index) => value === waiting.args[index])) return waiting.promise; 204 const next = tail.then(() => { 205 if (queued?.promise === next) queued = void 0; 206 return run(...args); 207 }); 208 queued = { 209 args, 210 promise: next 211 }; 212 tail = next.catch(() => void 0); 213 return next; 214 }; 215 }; 216 /** 217 * Drain the span + log buffers and POST them. Errors are swallowed per signal 218 * (see {@link flushSignal}); the returned promise never rejects. 219 * 220 * - `noOp`: drain so the buffers don't grow unbounded, fire `onNoOp` (one-shot 221 * "telemetry disabled" notice), never POST. 222 * - empty buffers: short-circuit without a request. 223 */ 224 const runFlush = async (args) => { 225 const { resolved: r, spans, logs, metrics, tracesState, logsState, metricsState, transport, logPrefix, onNoOp } = args; 226 if (r.noOp) { 227 spans.drain(); 228 logs.drain(); 229 metrics.drain(); 230 onNoOp?.(); 231 return; 232 } 233 await Promise.all([ 234 flushSignal({ 235 url: r.tracesUrl, 236 headers: r.headers, 237 buffer: spans, 238 body: (items) => makeTracesBody(items, r), 239 state: tracesState, 240 signal: "traces", 241 transport, 242 logPrefix 243 }), 244 flushSignal({ 245 url: r.logsUrl, --- @maple-dev/effect-sdk/dist/client-BR_-p5Xk.mjs lines 804-873 (NEW: KEEPALIVE_BUDGET_BYTES shared budget + byteLength + postToIngest (used by session meta/events/blob only)) --- 804 /** 805 * Combined budget for this SDK's in-flight `keepalive` bodies. 806 * 807 * The Fetch spec caps the *combined* in-flight keepalive body at 64 KiB per 808 * page, and the browser rejects a request that would cross it. On the way out 809 * up to three of our writes go at once (metadata row, final events batch, last 810 * replay chunk), so the budget has to be shared rather than checked per 811 * request. It stops short of 64 KiB because the OTLP trace exporter spends 812 * from the same page-wide allowance under its own accounting. 813 * 814 * Over the budget a write goes out as a normal request, which the page may or 815 * may not survive long enough to finish: strictly better than a guaranteed 816 * rejection. 817 */ 818 const KEEPALIVE_BUDGET_BYTES = 49152; 819 /** On `globalThis`: two bundled SDK copies still share one page-wide allowance. */ 820 const KEEPALIVE_KEY = "__MAPLE_KEEPALIVE_INFLIGHT__"; 821 function keepaliveInflight() { 822 const owner = globalThis; 823 const existing = owner[KEEPALIVE_KEY]; 824 if (existing) return existing; 825 const fresh = { bytes: 0 }; 826 owner[KEEPALIVE_KEY] = fresh; 827 return fresh; 828 } 829 /** 830 * Reserve `bytes` of the shared keepalive budget. Returns the release function 831 * when the request may use `keepalive`, `undefined` when it must not. 832 */ 833 function reserveKeepalive(requested, bytes) { 834 if (!requested) return void 0; 835 const inflight = keepaliveInflight(); 836 if (inflight.bytes + bytes > KEEPALIVE_BUDGET_BYTES) return void 0; 837 inflight.bytes += bytes; 838 let released = false; 839 return () => { 840 if (released) return; 841 released = true; 842 inflight.bytes = Math.max(0, inflight.bytes - bytes); 843 }; 844 } 845 /** Body size in bytes: the keepalive limit counts bytes, and `string.length` counts UTF-16 units. */ 846 function byteLength(body) { 847 return typeof body === "string" ? new TextEncoder().encode(body).byteLength : body.byteLength; 848 } 849 /** 850 * POST to ingest, spending the shared keepalive budget when `keepalive` is 851 * requested. Resolves with the status only: the response body is cancelled 852 * before the reservation is released, because the browser counts a keepalive 853 * request against the page-wide limit until its response body ends, not 854 * until headers arrive. Rejects exactly as `fetch` does; callers own the 855 * error policy. 856 */ 857 async function postToIngest(url, headers, body, keepalive) { 858 const release = reserveKeepalive(keepalive, byteLength(body)); 859 try { 860 const response = await fetch(url, { 861 method: "POST", 862 headers, 863 body, 864 keepalive: release !== void 0 865 }); 866 await response.body?.cancel().catch(() => {}); 867 return { 868 ok: response.ok, 869 status: response.status 870 }; 871 } finally { 872 release?.(); 873 } --- @maple-dev/effect-sdk/dist/client-BR_-p5Xk.mjs lines 2446-2458 (MapleFlush keepaliveTransport - unchanged: always keepalive:true, no budget reservation, no size check) --- 2446 /** Default auto-flush cadence (ms), matching `Otlp.layerJson`'s 5s export interval. */ 2447 const DEFAULT_AUTO_FLUSH_MS = 5e3; 2448 const browserInstanceId = globalThis.crypto?.randomUUID?.() ?? `browser-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; 2449 /** `fetch(keepalive)` transport — see file header for why not `sendBeacon`. */ 2450 const keepaliveTransport = { post: async (url, headers, body) => { 2451 const res = await fetch(url, { 2452 method: "POST", 2453 headers, 2454 body: JSON.stringify(body), 2455 keepalive: true 2456 }); 2457 if (!res.ok) throw new Error(`OTLP ${res.status} ${res.statusText}`); 2458 } }; --- @maple-dev/effect-sdk/dist/replay-BNkcfu5w.mjs lines 10764-10812 (replay uploadChunk - chunk seq claimed, then async gzip, then POST (unchanged ordering)) --- 10764 /** 10765 * gzip and POST one chunk. `seq` is claimed here, monotonic across reloads 10766 * (persisted on the session record), so a refresh continues the sequence 10767 * instead of overwriting the previous load's blobs. 10768 */ 10769 async function uploadChunk(config, sessionId, body, chunk, keepalive) { 10770 const chunkSeq = nextChunkSeq(); 10771 let gzipped; 10772 try { 10773 gzipped = await gzip(new TextEncoder().encode(body)); 10774 } catch (error) { 10775 warnDropped("chunk compression", error); 10776 return; 10777 } 10778 return postSessionBlob(config, { 10779 sessionId, 10780 chunkSeq, 10781 ...chunk 10782 }, gzipped, keepalive); 10783 } 10784 function startRecording(config, sessionId) { 10785 let parts = []; 10786 let bufferBytes = 0; 10787 let bufferHasCheckpoint = false; 10788 let firstTimestamp = 0; 10789 let lastTimestamp = 0; 10790 let droppedChunk = false; 10791 let clickCount = 0; 10792 let stopped = false; 10793 let exhausted = false; 10794 const resetBuffer = () => { 10795 parts = []; 10796 bufferBytes = 0; 10797 bufferHasCheckpoint = false; 10798 firstTimestamp = 0; 10799 lastTimestamp = 0; 10800 }; 10801 const flush = async (keepalive = false) => { 10802 if (stopped || exhausted || parts.length === 0) return; 10803 const body = `[${parts.join(",")}]`; 10804 const isCheckpoint = bufferHasCheckpoint; 10805 const eventCount = parts.length; 10806 const durationMs = Math.max(0, lastTimestamp - firstTimestamp); 10807 resetBuffer(); 10808 if (await uploadChunk(config, sessionId, body, { 10809 isCheckpoint, 10810 eventCount, 10811 durationMs 10812 }, keepalive) === "exhausted" && !exhausted) { @@@@@@@@@@ END FILE: sdk-excerpts/12-v0.10.0-what-changed-and-what-did-not.txt @@@@@@@@@@ FILE: evidence/headless-new/runner-console.txt (29525 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112", startedAt: "2026-10-07T23:50:02.862Z", headlessMode: "new headless (full Chromium, channel 'chromium')", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.0.0 Safari/537.36", groups: [ "B", "G", "K", "E" ], } [B-probe/probe#0] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-probe/probe#1] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-sdk-concurrent-signals/manual-flush#0] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 [B-sdk-concurrent-signals/manual-flush#1] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 [B-sdk-concurrent-signals/reload#0] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 [B-sdk-concurrent-signals/reload#1] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4183/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4183/dist/app.js:12655:21) at try (http://localhost:4183/dist/app.js:8521:26) at http://localhost:4183/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4183/dist/app.js:833:14) at MixedScheduler. (http://localhost:4183/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4183/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4183/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4183/dist/app.js:2741:23) at http://localhost:4183/dist/app.js:3827:9 [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#0] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#1] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#0] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#1] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) [G-playwright-keepalive-artifact/steady-state-no-navigation#0] no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/steady-state-no-navigation#1] no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/with-final-reload#0] ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/with-final-reload#1] ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [K-reload-150ms-after-action/replay-on(default)#0] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1856; requests issued around the reload (Playwright): traces(1162B)@1871 logs(1011B)@1871 sessionReplays/meta(1160B)@1872 sessionEvents(1019B)@1872 sessionReplays/meta(1056B)@1931; Playwright failed: sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1865 logs@1867 sessionReplays/meta@1867 sessionEvents@1868 sessionReplays/meta@1930; console: [MapleClientSDK] logs flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1873 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1873 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1872 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1872 [K-reload-150ms-after-action/replay-on(default)#1] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1880; requests issued around the reload (Playwright): traces(1162B)@1899 logs(1011B)@1899 sessionReplays/meta(1160B)@1900 sessionEvents(1019B)@1900 sessionReplays/meta(1056B)@1964; Playwright failed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: traces@1890 logs@1891 sessionReplays/meta@1893 sessionEvents@1895 sessionReplays/meta@1963; console: [MapleClientSDK] traces flush failed; cooldown 60s@1901 | [MapleClientSDK] logs flush failed; cooldown 60s@1901 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1901 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1902 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1900 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1900 [K-reload-150ms-after-action/replay-off#0] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1833; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1846 sessionEvents(498B)@1847 traces(1162B)@1847 logs(1011B)@1847 sessionReplays/meta(1066B)@1888; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1842 traces@1844 sessionEvents@1844 logs@1845 sessionReplays/meta@1886; console: [MapleClientSDK] traces flush failed; cooldown 60s@1852 | [MapleClientSDK] logs flush failed; cooldown 60s@1852 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1853 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1853 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1847 [K-reload-150ms-after-action/replay-off#1] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1819; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1831 sessionEvents(498B)@1831 traces(1162B)@1831 logs(1011B)@1831 sessionReplays/meta(1066B)@1868; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1827 sessionEvents@1828 traces@1828 logs@1830 sessionReplays/meta@1867; console: [MapleClientSDK] traces flush failed; cooldown 60s@1832 | [MapleClientSDK] logs flush failed; cooldown 60s@1832 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1832 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1832 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1831 Scenario summary (new headless (full Chromium, channel 'chromium'), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:50:02.862Z, finished 2026-10-07T23:53:57.374Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/headless-new/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/headless-new/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/reload-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: ../evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: ../evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 1/2; span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 1/2] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1856; requests issued around the reload (Playwright): traces(1162B)@1871 logs(1011B)@1871 sessionReplays/meta(1160B)@1872 sessionEvents(1019B)@1872 sessionReplays/meta(1056B)@1931; Playwright failed: sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1865 logs@1867 sessionReplays/meta@1867 sessionEvents@1868 sessionReplays/meta@1930; console: [MapleClientSDK] logs flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1873 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1873 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1872 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1872 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1880; requests issued around the reload (Playwright): traces(1162B)@1899 logs(1011B)@1899 sessionReplays/meta(1160B)@1900 sessionEvents(1019B)@1900 sessionReplays/meta(1056B)@1964; Playwright failed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: traces@1890 logs@1891 sessionReplays/meta@1893 sessionEvents@1895 sessionReplays/meta@1963; console: [MapleClientSDK] traces flush failed; cooldown 60s@1901 | [MapleClientSDK] logs flush failed; cooldown 60s@1901 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1901 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1902 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1900 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1900 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1833; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1846 sessionEvents(498B)@1847 traces(1162B)@1847 logs(1011B)@1847 sessionReplays/meta(1066B)@1888; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1842 traces@1844 sessionEvents@1844 logs@1845 sessionReplays/meta@1886; console: [MapleClientSDK] traces flush failed; cooldown 60s@1852 | [MapleClientSDK] logs flush failed; cooldown 60s@1852 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1853 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1853 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1847 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1819; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1831 sessionEvents(498B)@1831 traces(1162B)@1831 logs(1011B)@1831 sessionReplays/meta(1066B)@1868; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1827 sessionEvents@1828 traces@1828 logs@1830 sessionReplays/meta@1867; console: [MapleClientSDK] traces flush failed; cooldown 60s@1832 | [MapleClientSDK] logs flush failed; cooldown 60s@1832 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1832 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1832 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1831 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-off-run1.json summary written to ../evidence/headless-new/summary-new-2026-10-07T23-50-02-862Z.txt @@@@@@@@@@ END FILE: evidence/headless-new/runner-console.txt @@@@@@@@@@ FILE: evidence/headless-new/summary-new-2026-10-07T23-50-02-862Z.txt (13911 bytes) Scenario summary (new headless (full Chromium, channel 'chromium'), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:50:02.862Z, finished 2026-10-07T23:53:57.374Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/headless-new/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/headless-new/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38469 logs:32132 metrics:928]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/headless-new/B-sdk-concurrent-signals/reload-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18704] meta-ended[2698] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18704B) sessionReplays/meta(2698B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60649] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60649B) sessionReplays/meta(6373B) sessionEvents(21011B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: ../evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6382] events[496] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6382B) sessionEvents(496B) traces(60649B) sessionReplays/meta(1064B) evidence: ../evidence/headless-new/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1124] events[2097] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1124B) sessionEvents(2097B) sessionReplays/meta(1054B) evidence: ../evidence/headless-new/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/headless-new/G-playwright-keepalive-artifact/with-final-reload-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 1/2; span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 1/2] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1856; requests issued around the reload (Playwright): traces(1162B)@1871 logs(1011B)@1871 sessionReplays/meta(1160B)@1872 sessionEvents(1019B)@1872 sessionReplays/meta(1056B)@1931; Playwright failed: sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1865 logs@1867 sessionReplays/meta@1867 sessionEvents@1868 sessionReplays/meta@1930; console: [MapleClientSDK] logs flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush failed; cooldown 60s@1872 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1873 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1873 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1872 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1872 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1880; requests issued around the reload (Playwright): traces(1162B)@1899 logs(1011B)@1899 sessionReplays/meta(1160B)@1900 sessionEvents(1019B)@1900 sessionReplays/meta(1056B)@1964; Playwright failed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: traces@1890 logs@1891 sessionReplays/meta@1893 sessionEvents@1895 sessionReplays/meta@1963; console: [MapleClientSDK] traces flush failed; cooldown 60s@1901 | [MapleClientSDK] logs flush failed; cooldown 60s@1901 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1901 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1902 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1900 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1900 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1833; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1846 sessionEvents(498B)@1847 traces(1162B)@1847 logs(1011B)@1847 sessionReplays/meta(1066B)@1888; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1842 traces@1844 sessionEvents@1844 logs@1845 sessionReplays/meta@1886; console: [MapleClientSDK] traces flush failed; cooldown 60s@1852 | [MapleClientSDK] logs flush failed; cooldown 60s@1852 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1853 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1853 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1847 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1819; requests issued around the reload (Playwright): sessionReplays/meta(1170B)@1831 sessionEvents(498B)@1831 traces(1162B)@1831 logs(1011B)@1831 sessionReplays/meta(1066B)@1868; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1827 sessionEvents@1828 traces@1828 logs@1830 sessionReplays/meta@1867; console: [MapleClientSDK] traces flush failed; cooldown 60s@1832 | [MapleClientSDK] logs flush failed; cooldown 60s@1832 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1832 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1832 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1831 evidence: ../evidence/headless-new/K-reload-150ms-after-action/replay-off-run1.json @@@@@@@@@@ END FILE: evidence/headless-new/summary-new-2026-10-07T23-50-02-862Z.txt @@@@@@@@@@ FILE: evidence/runner-console-A.txt (5750 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112", startedAt: "2026-10-07T23:39:09.004Z", headlessMode: "chromium-headless-shell (Playwright default)", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36", groups: [ "A" ], } [A-oversized-batch/steady30spansPerSec-10s-interval5000(default)#1] NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms [A-oversized-batch/steady40spansPerSec-10s-interval5000(default)#1] PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms [A-oversized-batch/steady40spansPerSec-10s-interval5000(default)#0] PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms [A-oversized-batch/steady30spansPerSec-10s-interval5000(default)#0] NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms [A-oversized-batch/burst200x500B-interval1000#1] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2565,63567,124567ms [A-oversized-batch/burst200x500B-interval1000#0] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2548,63547,124547ms [A-oversized-batch/burst200x500B-interval5000(default)#0] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5491,70488,135493ms [A-oversized-batch/burst200x500B-interval5000(default)#1] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5483,70483,135481ms Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:39:09.004Z, finished 2026-10-07T23:41:28.743Z A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: ../evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: ../evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2565,63567,124567ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2548,63547,124547ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5491,70488,135493ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval5000(default)-run0.json run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5483,70483,135481ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval5000(default)-run1.json summary written to ../evidence/summary-shell-2026-10-07T23-39-09-004Z.txt @@@@@@@@@@ END FILE: evidence/runner-console-A.txt @@@@@@@@@@ FILE: evidence/runner-console-F-rerun.txt (900 bytes) [F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight#0] REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch [F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight#1] REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch [F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight#0] REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch [F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight#1] REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch @@@@@@@@@@ END FILE: evidence/runner-console-F-rerun.txt @@@@@@@@@@ FILE: evidence/runner-console-W.txt (2528 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112", startedAt: "2026-10-07T23:58:29.444Z", headlessMode: "chromium-headless-shell (Playwright default)", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36", groups: [ "W" ], } [W-workaround-keepalive-downgrade-shim/without-shim#0] LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 [W-workaround-keepalive-downgrade-shim/without-shim#1] LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 [W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim#0] all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 [W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim#1] all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:58:29.444Z, finished 2026-10-07T23:59:04.742Z W-workaround-keepalive-downgrade-shim / without-shim [LOSS (burst 0/200, ticks 0/5): 2/2] run 0: LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/without-shim-run0.json run 1: LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/without-shim-run1.json W-workaround-keepalive-downgrade-shim / with-keepalive-downgrade-shim [all delivered: 2/2] run 0: all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run0.json run 1: all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run1.json summary written to ../evidence/summary-shell-2026-10-07T23-58-29-444Z.txt @@@@@@@@@@ END FILE: evidence/runner-console-W.txt @@@@@@@@@@ FILE: evidence/runner-console-main.txt (102949 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.8.2 + effect@4.0.0-rc.112", startedAt: "2026-10-07T23:39:09.001Z", headlessMode: "chromium-headless-shell (Playwright default)", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36", groups: [ "B", "A0", "C", "D", "Dr", "E", "F", "G", "H", "I", "K", "L" ], } [B-probe/probe#0] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-probe/probe#1] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-sdk-concurrent-signals/manual-flush#0] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [B-sdk-concurrent-signals/manual-flush#1] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [B-sdk-concurrent-signals/manual-flush#2] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [B-sdk-concurrent-signals/reload#0] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [B-sdk-concurrent-signals/reload#1] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [B-sdk-concurrent-signals/reload#2] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 | [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [A0-threshold/n100#0] DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none [A0-threshold/n150#0] DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none [A0-threshold/n160#0] DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none [A0-threshold/n163#0] DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none [A0-threshold/n164#0] REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [A0-threshold/n165#0] REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [A0-threshold/n170#0] REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [A0-threshold/n200#0] REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at Object.post (http://localhost:4173/dist/app.js:12655:21) at try (http://localhost:4173/dist/app.js:8521:26) at http://localhost:4173/dist/app.js:2990:26 at ~effect/Utils/internal (http://localhost:4173/dist/app.js:833:14) at MixedScheduler. (http://localhost:4173/dist/app.js:2990:7) at ~effect/Effect/evaluate (http://localhost:4173/dist/app.js:3005:22) at FiberImpl.runLoop (http://localhost:4173/dist/app.js:2779:107) at FiberImpl.evaluate (http://localhost:4173/dist/app.js:2741:23) at http://localhost:4173/dist/app.js:3827:9 [C-unload-during-cooldown/inject503-then-reload#0] REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2937,t=2942 [C-unload-during-cooldown/inject503-then-reload#1] REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2947,t=2956 [C-unload-during-cooldown/inject503-then-reload#2] REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2122,t=2952,t=2958 [C-unload-during-cooldown/inject503-then-navigate#0] REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2937,t=2941 [C-unload-during-cooldown/inject503-then-navigate#1] REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2108,t=2937,t=2943 [C-unload-during-cooldown/inject503-then-navigate#2] REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2147,t=2960,t=2965 [C-unload-during-cooldown/inject503-then-close#0] REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2938,t=2942 [C-unload-during-cooldown/inject503-then-close#1] REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2930,t=2935 [C-unload-during-cooldown/inject503-then-close#2] REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2939,t=2945 [C-unload-during-cooldown/control-no-failure-reload#0] CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2972 [C-unload-during-cooldown/control-no-failure-reload#1] CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2949 [C-unload-during-cooldown/control-no-failure-reload#2] CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2970 [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000#0] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5125ms); D-tail delivered=false; reload at t=1401ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000#1] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5107ms); D-tail delivered=false; reload at t=1382ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000#2] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5108ms); D-tail delivered=false; reload at t=1375ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000#0] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9124ms); D-tail delivered=false; reload at t=5383ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000#1] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9115ms); D-tail delivered=false; reload at t=5367ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000#2] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9114ms); D-tail delivered=false; reload at t=5373ms [D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload#0] REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms [D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload#1] REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms [D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload#2] REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms [D-pagehide-behind-inflight-flush/control-no-latency-interval1000#0] CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1405ms); reload at t=1393ms [D-pagehide-behind-inflight-flush/control-no-latency-interval1000#1] CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1412ms); reload at t=1406ms [D-pagehide-behind-inflight-flush/control-no-latency-interval1000#2] CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1123ms); D-tail delivered=true (received t=1401ms); reload at t=1394ms [D-hang-head-of-line-blocking/first-POST-held-15s#0] REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1153ms): span export delay max 14990 ms / min 909 ms; log export delay max 14991 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17143:1sp/1157B 18143:1sp/1157B 19144:1sp/1157B] [D-hang-head-of-line-blocking/first-POST-held-15s#1] REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1137ms): span export delay max 14977 ms / min 905 ms; log export delay max 14977 ms (logs endpoint itself is fast); traces POSTs at server t=[1116:1sp/1160B 16120:15sp/6677B 17115:1sp/1157B 18115:1sp/1157B 19115:1sp/1157B] [Dr-random-reload-latency/latency150-interval1000#0] no loss — latency 150 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#1] no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#2] no loss — latency 150 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#3] no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#4] no loss — latency 150 ms, interval 1000: created 38, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#5] no loss — latency 150 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#6] no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#7] LOSS (1 spans) — latency 150 ms, interval 1000: created 40, lost 1 (created 100 ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency150-interval1000#8] no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency150-interval1000#9] no loss — latency 150 ms, interval 1000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval1000#0] no loss — latency 600 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval1000#1] no loss — latency 600 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval1000#2] LOSS (5 spans) — latency 600 ms, interval 1000: created 54, lost 5 (created 513,411,305,202,100 ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval1000#3] LOSS (2 spans) — latency 600 ms, interval 1000: created 22, lost 2 (created 202,100 ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval1000#4] no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval1000#5] no loss — latency 600 ms, interval 1000: created 57, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval1000#6] no loss — latency 600 ms, interval 1000: created 39, lost 0 (created ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval1000#7] LOSS (4 spans) — latency 600 ms, interval 1000: created 34, lost 4 (created 406,304,202,100 ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval1000#8] no loss — latency 600 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval1000#9] no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#0] no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#1] no loss — latency 600 ms, interval 5000: created 41, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#2] no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval5000#3] no loss — latency 600 ms, interval 5000: created 22, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#4] no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true [Dr-random-reload-latency/latency600-interval5000#5] no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#6] no loss — latency 600 ms, interval 5000: created 35, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#7] no loss — latency 600 ms, interval 5000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#8] no loss — latency 600 ms, interval 5000: created 21, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/latency600-interval5000#9] no loss — latency 600 ms, interval 5000: created 25, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#0] no loss — latency 0 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#1] no loss — latency 0 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#2] no loss — latency 0 ms, interval 1000: created 43, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#3] no loss — latency 0 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#4] no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#5] no loss — latency 0 ms, interval 1000: created 31, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#6] no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#7] no loss — latency 0 ms, interval 1000: created 33, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#8] no loss — latency 0 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false [Dr-random-reload-latency/control-latency0-interval1000#9] no loss — latency 0 ms, interval 1000: created 23, lost 0 (created ms before reload); trace flush in flight at reload: false [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#2] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#0] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#1] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#2] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#0] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#1] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#2] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#2] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#2] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) [F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight#0] REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at flush (http://localhost:4173/dist/app.js:11993:11) at http://localhost:4173/dist/app.js:12025:45; Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight#1] REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at flush (http://localhost:4173/dist/app.js:11993:11) at http://localhost:4173/dist/app.js:12025:45; Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight#0] REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at startRun (http://localhost:4173/dist/app.js:12288:5) at startSessionLifecycle (http://localhost:4173/dist/app.js:12345:3) at startReplaySession (http://localhost:4173/dist/chunk-9ehzfcba.js:12322:10) at http://localhost:4173/dist/app.js:12573:23; Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight#1] REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at startRun (http://localhost:4173/dist/app.js:12288:5) at startSessionLifecycle (http://localhost:4173/dist/app.js:12345:3) at startReplaySession (http://localhost:4173/dist/chunk-9ehzfcba.js:12322:10) at http://localhost:4173/dist/app.js:12573:23; Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] [Fr-random-reloads/flush-8-random-reloads-latency300#0] REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"traces[net::ERR_ABORTED]":1,"sessionReplays/meta[net::ERR_ABORTED]":8} [Fr-random-reloads/flush-8-random-reloads-latency300#1] REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST; [maple] session replay blob PUT], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":5,"sessionReplays/blob[net::ERR_ABORTED]":1,"traces[net::ERR_ABORTED]":1} [F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight#0] REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33); Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight#1] REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at endRun (http://localhost:4173/dist/app.js:12308:7) at onPageHide (http://localhost:4173/dist/app.js:12322:33); Playwright requestfailed: sessionReplays/meta[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight#0] REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at startRun (http://localhost:4173/dist/app.js:12288:5) at startSessionLifecycle (http://localhost:4173/dist/app.js:12345:3) at startReplaySession (http://localhost:4173/dist/chunk-9ehzfcba.js:12322:10) at http://localhost:4173/dist/app.js:12573:23; Playwright requestfailed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] [F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight#1] REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionEvents (http://localhost:4173/dist/app.js:11582:9) at Object.flush (http://localhost:4173/dist/app.js:11993:11) at Object.onSuspend (http://localhost:4173/dist/chunk-9ehzfcba.js:12341:96) at endRun (http://localhost:4173/dist/app.js:12309:29) at onPageHide (http://localhost:4173/dist/app.js:12322:33) | [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch at window.fetch (http://localhost:4173/dist/chunk-9ehzfcba.js:71:27) at postSessionMeta (http://localhost:4173/dist/app.js:11564:9) at Object.post (http://localhost:4173/dist/chunk-9ehzfcba.js:12328:7) at post (http://localhost:4173/dist/app.js:12253:11) at startRun (http://localhost:4173/dist/app.js:12288:5) at startSessionLifecycle (http://localhost:4173/dist/app.js:12345:3) at startReplaySession (http://localhost:4173/dist/chunk-9ehzfcba.js:12322:10) at http://localhost:4173/dist/app.js:12573:23; Playwright requestfailed: sessionEvents[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] [Fr-random-reloads/layer-8-random-reloads-latency300#0] REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"sessionReplays/meta[net::ERR_ABORTED]":8} [Fr-random-reloads/layer-8-random-reloads-latency300#1] REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":6} [G-playwright-keepalive-artifact/steady-state-no-navigation#0] no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/steady-state-no-navigation#1] no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/with-final-reload#0] ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [G-playwright-keepalive-artifact/with-final-reload#1] ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 [H-maple-layer-unload/layer#0] tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false [H-maple-layer-unload/layer#1] tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false [H-maple-layer-unload/layer#2] tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false [H-maple-layer-unload/layer-flusher#0] tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [H-maple-layer-unload/layer-flusher#1] tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [H-maple-layer-unload/layer-flusher#2] tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [H-maple-layer-unload/flush#0] tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [H-maple-layer-unload/flush#1] tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [H-maple-layer-unload/flush#2] tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true [I-cross-origin/same-origin#0] tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 [I-cross-origin/same-origin#1] tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 [I-cross-origin/cross-origin#0] tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 [I-cross-origin/cross-origin#1] tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 [K-reload-150ms-after-action/replay-on(default)#0] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1795; requests issued around the reload (Playwright): traces(1165B)@1807 logs(1014B)@1807 sessionReplays/meta(1164B)@1807 sessionEvents(1021B)@1807 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1801 logs@1802 sessionReplays/meta@1802 sessionEvents@1804 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1808 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1808 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1808 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1808 [K-reload-150ms-after-action/replay-on(default)#1] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1810; requests issued around the reload (Playwright): traces(1165B)@1824 logs(1014B)@1824 sessionReplays/meta(1164B)@1824 sessionEvents(1021B)@1824 sessionReplays/meta(1060B)@1891; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1817 sessionReplays/meta@1818 logs@1818 sessionEvents@1820 sessionReplays/meta@1890; console: [MapleClientSDK] logs flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1825 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1825 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1825 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1825 [K-reload-150ms-after-action/replay-on(default)#2] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1803; requests issued around the reload (Playwright): traces(1165B)@1815 logs(1014B)@1816 sessionReplays/meta(1164B)@1816 sessionEvents(1021B)@1816 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1809 logs@1810 sessionReplays/meta@1813 sessionEvents@1814 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1816 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1816 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1816 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1816 [K-reload-150ms-after-action/replay-off#0] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1770; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1781 sessionEvents(499B)@1781 traces(1165B)@1782 logs(1014B)@1782 sessionReplays/meta(1070B)@1814; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1777 traces@1778 sessionEvents@1779 logs@1781 sessionReplays/meta@1811; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1787 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1787 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1788 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1782 [K-reload-150ms-after-action/replay-off#1] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1771; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1783 sessionEvents(499B)@1783 traces(1165B)@1784 logs(1014B)@1784 sessionReplays/meta(1070B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1778 sessionEvents@1779 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1785 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1785 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1785 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 [K-reload-150ms-after-action/replay-off#2] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1769; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1780 sessionEvents(499B)@1780 traces(1165B)@1781 logs(1014B)@1781 sessionReplays/meta(1070B)@1813; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1776 traces@1777 sessionEvents@1778 logs@1780 sessionReplays/meta@1810; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1782 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1782 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1782 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1781 [L-multi-arg-log-body/logError-4-args#0] body is an OTLP arrayValue (structured), not a JSON string — OTLP logRecord.body as POSTed by the SDK: {"arrayValue":{"values":[{"stringValue":"L Failed to connect"},{"stringValue":"Error: ClientError: boom"},{"stringValue":"Unknown Error"},{"stringValue":"{\"code\":42,\"nested\":{\"a\":1}}"}]}} Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:39:09.001Z, finished 2026-10-07T23:58:20.324Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 3/3] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run1.json run 2: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run2.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 3/3] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run1.json run 2: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run2.json A0-threshold / n100 [DELIVERED: 1/1] run 0: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/A0-threshold/n100-run0.json A0-threshold / n150 [DELIVERED: 1/1] run 0: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/A0-threshold/n150-run0.json A0-threshold / n160 [DELIVERED: 1/1] run 0: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/A0-threshold/n160-run0.json A0-threshold / n163 [DELIVERED: 1/1] run 0: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/A0-threshold/n163-run0.json A0-threshold / n164 [REJECTED: 1/1] run 0: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n164-run0.json A0-threshold / n165 [REJECTED: 1/1] run 0: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n165-run0.json A0-threshold / n170 [REJECTED: 1/1] run 0: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n170-run0.json A0-threshold / n200 [REJECTED: 1/1] run 0: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n200-run0.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2937,t=2942 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2947,t=2956 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2122,t=2952,t=2958 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run2.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2937,t=2941 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2108,t=2937,t=2943 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2147,t=2960,t=2965 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run2.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2938,t=2942 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2930,t=2935 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2939,t=2945 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run2.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 3/3] run 0: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2972 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2949 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run1.json run 2: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2970 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5125ms); D-tail delivered=false; reload at t=1401ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5107ms); D-tail delivered=false; reload at t=1382ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5108ms); D-tail delivered=false; reload at t=1375ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9124ms); D-tail delivered=false; reload at t=5383ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9115ms); D-tail delivered=false; reload at t=5367ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9114ms); D-tail delivered=false; reload at t=5373ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run2.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run2.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 3/3] run 0: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1405ms); reload at t=1393ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1412ms); reload at t=1406ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json run 2: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1123ms); D-tail delivered=true (received t=1401ms); reload at t=1394ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run2.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1153ms): span export delay max 14990 ms / min 909 ms; log export delay max 14991 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17143:1sp/1157B 18143:1sp/1157B 19144:1sp/1157B] evidence: ../evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1137ms): span export delay max 14977 ms / min 905 ms; log export delay max 14977 ms (logs endpoint itself is fast); traces POSTs at server t=[1116:1sp/1160B 16120:15sp/6677B 17115:1sp/1157B 18115:1sp/1157B 19115:1sp/1157B] evidence: ../evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json Dr-random-reload-latency / latency150-interval1000 [no loss: 9/10; LOSS (1 spans): 1/10] run 0: no loss — latency 150 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run0.json run 1: no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run1.json run 2: no loss — latency 150 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run2.json run 3: no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run3.json run 4: no loss — latency 150 ms, interval 1000: created 38, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run4.json run 5: no loss — latency 150 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run5.json run 6: no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run6.json run 7: LOSS (1 spans) — latency 150 ms, interval 1000: created 40, lost 1 (created 100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run7.json run 8: no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run8.json run 9: no loss — latency 150 ms, interval 1000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run9.json Dr-random-reload-latency / latency600-interval1000 [no loss: 7/10; LOSS (5 spans): 1/10; LOSS (2 spans): 1/10; LOSS (4 spans): 1/10] run 0: no loss — latency 600 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run0.json run 1: no loss — latency 600 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run1.json run 2: LOSS (5 spans) — latency 600 ms, interval 1000: created 54, lost 5 (created 513,411,305,202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run2.json run 3: LOSS (2 spans) — latency 600 ms, interval 1000: created 22, lost 2 (created 202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run3.json run 4: no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run4.json run 5: no loss — latency 600 ms, interval 1000: created 57, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run5.json run 6: no loss — latency 600 ms, interval 1000: created 39, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run6.json run 7: LOSS (4 spans) — latency 600 ms, interval 1000: created 34, lost 4 (created 406,304,202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run7.json run 8: no loss — latency 600 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run8.json run 9: no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run9.json Dr-random-reload-latency / latency600-interval5000 [no loss: 10/10] run 0: no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run0.json run 1: no loss — latency 600 ms, interval 5000: created 41, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run1.json run 2: no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run2.json run 3: no loss — latency 600 ms, interval 5000: created 22, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run3.json run 4: no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run4.json run 5: no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run5.json run 6: no loss — latency 600 ms, interval 5000: created 35, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run6.json run 7: no loss — latency 600 ms, interval 5000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run7.json run 8: no loss — latency 600 ms, interval 5000: created 21, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run8.json run 9: no loss — latency 600 ms, interval 5000: created 25, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run9.json Dr-random-reload-latency / control-latency0-interval1000 [no loss: 10/10] run 0: no loss — latency 0 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run0.json run 1: no loss — latency 0 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run1.json run 2: no loss — latency 0 ms, interval 1000: created 43, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run2.json run 3: no loss — latency 0 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run3.json run 4: no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run4.json run 5: no loss — latency 0 ms, interval 1000: created 31, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run5.json run 6: no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run6.json run 7: no loss — latency 0 ms, interval 1000: created 33, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run7.json run 8: no loss — latency 0 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run8.json run 9: no loss — latency 0 ms, interval 1000: created 23, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run9.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run2.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 3/3] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json run 2: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run2.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 3/3] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json run 2: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run2.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run2.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run2.json F-replay-posts-aborted-by-navigation / flush-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run0.json run 1: REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / flush-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run0.json run 1: REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run1.json Fr-random-reloads / flush-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"traces[net::ERR_ABORTED]":1,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: ../evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run0.json run 1: REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST; [maple] session replay blob PUT], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":5,"sessionReplays/blob[net::ERR_ABORTED]":1,"traces[net::ERR_ABORTED]":1} evidence: ../evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run1.json F-replay-posts-aborted-by-navigation / layer-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run0.json run 1: REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / layer-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run0.json run 1: REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run1.json Fr-random-reloads / layer-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: ../evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run0.json run 1: REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":6} evidence: ../evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/with-final-reload-run1.json H-maple-layer-unload / layer [tail LOST: 3/3] run 0: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run0.json run 1: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run1.json run 2: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run2.json H-maple-layer-unload / layer-flusher [tail delivered: 3/3] run 0: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run0.json run 1: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run1.json run 2: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run2.json H-maple-layer-unload / flush [tail delivered: 3/3] run 0: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run0.json run 1: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run1.json run 2: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run2.json I-cross-origin / same-origin [tail delivered: 2/2] run 0: tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/same-origin-run0.json run 1: tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/same-origin-run1.json I-cross-origin / cross-origin [tail delivered: 2/2] run 0: tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/cross-origin-run0.json run 1: tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/cross-origin-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1795; requests issued around the reload (Playwright): traces(1165B)@1807 logs(1014B)@1807 sessionReplays/meta(1164B)@1807 sessionEvents(1021B)@1807 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1801 logs@1802 sessionReplays/meta@1802 sessionEvents@1804 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1808 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1808 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1808 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1808 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1810; requests issued around the reload (Playwright): traces(1165B)@1824 logs(1014B)@1824 sessionReplays/meta(1164B)@1824 sessionEvents(1021B)@1824 sessionReplays/meta(1060B)@1891; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1817 sessionReplays/meta@1818 logs@1818 sessionEvents@1820 sessionReplays/meta@1890; console: [MapleClientSDK] logs flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1825 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1825 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1825 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1825 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run1.json run 2: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1803; requests issued around the reload (Playwright): traces(1165B)@1815 logs(1014B)@1816 sessionReplays/meta(1164B)@1816 sessionEvents(1021B)@1816 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1809 logs@1810 sessionReplays/meta@1813 sessionEvents@1814 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1816 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1816 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1816 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1816 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run2.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1770; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1781 sessionEvents(499B)@1781 traces(1165B)@1782 logs(1014B)@1782 sessionReplays/meta(1070B)@1814; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1777 traces@1778 sessionEvents@1779 logs@1781 sessionReplays/meta@1811; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1787 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1787 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1788 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1782 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1771; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1783 sessionEvents(499B)@1783 traces(1165B)@1784 logs(1014B)@1784 sessionReplays/meta(1070B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1778 sessionEvents@1779 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1785 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1785 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1785 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run1.json run 2: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1769; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1780 sessionEvents(499B)@1780 traces(1165B)@1781 logs(1014B)@1781 sessionReplays/meta(1070B)@1813; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1776 traces@1777 sessionEvents@1778 logs@1780 sessionReplays/meta@1810; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1782 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1782 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1782 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1781 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run2.json L-multi-arg-log-body / logError-4-args [body is an OTLP arrayValue (structured), not a JSON string: 1/1] run 0: body is an OTLP arrayValue (structured), not a JSON string — OTLP logRecord.body as POSTed by the SDK: {"arrayValue":{"values":[{"stringValue":"L Failed to connect"},{"stringValue":"Error: ClientError: boom"},{"stringValue":"Unknown Error"},{"stringValue":"{\"code\":42,\"nested\":{\"a\":1}}"}]}} evidence: ../evidence/L-multi-arg-log-body/logError-4-args-run0.json summary written to ../evidence/summary-shell-2026-10-07T23-39-09-001Z.txt @@@@@@@@@@ END FILE: evidence/runner-console-main.txt @@@@@@@@@@ FILE: evidence/sdk-0.10.0/runner-console-A.txt (5863 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.10.0 (optional variant build)", startedAt: "2026-10-07T23:41:29.113Z", headlessMode: "chromium-headless-shell (Playwright default)", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36", groups: [ "A" ], } [A-oversized-batch/steady40spansPerSec-10s-interval5000(default)#1] PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10460,75460ms [A-oversized-batch/steady30spansPerSec-10s-interval5000(default)#1] NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms [A-oversized-batch/steady30spansPerSec-10s-interval5000(default)#0] NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms [A-oversized-batch/steady40spansPerSec-10s-interval5000(default)#0] PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10481,75482ms [A-oversized-batch/burst200x500B-interval1000#1] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2447,63447,124447ms [A-oversized-batch/burst200x500B-interval1000#0] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2513,63514,124513ms [A-oversized-batch/burst200x500B-interval5000(default)#1] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5451,70449,135456ms [A-oversized-batch/burst200x500B-interval5000(default)#0] REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5488,70485,135492ms Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:41:29.113Z, finished 2026-10-07T23:43:48.562Z A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10460,75460ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10481,75482ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2447,63447,124447ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2513,63514,124513ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5451,70449,135456ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5488,70485,135492ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run0.json summary written to ../evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-41-29-113Z.txt @@@@@@@@@@ END FILE: evidence/sdk-0.10.0/runner-console-A.txt @@@@@@@@@@ FILE: evidence/sdk-0.10.0/runner-console-main.txt (49901 bytes) environment: { sdkUnderTest: "@maple-dev/effect-sdk@0.10.0 (optional variant build)", startedAt: "2026-10-07T23:43:48.971Z", headlessMode: "chromium-headless-shell (Playwright default)", chromiumVersion: "143.0.7499.4", playwrightVersion: "1.57.0", bunVersion: "1.4.2", platform: "linux x64", userAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/143.0.7499.4 Safari/537.36", groups: [ "A0", "B", "C", "D", "E", "K" ], } [B-probe/probe#0] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-probe/probe#1] MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 [B-sdk-concurrent-signals/manual-flush#0] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [B-sdk-concurrent-signals/manual-flush#1] REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [B-sdk-concurrent-signals/reload#0] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [B-sdk-concurrent-signals/reload#1] REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 | [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n100#0] DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none [A0-threshold/n100#1] DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none [A0-threshold/n150#0] DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none [A0-threshold/n150#1] DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none [A0-threshold/n160#0] DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none [A0-threshold/n160#1] DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none [A0-threshold/n163#0] DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none [A0-threshold/n163#1] DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none [A0-threshold/n164#0] REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n164#1] REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n165#0] REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n165#1] REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n170#0] REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n170#1] REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n200#0] REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [A0-threshold/n200#1] REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch at http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:47 at withStartedTraceId (http://localhost:4183/dist-0.10.0/app.js:12140:15) at window.fetch (http://localhost:4183/dist-0.10.0/chunk-kz9b2wg9.js:116:22) at Object.post (http://localhost:4183/dist-0.10.0/app.js:13602:21) at try (http://localhost:4183/dist-0.10.0/app.js:9466:26) at MixedScheduler. (http://localhost:4183/dist-0.10.0/app.js:2965:7) at ~effect/Effect/evaluate (http://localhost:4183/dist-0.10.0/app.js:2979:38) at FiberImpl.runLoop (http://localhost:4183/dist-0.10.0/app.js:2734:95) at FiberImpl.evaluate (http://localhost:4183/dist-0.10.0/app.js:2688:23) at http://localhost:4183/dist-0.10.0/app.js:3882:9 [C-unload-during-cooldown/inject503-then-reload#0] REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2115,t=2950,t=2956 [C-unload-during-cooldown/inject503-then-reload#1] REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2949,t=2953 [C-unload-during-cooldown/inject503-then-navigate#0] REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2119,t=2954,t=2960 [C-unload-during-cooldown/inject503-then-navigate#1] REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2956,t=2962 [C-unload-during-cooldown/inject503-then-close#0] REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2931,t=2936 [C-unload-during-cooldown/inject503-then-close#1] REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2134,t=2949,t=2959 [C-unload-during-cooldown/control-no-failure-reload#0] CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2960 [C-unload-during-cooldown/control-no-failure-reload#1] CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2954 [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000#0] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5114ms); D-tail delivered=false; reload at t=1383ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000#1] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5124ms); D-tail delivered=false; reload at t=1380ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000#0] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9123ms); D-tail delivered=false; reload at t=5383ms [D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000#1] REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9116ms); D-tail delivered=false; reload at t=5370ms [D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload#0] REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8113ms); D-tail delivered=false; reload at t=6231ms [D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload#1] REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6241ms [D-pagehide-behind-inflight-flush/control-no-latency-interval1000#0] CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1145ms); D-tail delivered=true (received t=1408ms); reload at t=1401ms [D-pagehide-behind-inflight-flush/control-no-latency-interval1000#1] CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1115ms); D-tail delivered=true (received t=1396ms); reload at t=1390ms [D-hang-head-of-line-blocking/first-POST-held-15s#0] REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1161ms): span export delay max 14983 ms / min 886 ms; log export delay max 14983 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17144:1sp/1157B 18144:1sp/1157B 19144:1sp/1157B] [D-hang-head-of-line-blocking/first-POST-held-15s#1] REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1131ms): span export delay max 14992 ms / min 922 ms; log export delay max 14992 ms (logs endpoint itself is fast); traces POSTs at server t=[1123:1sp/1160B 16128:15sp/6677B 17123:1sp/1157B 18123:1sp/1157B 19122:1sp/1157B] [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) [E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#0] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) [E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines#1] LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#0] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) [E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)#1] LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) [E-combined-unload-budget/E4-no-spans-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#0] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) [E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation#1] LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) [K-reload-150ms-after-action/replay-on(default)#0] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1777; requests issued around the reload (Playwright): traces(1165B)@1789 logs(1014B)@1790 sessionReplays/meta(1194B)@1790 sessionEvents(1043B)@1790 sessionReplays/meta(1090B)@1850; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1783 sessionReplays/meta@1785 logs@1785 sessionEvents@1787 sessionReplays/meta@1849; console: [MapleClientSDK] logs flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1790 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1790 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1790 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1790 [K-reload-150ms-after-action/replay-on(default)#1] span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1807; requests issued around the reload (Playwright): traces(1165B)@1819 logs(1014B)@1819 sessionReplays/meta(1194B)@1819 sessionEvents(1043B)@1819 sessionReplays/meta(1090B)@1882; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1813 logs@1814 sessionReplays/meta@1815 sessionEvents@1816 sessionReplays/meta@1882; console: [MapleClientSDK] logs flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1820 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1820 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1819 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1819 [K-reload-150ms-after-action/replay-off#0] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1783; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1792 sessionEvents(510B)@1793 traces(1165B)@1793 logs(1014B)@1793 sessionReplays/meta(1100B)@1825; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1789 sessionEvents@1790 traces@1790 logs@1791 sessionReplays/meta@1822; console: [MapleClientSDK] traces flush failed; cooldown 60s@1793 | [MapleClientSDK] logs flush failed; cooldown 60s@1793 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1793 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1793 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1793 [K-reload-150ms-after-action/replay-off#1] span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1772; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1782 sessionEvents(510B)@1783 traces(1165B)@1783 logs(1014B)@1784 sessionReplays/meta(1100B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1779 sessionEvents@1780 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1784 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1784 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1784 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:43:48.971Z, finished 2026-10-07T23:50:02.504Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/sdk-0.10.0/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/sdk-0.10.0/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run1.json A0-threshold / n100 [DELIVERED: 2/2] run 0: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n100-run0.json run 1: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n100-run1.json A0-threshold / n150 [DELIVERED: 2/2] run 0: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n150-run0.json run 1: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n150-run1.json A0-threshold / n160 [DELIVERED: 2/2] run 0: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n160-run0.json run 1: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n160-run1.json A0-threshold / n163 [DELIVERED: 2/2] run 0: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n163-run0.json run 1: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n163-run1.json A0-threshold / n164 [REJECTED: 2/2] run 0: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n164-run0.json run 1: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n164-run1.json A0-threshold / n165 [REJECTED: 2/2] run 0: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n165-run0.json run 1: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n165-run1.json A0-threshold / n170 [REJECTED: 2/2] run 0: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n170-run0.json run 1: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n170-run1.json A0-threshold / n200 [REJECTED: 2/2] run 0: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n200-run0.json run 1: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n200-run1.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2115,t=2950,t=2956 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2949,t=2953 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run1.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2119,t=2954,t=2960 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2956,t=2962 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run1.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2931,t=2936 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2134,t=2949,t=2959 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run1.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 2/2] run 0: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2960 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2954 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5114ms); D-tail delivered=false; reload at t=1383ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5124ms); D-tail delivered=false; reload at t=1380ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9123ms); D-tail delivered=false; reload at t=5383ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9116ms); D-tail delivered=false; reload at t=5370ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8113ms); D-tail delivered=false; reload at t=6231ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6241ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 2/2] run 0: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1145ms); D-tail delivered=true (received t=1408ms); reload at t=1401ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1115ms); D-tail delivered=true (received t=1396ms); reload at t=1390ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1161ms): span export delay max 14983 ms / min 886 ms; log export delay max 14983 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17144:1sp/1157B 18144:1sp/1157B 19144:1sp/1157B] evidence: ../evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1131ms): span export delay max 14992 ms / min 922 ms; log export delay max 14992 ms (logs endpoint itself is fast); traces POSTs at server t=[1123:1sp/1160B 16128:15sp/6677B 17123:1sp/1157B 18123:1sp/1157B 19122:1sp/1157B] evidence: ../evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1777; requests issued around the reload (Playwright): traces(1165B)@1789 logs(1014B)@1790 sessionReplays/meta(1194B)@1790 sessionEvents(1043B)@1790 sessionReplays/meta(1090B)@1850; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1783 sessionReplays/meta@1785 logs@1785 sessionEvents@1787 sessionReplays/meta@1849; console: [MapleClientSDK] logs flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1790 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1790 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1790 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1790 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1807; requests issued around the reload (Playwright): traces(1165B)@1819 logs(1014B)@1819 sessionReplays/meta(1194B)@1819 sessionEvents(1043B)@1819 sessionReplays/meta(1090B)@1882; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1813 logs@1814 sessionReplays/meta@1815 sessionEvents@1816 sessionReplays/meta@1882; console: [MapleClientSDK] logs flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1820 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1820 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1819 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1819 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1783; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1792 sessionEvents(510B)@1793 traces(1165B)@1793 logs(1014B)@1793 sessionReplays/meta(1100B)@1825; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1789 sessionEvents@1790 traces@1790 logs@1791 sessionReplays/meta@1822; console: [MapleClientSDK] traces flush failed; cooldown 60s@1793 | [MapleClientSDK] logs flush failed; cooldown 60s@1793 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1793 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1793 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1793 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1772; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1782 sessionEvents(510B)@1783 traces(1165B)@1783 logs(1014B)@1784 sessionReplays/meta(1100B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1779 sessionEvents@1780 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1784 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1784 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1784 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run1.json summary written to ../evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-43-48-971Z.txt @@@@@@@@@@ END FILE: evidence/sdk-0.10.0/runner-console-main.txt @@@@@@@@@@ FILE: evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-41-29-113Z.txt (3149 bytes) Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:41:29.113Z, finished 2026-10-07T23:43:48.562Z A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10460,75460ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 156 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10481,75482ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2447,63447,124447ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2513,63514,124513ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5451,70449,135456ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5488,70485,135492ms evidence: ../evidence/sdk-0.10.0/A-oversized-batch/burst200x500B-interval5000(default)-run0.json @@@@@@@@@@ END FILE: evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-41-29-113Z.txt @@@@@@@@@@ FILE: evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-43-48-971Z.txt (22139 bytes) Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:43:48.971Z, finished 2026-10-07T23:50:02.504Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/sdk-0.10.0/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/sdk-0.10.0/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/manual-flush-run1.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 2/2] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/B-sdk-concurrent-signals/reload-run1.json A0-threshold / n100 [DELIVERED: 2/2] run 0: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n100-run0.json run 1: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n100-run1.json A0-threshold / n150 [DELIVERED: 2/2] run 0: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n150-run0.json run 1: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n150-run1.json A0-threshold / n160 [DELIVERED: 2/2] run 0: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n160-run0.json run 1: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n160-run1.json A0-threshold / n163 [DELIVERED: 2/2] run 0: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n163-run0.json run 1: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/sdk-0.10.0/A0-threshold/n163-run1.json A0-threshold / n164 [REJECTED: 2/2] run 0: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n164-run0.json run 1: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n164-run1.json A0-threshold / n165 [REJECTED: 2/2] run 0: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n165-run0.json run 1: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n165-run1.json A0-threshold / n170 [REJECTED: 2/2] run 0: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n170-run0.json run 1: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n170-run1.json A0-threshold / n200 [REJECTED: 2/2] run 0: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n200-run0.json run 1: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/sdk-0.10.0/A0-threshold/n200-run1.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2115,t=2950,t=2956 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2949,t=2953 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-reload-run1.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2119,t=2954,t=2960 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2956,t=2962 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-navigate-run1.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 2/2] run 0: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2124,t=2931,t=2936 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2134,t=2949,t=2959 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/inject503-then-close-run1.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 2/2] run 0: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2960 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2954 evidence: ../evidence/sdk-0.10.0/C-unload-during-cooldown/control-no-failure-reload-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5114ms); D-tail delivered=false; reload at t=1383ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5124ms); D-tail delivered=false; reload at t=1380ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9123ms); D-tail delivered=false; reload at t=5383ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9116ms); D-tail delivered=false; reload at t=5370ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 2/2] run 0: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8113ms); D-tail delivered=false; reload at t=6231ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6241ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 2/2] run 0: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1145ms); D-tail delivered=true (received t=1408ms); reload at t=1401ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1115ms); D-tail delivered=true (received t=1396ms); reload at t=1390ms evidence: ../evidence/sdk-0.10.0/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1161ms): span export delay max 14983 ms / min 886 ms; log export delay max 14983 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17144:1sp/1157B 18144:1sp/1157B 19144:1sp/1157B] evidence: ../evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1131ms): span export delay max 14992 ms / min 922 ms; log export delay max 14992 ms (logs endpoint itself is fast); traces POSTs at server t=[1123:1sp/1160B 16128:15sp/6677B 17123:1sp/1157B 18123:1sp/1157B 19122:1sp/1157B] evidence: ../evidence/sdk-0.10.0/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2732] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2732B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 2/2] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6407B) sessionEvents(21371B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 2/2] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6416] events[508] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6416B) sessionEvents(508B) traces(60652B) sessionReplays/meta(1098B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 2/2] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1158] events[2133] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1158B) sessionEvents(2133B) sessionReplays/meta(1088B) evidence: ../evidence/sdk-0.10.0/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1777; requests issued around the reload (Playwright): traces(1165B)@1789 logs(1014B)@1790 sessionReplays/meta(1194B)@1790 sessionEvents(1043B)@1790 sessionReplays/meta(1090B)@1850; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1783 sessionReplays/meta@1785 logs@1785 sessionEvents@1787 sessionReplays/meta@1849; console: [MapleClientSDK] logs flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush failed; cooldown 60s@1790 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1790 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1790 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1790 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1790 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1807; requests issued around the reload (Playwright): traces(1165B)@1819 logs(1014B)@1819 sessionReplays/meta(1194B)@1819 sessionEvents(1043B)@1819 sessionReplays/meta(1090B)@1882; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: traces@1813 logs@1814 sessionReplays/meta@1815 sessionEvents@1816 sessionReplays/meta@1882; console: [MapleClientSDK] logs flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush failed; cooldown 60s@1820 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1820 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1820 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1819 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1819 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-on(default)-run1.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 2/2] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1783; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1792 sessionEvents(510B)@1793 traces(1165B)@1793 logs(1014B)@1793 sessionReplays/meta(1100B)@1825; Playwright failed: sessionReplays/meta[net::ERR_ABORTED] traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1789 sessionEvents@1790 traces@1790 logs@1791 sessionReplays/meta@1822; console: [MapleClientSDK] traces flush failed; cooldown 60s@1793 | [MapleClientSDK] logs flush failed; cooldown 60s@1793 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1793 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1793 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1793 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1772; requests issued around the reload (Playwright): sessionReplays/meta(1204B)@1782 sessionEvents(510B)@1783 traces(1165B)@1783 logs(1014B)@1784 sessionReplays/meta(1100B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1779 sessionEvents@1780 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1784 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1784 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1784 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 evidence: ../evidence/sdk-0.10.0/K-reload-150ms-after-action/replay-off-run1.json @@@@@@@@@@ END FILE: evidence/sdk-0.10.0/summary-shell-sdk0.10.0-2026-10-07T23-43-48-971Z.txt @@@@@@@@@@ FILE: evidence/summary-shell-2026-10-07T23-39-09-001Z.txt (46826 bytes) Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:39:09.001Z, finished 2026-10-07T23:58:20.324Z B-probe / probe [MEASURED: 2/2] run 0: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/B-keepalive-probe/probe-run0.json run 1: MEASURED — single: 1024:ok 49152:ok 65535:ok 65536:ok 65537:REJECTED 70000:REJECTED 131072:REJECTED | 2x40000 concurrent: ok/REJECTED | 4x20000 concurrent: ok/ok/ok/REJECTED | unload 2x40000 arrived 1/2, 4x20000 arrived 3/4 | B10 unload-while-inflight arrived B10-periodic-hold50000 evidence: ../evidence/B-keepalive-probe/probe-run1.json B-sdk-concurrent-signals / manual-flush [REPRODUCED (one signal rejected): 3/3] run 0: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run0.json run 1: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run1.json run 2: REPRODUCED (one signal rejected) — trigger=flush; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] logs flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/manual-flush-run2.json B-sdk-concurrent-signals / reload [REPRODUCED (one signal rejected): 3/3] run 0: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run0.json run 1: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] metrics flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run1.json run 2: REPRODUCED (one signal rejected) — trigger=reload; spans 40/40, logs 0/30, metrics POSTs 1; request bodies seen by Playwright [traces:38472 logs:32135 metrics:931]; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/B-sdk-concurrent-signals/reload-run2.json A0-threshold / n100 [DELIVERED: 1/1] run 0: DELIVERED — 100 spans in one flush: arrived 100; traces POST body bytes (Playwright) 40252; console: none evidence: ../evidence/A0-threshold/n100-run0.json A0-threshold / n150 [DELIVERED: 1/1] run 0: DELIVERED — 150 spans in one flush: arrived 150; traces POST body bytes (Playwright) 60052; console: none evidence: ../evidence/A0-threshold/n150-run0.json A0-threshold / n160 [DELIVERED: 1/1] run 0: DELIVERED — 160 spans in one flush: arrived 160; traces POST body bytes (Playwright) 64012; console: none evidence: ../evidence/A0-threshold/n160-run0.json A0-threshold / n163 [DELIVERED: 1/1] run 0: DELIVERED — 163 spans in one flush: arrived 163; traces POST body bytes (Playwright) 65200; console: none evidence: ../evidence/A0-threshold/n163-run0.json A0-threshold / n164 [REJECTED: 1/1] run 0: REJECTED — 164 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65596; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n164-run0.json A0-threshold / n165 [REJECTED: 1/1] run 0: REJECTED — 165 spans in one flush: arrived 0; traces POST body bytes (Playwright) 65992; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n165-run0.json A0-threshold / n170 [REJECTED: 1/1] run 0: REJECTED — 170 spans in one flush: arrived 0; traces POST body bytes (Playwright) 67972; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n170-run0.json A0-threshold / n200 [REJECTED: 1/1] run 0: REJECTED — 200 spans in one flush: arrived 0; traces POST body bytes (Playwright) 79852; console: [MapleClientSDK] traces flush failed; cooldown 60s: TypeError: Failed to fetch evidence: ../evidence/A0-threshold/n200-run0.json C-unload-during-cooldown / inject503-then-reload [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2937,t=2942 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2947,t=2956 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=reload: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2122,t=2952,t=2958 evidence: ../evidence/C-unload-during-cooldown/inject503-then-reload-run2.json C-unload-during-cooldown / inject503-then-navigate [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2937,t=2941 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2108,t=2937,t=2943 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=navigate: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2147,t=2960,t=2965 evidence: ../evidence/C-unload-during-cooldown/inject503-then-navigate-run2.json C-unload-during-cooldown / inject503-then-close [REPRODUCED (both spans lost): 3/3] run 0: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2116,t=2938,t=2942 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run0.json run 1: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2120,t=2930,t=2935 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run1.json run 2: REPRODUCED (both spans lost) — inject503=true unload=close: C-first delivered=false, C-before-unload delivered=false; console failed=1 skipped=t=2111,t=2939,t=2945 evidence: ../evidence/C-unload-during-cooldown/inject503-then-close-run2.json C-unload-during-cooldown / control-no-failure-reload [CONTROL OK (both delivered): 3/3] run 0: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2972 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run0.json run 1: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2949 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run1.json run 2: CONTROL OK (both delivered) — inject503=false unload=reload: C-first delivered=true, C-before-unload delivered=true; console failed=1 skipped=t=2970 evidence: ../evidence/C-unload-during-cooldown/control-no-failure-reload-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval1000 [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5125ms); D-tail delivered=false; reload at t=1401ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5107ms); D-tail delivered=false; reload at t=1382ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=5108ms); D-tail delivered=false; reload at t=1375ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval1000-run2.json D-pagehide-behind-inflight-flush / first-POST-4000ms-interval5000 [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9124ms); D-tail delivered=false; reload at t=5383ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9115ms); D-tail delivered=false; reload at t=5367ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 4000 ms, autoFlushInterval 5000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=9114ms); D-tail delivered=false; reload at t=5373ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-4000ms-interval5000-run2.json D-pagehide-behind-inflight-flush / first-POST-7000ms-interval1000-tail-5s-before-reload [REPRODUCED (tail span lost): 3/3] run 0: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8107ms); D-tail delivered=false; reload at t=6226ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run0.json run 1: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8114ms); D-tail delivered=false; reload at t=6240ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run1.json run 2: REPRODUCED (tail span lost) — first traces POST held 7000 ms, autoFlushInterval 1000, D-tail ended 5000 ms before reload: D-early delivered=true (server responded t=8150ms); D-tail delivered=false; reload at t=6268ms evidence: ../evidence/D-pagehide-behind-inflight-flush/first-POST-7000ms-interval1000-tail-5s-before-reload-run2.json D-pagehide-behind-inflight-flush / control-no-latency-interval1000 [CONTROL OK: 3/3] run 0: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1405ms); reload at t=1393ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run0.json run 1: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1134ms); D-tail delivered=true (received t=1412ms); reload at t=1406ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run1.json run 2: CONTROL OK — first traces POST held 0 ms, autoFlushInterval 1000, D-tail ended 150 ms before reload: D-early delivered=true (server responded t=1123ms); D-tail delivered=true (received t=1401ms); reload at t=1394ms evidence: ../evidence/D-pagehide-behind-inflight-flush/control-no-latency-interval1000-run2.json D-hang-head-of-line-blocking / first-POST-held-15s [REPRODUCED (exports stalled behind one slow request): 2/2] run 0: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1153ms): span export delay max 14990 ms / min 909 ms; log export delay max 14991 ms (logs endpoint itself is fast); traces POSTs at server t=[1145:1sp/1160B 16149:15sp/6677B 17143:1sp/1157B 18143:1sp/1157B 19144:1sp/1157B] evidence: ../evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run0.json run 1: REPRODUCED (exports stalled behind one slow request) — first traces POST held 15000 ms (from t=1137ms): span export delay max 14977 ms / min 905 ms; log export delay max 14977 ms (logs endpoint itself is fast); traces POSTs at server t=[1116:1sp/1160B 16120:15sp/6677B 17115:1sp/1157B 18115:1sp/1157B 19115:1sp/1157B] evidence: ../evidence/D-hang-head-of-line-blocking/first-POST-held-15s-run1.json Dr-random-reload-latency / latency150-interval1000 [no loss: 9/10; LOSS (1 spans): 1/10] run 0: no loss — latency 150 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run0.json run 1: no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run1.json run 2: no loss — latency 150 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run2.json run 3: no loss — latency 150 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run3.json run 4: no loss — latency 150 ms, interval 1000: created 38, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run4.json run 5: no loss — latency 150 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run5.json run 6: no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run6.json run 7: LOSS (1 spans) — latency 150 ms, interval 1000: created 40, lost 1 (created 100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run7.json run 8: no loss — latency 150 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run8.json run 9: no loss — latency 150 ms, interval 1000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency150-interval1000-run9.json Dr-random-reload-latency / latency600-interval1000 [no loss: 7/10; LOSS (5 spans): 1/10; LOSS (2 spans): 1/10; LOSS (4 spans): 1/10] run 0: no loss — latency 600 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run0.json run 1: no loss — latency 600 ms, interval 1000: created 46, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run1.json run 2: LOSS (5 spans) — latency 600 ms, interval 1000: created 54, lost 5 (created 513,411,305,202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run2.json run 3: LOSS (2 spans) — latency 600 ms, interval 1000: created 22, lost 2 (created 202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run3.json run 4: no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run4.json run 5: no loss — latency 600 ms, interval 1000: created 57, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run5.json run 6: no loss — latency 600 ms, interval 1000: created 39, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run6.json run 7: LOSS (4 spans) — latency 600 ms, interval 1000: created 34, lost 4 (created 406,304,202,100 ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run7.json run 8: no loss — latency 600 ms, interval 1000: created 28, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run8.json run 9: no loss — latency 600 ms, interval 1000: created 47, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval1000-run9.json Dr-random-reload-latency / latency600-interval5000 [no loss: 10/10] run 0: no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run0.json run 1: no loss — latency 600 ms, interval 5000: created 41, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run1.json run 2: no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run2.json run 3: no loss — latency 600 ms, interval 5000: created 22, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run3.json run 4: no loss — latency 600 ms, interval 5000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: true evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run4.json run 5: no loss — latency 600 ms, interval 5000: created 42, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run5.json run 6: no loss — latency 600 ms, interval 5000: created 35, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run6.json run 7: no loss — latency 600 ms, interval 5000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run7.json run 8: no loss — latency 600 ms, interval 5000: created 21, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run8.json run 9: no loss — latency 600 ms, interval 5000: created 25, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/latency600-interval5000-run9.json Dr-random-reload-latency / control-latency0-interval1000 [no loss: 10/10] run 0: no loss — latency 0 ms, interval 1000: created 49, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run0.json run 1: no loss — latency 0 ms, interval 1000: created 58, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run1.json run 2: no loss — latency 0 ms, interval 1000: created 43, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run2.json run 3: no loss — latency 0 ms, interval 1000: created 26, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run3.json run 4: no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run4.json run 5: no loss — latency 0 ms, interval 1000: created 31, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run5.json run 6: no loss — latency 0 ms, interval 1000: created 54, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run6.json run 7: no loss — latency 0 ms, interval 1000: created 33, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run7.json run 8: no loss — latency 0 ms, interval 1000: created 44, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run8.json run 9: no loss — latency 0 ms, interval 1000: created 23, lost 0 (created ms before reload); trace flush in flight at reload: false evidence: ../evidence/Dr-random-reload-latency/control-latency0-interval1000-run9.json E-combined-unload-budget / E1-spans45-plus-60kB-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 45/45; old-page unload arrivals (bytes): traces[18707] meta-ended[2702] events[] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: traces(18707B) sessionReplays/meta(2702B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E1-spans45-plus-60kB-DOM-mutation-run2.json E-combined-unload-budget / E2-spans150(~60kB)-plus-30-console-lines [LOST: meta(ended), final sessionEvents: 3/3] run 0: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run0.json run 1: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run1.json run 2: LOST: meta(ended), final sessionEvents — spans 150/150; old-page unload arrivals (bytes): traces[60652] meta-ended[] events[] blob[]; replay chunkSeqs at server [0,1]; Playwright during unload: traces(60652B) sessionReplays/meta(6377B) sessionEvents(21041B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E2-spans150(~60kB)-plus-30-console-lines-run2.json E-combined-unload-budget / E3-replay-disabled-spans150(~60kB) [LOST: traces: 3/3] run 0: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run0.json run 1: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run1.json run 2: LOST: traces — spans 0/150; old-page unload arrivals (bytes): traces[] meta-ended[6386] events[497] blob[]; replay chunkSeqs at server []; Playwright during unload: sessionReplays/meta(6386B) sessionEvents(497B) traces(60652B) sessionReplays/meta(1068B) evidence: ../evidence/E-combined-unload-budget/E3-replay-disabled-spans150(~60kB)-run2.json E-combined-unload-budget / E4-no-spans-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E4-no-spans-small-DOM-mutation-run2.json E-combined-unload-budget / E5-Maple.layer-small-DOM-mutation [LOST: replay chunk(s) 1 (seq consumed, never posted): 3/3] run 0: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run0.json run 1: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run1.json run 2: LOST: replay chunk(s) 1 (seq consumed, never posted) — spans 0/0; old-page unload arrivals (bytes): traces[] meta-ended[1128] events[2100] blob[]; replay chunkSeqs at server [0,2]; Playwright during unload: sessionReplays/meta(1128B) sessionEvents(2100B) sessionReplays/meta(1058B) evidence: ../evidence/E-combined-unload-budget/E5-Maple.layer-small-DOM-mutation-run2.json F-replay-posts-aborted-by-navigation / flush-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run0.json run 1: REPRODUCED — flush/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / flush-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run0.json run 1: REPRODUCED — flush/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/flush-active-meta-POST-in-flight-run1.json Fr-random-reloads / flush-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"traces[net::ERR_ABORTED]":1,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: ../evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run0.json run 1: REPRODUCED — flush, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST; [maple] session replay blob PUT], SDK flush failed 1, skipped 1; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":5,"sessionReplays/blob[net::ERR_ABORTED]":1,"traces[net::ERR_ABORTED]":1} evidence: ../evidence/Fr-random-reloads/flush-8-random-reloads-latency300-run1.json F-replay-posts-aborted-by-navigation / layer-periodic-events-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run0.json run 1: REPRODUCED — layer/events: console: [maple] session replay metadata POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-periodic-events-POST-in-flight-run1.json F-replay-posts-aborted-by-navigation / layer-active-meta-POST-in-flight [REPRODUCED: 2/2] run 0: REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run0.json run 1: REPRODUCED — layer/meta: console: [maple] session replay events POST failed (dropping; will retry on next chunk): TypeError: Failed to fetch evidence: ../evidence/F-replay-posts-aborted-by-navigation/layer-active-meta-POST-in-flight-run1.json Fr-random-reloads / layer-8-random-reloads-latency300 [REPRODUCED: 2/2] run 0: REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 15 [[maple] session replay events POST; [maple] session replay metadata POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionEvents[net::ERR_ABORTED]":7,"sessionReplays/meta[net::ERR_ABORTED]":8} evidence: ../evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run0.json run 1: REPRODUCED — layer, latency 300 ms, 8 reloads: replay-post console errors 14 [[maple] session replay metadata POST; [maple] session replay events POST], SDK flush failed 0, skipped 0; Playwright requestfailed by path: {"sessionReplays/meta[net::ERR_ABORTED]":8,"sessionEvents[net::ERR_ABORTED]":6} evidence: ../evidence/Fr-random-reloads/layer-8-random-reloads-latency300-run1.json G-playwright-keepalive-artifact / steady-state-no-navigation [no requestfailed events: 2/2] run 0: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run0.json run 1: no requestfailed events — reload=false: traces: pw req 8/fin 8/fail 0 vs server200 8; sessionReplays/meta: pw req 1/fin 1/fail 0 vs server200 1; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/steady-state-no-navigation-run1.json G-playwright-keepalive-artifact / with-final-reload [ARTIFACT (Playwright reports failures for requests the server answered 200): 2/2] run 0: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/with-final-reload-run0.json run 1: ARTIFACT (Playwright reports failures for requests the server answered 200) — reload=true: traces: pw req 9/fin 8/fail 1[net::ERR_ABORTED] vs server200 9; sessionReplays/meta: pw req 3/fin 2/fail 1[net::ERR_ABORTED] vs server200 3; sessionEvents: pw req 1/fin 1/fail 0 vs server200 1; sessionReplays/blob: pw req 1/fin 1/fail 0 vs server200 1; logs: pw req 0/fin 0/fail 0 vs server200 0; metrics: pw req 0/fin 0/fail 0 vs server200 0 evidence: ../evidence/G-playwright-keepalive-artifact/with-final-reload-run1.json H-maple-layer-unload / layer [tail LOST: 3/3] run 0: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run0.json run 1: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run1.json run 2: tail LOST — layer: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=false evidence: ../evidence/H-maple-layer-unload/layer-run2.json H-maple-layer-unload / layer-flusher [tail delivered: 3/3] run 0: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run0.json run 1: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run1.json run 2: tail delivered — layer-flusher: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/layer-flusher-run2.json H-maple-layer-unload / flush [tail delivered: 3/3] run 0: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run0.json run 1: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run1.json run 2: tail delivered — flush: H-early (created 7.5 s before reload) delivered=true; H-tail (created 1 s before reload) delivered=true evidence: ../evidence/H-maple-layer-unload/flush-run2.json I-cross-origin / same-origin [tail delivered: 2/2] run 0: tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/same-origin-run0.json run 1: tail delivered — same-origin: I-tail delivered=true; preflights 0 (access-control-request-headers: n/a); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/same-origin-run1.json I-cross-origin / cross-origin [tail delivered: 2/2] run 0: tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/cross-origin-run0.json run 1: tail delivered — cross-origin: I-tail delivered=true; preflights 5 (access-control-request-headers: authorization,content-type,x-maple-sdk); traces POST user-agent header: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, l, x-maple-sdk: maple-effect-sdk-client/0.8.2 evidence: ../evidence/I-cross-origin/cross-origin-run1.json K-reload-150ms-after-action / replay-on(default) [span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1795; requests issued around the reload (Playwright): traces(1165B)@1807 logs(1014B)@1807 sessionReplays/meta(1164B)@1807 sessionEvents(1021B)@1807 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1801 logs@1802 sessionReplays/meta@1802 sessionEvents@1804 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush failed; cooldown 60s@1808 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1808 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1808 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1808 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1808 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run0.json run 1: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1810; requests issued around the reload (Playwright): traces(1165B)@1824 logs(1014B)@1824 sessionReplays/meta(1164B)@1824 sessionEvents(1021B)@1824 sessionReplays/meta(1060B)@1891; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1817 sessionReplays/meta@1818 logs@1818 sessionEvents@1820 sessionReplays/meta@1890; console: [MapleClientSDK] logs flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush failed; cooldown 60s@1825 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1825 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1825 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1825 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1825 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run1.json run 2: span delivered, log delivered; console flush-failed: [logs flush failed, traces flush failed] — reload at t=1803; requests issued around the reload (Playwright): traces(1165B)@1815 logs(1014B)@1816 sessionReplays/meta(1164B)@1816 sessionEvents(1021B)@1816 sessionReplays/meta(1060B)@1871; Playwright failed: logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] traces[net::ERR_ABORTED]; server 200 in same window: traces@1809 logs@1810 sessionReplays/meta@1813 sessionEvents@1814 sessionReplays/meta@1870; console: [MapleClientSDK] logs flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush failed; cooldown 60s@1816 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1816 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1816 | [maple] session replay metadata POST failed (dropping; will retry on next chunk)@1816 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1816 evidence: ../evidence/K-reload-150ms-after-action/replay-on(default)-run2.json K-reload-150ms-after-action / replay-off [span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed]: 3/3] run 0: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1770; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1781 sessionEvents(499B)@1781 traces(1165B)@1782 logs(1014B)@1782 sessionReplays/meta(1070B)@1814; Playwright failed: traces[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1777 traces@1778 sessionEvents@1779 logs@1781 sessionReplays/meta@1811; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1787 | [MapleClientSDK] traces flush skipped (cooldown 60000ms remaining)@1787 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1788 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1782 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run0.json run 1: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1771; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1783 sessionEvents(499B)@1783 traces(1165B)@1784 logs(1014B)@1784 sessionReplays/meta(1070B)@1817; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1778 sessionEvents@1779 traces@1780 logs@1781 sessionReplays/meta@1814; console: [MapleClientSDK] traces flush failed; cooldown 60s@1784 | [MapleClientSDK] logs flush failed; cooldown 60s@1785 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1785 | [MapleClientSDK] logs flush skipped (cooldown 59999ms remaining)@1785 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1784 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run1.json run 2: span delivered, log delivered; console flush-failed: [traces flush failed, logs flush failed] — reload at t=1769; requests issued around the reload (Playwright): sessionReplays/meta(1174B)@1780 sessionEvents(499B)@1780 traces(1165B)@1781 logs(1014B)@1781 sessionReplays/meta(1070B)@1813; Playwright failed: traces[net::ERR_ABORTED] sessionEvents[net::ERR_ABORTED] logs[net::ERR_ABORTED] sessionReplays/meta[net::ERR_ABORTED]; server 200 in same window: sessionReplays/meta@1776 traces@1777 sessionEvents@1778 logs@1780 sessionReplays/meta@1810; console: [MapleClientSDK] traces flush failed; cooldown 60s@1782 | [MapleClientSDK] logs flush failed; cooldown 60s@1782 | [MapleClientSDK] traces flush skipped (cooldown 59999ms remaining)@1782 | [MapleClientSDK] logs flush skipped (cooldown 60000ms remaining)@1782 | [maple] session replay events POST failed (dropping; will retry on next chunk)@1781 evidence: ../evidence/K-reload-150ms-after-action/replay-off-run2.json L-multi-arg-log-body / logError-4-args [body is an OTLP arrayValue (structured), not a JSON string: 1/1] run 0: body is an OTLP arrayValue (structured), not a JSON string — OTLP logRecord.body as POSTed by the SDK: {"arrayValue":{"values":[{"stringValue":"L Failed to connect"},{"stringValue":"Error: ClientError: boom"},{"stringValue":"Unknown Error"},{"stringValue":"{\"code\":42,\"nested\":{\"a\":1}}"}]}} evidence: ../evidence/L-multi-arg-log-body/logError-4-args-run0.json @@@@@@@@@@ END FILE: evidence/summary-shell-2026-10-07T23-39-09-001Z.txt @@@@@@@@@@ FILE: evidence/summary-shell-2026-10-07T23-39-09-004Z.txt (3061 bytes) Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:39:09.004Z, finished 2026-10-07T23:41:28.743Z A-oversized-batch / steady30spansPerSec-10s-interval5000(default) [NOT REPRODUCED: 2/2] run 1: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run1.json run 0: NOT REPRODUCED — created 338 spans (38 small 'tick' spans after the burst); delivered 338 (ticks 38/38); traces POSTs reaching server: 18; logs delivered 8; console 'flush failed' at t=ms evidence: ../evidence/A-oversized-batch/steady30spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / steady40spansPerSec-10s-interval5000(default) [PARTIAL LOSS: 2/2] run 1: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: ../evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run1.json run 0: PARTIAL LOSS — created 438 spans (38 small 'tick' spans after the burst); delivered 152 (ticks 0/38); traces POSTs reaching server: 1; logs delivered 8; console 'flush failed' at t=10506,75510ms evidence: ../evidence/A-oversized-batch/steady40spansPerSec-10s-interval5000(default)-run0.json A-oversized-batch / burst200x500B-interval1000 [REPRODUCED (no span ever delivered): 2/2] run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2565,63567,124567ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval1000-run1.json run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=2548,63547,124547ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval1000-run0.json A-oversized-batch / burst200x500B-interval5000(default) [REPRODUCED (no span ever delivered): 2/2] run 0: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5491,70488,135493ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval5000(default)-run0.json run 1: REPRODUCED (no span ever delivered) — created 265 spans (65 small 'tick' spans after the burst); delivered 0 (ticks 0/65); traces POSTs reaching server: 0; logs delivered 13; console 'flush failed' at t=5483,70483,135481ms evidence: ../evidence/A-oversized-batch/burst200x500B-interval5000(default)-run1.json @@@@@@@@@@ END FILE: evidence/summary-shell-2026-10-07T23-39-09-004Z.txt @@@@@@@@@@ FILE: evidence/summary-shell-2026-10-07T23-58-29-444Z.txt (1271 bytes) Scenario summary (chromium-headless-shell (Playwright default), Chromium 143.0.7499.4, Playwright 1.57.0, Bun 1.4.2) started 2026-10-07T23:58:29.444Z, finished 2026-10-07T23:59:04.742Z W-workaround-keepalive-downgrade-shim / without-shim [LOSS (burst 0/200, ticks 0/5): 2/2] run 0: LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/without-shim-run0.json run 1: LOSS (burst 0/200, ticks 0/5) — shim=false: burst spans delivered 0/200, later spans 0/5; console flush-failed 1, skipped 6 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/without-shim-run1.json W-workaround-keepalive-downgrade-shim / with-keepalive-downgrade-shim [all delivered: 2/2] run 0: all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run0.json run 1: all delivered — shim=true: burst spans delivered 200/200, later spans 5/5; console flush-failed 0, skipped 0 evidence: ../evidence/W-workaround-keepalive-downgrade-shim/with-keepalive-downgrade-shim-run1.json @@@@@@@@@@ END FILE: evidence/summary-shell-2026-10-07T23-58-29-444Z.txt @@@@@@@@@@ FILE: evidence/webkit-firefox-launch.txt (1867 bytes) webkit launch failed: Error: launch: ╔══════════════════════════════════════════════════════╗ ║ Host system is missing dependencies to run browsers. ║ ║ Please install them with the following command: ║ ║ ║ ║ sudo npx playwright install-deps ║ ║ ║ ║ Alternatively, use apt: ║ ║ sudo apt-get install libicu74\ ║ ║ libatomic1\ ║ ║ libvpx9\ ║ ║ libjpeg-turbo8\ ║ firefox launch failed: Error: launch: Executable doesn't exist at ~/.cache/ms-playwright/firefox-1497/firefox/firefox ╔═════════════════════════════════════════════════════════════════════════╗ ║ Looks like Playwright Test or Playwright was just installed or updated. ║ ║ Please run the following command to download new browsers: ║ ║ ║ ║ npx playwright install ║ ║ ║ ║ <3 Playwright Team ║ ╚═════════════════════════════════════════════════════════════════════════╝ @@@@@@@@@@ END FILE: evidence/webkit-firefox-launch.txt