Skip to content

feat: dynamic actors#4397

Merged
MasterPtato merged 1 commit intomainfrom
03-08-feat_rivetkit_dynamic_actors
Apr 8, 2026
Merged

feat: dynamic actors#4397
MasterPtato merged 1 commit intomainfrom
03-08-feat_rivetkit_dynamic_actors

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Summary

  • Dynamic actors: load and run actor code at runtime from arbitrary sources
  • Hibernatable WebSocket improvements
  • Engine driver enhancements

Test plan

  • Existing driver test suite passes
  • pkg.pr.new publishes successfully

🤖 Generated with Claude Code

@claude
Copy link
Copy Markdown

claude bot commented Mar 10, 2026

PR Review: feat: dynamic actors

This is a large, high-quality feature PR introducing dynamic actor support, a hibernatable WebSocket ack-state system, runner WebSocket stale-message guarding, and miscellaneous fixes. The architecture is well-documented and the design is sound overall. Several issues follow, ranging from a likely type-error bug to code quality and convention violations.


Bugs / Issues

1. this.config.runtimeConfig does not exist -- likely TypeScript type error

In isolate-runtime.ts, setIndexedWebSocketTestSender is called with this.config.runtimeConfig.testEnabled, but DynamicActorIsolateRuntimeConfig does not have a runtimeConfig field. This will either cause a TS compile error or silently evaluate testEnabled as undefined (falsy), permanently disabling test hooks for dynamic actors.

2. Typo: __unsafeIsoalte (should be __unsafeIsolate)

The interface property and its call site both spell it Isoalte. If this does not match the upstream secure-exec API spelling, it will fail at runtime. Needs verification against the upstream API and a comment if it is intentionally misspelled to match upstream.

3. console.error leftover in production code

In inline-websocket-adapter.ts, the handleError method calls both console.error and logger. All logging must go through logger per project conventions -- the console.error call should be removed.

4. Duplicate rivetkit/errors path in tsconfig.json

The paths section contains two identical rivetkit/errors entries. JSON does not error on duplicates but TypeScript behavior is non-deterministic -- the duplicate line should be removed.

5. RIVET_EXPOSE_ERRORS hardcoded to 1 in sandbox config

The sandbox process config unconditionally sets RIVET_EXPOSE_ERRORS to 1, meaning dynamic actors in production always expose full internal error details to clients. This should be tied to the host's getRequestExposeInternalError() value rather than hardcoded.

6. No size limit on dynamic actor source

DynamicActorLoadResult.source has no enforced size limit before being written to the sandbox filesystem. An unbounded source string could exhaust memory or disk. A configurable maxSourceBytes should be added to DynamicActorConfigInput and validated in normalizeLoadResult.


Code Quality and Convention Issues

7. isStaticActorInstance duck-typing fallback is risky

The function first checks instanceof ActorInstance (correct), then falls back to duck-typing multiple method names. If a DynamicActorInstance is ever extended with these methods, it would be misidentified as static. The duck-typing branch should be removed since ActorInstance is the only static implementation.

8. Module-level singleton promises for dynamic loading lack documentation

The three module-level singletons (dynamicRuntimeModuleAccessCwdPromise, secureExecModulePromise, isolatedVmModulePromise) in isolate-runtime.ts are cached for the process lifetime. This means RIVETKIT_DYNAMIC_SECURE_EXEC_SPECIFIER set after first call is silently ignored. A comment explaining why these are process-level singletons would help future maintainers.

9. readFileSync in async context

resolveEsmPackageEntry() uses readFileSync inside an async function. Since this is already inside a promise chain, readFile (async) would be more consistent and avoids blocking the event loop during first-use resolution.

10. DYNAMIC_SANDBOX_APP_ROOT hardcoded to /root

The path /root is hardcoded as the sandbox app root and also injected as HOME. If the sandbox VFS root mapping changes, this will silently break. Consider making it a named constant with a comment explaining the sandbox VFS root contract.

11. DynamicSourceFormat missing typescript value

The architecture doc references a default sourceFormat of typescript triggering transpilation, but DynamicSourceFormat only defines commonjs-js or esm-js. The JSDoc says Defaults to esm-js. This three-way inconsistency between the architecture doc, JSDoc, and type definition should be resolved.


Architecture and Design Observations

12. Vercel equivalents not generated for new examples

CLAUDE.md states that when adding new examples, the Vercel equivalent must also be updated. The examples/dynamic-actors/ and examples/ai-generated-actor/ directories are new but no Vercel equivalents appear in the diff, and neither has skipVercel set to true in its package.json template object. Either run the Vercel example generator script or add skipVercel: true with a reason.

13. scripts/ralph/prd.json committed to the repo

