Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Render attr_set events and run attributes in the observability UI - #2393

Merged
pranaygp merged 2 commits into
mainfrom
attr-set-observability
Jun 13, 2026
Merged

Render attr_set events and run attributes in the observability UI#2393
pranaygp merged 2 commits into
mainfrom
attr-set-observability

Conversation

@pranaygp

@pranaygppranaygp commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Workflow Attributes shipped in v5 (experimental_setAttributes, initial attributes on start(), and the event-sourced attr_set event), but the observability surfaces never learned how to display them: attr_set events rendered as opaque rows with no payload, had no presence on the trace timeline, run-level events (including attr_set) were unreachable from the trace sidebar, and run attributes were dumped as a raw JSON blob with no treatment for reserved $-prefixed keys.

This PR makes attributes a first-class citizen across the observability UI (@workflow/web-shared, used by both the local workflow web dashboard and the hosted dashboard):

  • Trace timeline: attr_set events render as teal diamond markers on the span row at the moment the write happened, with a tooltip showing the time. (The new trace viewer previously had no per-event rendering at all — attr_set is the only workflow event with no duration/segment representation, so it gets point markers.)
  • Run sidebar: selecting the run root span now shows run-level events (run lifecycle + attr_set); previously the events list was hidden entirely for runs and attr_set events were unreachable (their correlationId is a dedup token, not a child-entity ID, so they matched no step/hook/sleep span).
  • attr_set payload rendering: expanding an attr_set event (run sidebar or Events tab) renders the changed keys, removed keys (shown as removed), and the writer — Set by workflow or Set by step <stepId> (attempt N) — instead of raw JSON. Added to DATA_EVENT_TYPES so the payload lazy-loads like other event types.
  • Attributes card: run details renders attributes as key-value rows instead of a JSON blob. Reserved $-prefixed keys get a Reserved badge, are de-emphasized, and sort after user keys. The card header shows the count.
  • Events tab: attr_set rows get a teal status dot, group/label with the run (instead of an orphaned - name), and render the nice payload block when expanded.
  • Old trace viewer parity: attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS (Attributes set), and getEventColor (teal palette).

