Skip to content

feat(dev): own the local Workflow World in the CLI parent process#790

Open
ctgowrie wants to merge 10 commits into
feat/dev2-rebuildsfrom
feat/dev2-world
Open

feat(dev): own the local Workflow World in the CLI parent process#790
ctgowrie wants to merge 10 commits into
feat/dev2-rebuildsfrom
feat/dev2-world

Conversation

@ctgowrie

Copy link
Copy Markdown
Collaborator

Part of the stock-Nitro dev server route (stacked on #787, which is stacked on #782, based off #759).

What

Moves the default local Workflow World out of the dev worker and into the CLI parent process, so child Workflow runs survive worker replacement without any bespoke transport:

  • The parent instantiates the vendored @workflow/world-local once and serves it to workers over an RPC codec on the public listener, guarded by a per-process secret (EVE_DEV_WORKFLOW_TRANSPORT_SECRET). Explicitly configured Worlds stay inside the worker, untouched.
  • Queue deliveries flow through the public listener to the active worker like any other request — no interception. The worker resolves the delivery's generation server-side from run records (runs.getdeploymentId), then loads that generation's modules from its generation.json.
  • Turns straddling a structural edit keep their generation's authored modules but execute in the current worker, so they observe current instrumentation (deliberate contract change; the two Stage-5 scenario assertions are flipped with inline notes).
  • Generation pruning consults the World for runs still in flight and retains only generations owned by active work (plus the pointer target); when the World is externally configured, pruning protects everything.

Tests

  • development-world-server.integration.test.ts rewritten as two-sided in-memory tests: a real client queue handler wired to the real parent World via a patched fetch, covering pin-until-terminal, driver-to-active routing, untrusted-delivery rejection (401), and missing-generation startup failure.
  • The two generation-pinning dev-server scenario tests ported from the previous stack run green against the real dev server, including crash-restart recovery.
  • start-development-server.ts split (dev-workflow-world-setup.ts, dev-server-url.ts) to stay under the file-length cap.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
eve-docs Ready Ready Preview, Comment Jul 14, 2026 10:44pm
eve-docs-1644 Ready Ready Preview, Comment, Open in v0 Jul 14, 2026 10:44pm
eve-docs-4759 Ready Ready Preview, Comment, Open in v0 Jul 14, 2026 10:44pm

Request Review

compiledArtifactsBootstrapPath: string;
configuredWorld: AgentWorkflowWorldDefinition | undefined;
}): string {
const packageName = getWorldImport({ WORKFLOW_TARGET_WORLD: input.configuredWorld ?? "local" });

@vercel vercel Bot Jul 14, 2026

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.

Explicit world: "local" makes the dev worker use the parent-backed RPC World client even though the parent never creates a World to serve those requests, so workflow world calls fail in dev.

Fix on Vercel

@vercel vercel Bot temporarily deployed to Preview – eve-docs-1644 July 14, 2026 18:25 Inactive
@vercel vercel Bot temporarily deployed to Preview – eve-docs-4759 July 14, 2026 18:25 Inactive
@ctgowrie ctgowrie force-pushed the feat/dev2-rebuilds branch from a69e44e to ad41594 Compare July 14, 2026 18:47
@ctgowrie ctgowrie force-pushed the feat/dev2-rebuilds branch from ad41594 to 2eec894 Compare July 14, 2026 18:49
@vercel vercel Bot temporarily deployed to Preview – eve-docs-1644 July 14, 2026 18:49 Inactive
@vercel vercel Bot temporarily deployed to Preview – eve-docs-4759 July 14, 2026 18:49 Inactive
@ctgowrie ctgowrie force-pushed the feat/dev2-rebuilds branch from 2eec894 to 98c9d48 Compare July 14, 2026 19:15
@vercel vercel Bot temporarily deployed to Preview – eve-docs-4759 July 14, 2026 19:15 Inactive
@vercel vercel Bot temporarily deployed to Preview – eve-docs-1644 July 14, 2026 19:15 Inactive
@ctgowrie ctgowrie force-pushed the feat/dev2-rebuilds branch from 98c9d48 to 51ba184 Compare July 14, 2026 19:19
ctgowrie and others added 10 commits July 14, 2026 18:33
Instantiate the vendored local World once in the dev CLI parent so child
Workflow runs survive worker replacement. Workers reach the World through
an RPC codec over the public listener guarded by a per-process secret, and
queue deliveries resolve their generation server-side from run records —
turns keep their generation's authored modules while executing in the
current worker. Generation pruning now retains only generations still
referenced by active Workflow work.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
The World begins redelivering persisted queue messages the moment it
starts, but the parent registered its control handler only after the
authored-source watcher was up. Deliveries in that window reached the
worker, whose World calls back to the parent fell through to the worker
app and 404ed. Register the control handler before the listener binds,
late-binding the watcher; rebuild requests before the watcher exists
answer 503.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
The generated worker plugin selects the parent RPC World client whenever
the configured world resolves to the vendored local package — including an
explicit `world: "local"` — but the parent only created its World when the
config was absent, so explicitly-local apps failed every World call in
dev. Share one predicate between the parent and the generated plugin so
the two sides cannot disagree.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
Document the intent behind the parent-owned World and its worker shim: the
World's lifetime must match run data rather than the disposable worker, the
already-async World interface is what makes an interface-faithful HTTP
client transparent to the runtime, deliveries ride the public listener so
drained replacement covers them, and the worker plugin's world selection
shares one predicate with the parent. Also document the rebuild
coordinator's worker-first commit ordering and the codec's reason to exist.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
Move the parent World's run store from <appRoot>/.workflow-data to
<appRoot>/.eve/workflow-data so eve-owned development state lives in one
directory. The top-level path remains gitignored for apps that configure
the stock local World explicitly, which still uses its own default.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
- Requests forwarded to the worker carry the accepted socket's peer
  address as parent-signed metadata; forged public copies are stripped,
  and channel context.requestIp resolves the verified value instead of
  the proxy's loopback hop.
- A delivery whose recorded generation no longer exists is acknowledged
  and dropped with an explicit error instead of redelivering for the full
  retry budget, and boot quarantines such runs instead of refusing to
  start over one poisoned record. Recently enqueued generations are
  protected from pruning for a horizon so a delivery that has not yet
  persisted its run record cannot lose its snapshot.
- Transport comparisons are timing-safe, an unusably short secret is
  rejected at construction, generation ids reject dot segments, and the
  direct-handler gate shares the same world predicate as the parent and
  the generated plugin.
- Paused pruning (protect-all) is reported once per cause instead of
  silently retaining every generation, boot failures reading run records
  carry the workflow-data hint, and stream-route errors serialize like
  every other World operation.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
Replace the client's hand-written method forwards and the server's
enumerated dispatch switch with generic forwarding driven by a single
DEVELOPMENT_WORLD_OPERATIONS table in the protocol module. Adapting to a
vendored-world interface change becomes one table entry, the two sides
cannot drift, and the server validates calls against the same allowlist.
The deliberate exceptions stay explicit: deployment identity and queue
enqueueing carry eve semantics, streams.get rides its own route, and
createQueueHandler runs entirely in the worker.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
Durable Workflow payloads serialized every dev disk source as a logical
generation selector keyed off EVE_DEV alone, but only parent-owned World
deliveries install the context that resolves the selector — a custom
World's first execution threw resolving its own payload. The durable
strategy is now an explicit property of the artifact source, derived from
the same predicate that decides World placement: parent-owned Worlds
store logical selectors, custom Worlds store the exact snapshot path and
replay without any eve-specific delivery context, as before the parent
World existed. Pruning already protects every generation when the World
is opaque, so pinned paths cannot dangle within a session.

Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Casey Gowrie <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants