Uh oh!
There was an error while loading. Please reload this page.
workflow: read spec 7 and skip sealed noops - #319
Merged
Conversation
`@workflow/world-local` and `@workflow/world-vercel` both stamp specVersion 7 now (vercel/workflow#3634), so every event a TypeScript driver writes arrives labelled 7 and our reader ceiling of 6 rejected the whole log -- including on the local world the Python e2e lane runs on. Raise the ceiling, and add the one thing the version gates: a `noop` row, written by a World's backend to occupy a slot whose writer allocated the position and then died. A noop parses out of the read union but is dropped from the log the replay walks. Its `createdAt` is the sealer's wall clock, which can postdate every real event around it, so letting it reach the deterministic clock would make a log whose hole was sealed replay differently from the same log whose hole its own writer filled. Nothing here writes one: we allocate each event id at the commit that fills it, so we have no holes to seal, and `noop` stays out of the create union. Co-Authored-By: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
fantix
reviewed
Aug 24, 2026
fantix
left a comment
Member
There was a problem hiding this comment.
LGTM; let me trim off some AI in-code chats and merge this to unbreak the e2e test.
fantix
approved these changes
Aug 24, 2026
fantixforce-pushed
the
pgp/port-noop-event-to-py
branch
from
August 24, 2026 18:44
3116c4e to
abb6ce9CompareUh oh!
There was an error while loading. Please reload this page.
scotttrinh added a commit
that referenced
this pull request
Aug 26, 2026
vercel-internal-core -------------------- 0.1.3 - 2026-08-26 ------------------ Internal -------- - Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307) vercel-connect -------------- 0.1.1 - 2026-08-26 ------------------ - Update dependencies. vercel-queue ------------ 0.8.1 - 2026-08-26 ------------------ Documentation ------------- - Remove documentation and examples for `asgi_app` in preparation for its removal. (#309) vercel-sandbox -------------- 0.5.0 - 2026-08-26 ------------------ Features -------- - Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257) - Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308) Bug Fixes --------- - Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307) vercel-workflow --------------- 0.10.0 - 2026-08-26 ------------------- Breaking Changes ---------------- - Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317) - This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317) - Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317) Features -------- - `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320) - One current limitation is that `started_at` is `None` from inside a step. (#320) - `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301) - A step can raise `RetryableError` to control when its next attempt runs. (#302) - Accept `specVersion` 7 sealed noop event logs. (#319) - A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303) - Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310) - Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294) - Added semi-internal manifest API for TS tools and e2e test. (#296) Bug Fixes --------- - Fix failing or even crashing cipher calls inside the workflow sandbox. (#305) - Support resuming hooks with payload in the queue message. (#300) - Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321) - Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312) Internal -------- - Construct the protocol models by Python field name. (#322) vercel ------ 0.11.0 - 2026-08-26 ------------------- Features -------- - Expose `get_deadline()` for reading the current Function invocation deadline. (#306) - Answer workflow health checks for both queue-based transport and HTTP. (#292) - Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297) Bug Fixes --------- - Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334) - Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284) Internal -------- - The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299) vercel-apscheduler ------------------ 0.3.0 - 2026-08-26 ------------------ Breaking Changes ---------------- - The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286) vercel-celery ------------- 0.7.5 - 2026-08-26 ------------------ - Update dependencies. vercel-django-tasks ------------------- 0.7.0 - 2026-08-26 ------------------ Features -------- - Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291) vercel-dramatiq --------------- 0.7.4 - 2026-08-26 ------------------ - Update dependencies.
scotttrinh added a commit
that referenced
this pull request
Aug 31, 2026
vercel-headers -------------- 0.7.2 - 2026-08-31 ------------------ Bug Fixes --------- - Accept request objects with concrete header implementations in the IP address and geolocation type annotations. (#337) vercel-internal-core -------------------- 0.1.3 - 2026-08-31 ------------------ Internal -------- - Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307) vercel-oidc ----------- 0.8.1 - 2026-08-31 ------------------ - Update dependencies. vercel-connect -------------- 0.1.1 - 2026-08-31 ------------------ - Update dependencies. vercel-internal-telemetry ------------------------- 0.7.3 - 2026-08-31 ------------------ - Update dependencies. vercel-queue ------------ 0.8.1 - 2026-08-31 ------------------ Bug Fixes --------- - Force embedded development servers to exit when graceful shutdown stalls. (#351) Documentation ------------- - Remove documentation and examples for `asgi_app` in preparation for its removal. (#309) vercel-sandbox -------------- 0.5.0 - 2026-08-31 ------------------ Features -------- - Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257) - Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308) - Forward private ``__``-prefixed parameters to the Sandbox API. (#350) Bug Fixes --------- - Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307) - Expose Linux process signals consistently on every SDK host platform. (#352) vercel-cache ------------ 0.7.3 - 2026-08-31 ------------------ - Update dependencies. vercel-workflow --------------- 0.10.0 - 2026-08-31 ------------------- Breaking Changes ---------------- - Make sleep() and retry delays treat numbers as seconds, not ms (#346) - This matches Python standard library APIs. (#346) - Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317) - This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317) - Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317) Features -------- - Support `call_later`, `call_at`, and `now` in the event loop implementation. (#343) - This enables use of `asyncio.sleep()` as well as `asyncio.timeout` and the `timeout` parameter of `asyncio.wait_for`. (#343) - `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320) - One current limitation is that `started_at` is `None` from inside a step. (#320) - `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301) - A step can raise `RetryableError` to control when its next attempt runs. (#302) - Accept `specVersion` 7 sealed noop event logs. (#319) - Failed run and step events now preserve serialized error classes, messages, stacks, and causes. Failed runs also expose a plaintext `errorCode`. (#304) - A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303) - Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310) - Support `timedelta` arguments for workflow `sleep()` and retry delays. (#342) - Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294) - Added semi-internal manifest API for TS tools and e2e test. (#296) Bug Fixes --------- - Fix failing or even crashing cipher calls inside the workflow sandbox. (#305) - Fail a workflow run with `HookConflictError` when another run already owns its hook token instead of leaving it running indefinitely. (#327) - Support resuming hooks with payload in the queue message. (#300) - Fix some bugs involving hooks arriving when the workflow was not yet blocked on them. (#339) - Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321) - Prevent workflows from having side effects while suspending. (#332) - `hook.dispose()` will now work properly in a `finally` block. (That is, the hook will be disposed only when the workflow is actually terminating, and not every time it gets replayed.) (#332) - More reliably fail runs whose replay diverges from the event log. (#347) - Runs will now fail even in the case where the main thread of execution is not directly blocked on the suspension that is erroring. (#347) - Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312) Internal -------- - Remove a just-added return from a finally block. (#344) - Correct internal workflow type annotations found by checking untyped function bodies. (#337) - Refactored event replay. (#341) - Construct the protocol models by Python field name. (#322) vercel ------ 0.11.0 - 2026-08-31 ------------------- Features -------- - Expose `get_deadline()` for reading the current Function invocation deadline. (#306) - Answer workflow health checks for both queue-based transport and HTTP. (#292) - Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297) Bug Fixes --------- - Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334) - Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284) Internal -------- - The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299) vercel-apscheduler ------------------ 0.3.0 - 2026-08-31 ------------------ Breaking Changes ---------------- - The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286) vercel-celery ------------- 0.7.5 - 2026-08-31 ------------------ - Update dependencies. vercel-django-tasks ------------------- 0.7.0 - 2026-08-31 ------------------ Features -------- - Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291) vercel-dramatiq --------------- 0.7.4 - 2026-08-31 ------------------ - Update dependencies.
scotttrinh added a commit
that referenced
this pull request
Aug 31, 2026
vercel-headers -------------- 0.7.2 - 2026-08-31 ------------------ Bug Fixes --------- - Accept request objects with concrete header implementations in the IP address and geolocation type annotations. (#337) vercel-internal-core -------------------- 0.1.3 - 2026-08-31 ------------------ Internal -------- - Support disabling HTTP timeouts for selected SDK operations while preserving the client default elsewhere. (#307) vercel-oidc ----------- 0.8.1 - 2026-08-31 ------------------ - Update dependencies. vercel-connect -------------- 0.1.1 - 2026-08-31 ------------------ - Update dependencies. vercel-internal-telemetry ------------------------- 0.7.3 - 2026-08-31 ------------------ - Update dependencies. vercel-queue ------------ 0.8.1 - 2026-08-31 ------------------ Bug Fixes --------- - Force embedded development servers to exit when graceful shutdown stalls. (#351) Documentation ------------- - Remove documentation and examples for `asgi_app` in preparation for its removal. (#309) vercel-sandbox -------------- 0.5.0 - 2026-08-31 ------------------ Features -------- - Add sync and async `fork_sandbox(...)` support for creating a sandbox from an existing named sandbox with optional configuration overrides. (#257) - Add `region` and `failover_regions` configuration for sandbox creation, forks, and updates, plus multi-region snapshot availability reporting. (#308) - Forward private ``__``-prefixed parameters to the Sandbox API. (#350) Bug Fixes --------- - Allow Sandbox process waits and log streams to remain idle longer than the session HTTP timeout. (#307) - Expose Linux process signals consistently on every SDK host platform. (#352) vercel-cache ------------ 0.7.3 - 2026-08-31 ------------------ - Update dependencies. vercel-workflow --------------- 0.10.0 - 2026-08-31 ------------------- Breaking Changes ---------------- - Make `await hook` never return `None` - Raises a new `HookDisposedError` instead of returning `None` when the hook has been disposed. It is now typed to return `T` instead of `T | None`. `async for` over a hook will stop iterating on disposal, still. - Make sleep() and retry delays treat numbers as seconds, not ms (#346) - This matches Python standard library APIs. (#346) - Use type annotations on workflows and step to allow passing Pydantic models and dataclasses. (#317) - This is a breaking change, because type annotations will now be enforced. Passing a `dict` when the declaration expects a `list` will fail. (#317) - Pydantic models and dataclasses can no longer be passed to `@serializable` or `register_serializable()`. Annotate the workflow or step parameter or return value with their type instead. (#317) Features -------- - Support `call_later`, `call_at`, and `now` in the event loop implementation. (#343) - This enables use of `asyncio.sleep()` as well as `asyncio.timeout` and the `timeout` parameter of `asyncio.wait_for`. (#343) - `get_workflow_metadata()` returns the current run's `WorkflowInfo` (run id, workflow name, start time, deployment URL, and feature flags), callable from a workflow body or a step body — mirroring the JS SDK's `getWorkflowMetadata()`. (#320) - One current limitation is that `started_at` is `None` from inside a step. (#320) - Make `HookEvent` an async context manager - This matches TS, which supports `using`. ``` # disposes the hook on block exit async with SomeHook.wait(...) as hook: res = await hook ``` - `BaseHook.wait()` accepts `metadata` to record on the hook, and `get_hook_by_token()` reads it back for a resumer. (#301) - A step can raise `RetryableError` to control when its next attempt runs. (#302) - Accept `specVersion` 7 sealed noop event logs. (#319) - Failed run and step events now preserve serialized error classes, messages, stacks, and causes. Failed runs also expose a plaintext `errorCode`. (#304) - A workflow or step can attach plaintext metadata to its run with `set_attributes()`. (#303) - Add a `share_sandboxes` parameter to `SandboxPolicy` to enable reusing already created sandboxes instead of creating a new one on each invocation. This speeds up workflows but means that modifications to global state may persist between invocations. (#310) - Support `timedelta` arguments for workflow `sleep()` and retry delays. (#342) - Expose unstable API to serve workflow HTTP endpoint from your own web framework. (#294) - Added semi-internal manifest API for TS tools and e2e test. (#296) Bug Fixes --------- - Fix failing or even crashing cipher calls inside the workflow sandbox. (#305) - Fail a workflow run with `HookConflictError` when another run already owns its hook token instead of leaving it running indefinitely. (#327) - Support resuming hooks with payload in the queue message. (#300) - Fix some bugs involving hooks arriving when the workflow was not yet blocked on them. (#339) - Fixed nulls rejected by server, requiring Pydantic 2.12 or newer. (#321) - Prevent workflows from having side effects while suspending. (#332) - `hook.dispose()` will now work properly in a `finally` block. (That is, the hook will be disposed only when the workflow is actually terminating, and not every time it gets replayed.) (#332) - More reliably fail runs whose replay diverges from the event log. (#347) - Runs will now fail even in the case where the main thread of execution is not directly blocked on the suspension that is erroring. (#347) - Fixed workflow and step calls with both positional-or-keyword parameters and `*args` failing during replay because their arguments were recorded in an unbindable shape. (#312) Internal -------- - Remove a just-added return from a finally block. (#344) - Correct internal workflow type annotations found by checking untyped function bodies. (#337) - Refactored event replay. (#341) - Construct the protocol models by Python field name. (#322) vercel ------ 0.11.0 - 2026-08-31 ------------------- Features -------- - Expose `get_deadline()` for reading the current Function invocation deadline. (#306) - Answer workflow health checks for both queue-based transport and HTTP. (#292) - Add support to read the sealed (`encp`) workflow payloads (X25519 + AES-GCM) an outside writer addresses to a run, under the `encryption` extra. (#297) Bug Fixes --------- - Remove upper bounds on aggregate Sandbox and Workflow dependencies so sibling releases cannot make the `vercel` package un-installable. (#334) - Start a workflow run even when its queue message arrives before the `run_created` event has landed. (#284) Internal -------- - The Workflows implementation now ships in the separate `vercel-workflow` distribution, which `vercel` depends on, so `vercel.workflow` imports keep working without installing anything extra. (#299) vercel-apscheduler ------------------ 0.3.0 - 2026-08-31 ------------------ Breaking Changes ---------------- - The managed Redis backend was removed. The integration now always runs on its managed job store (Vercel Runtime Cache); a configured default `RedisJobStore` is rejected at import, `VERCEL_APSCHEDULER_BACKEND` accepts only `cache`, and the `redis` dependency is gone. The scheduler's durable identity now always derives from the builder-assigned subscriber id (previously the Redis `jobs_key`); the `scheduler_id` option still pins an identity explicitly. (#286) vercel-celery ------------- 0.7.5 - 2026-08-31 ------------------ - Update dependencies. vercel-django-tasks ------------------- 0.7.0 - 2026-08-31 ------------------ Features -------- - Add a Vercel Queues backend for Django Tasks and use it by default when no task backends are configured. (#291) vercel-dramatiq --------------- 0.7.4 - 2026-08-31 ------------------ - Update dependencies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Python half of vercel/workflow#3634 (Add support for 'noop' event type — spec version 7).
Why this is urgent for us
That PR moves
SPEC_VERSION_CURRENTandSPEC_VERSION_MAX_SUPPORTEDto 7 and switches both@workflow/world-localand@workflow/world-verceltomintedSpecVersion(), which returns 7 by default. Our reader ceiling was 6, and it is a pydanticle=onBaseEvent.specVersion— so a spec-7 row does not degrade, it fails validation, and with it the whole page. Thee2e-pythonconformance lane runs the TypeScript driver againstworkbench/pythonover a sharedworld-localdata dir, so it is the first thing that breaks: the driver'srun_createdis now labelled 7 and the Python app cannot parse the log it is supposed to replay.What spec 7 actually asks of a client
On the server, a spec-7 run's slot positions come from a per-run counter handed out before the write that fills them, so concurrent writers never race for a position. The price is that a writer that takes a position and dies leaves a hole, and the backend's read path fills provably-abandoned holes with a server-written
noopso returned pages stay dense prefixes.So the client contract is one thing, and it is read-only: a reader must know a
noopoccupies its slot and means nothing.There is no writer half for us. A World only owes the sealing half if it pre-assigns positions ahead of the commit; our local world allocates each id at the commit that fills it, so it has no holes to seal and will never emit one.
The change
world.pySPEC_VERSION_SUPPORTS_SEALED_LOG = 7, andSPEC_VERSION_MAX_SUPPORTEDfollows it.SPEC_VERSION_CURRENTstays 2 — we still stamp what we always stamped, and a run created elsewhere keeps its creator's version.NoopEvent/NoopEventData, in the read union and deliberately not inCreateEventRequest.eventDatais optional and open (extra="allow", matching the.passthrough()on the TS schema): nothing reads it, the shape belongs to whichever backend sealed the slot, and a reader whose only interest is skipping the row has no business rejecting a field it has not heard of.is_sealed_noop_event(), mirroringisSealedNoopEvent— one home for the test.runtime.py—get_all_workflow_run_events()drops seals, so the log the replay walks is the log without them.That is the whole skip, and the reason it is one line at the loader rather than a
continueinresume()is the clock. Everything downstream of that function is reconstructing what the run did — matching correlation ids to suspensions, looking for a terminal event, deciding which waits elapsed, and readingcreatedAtfornow(). Nothing on this side walks positions, so there is no caller that wants the seal. Meanwhile a seal'screatedAtis the sealer's wall clock, which can postdate every real event around it;now()dates the run from the last event the replay consumed, so a seal left in the list hands the body a time no event of the run happened at — and then, because the clock only moves forward, every laternow()too. Dropping it at the boundary makes the equivalence the contract is about (a sealed log replays exactly like the same log whose holes their own writers filled) hold by construction, rather than by three call sites remembering. The cursor is untouched: it is the World's, and still points past every row that was read.Tests
tests/unit/test_workflow_sealed_log_noop.py, 12 tests in four parts:eventData, without it, and with aneventDatafield we have never heard of; the predicate answers for seals only;NoopEventis inEventand absent fromCreateEventRequest; 7 is the ceiling and 8 is still rejected.slot-identity.test.ts'sstores, lists, and numbers past a noop event.workflow_handler: one recorded two-sleep run, twice, differing only in seals at the head, in the middle (including one splitting await_created/wait_completedpair), and at the tail. Same output, same events written back, and the threenow()readings pinned literally so a change that broke both logs the same way cannot slip past the comparison. Plus the degenerate all-seals-before-the-run shape, where the firstnow()has to find the first real event.Every seal in the file is stamped a minute into the future, so each of those clock assertions would pass by accident if a seal's
createdAtwere plausible. Reverting the one-line filter fails 5 of the 12.test_workflow_local_world_format.py's two spec-version tests move 6 → 7; the ceiling assertion now derives its match fromSPEC_VERSION_MAX_SUPPORTEDinstead of hardcoding the number, so the next bump only has to touch the constant.Full suite: 1127 tests, 0 failures (
test_the_api_token_is_config_then_env_then_oidcfails locally on a box withVERCEL_TOKENexported, before and after this change). Lint and typecheck clean repo-wide.Follow-up, not in this PR
workbench/python/pyproject.tomlinvercel/workflowresolvesvercel-workflowfrom PyPI (0.9.0 today) regardless of its[tool.uv.sources]rev, so thee2e-pythonlane only picks this up once it is released and the lock is refreshed there — or once that file git-pinsvercel-workflowat a commit containing it, which is what the comment block above that source entry describes.🤖 Generated with Claude Code