Skip to content

web: read observability list views from world.analytics when available#2647

Open
karthikscale3 wants to merge 3 commits into
codex/workflow-analytics-apisfrom
codex/workflow-web-analytics
Open

web: read observability list views from world.analytics when available#2647
karthikscale3 wants to merge 3 commits into
codex/workflow-analytics-apisfrom
codex/workflow-web-analytics

Conversation

@karthikscale3

@karthikscale3 karthikscale3 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What changed

Routes the observability list server actions in @workflow/web through the
optional world.analytics namespace when the configured backend provides one,
and falls back to the existing runtime storage APIs otherwise.

Updated list actions in app/server/workflow-server-actions.server.ts:

  • fetchRunsworld.analytics.runs.list (else world.runs.list)
  • fetchStepsworld.analytics.steps.list (else world.steps.list)
  • fetchEventsworld.analytics.events.list (else world.events.list)
  • fetchEventsByCorrelationIdworld.analytics.events.listByCorrelationId (else runtime)
  • fetchHooksworld.analytics.hooks.list (else world.hooks.list)

Behavior

  • The analytics path is metadata-only, which is exactly what these list views
    need. Selection is a simple guard: use world.analytics when present,
    otherwise the runtime storage API. Backends that don't implement analytics
    (e.g. local/postgres) keep working unchanged.
  • Events: the analytics path is used only when no payload data is requested
    (!withData). When a caller asks for payloads, the action stays on the
    runtime path, which can resolve them.
  • Unchanged: all detail/get actions (runs.get, steps.get, events.get,
    hooks.get), stream actions, and mutations (cancel, recreate, wakeup, resume
    hook) continue to use the runtime storage APIs.

Notes

  • Analytics hook rows are metadata-only (no owner field); the hooks table
    relies on the common fields, so listing is unaffected.
  • Response shapes are preserved, so the UI and client hooks need no changes.

Stacking

Stacked on the world.analytics API PR (base branch
codex/workflow-analytics-apis). Merge that first; this should retarget to the
default branch afterward.

Validation

  • Typecheck: @workflow/web has no new type errors (pre-existing, unrelated
    errors only; the changed file is clean).
  • pnpm exec biome check app/server/workflow-server-actions.server.ts

Merge order

This work lands as a stack of four PRs on the world.analytics read-path:

  1. Add workflow analytics world APIs #2234 — add the optional world.analytics namespace (base main) — merge first
  2. cli: read list views from world.analytics when available #2648 — CLI list reads via world.analytics (stacked on Add workflow analytics world APIs #2234)
  3. web: read observability list views from world.analytics when available #2647 — web list reads (runs/steps/events) via world.analytics (stacked on Add workflow analytics world APIs #2234)
  4. web: list hooks from analytics, fetch token on demand #2652 — web lazy hook-token fetch (stacked on web: read observability list views from world.analytics when available #2647) — merge after web: read observability list views from world.analytics when available #2647

#2648 and #2647 both depend only on #2234 and are independent of each other. #2652 builds on #2647. After #2234 merges to main, the stacked PRs auto-retarget.

Route the runs/steps/events/hooks list server actions through the optional
world.analytics namespace when the backend provides one, falling back to the
runtime storage APIs otherwise. Events listing only uses the analytics path
when no payload data is requested. Detail/get actions, streams, and mutations
are unchanged.
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jun 25, 2026 11:51pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jun 25, 2026 11:51pm
example-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-astro-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-express-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-fastify-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-hono-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-nitro-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-nuxt-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workbench-vite-workflow Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Jun 25, 2026 11:51pm
workflow-swc-playground Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workflow-tarballs Ready Ready Preview, Comment Jun 25, 2026 11:51pm
workflow-web Ready Ready Preview, Comment Jun 25, 2026 11:51pm

@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4e802fe

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@workflow/web Patch
@workflow/cli Patch
@workflow/nitro Patch
workflow Patch
@workflow/world-testing Patch
@workflow/nuxt Patch
@workflow/core Patch
@workflow/web-shared Patch
@workflow/builders Patch
@workflow/next Patch
@workflow/vitest Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/web/app/server/workflow-server-actions.server.ts Outdated
Comment thread packages/web/app/server/workflow-server-actions.server.ts Outdated
@karthikscale3 karthikscale3 marked this pull request as ready for review June 25, 2026 22:59
@karthikscale3 karthikscale3 requested review from a team and ijjk as code owners June 25, 2026 22:59
The Events tab and trace viewer derive step names and wait resumeAt from
resolved event payloads, and the hooks table needs the secret token and
ownerId for its resume/copy-token actions. The metadata-only analytics rows
do not carry these, so only the runs and steps list views use world.analytics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@karthikscale3

Copy link
Copy Markdown
Contributor Author

Thanks — both findings are valid and addressed in 7f374453a.

The analytics rows are metadata-only, and both events and hooks have list-view dependencies on resolved payloads that those rows don't carry:

  • Events — the Events tab and trace viewer derive step names and wait resumeAt from the resolved event payload.
  • Hooks — the hooks table needs the secret token (resume action + copy-token affordance) and ownerId.

So I reverted fetchEvents, fetchEventsByCorrelationId, and fetchHooks to always use the runtime storage API. The analytics read path is now limited to the runs and steps list views, whose columns map cleanly to the metadata-only rows. Detail views, payload resolution, streams, and mutations were already unchanged.

The events list/trace consumers read only top-level eventType, correlationId,
and createdAt; event payloads are loaded lazily per event via fetchEvent(...,
'all') on the runtime path. Map the flat analytics event rows into the runtime
Event shape (reconstructing eventData.stepName) so fetchEvents and
fetchEventsByCorrelationId can use the analytics read path when available.
Hooks remain on the runtime path (they need the secret token + ownerId).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@karthikscale3

Copy link
Copy Markdown
Contributor Author

Follow-up (4e802fe7e): re-enabled the events list on the analytics read path.

After tracing every consumer of the events list, they read only top-level eventType, correlationId, and createdAt — all of which the analytics event rows carry:

  • the trace/graph mapper (graph-execution-mapper.ts) correlates by correlationId and times by createdAt
  • analyzeEvents (@workflow/web-shared) reads correlationId/eventType/createdAt
  • event payloads are loaded lazily per-event via fetchEvent(..., 'all') on the runtime path — the list never carries them

The only mismatch was structural: the runtime Event nests step metadata under eventData while analytics rows are flat. Added analyticsEventToEvent() to map the flat rows back into the runtime shape (reconstructing eventData.stepName, matching what the runtime list returns with resolveData:'none'). fetchEvents and fetchEventsByCorrelationId now use world.analytics.events.* when available and no payloads are requested, falling back to runtime otherwise.

Hooks remain on the runtime path — those genuinely need the secret token (resume + copy-token) and ownerId, which are not in the metadata-only analytics rows.

So the analytics read path now covers runs, steps, and events; hooks stay on runtime. Typecheck diff vs base is empty (no new errors); biome clean.

eventType: event.eventType,
createdAt: event.createdAt,
...(event.correlationId ? { correlationId: event.correlationId } : {}),
...(event.specVersion !== undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analyticsEventToEvent remap drops resumeAt, so on the analytics events path wait_created events lose their resume time, breaking the trace viewer's sleep-span end-time cap and the wait's displayed resume time.

Fix on Vercel

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