Conversation
PR Review: feat: dynamic actorsThis 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
|
7461d0d to
ad0466b
Compare
ad0466b to
c4dea0a
Compare
18aa8d1 to
4777c4d
Compare
4777c4d to
ee1bb10
Compare
5373668 to
8b3a33e
Compare
ee1bb10 to
db7311c
Compare
8b3a33e to
67870e8
Compare
Merge activity
|
db7311c to
ea13dbe
Compare

Summary
Test plan
🤖 Generated with Claude Code