cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, '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

cli: show world-specific run fields in inspect output via World.describeRun - #2896

Merged
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column
Jul 13, 2026
Merged

cli: show world-specific run fields in inspect output via World.describeRun#2896
TooTallNate merged 5 commits into
mainfrom
feat/inspect-region-column

Conversation

@TooTallNate

@TooTallNateTooTallNate commented Jul 13, 2026

Copy link
Copy Markdown
Member

What

World-specific fields in the workflow inspect commands — on Vercel, each run's region — implemented via a new optional hook on the World interface:

describeRun?(run: Readonly<Record<string,unknown>>): Record<string,string|null>|null;

The hook receives the run entity (not just the ID), so worlds can derive display fields from anything on it — a region tag in the run ID, executionContext, a shard, etc. — and can add fields later without another interface change. Each returned key becomes an inspect column/property.

  • @workflow/world — the hook + contract: synchronous/pure, read only recognized fields (loosely typed, mirroring createRunId), must not throw; null field value = "applicable but undeterminable" (preserved in structured output), vs. hook absent = keys don't exist at all.
  • @workflow/world-verceldescribeRun returns { region } decoded from the run-ID tag: tagged → embedded region, untagged legacy → default region, undecodable → null. regionForRunId remains exported as a utility.
  • @workflow/cli
    • workflow inspect runs: extra columns from the union of keys the world returns for the page, inserted before status; both analytics and storage list paths; table + JSON
    • workflow inspect run <id>: fields merged into the detail/JSON output
    • Worlds without the hook (local/postgres): zero schema noise

Stacked on #1981

Based on world-create-run-id — the Vercel implementation decodes the region tags that PR introduces. Merge after (or into) #1981.

Tests

  • CLI: multi-key field merging + hook invoked with the entity; null preservation in JSON; hook-absent case (20 green)
  • world-vercel: tagged→region, untagged→default, undecodable→{region: null}, no-usable-runId→nothing (260 green)

Changeset: patch × 3 packages.

@TooTallNate
TooTallNate requested a review from ijjk as a code ownerJuly 13, 2026 16:30
CopilotAI review requested due to automatic review settings July 13, 2026 16:30
@TooTallNate
TooTallNate requested a review from a team as a code ownerJuly 13, 2026 16:30
@changeset-bot

changeset-botBot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ecb538d

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldPatch
@workflow/world-vercelPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
workflowPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nuxtPatch
@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 Jul 13, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production145302301683
✅ 💻 Local Development161702191836
✅ 📦 Local Production161702191836
✅ 🐘 Local Postgres161702191836
✅ 🪟 Windows15300153
✅ 📋 Other89401771071
✅ vercel-multi-region270027
Total7378010648442

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro126027
✅ example126027
✅ express126027
✅ fastify126027
✅ hono126027
✅ nextjs-turbopack15003
✅ nextjs-webpack15003
✅ nitro126027
✅ nuxt126027
✅ sveltekit14508
✅ vite126027
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable128025
✅ express-stable128025
✅ fastify-stable128025
✅ hono-stable128025
✅ nextjs-turbopack-canary134019
✅ nextjs-turbopack-stable15300
✅ nextjs-webpack-canary134019
✅ nextjs-webpack-stable15300
✅ nitro-stable128025
✅ nuxt-stable128025
✅ sveltekit-stable14706
✅ vite-stable128025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack15300
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable128025
✅ e2e-local-dev-tanstack-start-128025
✅ e2e-local-postgres-nest-stable128025
✅ e2e-local-postgres-tanstack-start-128025
✅ e2e-local-prod-nest-stable128025
✅ e2e-local-prod-tanstack-start-128025
✅ e2e-vercel-prod-tanstack-start126027
✅ vercel-multi-region
AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

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

Adds a new optional reverse-lookup hook on the World interface to derive a workflow run’s region from its run ID, and wires it into workflow inspect output (implemented for the Vercel world).

Changes:

  • Extend @workflow/world’s World interface with optional regionForRunId?(runId: string): string | null.
  • Implement regionForRunId in @workflow/world-vercel by decoding region-tagged ULIDs (fallback to default region for legacy/unknown tags; null for malformed).
  • Update @workflow/cli inspect output to include a region column/property only when the world defines the hook, with tests and a changeset.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
packages/world/src/interfaces.tsAdds World.regionForRunId contract and documentation.
packages/world-vercel/src/index.tsExports and wires regionForRunId into the Vercel createWorld() object.
packages/world-vercel/src/create-run-id.tsImplements regionForRunId via tagged ULID decoding with safe fallback/null behavior.
packages/world-vercel/src/create-run-id.test.tsAdds unit tests for regionForRunId behavior.
packages/cli/src/lib/inspect/output.tsAdds region column/property to inspect outputs when supported by the world.
packages/cli/src/lib/inspect/output.test.tsAdds CLI test coverage for region presence/absence based on hook availability.
.changeset/inspect-region-column.mdDeclares patch bumps for the three affected packages.

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

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.test.ts Outdated

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Comment threadpackages/cli/src/lib/inspect/output.ts Outdated
Adds an optional reverse-lookup hook to the World interface —
regionForRunId(runId): string | null — so tooling can display a run's
region generically. Worlds without a regional dimension simply omit
the hook and no region output appears.
- @workflow/world: new optional interface member (documented: must not
throw; null = undeterminable)
- @workflow/world-vercel: implements it from the run-ID region tag
(tagged -> embedded region, untagged legacy -> default region,
malformed -> null)
- @workflow/cli: 'workflow inspect runs' gains a region column
(between workflowName and status) and 'workflow inspect run <id>'
a region property, in both table and JSON output — only when the
world defines the hook
Replaces regionForRunId on the World interface with describeRun, per
review: worlds may want to expose more than a region, and the
information need not be encoded in the run ID — describeRun receives
the run entity itself (loosely typed, mirroring createRunId), so a
world can derive fields from executionContext or any other property.
Each returned key becomes an inspect column/property; null values are
preserved in structured output ('applicable but undeterminable' vs.
the hook being absent entirely).
- world-vercel: describeRun returns { region } decoded from the run
ID tag (regionForRunId stays exported as a utility); entities
without a usable runId contribute nothing
- CLI listing: columns come from the union of keys the world returns
for the page, inserted before status; both analytics and storage
paths; detached call site binds this
- CLI showRun: merges the world fields into detail/JSON output via a
method-style call (preserves this), keeping nulls
- tests: field merging (multi-key), null preservation in JSON, hook
absent, and world-vercel describeRun coverage incl. no-runId
entities
Per review: the World interface says describeRun is pure and must not
throw, but it is an external extension point and the CLI should not
trust that. New safeWorldFields helper, used by both the listing and
showRun paths:
- a throwing implementation contributes no fields instead of crashing
the inspect command
- keys that already exist on the run row are dropped, so a world can
never overwrite canonical fields (status, runId, ...) in output
Tests: canonical fields survive a clobbering describeRun (extra keys
still merged); a throwing describeRun leaves rows untouched and the
command succeeds.
Per review: widening a sync signature to async later would break every
consumer, while accepting sync-or-async from day one is free — sync
implementations (like world-vercel's) remain valid, and consumers
simply await, which handles both. The performance intent lives on as
documented guidance: the hook is called once per displayed run, so
implementations should stay cheap and avoid I/O; the CLI evaluates a
page's rows concurrently so an async world costs one await per page,
not per row. Promise rejections get the same treatment as throws:
no fields, never a crash.
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

could you elaborate why describeRun is synchronous? Feels easier to make async from the get go

Fair challenge — done in c7d5a29. The sync signature was a deliberate type-enforced "no I/O in the render path" contract (the hook runs once per displayed row, and createRunId set the same precedent on the start path). But you're right about the asymmetry: widening sync→async later breaks every consumer, while accepting sync-or-async now is free — so the type is now:

describeRun?(run): Record<string,string|null>|null|Promise<Record<string,string|null>|null>;
  • Sync implementations (world-vercel's) remain valid as-is
  • The performance intent survives as documented guidance ("cheap and pure; avoid I/O — called once per displayed run"), and the CLI evaluates a page's rows with Promise.all, so an async world costs one await per page, not per row
  • Promise rejections get the same defensive treatment as throws (no fields, never a crash) — with tests for both the async-success and async-rejection paths

Comment threadpackages/world/src/interfaces.ts Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for c31e30c (AI decision).

This commit is explicitly stacked on #1981 (world-create-run-id), which is main-only: I verified that packages/world-vercel/src/create-run-id.ts does not exist on stable, the World interface has no createRunId? member there, and world-vercel/src/index.ts never wires createRunId. The new describeRun hook builds directly on that missing infrastructure (it's added adjacent to createRunId? and implemented in the absent create-run-id.ts), so it depends on main-only behavior and cannot be cleanly backported.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

c31e30caacab20c0d9c0df38349929ae1e0aebdf

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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3