Attributes set at start() time (including reserved keys via allowReservedAttributes, #2385) need no special UI path — they materialize onto run.attributes at creation and appear in the Attributes card; verified end-to-end below.

Screenshots

All screenshots are committed under docs/public/screenshots/attributes/, so after merge they are statically served by the docs site (https://workflow-sdk.dev/screenshots/attributes/<name>.png) and can be reused in docs pages and public changelogs. The attributes docs page now embeds three of them.

The demo run was started with start(processOrder, ['ord_42'], { attributes: { 'customer.tier': 'pro', region: 'iad1', '$workflow.source': 'checkout-api' }, allowReservedAttributes: true }), then set attributes from the workflow body (including a reserved key and a key removal) and from two steps.

Trace timeline — attr_set diamond markers on the run row

Trace timeline with attr_set markers

Marker tooltip on hover

Marker tooltip

Run details — Attributes card (reserved keys badged + sorted last) and run-level events

Attributes card with Reserved badges

Expanded attr_set events — step writer with attempt, and key removal

Expanded attr_set events showing writer and removal

Events tab — teal dots, run-level grouping, expanded payload

Events tab with attr_set rows

Events tab step writer payload

CLI

workflow inspect run <id> already surfaces materialized attributes (no change needed):

{
runId: 'wrun_01KTZ1BEX0N7FGVZMCK7KQ8HDV',
status: 'completed',
workflowName: 'workflow//./workflows/999_attributes_demo//processOrder',
...
attributes: {
'customer.tier': 'pro',
region: 'iad1',
'$workflow.source': 'checkout-api',
stage: 'complete',
'order.id': 'ord_42',
'$sdk.demo': 'attributes',
'payment.status': 'charged',
'payment.method': 'card',
'inventory.reserved': 'true'
},
...
}

Implementation notes

  • filterSpanRawEvents (new, in lib/trace-builder.ts) centralizes the sidebar's raw-event filtering for both trace viewers; run-level classification mirrors groupEventsByCorrelation's fallthrough.
  • The shared AttrSetEventBlock / RunAttributesCard components live in sidebar/attributes-block.tsx and use RESERVED_ATTRIBUTE_KEY_PREFIX from @workflow/world.
  • pnpm test in web-shared passes (73 tests); tsc build clean; biome error count reduced vs. base (import organization in touched files).

Docs Preview

PagePreview
Observability → Attributes (v5)/v5/docs/observability/attributes#viewing-attributes

Hosted screenshot example (served by the docs deployment): /screenshots/attributes/trace-timeline.png

🤖 Generated with Claude Code

- Teal diamond markers for attr_set events on the trace timeline with
time tooltips (new trace viewer)
- attr_set payloads render changed/removed keys and the writer
(workflow vs step + attempt) in the run sidebar and Events tab
- Run root span selection now shows run-level events (run lifecycle +
attr_set) in the sidebar
- Attributes card on run details renders key-value rows with reserved
$-prefixed keys badged and sorted after user keys
- attr_set added to MARKER_EVENT_TYPES, BOUNDARY_LABELS, event colors
(teal), and the flat events list run-level grouping
- Docs: screenshots on the attributes page, served from docs/public
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code ownerJune 12, 2026 23:19
CopilotAI review requested due to automatic review settings June 12, 2026 23:19
@changeset-bot

changeset-botBot commented Jun 12, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b82ecf

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

This PR includes changesets to release 16 packages
NameType
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/cliPatch
@workflow/nitroPatch
workflowPatch
@workflow/world-testingPatch
@workflow/nuxtPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/vitestPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch

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

@vercel

vercelBot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

CopilotAI left a comment

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.

Pull request overview

This PR makes workflow attributes (materialized run.attributes plus the event-sourced attr_set events) visible and readable across the observability UI surfaces in @workflow/web-shared, including both the old and new trace viewers, the run sidebar, and the Events tab.

Changes:

  • Add run-level event selection support so attr_set (and other run-level events) are reachable from the run root span sidebar.
  • Render attr_set as point-in-time markers in the new trace timeline and add consistent coloring/labels for attr_set markers in trace viewers.
  • Introduce shared UI blocks to render run attributes as key/value rows (reserved $ keys badged + sorted last) and render attr_set payloads as human-friendly change lists; update docs + changeset accordingly.

Reviewed changes

Copilot reviewed 14 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
packages/web-shared/src/lib/trace-builder.tsAdds isRunLevelEvent + filterSpanRawEvents to consistently map raw events to the selected span (including run root).
packages/web-shared/src/components/workflow-traces/trace-span-construction.tsAdds attr_set to marker event types so it can be represented in span events.
packages/web-shared/src/components/workflow-traces/event-colors.tsAdds a teal palette for attr_set markers.
packages/web-shared/src/components/workflow-trace-view.tsxUses filterSpanRawEvents so run root selection shows run-level events, not only correlationId-matched child events.
packages/web-shared/src/components/trace-viewer/components/node.tsxAdds attr_set to boundary labels (“Attributes set”) for marker display in the old trace viewer.
packages/web-shared/src/components/sidebar/events-list.tsxEnables lazy-loading and custom payload rendering for attr_set via AttrSetEventBlock.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsxAllows the Events list to render for the run resource (run root span) as well.
packages/web-shared/src/components/sidebar/attributes-block.tsxNew shared components: RunAttributesCard and AttrSetEventBlock (+ reserved-key handling/sorting).
packages/web-shared/src/components/sidebar/attribute-panel.tsxSwitches run attributes rendering from raw JSON to RunAttributesCard.
packages/web-shared/src/components/new-trace-viewer/trace-viewer.tsxUses filterSpanRawEvents for sidebar raw event selection in the new trace viewer; minor math refactor.
packages/web-shared/src/components/new-trace-viewer/components/timeline.tsxRenders diamond markers for attr_set events on the timeline row with tooltip timestamps.
packages/web-shared/src/components/event-list-view.tsxAdds teal status dot + run-level grouping and expanded payload rendering for attr_set in the Events tab.
docs/content/docs/v5/observability/attributes.mdxDocuments how attributes and attr_set events appear in the UI with embedded screenshots.
.changeset/witty-pugs-applaud.mdPublishes a patch changeset for the @workflow/web-shared UI enhancements.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +358 to +371
<div
aria-label="Attributes set"
className="absolute top-1/2 z-10 flex h-4 w-4 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center"
style={{ left: `${frac * 100}%` }}
>
<div
className="h-2 w-2 rotate-45 rounded-[1px] border"
style={{
backgroundColor: 'var(--ds-teal-400)',
borderColor: 'var(--ds-teal-900)',
}}
/>
</div>
</TooltipTrigger>
* origin/main:
fix(docs): repair broken links, fix the link linter, and version-correct v5 Card + edit links (#2391)
feat(web-shared): RelativeTimeCard with shared ContextCard provider (#2328)
# Conflicts:
#	packages/web-shared/src/components/event-list-view.tsx

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice info here too for the step 👌

Image


![Run details panel showing the Attributes card with reserved keys badged](/screenshots/attributes/run-details-attributes.png)

Each `experimental_setAttributes` call appears on the trace timeline as a diamond marker at the moment the attributes were written:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess we can move it to be setAttributes soon? But not in this PR, since it's also a core method rename, etc.

@pranaygp
pranaygp merged commit dde689a into mainJun 13, 2026
59 checks passed
@pranaygp
pranaygp deleted the attr-set-observability branch June 13, 2026 21:58
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for dde689a due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

dde689a056c9b7df8025c8531e73a499a0967331

pranaygp added a commit that referenced this pull request Jun 14, 2026
* origin/main:
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
pranaygp added a commit that referenced this pull request Jun 16, 2026
* origin/main:
Small detail panel cleanup (#2459)
Fix lazy Next workflow HMR (#2438)
Prevent peer dependency-only major bumps (#2437)
fix(changesets): only major-bump peer dependents when out of range (#2439)
Version Packages (beta) (#2428)
otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
[next] Clarify `serverExternalPackages` warning (#2417)
Add .swc gitignore handling to builder (#2427)
Version Packages (beta) (#2390)
[ci] Increase dev.test.ts cleanup hook timeout (#2416)
[world-vercel] Switch event endpoints to v4 wire format (#2055)
docs: document run idempotency (#2011)
Render attr_set events and run attributes in observability UI (#2393)
[ci] Fix backport job model slug (#2403)
[ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
Update queue client to 0.3.1 (#2399)
fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
test: e2e coverage for run-idempotency conflict-handling strategies (#2387)
# Conflicts:
#	pnpm-lock.yaml
Sign up for freeto 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.

3 participants

@pranaygp@VaguelySerious