A 365-line PRD artifact from the ralph agent workflow is committed at scripts/ralph/prd.json. Per CLAUDE.md, planning artifacts belong in .agent/specs/, not scripts/ralph/. If this is intentional documentation, it should be moved there.


Test Coverage

14. dynamic-reload.ts uses a fixed 250ms sleep

A fixed 250ms delay after actor.reload() is timing-dependent and prone to flakiness in slow CI. Polling startCount with a timeout using vi.waitFor would be more reliable.

15. waitForJsonMessage and waitForMatchingJsonMessages duplicated across test files

These helpers are copy-pasted verbatim from raw-websocket.ts into hibernatable-websocket-protocol.ts. They should be extracted into a shared utility file to avoid drift.

16. Dynamic engine test suite skips are broad

driver-engine-dynamic.test.ts skips the entire suite when hasSecureExecDist is false. A comment clarifying the CI expectation and whether the static driver suite covers parity would be helpful.


Minor Issues

  • dynamic-isolate-runtime/tsconfig.json has an @/* path alias in paths that appears unused (the bootstrap file uses relative imports). It should be removed.
  • DynamicActorLoaderContext and DynamicActorAuthContext are exported but DynamicActorContextBase is not. Consider whether the base should also be exported or the two classes merged.
  • The scheduleActorSleep helper introduced for fixtures is not shown in the diff. If c.sleep() is now disallowed from synchronous action handlers, a runtime guard or lint rule would be more reliable than a naming convention.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 11, 2026

More templates

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@4397

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@4397

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@4397

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@4397

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@4397

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@4397

@rivetkit/sqlite-vfs

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sqlite-vfs@4397

@rivetkit/traces

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/traces@4397

@rivetkit/workflow-engine

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/workflow-engine@4397

@rivetkit/virtual-websocket

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/virtual-websocket@4397

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@4397

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@4397

commit: 60274e6

@NathanFlurry NathanFlurry force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from 7461d0d to ad0466b Compare March 12, 2026 05:47
@NathanFlurry NathanFlurry force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from ad0466b to c4dea0a Compare March 20, 2026 06:13
@NathanFlurry NathanFlurry changed the base branch from main to graphite-base/4397 April 7, 2026 01:19
@NathanFlurry NathanFlurry force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from 18aa8d1 to 4777c4d Compare April 7, 2026 01:19
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4397 to 04-05-wip_fix_whatever_is_going_on_with_envoys April 7, 2026 01:19
@NathanFlurry NathanFlurry marked this pull request as draft April 7, 2026 01:31
@NathanFlurry NathanFlurry changed the base branch from 04-05-wip_fix_whatever_is_going_on_with_envoys to graphite-base/4397 April 7, 2026 01:33
@NathanFlurry NathanFlurry mentioned this pull request Apr 7, 2026
11 tasks
@NathanFlurry NathanFlurry force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from 4777c4d to ee1bb10 Compare April 7, 2026 05:12
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4397 to 04-05-wip_fix_whatever_is_going_on_with_envoys April 7, 2026 05:13
@NathanFlurry NathanFlurry marked this pull request as ready for review April 7, 2026 05:46
@NathanFlurry NathanFlurry changed the base branch from 04-05-wip_fix_whatever_is_going_on_with_envoys to graphite-base/4397 April 7, 2026 17:07
@NathanFlurry NathanFlurry mentioned this pull request Apr 8, 2026
11 tasks
@MasterPtato MasterPtato force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from ee1bb10 to db7311c Compare April 8, 2026 20:30
@MasterPtato MasterPtato changed the base branch from graphite-base/4397 to 04-05-wip_fix_whatever_is_going_on_with_envoys April 8, 2026 20:30
Copy link
Copy Markdown
Contributor

MasterPtato commented Apr 8, 2026

Merge activity

  • Apr 8, 8:52 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 8, 9:10 PM UTC: Graphite rebased this pull request as part of a merge.
  • Apr 8, 9:11 PM UTC: @MasterPtato merged this pull request with Graphite.

@MasterPtato MasterPtato changed the base branch from 04-05-wip_fix_whatever_is_going_on_with_envoys to graphite-base/4397 April 8, 2026 21:07
@MasterPtato MasterPtato changed the base branch from graphite-base/4397 to main April 8, 2026 21:09
@MasterPtato MasterPtato force-pushed the 03-08-feat_rivetkit_dynamic_actors branch from db7311c to ea13dbe Compare April 8, 2026 21:10
@MasterPtato MasterPtato merged commit 58b2179 into main Apr 8, 2026
5 of 14 checks passed
@MasterPtato MasterPtato deleted the 03-08-feat_rivetkit_dynamic_actors branch April 8, 2026 21:11
@MasterPtato MasterPtato mentioned this pull request Apr 9, 2026
11 tasks
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