Skip to content

E2e distributed tracing: suite-owned motel, browser OTLP, end-to-end trace join#979

Merged
RhysSullivan merged 4 commits into
mainfrom
claude/e2e-distributed-tracing
Jun 12, 2026
Merged

E2e distributed tracing: suite-owned motel, browser OTLP, end-to-end trace join#979
RhysSullivan merged 4 commits into
mainfrom
claude/e2e-distributed-tracing

Conversation

@RhysSullivan

@RhysSullivan RhysSullivan commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Every e2e run now captures distributed traces, hermetically.

  • Suite-owned motel: a local OTLP store boots alongside the WorkOS/Autumn emulators (own port, store under runs/.motel wiped per boot, torn down with the suite). The cloud boot recipe gains an extraEnv seam so the globalsetup points the app's exporter at it — the same endpoint-agnostic layer that ships production traces to Axiom.
  • Browser OTLP seam (packages/react): when the build names an endpoint (VITE_PUBLIC_OTLP_TRACES_URL), the web client exports spans and Effect's HttpClient sends a W3C traceparent on every API request. Same-origin in dev via a vite proxy to motel.
  • Trace join (apps/cloud/src/server.ts): the worker's http.server span adopts the caller's traceparent — one trace id from the click in the browser through the worker to the database.
  • CLI/MCP calls join the ledger too: mcporter's plain fetch sends no traceparent, so the MCP surface wraps the global fetch and MINTS one per call to the target's MCP endpoint; entries land in traces.json labeled by JSON-RPC method/tool with duration + status. traces.json is a shared append-merge ledger (e2e/src/trace-harvest.ts) interleaving browser-harvested and terminal-minted entries.
  • Span processor Simple → Batch: per-span synchronous export under the dev worker added enough latency to flip the approval pause/resume race in e2e; batching removes the class, ctx.waitUntil(forceFlush) still drains per request in prod.
  • Run pages list every harvested trace with links into motel's per-trace waterfall.

Stack

  1. Developer-session e2e: chat-theater recordings, focus timeline, one-mp4 films #962
  2. E2e distributed tracing: suite-owned motel, browser OTLP, end-to-end trace join #979 👈 current
  3. Runs viewer: live session player with URL bar, trace rail, self-hosted trace viewer #980
  4. Production browser span export: same-origin OTLP forwarded to Axiom #981

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
executor-marketing 3b88f1b Jun 12 2026, 05:46 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
executor-cloud 3b88f1b Jun 12 2026, 05:46 PM

@RhysSullivan RhysSullivan force-pushed the claude/e2e-developer-session branch from ad5a637 to aa3e919 Compare June 12, 2026 17:22
@RhysSullivan RhysSullivan force-pushed the claude/e2e-distributed-tracing branch from 769da87 to efa0fb3 Compare June 12, 2026 17:22
@RhysSullivan RhysSullivan force-pushed the claude/e2e-developer-session branch 2 times, most recently from 9486dc2 to 0c1d824 Compare June 12, 2026 17:40
@RhysSullivan RhysSullivan force-pushed the claude/e2e-distributed-tracing branch 2 times, most recently from 5c2d4cc to aab65e1 Compare June 12, 2026 17:46
@RhysSullivan RhysSullivan changed the base branch from claude/e2e-developer-session to main June 12, 2026 17:46
Our fork of kitlangton/motel (UsefulSoftwareCo/motel) with the query-
worker serialization fix: trace/span detail endpoints 500ed upstream
('Expected JSON value' — shared span objects tripping Schema.Unknown's
isJson guard). Run it with `bun run motel` in e2e/, then E2E_MOTEL=1
sends browser + server spans there during runs.
The browser surface harvests the W3C traceparent the web app already
sends on each API request into the run's traces.json (id + offset +
url). The runs viewer renders them as a Distributed traces table — one
row per call, linking into the local motel's per-trace waterfall — so
'why was that page slow in this recording' goes from the recording to
the exact click→server→DB trace in two clicks. Spans exist in motel
when the run exported there (E2E_MOTEL=1); ids harvest regardless.
Every e2e run captures distributed traces. A suite-owned motel (local
OTLP store) boots alongside the WorkOS/Autumn emulators — own port,
runs-local store wiped per boot, torn down with the suite — and the
cloud boot recipe gains an extraEnv seam so the globalsetup points the
app's exporter at it (the same endpoint-agnostic layer that ships prod
traces to Axiom).

The web client exports OTLP spans when the build names an endpoint
(VITE_PUBLIC_OTLP_TRACES_URL, proxied same-origin to motel in dev), and
the cloud worker's http.server span adopts the caller's W3C traceparent
— one trace id from the click in the browser through the worker to the
database. Run pages list every harvested trace id with links into
motel's waterfall.

Also switches the worker's span processor Simple → Batch: per-span
synchronous export fetches under the dev worker added enough request
latency to flip the approval pause/resume race in e2e; batching removes
the class, and the existing ctx.waitUntil(forceFlush) still drains per
request in prod.
The web app's HttpClient sends a traceparent on its own, but mcporter's
plain fetch does not — so the terminal half of a session was invisible
in traces.json. The MCP surface now wraps the global fetch: every POST
to the target's MCP endpoint gets a minted traceparent (the worker and
DO already join whatever arrives) and lands in the ledger with the
JSON-RPC method or tool name as its label, duration, status, and
source: terminal. traces.json becomes a shared append-merge ledger
(src/trace-harvest.ts) so the browser surface's harvested entries and
the MCP surface's minted ones interleave by wall clock, and the trace
rail tags each row with its window (⌨ terminal / ⊕ browser).
@RhysSullivan RhysSullivan force-pushed the claude/e2e-distributed-tracing branch from aab65e1 to 3b88f1b Compare June 12, 2026 17:46
@RhysSullivan RhysSullivan marked this pull request as ready for review June 12, 2026 17:47
@RhysSullivan RhysSullivan merged commit 59c3e24 into main Jun 12, 2026
2 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant