[world-vercel] Add /run-id sub-export with tagged ULID encode/decode#1978
[world-vercel] Add /run-id sub-export with tagged ULID encode/decode#1978TooTallNate wants to merge 5 commits into
Conversation
Encodes a tag bit, 5-bit version, and 6-bit Vercel region ID into a ULID-shaped string used for workflow run IDs. Tagged values remain valid 26-char Crockford-Base32 ULIDs so they still sort and round-trip through any system that accepts ULIDs.
🦋 Changeset detectedLatest commit: d077ad1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
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 |
🧪 E2E Test Results✅ All tests passed Summary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
❌ Some E2E test jobs failed:
Check the workflow run for details. |
📊 Benchmark Results
workflow with no steps💻 Local Development
workflow with 1 step💻 Local Development
workflow with 10 sequential steps💻 Local Development
workflow with 25 sequential steps💻 Local Development
workflow with 50 sequential steps💻 Local Development
Promise.all with 10 concurrent steps💻 Local Development
Promise.all with 25 concurrent steps💻 Local Development
Promise.all with 50 concurrent steps💻 Local Development
Promise.race with 10 concurrent steps💻 Local Development
Promise.race with 25 concurrent steps💻 Local Development
Promise.race with 50 concurrent steps💻 Local Development
workflow with 10 sequential data payload steps (10KB)💻 Local Development
workflow with 25 sequential data payload steps (10KB)💻 Local Development
workflow with 50 sequential data payload steps (10KB)💻 Local Development
workflow with 10 concurrent data payload steps (10KB)💻 Local Development
workflow with 25 concurrent data payload steps (10KB)💻 Local Development
workflow with 50 concurrent data payload steps (10KB)💻 Local Development
Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
stream pipeline with 5 transform steps (1MB)💻 Local Development
10 parallel streams (1MB each)💻 Local Development
fan-out fan-in 10 streams (1MB each)💻 Local Development
SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
❌ Some benchmark jobs failed:
Check the workflow run for details. |
Add exact-string expectations for encoded outputs at known inputs, covering the default region/version pair, numeric region IDs, version overrides, boundary values (all-zero, all-max), the dirty-input overwrite case, and the lexicographic-order checks. Also adds an explicit byte-array expectation for the canonical ULID-spec example string and an additional first-char-range coverage test for isTagged.
There was a problem hiding this comment.
Pull request overview
This PR introduces a new @workflow/world-vercel/run-id sub-export that can encode/decode tagged ULID-shaped workflow run IDs, embedding a tag bit, a 5-bit version, and a 6-bit Vercel region ID while preserving ULID sortability.
Changes:
- Add a region table (
REGION_IDS) and helpers for mapping between region codes and 6-bit region IDs. - Implement a Crockford-Base32 ↔ 16-byte codec and public
encode/decode/isTaggedAPI for tagged ULIDs. - Add tests for codec correctness and encode/decode behavior; expose the new subpath export via
package.jsonand add 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
| File | Description |
|---|---|
| packages/world-vercel/src/run-id/regions.ts | Adds stable region↔ID mapping and lookup helpers used by tagged run IDs. |
| packages/world-vercel/src/run-id/index.ts | Public API for encoding/decoding tagged ULIDs + re-exports/constants. |
| packages/world-vercel/src/run-id/index.test.ts | Tests for encode/decode semantics, validation, and ordering properties. |
| packages/world-vercel/src/run-id/codec.ts | Implements ULID base32 packing/unpacking and tag-bit detection helper. |
| packages/world-vercel/src/run-id/codec.test.ts | Tests for codec round-trips, validation, and tag-bit detection. |
| packages/world-vercel/package.json | Exposes ./run-id as a public package sub-export. |
| .changeset/tagged-run-id.md | Declares a minor release for the new sub-export/API surface. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
karthikscale3
left a comment
There was a problem hiding this comment.
Reviewed for blockers/regressions — none found. Change is purely additive (new sub-export, nothing in the repo consumes it yet). Three non-blocking nits below.
| * relevant. | ||
| * | ||
| * Tagged ULIDs remain valid ULIDs (lexicographically sortable, monotonic when | ||
| * generated with a monotonic factory), so they can flow through any system |
There was a problem hiding this comment.
The "monotonic when generated with a monotonic factory" claim is misleading. ulid's monotonicFactory() preserves intra-millisecond order by incrementing the bottom of the 80-bit randomness section — i.e. exactly the bits encode overwrites with the metadata. Two consecutive monotonic ULIDs in the same ms, tagged with the same (region, version), will collide (or worse, flip order if the carry only reaches into the bottom 11 bits but not above).
Suggest softening to something like: "Tagged ULIDs preserve lexicographic order across millisecond boundaries; intra-millisecond monotonicity is not preserved because the bottom 11 randomness bits are overwritten by the metadata."
Not a blocker — but a foot-gun for the eventual consumer if they wrap monotonicFactory() with encode.
| /** Encoded format version (0..31). */ | ||
| version: number; | ||
| /** Encoded region ID (0..63). 0 represents "unknown". */ | ||
| regionId: number; |
There was a problem hiding this comment.
When tagged === false, regionId and version are populated from whatever bits happen to sit in those positions of an arbitrary ULID — i.e. random garbage. The docstring on tagged above says callers should "generally ignore them," but it's an easy footgun to read decoded.region without first checking decoded.tagged.
Consider returning regionId: null, version: null, region: null when tagged === false so the type system forces the check. Not a correctness issue — just ergonomics.
| */ | ||
| export function regionIdFor(code: RegionCode): RegionId { | ||
| const id = REGION_IDS[code]; | ||
| if (id === undefined) { |
There was a problem hiding this comment.
This branch is unreachable: RegionCode = Exclude<RegionKey, 'unknown'>, and every non-'unknown' key of REGION_IDS maps to a defined number. At runtime, callers passing a stray string from outside TS will get undefined here and hit this throw, so it's not dead defensively — but the docstring says "Throws if the code is not recognized," which TS already prevents.
Fine to leave as a runtime backstop; just flagging that c8 will likely show this as uncovered.
Summary
Adds a new sub-export
@workflow/world-vercel/run-idexposingencodeanddecodehelpers that produce ULID-shaped workflow run IDs carrying:4..7.1;0is reserved as a "no metadata" sentinel.iad1,fra1,sfo1).Tagged values remain valid 26-char Crockford-Base32 ULIDs, so they continue to sort lexicographically and flow through any system that accepts ULIDs.
Bit layout
Randomness: 80 bits → 69 bits preserved (~5.9 × 10²⁰ values per ms).
Region table
Covers the 21 currently-deployed Vercel compute regions plus
hel1andzrh1, which are reserved for future rollout.0=unknownsentinel.IDs are stable and append-only — never reorder or reuse, since they're encoded on the wire in every run ID emitted.
API
decodeclears only the tag bit on the returnedulidfield — the 11 metadata bits remain in place so the encoded info is recoverable from the "cleared" value too.Verification
pnpm vitest run src/run-id/→ 34/34 pass (includes exact-string assertions for all encoded outputs)pnpm typecheck→ cleanpnpm build→ emitsdist/run-id/{index,codec,regions}.{js,d.ts,...}Files
packages/world-vercel/src/run-id/regions.ts— region table +lookupRegion/regionIdForpackages/world-vercel/src/run-id/codec.ts— Crockford-Base32 ↔ 16-byte bit-packingpackages/world-vercel/src/run-id/index.ts— publicencode/decode/isTaggedAPIpackages/world-vercel/src/run-id/codec.test.ts(13 tests) +index.test.ts(21 tests)packages/world-vercel/package.json—./run-idadded toexports.changeset/tagged-run-id.md— minor bump for@workflow/world-vercel