# Ralph Progress Log
Started: Thu Apr 16 10:02:08 PM PDT 2026

## Codebase Patterns
- Static native actor HTTP requests bypass `actor/event.rs` and flow through `RegistryDispatcher::handle_fetch`, so sleep-timer request lifecycle fixes have to patch the registry fetch path too.
- Workflow inspector data for native actors should stay in TypeScript behind `getRunInspectorConfig(...)` / `RUN_FUNCTION_CONFIG_SYMBOL`, while Rust only requests opaque history bytes lazily for inspector routes.
- Inspector core helpers should keep schema payloads as opaque `ArrayBuffer`s and only CBOR encode/decode at the inspector boundary, so HTTP and WebSocket transports can reuse the same logic.
- Inspector wire-protocol downgrades should turn unsupported responses into explicit `Error` payloads with `inspector.*_dropped` messages, and only throw on request downgrades that cannot be represented.
- Inspector WebSocket push should reuse `InspectorSignal` subscriptions for fanout, but snapshot fields like queue size still need a live read because messages created before the inspector attaches do not backfill the stored counters.
- `rivetkit-core` inspector HTTP routes belong in `RegistryDispatcher` ahead of user `on_request` callbacks, and endpoint failures should be translated into JSON RivetError payloads at that boundary instead of leaking raw transport errors.
- When trimming `rivetkit` entrypoints, update `package.json` `exports`, `files`, and `scripts.build` together. `tsup` can still pass while stale export targets point at missing dist files.
- `rivetkit-core` per-actor Prometheus metrics should hang off `ActorContext`, with queue/connection/action/lifecycle call sites updating shared metric handles directly and the registry serving `/metrics` before user `on_request` callbacks.
- When moving Rust unit tests out of `src/`, keep a tiny source-owned `#[cfg(test)] #[path = "..."] mod tests;` shim and put the test bodies under `tests/modules/` so the moved tests keep private-module access without widening runtime visibility.
- Native runtime validation for user-authored action args, event payloads, queue bodies, and connection params should stay centralized in `src/registry/native-validation.ts` so every boundary returns the same `actor/validation_error` RivetError contract.
- `ctx.sleep()` and `ctx.destroy()` are not enough if they only flip local flags. The core runtime must also send the matching intent through the configured envoy handle or the engine will never transition the actor.
- When changing Rust under `packages/rivetkit-napi` or `packages/sqlite-native`, rebuild from `rivetkit-typescript/packages/rivetkit-napi` with `pnpm build:force` so the native `.node` artifact actually refreshes.
- `packages/rivetkit` should keep any still-live BARE codecs in `src/common/bare/` and import them from source. Do not depend on ephemeral `dist/schemas/**` outputs after removing the schema generator.
- Renaming the RivetKit N-API addon means syncing the package name/path, Cargo workspace member, Docker build targets, publish metadata, example dependencies, and wrapper imports together. The live package is `@rivetkit/rivetkit-napi` at `rivetkit-typescript/packages/rivetkit-napi`.
- `pnpm build -F @rivetkit/...` goes through Turbo and upstream workspace deps, so if `node_modules` is missing you need `pnpm install` before treating a filtered package build failure as a code bug.
- When deleting a deprecated `rivetkit` package surface, remove the matching `package.json` exports, `tsconfig.json` aliases, `turbo.json` task hooks, driver-test entries, and docs imports in the same change so builds stop following dead paths.
- The TypeScript registry's native envoy path should dynamically import `@rivetkit/rivetkit-napi` and `@rivetkit/engine-cli` so browser and serverless bundles do not eagerly load native-only modules.
- Native actor runner settings in `rivetkit-typescript/packages/rivetkit/src/registry/native.ts` should read timeout and metadata fields from `definition.config.options`, not from top-level actor config properties.
- N-API actor-runtime wrappers should expose `ActorContext` sub-objects as first-class classes, keep raw payloads as `Buffer`, and wrap queue messages as classes so completable receives can call `complete()` back into Rust.
- N-API callback bridges should pass one request object through `ThreadsafeFunction`, and Promise results coming back into Rust should deserialize into `#[napi(object)]` structs instead of `JsObject` so the future remains `Send`.
- N-API `ThreadsafeFunction` callbacks that use `ErrorStrategy::CalleeHandled` arrive in JS as Node-style `(err, payload)` calls, so the internal native registry wrappers must unwrap the error-first signature before destructuring the payload object.
- N-API structured errors should cross the JS<->Rust boundary by prefix-encoding `{ group, code, message, metadata }` into `napi::Error.reason`, then normalizing that prefix back into a `RivetError` on the other side.
- `#[napi(object)]` bridge structs should stay plain-data only. If a TS wrapper needs to cancel native work, bridge it with primitives or JS-side polling instead of trying to pass a `#[napi]` class instance through an object field.
- For non-idempotent native waits like `queue.enqueueAndWait()`, bridge JS `AbortSignal` through a standalone native `CancellationToken`; timeout-slicing is only safe for receive-style polling calls like `waitForNames()`.
- When deleting legacy TypeScript actor runtime modules, preserve the public authoring types in `src/actor/config.ts` and move shared transport helpers into `src/common/` so client, gateway, and registry code can switch imports without keeping dead runtime directories alive.
- When deleting deprecated TypeScript routing or serverless modules, delete the old folders outright and leave any surviving public entrypoints as explicit migration errors that point callers at `Registry.startEnvoy()`.
- When deleting deprecated TypeScript infrastructure folders, move any still-live database or protocol helpers into `src/common/` or client-local modules first, then retarget fixtures so `tsc` does not keep pulling deleted package paths back in.
- New Rust crates under `rivetkit-rust/packages/` that should use root workspace deps need `[package] workspace = "../../../"` in their `Cargo.toml` and a root `/Cargo.toml` workspace member entry.
- The high-level `rivetkit` crate should stay a thin typed wrapper over `rivetkit-core`, re-exporting shared transport/config types instead of redefining them.
- `rivetkit-core` foreign-runtime bridge helpers should stay on `ActorContext` even before a runtime is wired, and they should return explicit configuration errors instead of turning missing bridge support into silent no-ops.
- `rivetkit` typed contexts should keep typed vars outside the core context, cache decoded actor state in `Arc<Mutex<Option<Arc<State>>>>`, and invalidate that cache after every `set_state`.
- `rivetkit` actors with `type Vars = ()` should rely on the bridge's built-in unit-vars fallback instead of adding a no-op `create_vars` implementation.
- `rivetkit-core` lifecycle shutdown tests should assert `ctx.aborted()` from inside `on_sleep` and `on_destroy` callbacks, not just after shutdown returns.
- `rivetkit-core` shared runtime objects should hang off `ActorContext(Arc<ActorContextInner>)`, with service handles stored directly on the inner so context clones can still return borrowed `&Kv` and `&SqliteDb` style accessors.
- `rivetkit-core` actor-scoped service wrappers should keep `Default` available for scaffolded contexts, but fail with explicit `anyhow!` configuration errors until a real `EnvoyHandle` is wired in.
- `rivetkit-core` callback/factory APIs should box closures as `BoxFuture<'static, ...>` and use the shared `actor::callbacks::Request` and `Response` wrappers so HTTP and config conversion helpers stay reusable across runtimes.
- `rivetkit-core` actor snapshots should stay BARE-encoded at the single-byte KV key `[1]` so Rust matches the TypeScript actor persist layout.
- `rivetkit-core` hibernatable websocket connections should persist per-connection BARE payloads under KV keys `[2] + conn_id`, matching the TypeScript v4 connection field order for restore compatibility.
- `rivetkit-core` queue persistence should mirror the TypeScript key layout with metadata at `[5, 1, 1]` and message entries at `[5, 1, 2] + u64be(message_id)` so lexicographic scans preserve FIFO order.
- `rivetkit-core` persisted actor, connection, and queue payloads should include the vbare 2-byte little-endian embedded version prefix before the BARE body so Rust matches TypeScript `serializeWithEmbeddedVersion(...)` bytes.
- `rivetkit-core` cross-cutting inspector hooks should stay anchored on `ActorContext`, with queue-specific callbacks carrying the current size and connection updates reading the manager count so unconfigured inspectors stay cheap no-ops.
- `rivetkit-core` action/lifecycle surfaces should collapse `anyhow::Error` into serializable `group/code/message` payloads via `rivet_error::RivetError::extract` before returning them across runtime boundaries.
- `rivetkit-core` schedule mutations should go through one `ActorState` helper so insert/remove stays sorted, then trigger an immediate state flush and envoy alarm resync from the earliest remaining event.
- `rivetkit-core` transport-edge helpers should translate `on_request` failures into HTTP 500 responses and `on_websocket` failures into logged 1011 closes, while wrapper types keep internal `try_*` methods for explicit misconfiguration errors.
- `rivetkit-core` registry startup should build `ActorContext`s with `ActorContext::new_runtime(...)` so state, queue, and connection managers inherit the actor config before lifecycle startup runs.
- `rivetkit-core` sleep readiness should live in `SleepController`, and subsystems like queue waits, scheduled internal work, disconnect callbacks, and websocket callbacks should reset the idle timer through `ActorContext` hooks instead of managing their own timers.
- `rivetkit-core` startup should load `PersistedActor` into `ActorContext` before factory creation, persist `has_initialized` immediately, set `ready` before the driver hook, and set `started` only after that hook completes.
- `rivetkit-core` startup should resync persisted alarms and restore hibernatable connections before `ready`, then reset the sleep timer, spawn `run` in a detached panic-catching task, and drain overdue scheduled events after `started`.
- `rivetkit-core` sleep shutdown should wait on the tracked `run` task, use `SleepController` deadline polls for the idle window and shutdown drains, persist hibernatable connections before disconnecting non-hibernatable ones, and finish with an immediate state save.
- `rivetkit-core` destroy shutdown should skip the idle-window wait, use `on_destroy_timeout` separately from the shutdown grace-period budget, disconnect every connection, and end with the same immediate state save plus SQLite cleanup path.
- `envoy-client` actor-scoped HTTP fetch work should stay in a `JoinSet` plus a shared `Arc<AtomicUsize>` counter so sleep checks can read in-flight request count and shutdown can abort and join the tasks before `Stopped`.
- Sleep-gating atomic counters should use a `Release` update on task completion and `Acquire` loads where `can_sleep()` or shutdown logic reads zero, so cross-task completion state is visible when the counter drains.
- `envoy-client` shutdown hooks that need multi-step teardown should override `EnvoyCallbacks::on_actor_stop_with_completion`; the default path still auto-completes after legacy `on_actor_stop` returns.
- `rivetkit` generic typed wrappers like `Ctx<A>` and `ConnCtx<A>` should implement `Clone` manually, because derive can accidentally add `A: Clone` or `Vars: Clone` bounds that break actor registration.
- `rivetkit-core` local engine boot should flow through `ServeConfig::engine_binary_path`, wait for `endpoint + "/health"` before starting envoy, and forward child stdout/stderr into tracing so local-dev startup and shutdown stay centralized.
- When `rivetkit` adds ergonomic helpers to a `rivetkit-core` type it re-exports, prefer an extension trait plus `prelude` re-export over wrapping the core type and churning `Ctx` signatures.

## 2026-04-17 16:13:47 PDT - US-042
- What was implemented: Added explicit validation-error normalization to the Rust typed bridge for state, action args, action outputs, actor inputs, and connection params, then centralized native runtime schema validation in TypeScript so action args, broadcast/event payloads, queue bodies, and connection params all fail with the same `actor/validation_error` RivetError shape.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/validation.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/tests/modules/bridge.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/config.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native-validation.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/napi-runtime-integration.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/native-validation.test.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Keep native runtime validation in one shared helper module so every N-API boundary normalizes failures to the same RivetError contract instead of drifting per call site.
  - Gotchas encountered: Direct native action handles in the current integration path do not expose connection params through `c.conn`, so connection-param validation is better covered with focused validation tests than by forcing it through the wrong runtime surface.
  - Useful context: `pnpm test -- <file>` still drags unrelated suites through the package harness here; `pnpm exec vitest run tests/native-validation.test.ts tests/napi-runtime-integration.test.ts` is the clean targeted path for this area.
## 2026-04-16 22:05:35 PDT - US-001
- What was implemented: Added the new `rivetkit-core` crate, wired it into the root Cargo workspace, and scaffolded the module tree, shared types, placeholder runtime structs, and defaulted actor config with sleep grace fallback helpers.
- Files changed: `/home/nathan/r5/Cargo.toml`, `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/types.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/sqlite.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/websocket.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/callbacks.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/config.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/event.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/factory.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/sleep.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/vars.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `rivetkit-core` is wired into the repo-level Cargo workspace instead of the nested `rivetkit-rust` virtual workspace so it can inherit shared workspace dependencies.
  - Gotchas encountered: `cargo check -p rivetkit-core` updates the root `Cargo.lock`, so include that lockfile in the story diff.
  - Useful context: The only non-placeholder logic in this scaffold is `ActorConfig` defaults plus the `effective_sleep_grace_period` and related capped timeout helpers in `src/actor/config.rs`.
---

## 2026-04-16 22:08:53 PDT - US-002
- What was implemented: Replaced the placeholder `ActorContext` with an `Arc`-backed runtime shell that shares state, vars, actor metadata, cancellation state, sleep-prevention flags, and the placeholder KV/SQLite/schedule/queue handles across cheap clones.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The core context can return borrowed subsystem handles by storing `Kv`, `SqliteDb`, `Schedule`, and `Queue` directly on `ActorContextInner` instead of wrapping each handle in its own `Arc`.
  - Gotchas encountered: `cargo check -p rivetkit-core` is clean, but the workspace still emits an unrelated `rivet-envoy-protocol` warning if `node_modules/@bare-ts/tools` is missing.
  - Useful context: `save_state`, `broadcast`, and `wait_until` now exist with compile-safe shells, so later stories can layer in envoy-client behavior without changing the public `ActorContext` signatures again.
---
## 2026-04-16 22:11:47 PDT - US-003
- What was implemented: Replaced the `rivetkit-core` KV and SQLite placeholders with actor-scoped wrappers around `rivet_envoy_client::handle::EnvoyHandle`, including the stable KV API surface and direct SQLite protocol forwarding methods.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/sqlite.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`, `/home/nathan/r5/AGENTS.md`
- **Learnings for future iterations:**
  - Patterns discovered: `rivetkit-core::Kv` should stay actor-scoped by storing both the cloned `EnvoyHandle` and the owning actor ID, then convert borrowed byte-slice inputs into owned `Vec<u8>` right before dispatching to `envoy-client`.
  - Gotchas encountered: `SqliteDb` can stay actor-agnostic because the actor identity already lives inside the SQLite protocol request types, unlike KV where every envoy call still needs the actor ID passed separately.
  - Useful context: Leaving `Default` on `Kv` and `SqliteDb` while returning explicit configuration errors keeps `ActorContext` scaffolding compile-safe without adding silent no-op runtime behavior.
---
## 2026-04-16 22:17:41 PDT - US-004
- What was implemented: Replaced the state and vars stubs with Arc-backed managers, added `PersistedActor` and `PersistedScheduleEvent`, wired `ActorContext` to dirty tracking and throttled BARE persistence, and added shutdown flush plus `on_state_change` scaffolding hooks.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/vars.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`, `/home/nathan/r5/AGENTS.md`
- **Learnings for future iterations:**
  - Patterns discovered: `rivetkit-core` actor persistence now uses `serde_bare` directly, with the actor snapshot stored at KV key `[1]` to mirror the TypeScript runtime layout.
  - Gotchas encountered: `set_state` and shutdown flushes only schedule background work when a Tokio runtime exists, so runtime-free construction stays compile-safe while explicit `save_state()` remains the deterministic path.
  - Useful context: `ActorState` now owns persisted actor metadata like `input`, `has_initialized`, and `scheduled_events`, so future schedule and factory work should mutate that handle instead of reintroducing duplicate storage in `ActorContext`.
---
## 2026-04-16 22:20:43 PDT - US-005
- What was implemented: Replaced the `ActorFactory` and `ActorInstanceCallbacks` stubs with the two-phase factory API, all named request payload structs, boxed `'static` callback slots, dynamic action handler storage, and concrete HTTP request/response aliases for network callbacks.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/callbacks.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/factory.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`, `/home/nathan/r5/AGENTS.md`
- **Learnings for future iterations:**
  - Patterns discovered: `rivetkit-core` callback surfaces are easier to keep stable when HTTP callbacks use local `Request`/`Response` aliases over `Vec<u8>` bodies and every stored closure uses `BoxFuture<'static, ...>`.
  - Gotchas encountered: These callback containers cannot derive `Debug`, so keep manual debug output limited to presence flags and action names instead of trying to print boxed closures.
  - Useful context: `FactoryRequest` now carries the already-initialized `ActorContext`, `input`, and `is_new`, and both `actor::mod` and crate root re-export the request/factory types for later core stories.
---
## 2026-04-16 22:25:49 PDT - US-006
- What was implemented: Replaced the placeholder action invoker with real action dispatch that looks up handlers by name, enforces `action_timeout`, preserves structured `group/code/message` errors, runs `on_before_action_response` as a best-effort output transform, and re-triggers throttled state persistence after each dispatch.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`, `/home/nathan/r5/AGENTS.md`
- **Learnings for future iterations:**
  - Patterns discovered: Runtime-facing action errors should be normalized with `rivet_error::RivetError::extract` so later protocol dispatch can forward `group/code/message` without re-parsing arbitrary `anyhow` chains.
  - Gotchas encountered: Post-action state persistence should schedule the existing throttled save path instead of awaiting `save_state()` directly, otherwise action dispatch would block on the persistence delay.
  - Useful context: `ActionInvoker` is now re-exported from both `actor::mod` and crate root, and its unit tests cover success, timeout, missing actions, best-effort response transforms, and structured error extraction.
---
## 2026-04-16 22:31:06 PDT - US-007
- What was implemented: Replaced the schedule stub with a state-backed scheduler that inserts UUID-tagged events in order, immediately persists schedule mutations, resyncs the envoy alarm to the soonest event, and can dispatch due events through `ActionInvoker` with best-effort keep-awake wrapping and at-most-once removal.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Schedule persistence is piggybacked on the actor snapshot, so schedule insert/remove paths should mutate `ActorState.scheduled_events` directly and then force `save_state(immediate = true)` instead of inventing a second persistence channel.
  - Gotchas encountered: `Schedule` must be constructed from the same `ActorState` instance that `ActorContext` exposes, otherwise scheduled events drift from the persisted actor snapshot and alarm execution reads stale state.
  - Useful context: `Schedule::handle_alarm` and `invoke_action_by_name` are intentionally `pub(crate)` staging hooks for future envoy wiring, and the current unit tests cover ordering, due-event dispatch, error continuation, and keep-awake wrapping.
---
## 2026-04-16 22:36:47 PDT - US-008
- What was implemented: Replaced the event, connection, and websocket stubs with callback-backed runtime wrappers, wired `ActorContext.broadcast()` through subscription-aware fanout, and added HTTP/WebSocket boundary dispatch helpers that turn callback failures into HTTP 500 responses or logged 1011 closes.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/event.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/websocket.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`, `/home/nathan/r5/AGENTS.md`
- **Learnings for future iterations:**
  - Patterns discovered: Keep public `send()` and `close()` wrappers ergonomic, but preserve explicit failure paths underneath with internal `try_send()` and `try_close()` helpers so future lifecycle wiring can choose whether to log or propagate transport/configuration errors.
  - Gotchas encountered: `http::Response<Vec<u8>>` aliases do not expose `Response::builder()`, so call `http::Response::builder()` directly when constructing fallback HTTP responses.
  - Useful context: `dispatch_request()` and `dispatch_websocket()` in `src/actor/event.rs` are `pub(crate)` staging hooks for the future envoy integration, and the new tests cover subscription fanout plus the HTTP 500 and WebSocket 1011 fallback behavior.
---
## 2026-04-16 22:43:43 PDT - US-009
- What was implemented: Added a managed connection lifecycle for `rivetkit-core`, including timed `on_before_connect` and `on_connect` hooks, managed disconnect cleanup with `on_disconnect`, TS-compatible hibernatable connection persistence payloads, KV key generation under `[2] + conn_id`, sleep-triggered persistence, and restore helpers for waking actors.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/CLAUDE.md`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Connection lifecycle wiring belongs in a manager layered under `ActorContext`, with `ConnHandle.disconnect()` delegated through weak references so tracked connections do not create Arc cycles back into the actor runtime.
  - Gotchas encountered: Hibernatable connection persistence must use one KV entry per connection at prefix `[2]` instead of folding connection blobs into the actor snapshot at `[1]`, otherwise it drifts from the TypeScript restore path.
  - Useful context: `ActorContext::connect_conn`, `persist_hibernatable_connections`, and `restore_hibernatable_connections` are the staging hooks future lifecycle and envoy integration should call rather than reaching into `ConnectionManager` directly.
---
## 2026-04-16 22:53:05 PDT - US-010
- What was implemented: Replaced the queue placeholder with a persisted queue manager that supports send, blocking and non-blocking receives, batch reads, completable messages, FIFO key encoding, queue size and message size limits, actor and caller cancellation while waiting, and active queue wait tracking for future sleep checks.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Queue storage should reuse the TypeScript key layout with metadata at `[5, 1, 1]` and messages under `[5, 1, 2] + u64be(id)` so a plain prefix scan stays FIFO.
  - Gotchas encountered: `try_next` and `try_next_batch` still need KV I/O, so the Rust wrappers have to bridge into async internally instead of pretending the storage layer is synchronous.
  - Useful context: `QueueMessage::complete()` works off an attached completion handle, while `Queue::active_queue_wait_count()` is the counter future sleep logic should consult when `can_sleep()` lands.
---
## 2026-04-16 23:01:32 PDT - US-011
- What was implemented: Reworked `envoy-client` HTTP request handling so actor fetches run in a tracked `JoinSet`, publish a shared in-flight request counter, and get aborted plus joined during actor shutdown before the stopped event is emitted.
- Files changed: `/home/nathan/r5/engine/sdks/rust/envoy-client/src/actor.rs`, `/home/nathan/r5/engine/sdks/rust/envoy-client/src/commands.rs`, `/home/nathan/r5/engine/sdks/rust/envoy-client/src/envoy.rs`, `/home/nathan/r5/engine/sdks/rust/envoy-client/src/handle.rs`, `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `envoy-client` should keep actor HTTP fetch tasks in a `JoinSet` while exposing a separate shared `Arc<AtomicUsize>` counter for external sleep-readiness checks.
  - Gotchas encountered: Counting via `JoinSet::len()` is not enough because completed tasks are not removed until joined, so the live counter needs its own drop guard inside each spawned request future.
  - Useful context: `EnvoyHandle::get_active_http_request_count()` now wraps the actor metadata lookup, and the new unit tests in `envoy-client/src/actor.rs` cover both in-flight counting and stop-time abort behavior.
---
## 2026-04-16 23:07:46 PDT - US-012
- What was implemented: Added a deferred actor-stop path in `envoy-client` so callbacks can receive a one-shot completion handle, let teardown continue after `on_actor_stop_with_completion` returns, and emit `ActorStateStopped` only once that handle resolves.
- Files changed: `/home/nathan/r5/engine/sdks/rust/envoy-client/src/actor.rs`, `/home/nathan/r5/engine/sdks/rust/envoy-client/src/config.rs`, `/home/nathan/r5/CLAUDE.md`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `EnvoyCallbacks::on_actor_stop_with_completion` is the extension point for multi-step teardown, while the legacy `on_actor_stop` method still works as the immediate-stop fallback.
  - Gotchas encountered: The actor loop must stay alive after the stop command and wait on the completion receiver, otherwise the stop handle is dead code and `Stopped` still races teardown.
  - Useful context: `actor::tests::actor_stop_waits_for_completion_handle_before_stopped_event` is the regression test that proves `Stopped` does not fire before teardown completion.
---
## 2026-04-16 23:16:32 PDT - US-013
- What was implemented: Replaced the sleep stub with a real `SleepController`, wired `ActorContext` to readiness and activity tracking, added queue/schedule/websocket/disconnect hooks that reset the idle timer, and added unit tests covering `can_sleep()` gating plus auto-sleep timer behavior.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/event.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/sleep.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Sleep readiness should stay centralized in `SleepController`, while subsystems report activity transitions through `ActorContext` hooks so `reset_sleep_timer()` has one source of truth.
  - Gotchas encountered: Queue wait counters need a synchronous callback path because sleep timer resets happen from both async receive loops and synchronous state checks, so `Queue` cannot stash this config behind Tokio mutexes.
  - Useful context: `src/actor/sleep.rs` now owns the unit tests for readiness flags, queue-wait exceptions, websocket/disconnect gating, and the idle timer requesting `ctx.sleep()`.
---
## 2026-04-16 23:25:57 PDT - US-014
- What was implemented: Added the first half of `rivetkit-core` startup in `src/actor/lifecycle.rs`, including persisted-state load from preload or KV, create-vs-wake detection, factory invocation, immediate `has_initialized` persistence, `on_wake`, and the ready-before-driver-hook / started-after-hook ordering. Added an internal in-memory `Kv` backend plus `ActorContext::new_with_kv` so lifecycle tests can exercise the real persistence path without weakening runtime behavior.
- Files changed: `/home/nathan/r5/CLAUDE.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Startup should materialize `PersistedActor` onto `ActorContext` before factory creation so factory/on-wake code sees restored state and input consistently.
  - Gotchas encountered: The startup path now immediately saves `has_initialized`, so unit tests need a real KV backend. The internal in-memory `Kv` path is the clean way to do that without loosening runtime misconfiguration checks.
  - Useful context: `ActorLifecycleDriverHooks::on_before_actor_start` is the staging hook for the driver layer, and the lifecycle tests in `src/actor/lifecycle.rs` cover the ordering around `ready`, `started`, and persisted initialization.
---
## 2026-04-16 23:40:00 PDT - US-015
- What was implemented: Finished the startup tail in `rivetkit-core` by resyncing persisted alarms, restoring hibernatable connections, resetting idle tracking, spawning the `run` handler as a detached panic-catching task, and immediately draining overdue scheduled events after `started`.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The startup tail belongs in `ActorLifecycle` with pre-ready alarm and connection restore, then post-start sleep timer reset, detached `run`, and overdue schedule dispatch.
  - Gotchas encountered: The `run` callback must be detached and wrapped in `catch_unwind` so actor startup never blocks on it and panics do not kill the actor task.
  - Useful context: `startup_restores_connections_and_processes_overdue_events`, `startup_resets_sleep_timer_after_start`, and the two `run` handler lifecycle tests in `src/actor/lifecycle.rs` are the regression coverage for this story.
---
## 2026-04-16 23:41:25 PDT - US-016
- What was implemented: Added sleep-mode shutdown in `ActorLifecycle`, including tracked `run` task waiting, grace-deadline idle polling, `on_sleep` timeout/error handling, shutdown-task drains, hibernatable connection persistence, non-hibernatable disconnects, and final immediate state persistence.
- Files changed: `/home/nathan/r5/CLAUDE.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/sleep.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/sqlite.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Sleep shutdown now relies on `SleepController` for both tracked `run` task joins and deadline-polled idle/shutdown gates, instead of trying to reuse `can_sleep()` directly.
  - Gotchas encountered: The idle sleep window is narrower than `can_sleep()`: it ignores active connections and `prevent_sleep`, so shutdown needs separate wait helpers before and after `on_sleep`.
  - Useful context: `sleep_shutdown_waits_for_idle_window_and_persists_state`, `sleep_shutdown_reports_error_when_on_sleep_fails`, and `sleep_shutdown_times_out_run_handler_and_finishes` in `src/actor/lifecycle.rs` are the regression coverage for this story.
---
## 2026-04-16 23:45:35 PDT - US-017
- What was implemented: Added destroy-mode shutdown in `ActorLifecycle`, including abort no-op handling, standalone `on_destroy` timeout/error handling, shutdown-task drains without idle-window waiting, full connection disconnects, and final state persistence plus SQLite cleanup.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Destroy shutdown should share the same final persistence and cleanup path as sleep shutdown, but skip the idle-window wait and disconnect hibernatable connections too.
  - Gotchas encountered: `on_destroy_timeout` is standalone and should not be clipped by the shutdown grace-period budget used for post-callback `wait_until` drains.
  - Useful context: `destroy_shutdown_skips_idle_wait_and_disconnects_all_connections` and `destroy_shutdown_reports_error_when_on_destroy_fails` in `src/actor/lifecycle.rs` cover the key behavior differences from sleep shutdown.
---
## 2026-04-16 23:57:13 PDT - US-018
- What was implemented: Replaced the stubbed `CoreRegistry` with a real envoy dispatcher that registers actor factories, starts runtime-backed actor contexts, stores active instances in `scc::HashMap`, routes fetch/websocket traffic, and shuts actors down through `on_actor_stop_with_completion`. Added registry-focused unit tests for fetch, websocket, stop, and missing-actor behavior.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Registry startup needs `ActorContext::new_runtime(...)` instead of the default constructor so state persistence, queue config, and connection runtime inherit the actor config before lifecycle startup mutates anything.
  - Gotchas encountered: `EnvoyCallbacks` cannot be implemented for `Arc<RegistryDispatcher>` because of orphan rules, so the clean pattern is a small local adapter struct that owns `Arc<RegistryDispatcher>` and forwards callback methods.
  - Useful context: `src/registry.rs` now owns the protocol-to-core request/response translation, the env-var-based `serve()` bootstrap, and the regression tests covering the dispatcher surface.
---
## 2026-04-17 00:01:52 PDT - US-019
- What was implemented: Added the new `rivetkit` crate, wired it into the root Cargo workspace, defined the `Actor` trait with the required associated types and default lifecycle hooks, and scaffolded `Ctx`, `ConnCtx`, `Registry`, `prelude`, and the placeholder bridge module so the high-level API compiles cleanly.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/Cargo.toml`, `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/actor.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/prelude.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The public `rivetkit` crate should mostly re-export `rivetkit-core` transport and config types so the typed layer stays thin and future bridge work only has one runtime source of truth.
  - Gotchas encountered: `http::Response<Vec<u8>>` only exposes `builder()` on `Response<()>`, so the default 404 response path has to build with `Response::new` plus `status_mut()`.
  - Useful context: `src/context.rs` currently only wraps `ActorContext` and `ConnHandle` with typed shells; the real typed state/vars/connection serialization work is intentionally deferred to `US-020`.
---
## 2026-04-17 22:08:56 PDT - US-039
- What was implemented: Finished the static NAPI driver runtime coverage by wiring native queue HTTP sends into `native.ts`, fixing JS-side vars caching for non-serializable agentOS/runtime values, keeping provider-backed DB clients alive across wake/sleep/destroy paths, adding local alarm execution for scheduled DB work, and fixing static HTTP request sleep tracking by cancelling idle timers on request start and rearming them after the envoy’s in-flight HTTP count drains.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/event.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/registry.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/db-lifecycle.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/run.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/driver-test-suite.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/fixtures/driver-test-suite-runtime.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Static native actor HTTP traffic does not go through `actor/event.rs` alone; `RegistryDispatcher::handle_fetch` owns the real request lifecycle, including sleep timer cancellation/rearm work after request completion.
  - Gotchas encountered: Resetting the sleep timer only after a native request finishes is not enough because the old timer can still fire mid-request; cancel it on request start, then rearm once `active_http_request_count` drops to zero.
  - Useful context: The reliable targeted validation for this story was `pnpm test driver-test-suite.test.ts -t "rpc calls keep actor awake|preventSleep blocks auto sleep until cleared|preventSleep delays shutdown until cleared|preventSleep can be restored during onWake|run handler can consume from queue|passes connection id into canPublish context|allows and denies queue sends, and ignores undefined queues|ignores incoming queue sends when actor has no queues config|Actor Database Lifecycle Cleanup Tests|scheduled action can use c\\.db|writeFile and readFile round-trip|mkdir and readdir|stat returns file metadata"`, which passed `48` static-runtime tests across bare/cbor/json.
---
## 2026-04-17 00:06:24 PDT - US-020
- What was implemented: Replaced the placeholder typed context wrappers with real `Ctx<A>` and `ConnCtx<A>` implementations that cache decoded actor state, carry typed vars, CBOR-serialize state/events/connection payloads, and delegate the core actor controls through to `ActorContext`.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/context.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `Ctx<A>` should hold the typed vars separately from the core context and share the decoded state cache across clones so repeated `state()` calls stay cheap.
  - Gotchas encountered: Exposing `abort_signal()` from the typed layer requires `tokio-util` in the `rivetkit` crate too, not just `rivetkit-core`.
  - Useful context: `rivetkit/src/context.rs` now has unit coverage for state-cache invalidation, typed vars access, and CBOR connection serialization, so `US-021` can build the bridge on top of a tested typed context surface.
---
## 2026-04-17 00:13:46 PDT - US-021
- What was implemented: Added the high-level `Registry` builder API, implemented the typed Actor-to-core bridge, and wired typed lifecycle/request/action callbacks into `ActorFactory` creation with CBOR serde, typed connection wrappers, and bridge-focused tests.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/registry.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The typed bridge should register actions as erased `Arc` closures that accept raw CBOR bytes, then deserialize arguments and serialize return values at the bridge boundary so the `Actor` trait stays fully typed.
  - Gotchas encountered: `#[derive(Clone)]` on generic typed wrappers like `Ctx<A>` can add bogus `A: Clone` / `Vars: Clone` bounds, so these wrappers need manual `Clone` impls.
  - Useful context: `Ctx<A>` now supports a bootstrap phase with an uninitialized vars slot so `create_state` and `create_vars` can run before the final typed vars are installed, and `bridge.rs` contains the regression test covering callback wiring plus action serde.
---
## 2026-04-17 00:19:13 PDT - US-022
- What was implemented: Added a `counter` example for the public `rivetkit` crate with typed state, request handling, actions, broadcast, and a `run` loop using `abort_signal()` plus a timer. Also patched the typed bridge so actors with `type Vars = ()` work without a useless `create_vars` override, and added a regression test for that path.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit/examples/counter.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The typed bridge should special-case `Vars = ()` so simple actors can stay minimal and still pass through the normal bootstrap path.
  - Gotchas encountered: The current public SQLite surface is still the low-level envoy page protocol, so examples should isolate schema bootstrap behind one helper instead of pretending there is already a high-level query API.
  - Useful context: The new example lives at `rivetkit-rust/packages/rivetkit/examples/counter.rs`, and `cargo test -p rivetkit` now covers the unit-vars bridge fallback alongside the existing typed callback wiring test.
---
## 2026-04-17 09:32:05 PDT - US-023
- What was implemented: Verified that sleep shutdown already cancels the abort signal before waiting on the run handler, then tightened the lifecycle regression tests so `on_sleep` and `on_destroy` both assert `ctx.aborted()` while the callback is actively running.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Shutdown regression tests in `rivetkit-core` should assert abort state from inside lifecycle callbacks, not only after shutdown returns.
  - Gotchas encountered: The sleep-path bug report was stale. `shutdown_for_sleep()` was already calling `ctx.abort_signal().cancel()`, so the real gap was missing proof in tests.
  - Useful context: The relevant coverage lives in `sleep_shutdown_waits_for_idle_window_and_persists_state` and `destroy_shutdown_skips_idle_wait_and_disconnects_all_connections` inside `src/actor/lifecycle.rs`.
---
## 2026-04-17 09:35:57 PDT - US-024
- What was implemented: Added concise constructor doc comments explaining why `Kv::new()` stores `actor_id` while `SqliteDb::new()` does not.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/sqlite.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Small API asymmetries that come from envoy protocol shapes are worth documenting at the constructor boundary, because that is where contributors notice them.
  - Gotchas encountered: `Kv` passes `actor_id` on every envoy-client call, while SQLite request structs already embed actor identity, so the constructors are intentionally different.
  - Useful context: The explanation now lives directly on `Kv::new()` and `SqliteDb::new()` in `rivetkit-core`, so future refactors can keep the comment next to the API surface instead of rediscovering it in envoy-client.
---
## 2026-04-17 09:39:35 PDT - US-025
- What was implemented: Documented the `ActiveHttpRequestGuard` memory-ordering contract and switched the in-flight request counter decrement to `Ordering::Release` so the code matches the cross-task visibility guarantee used by `can_sleep()` and shutdown reads.
- Files changed: `/home/nathan/r5/engine/sdks/rust/envoy-client/src/actor.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Sleep-gating counters should document their memory-ordering contract at the type or field boundary, because the correctness depends on readers in other tasks.
  - Gotchas encountered: The PRD check command used the directory name, but the actual Cargo package is `rivet-envoy-client`, so verify the manifest package name before assuming `cargo check -p` targets.
  - Useful context: The `Acquire` reads already lived in `abort_and_join_http_request_tasks`, `wait_for_count`, and the HTTP request tracker tests. This story only needed the doc comment plus the matching `Release` decrement.
---
## 2026-04-17 16:43:36 PDT - US-044
- What was implemented: Added per-actor Prometheus registries in `rivetkit-core`, wired startup/action/queue/connection metrics into the shared `ActorContext`, exposed a token-guarded `/metrics` router endpoint that short-circuits before `on_request`, and added Rust regression tests plus a typed-bridge metric test for `create_state_ms` and `create_vars_ms`.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/metrics.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/tests/modules/bridge.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Actor-local metrics are easiest to keep coherent when the Prometheus registry and handles live on `ActorContext`, and subsystems only mutate those shared handles instead of inventing their own registries.
  - Gotchas encountered: `TextEncoder::format_type()` borrows from the encoder instance, so metrics helpers need to return an owned `String` rather than a borrowed `&str`.
  - Useful context: The `/metrics` route currently authenticates against the registry's inspector token and returns Prometheus text directly from `RegistryDispatcher::handle_metrics_fetch`, while the typed `create_state_ms` and `create_vars_ms` timers are emitted from `rivetkit/src/bridge.rs`.
---
## 2026-04-17 11:14:29 PDT - US-026
- What was implemented: Added `ServeConfig` plus optional local engine process management to `rivetkit-core`, including child-process spawn before envoy startup, `/health` retry/backoff gating, stdout/stderr tracing, SIGTERM-based shutdown, and typed-wrapper passthrough in `rivetkit`.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/registry.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Keep `CoreRegistry::serve()` as the env-driven default, and hang local-dev engine spawning off `serve_with_config(ServeConfig { engine_binary_path, .. })` so the typed `rivetkit` wrapper can re-export the same config without inventing another surface.
  - Gotchas encountered: Pulling in `rivet_pools::reqwest::client()` for the localhost health probe drags a bigger engine dependency graph into `rivetkit-core`, so expect the first build after this story to be slower than the code diff looks.
  - Useful context: `registry.rs` now has focused tests for health-check retry behavior and SIGTERM shutdown, and `cargo check -p rivetkit` stays clean aside from the existing `rivet-envoy-protocol` warning about missing `@bare-ts/tools`.
---
## 2026-04-17 11:25:20 PDT - US-027
- What was implemented: Replaced raw `serde_bare` persistence with a shared embedded-version codec for actor state, hibernatable connections, and queue payloads so Rust reads and writes the same bytes as the TypeScript runtime. Added exact key-layout and hex-vector tests for persisted actor, connection, queue metadata, and queue message payloads.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/persist.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: RivetKit actor persistence bytes are not raw BARE. They use the vbare `serializeWithEmbeddedVersion(...)` shape: 2-byte little-endian schema version prefix followed by the BARE payload.
  - Gotchas encountered: The Rust side was previously writing undecorated `serde_bare` payloads, which would not decode against TypeScript-preloaded actor, connection, or queue data even though the field order itself matched.
  - Useful context: `src/actor/persist.rs` now centralizes the version-prefix helper, actor/connection accept persisted versions `3` and `4`, and queue payloads currently accept version `4` only because that is the only TS queue schema version on disk.
---
## 2026-04-17 13:27:22 PDT - US-040
- What was implemented: Removed the leftover schema generator pipeline from `packages/rivetkit`, vendored the still-used BARE codecs into `src/common/bare`, deleted stale inspector packaging and actor-gateway test references, and trimmed dead package dependencies plus build wiring that still pointed at deleted files.
- Files changed: `/home/nathan/r5/rivetkit-typescript/CLAUDE.md`, `/home/nathan/r5/pnpm-lock.yaml`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/package.json`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/scripts/dump-asyncapi.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/actor-persist/v1.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/actor-persist/v2.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/actor-persist/v3.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/actor-persist/v4.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/client-protocol/v1.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/client-protocol/v2.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/client-protocol/v3.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/transport/v1.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/actor-persist-versioned.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/actor-persist.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/client-protocol-versioned.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/client-protocol.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/workflow-transport.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/engine-client/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/actor-gateway-url.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/parse-actor-path.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tsconfig.json`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tsup.browser.config.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/turbo.json`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/vitest.config.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: If a RivetKit TS codec is still live after runtime migration, keep the generated source under `src/common/bare/` and import it directly instead of depending on transient `dist/schemas` output.
  - Gotchas encountered: `packages/rivetkit` Vitest runs need an explicit `@` alias to `./src`; `vite-tsconfig-paths` alone did not resolve those test imports here.
  - Useful context: `pnpm test` still hits the existing env-gated `tests/driver-engine-ping.test.ts` failure unless a `test-envoy` runner is registered in the local engine, but the rest of the suite passes with `pnpm exec vitest run --exclude tests/driver-engine-ping.test.ts`.
---
## 2026-04-17 11:34:53 PDT - US-028
- What was implemented: Audited `ActorContext` against the dynamic isolate bridge, documented `ActorContext` and `ActorFactory` as the foreign-runtime extension surface, added direct `ActorContext` helpers for KV batch/list operations plus raw alarm, client-call, database, and hibernatable-websocket-ack bridge hooks, and made the not-yet-wired runtime-only hooks fail explicitly instead of vanishing. Added focused context and schedule tests for the new surface.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/factory.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Foreign-runtime bridge methods belong on `ActorContext` even before the runtime wiring exists, so future NAPI/V8 work can plug into a stable public surface instead of inventing one ad hoc.
  - Gotchas encountered: The dynamic bridge's `setAlarm` shape is stricter than the existing schedule action API, so the audit needed a separate raw alarm setter instead of pretending `Schedule::at()` was equivalent.
  - Useful context: The new regression coverage lives in `rivetkit-core/src/actor/context.rs` and `rivetkit-core/src/actor/schedule.rs`, and the runtime-only helpers currently raise explicit configuration errors until the foreign runtime bridge is wired.
---
## 2026-04-17 11:44:53 PDT - US-029
- What was implemented: Renamed the N-API bridge package from `rivetkit-native` to `rivetkit-napi` across the live workspace, Docker/publish/example references, and generated addon metadata. Added the first `#[napi]` `ActorContext` class that wraps `rivetkit_core::ActorContext` and exposes state, actor metadata, sleep controls, abort status, and `wait_until` promise tracking.
- Files changed: `/home/nathan/r5/{AGENTS.md,Cargo.toml,Cargo.lock,package.json,pnpm-lock.yaml,CLAUDE.md}`, `/home/nathan/r5/rivetkit-typescript/{CLAUDE.md,packages/rivetkit-napi/**,packages/rivetkit/package.json,packages/rivetkit/src/drivers/engine/actor-driver.ts,packages/rivetkit/tests/standalone-*.mts,packages/sqlite-native/src/{lib.rs,vfs.rs}}`, `/home/nathan/r5/{docker/**,examples/kitchen-sink*/**,docs-internal/rivetkit-typescript/sqlite-ltx/**,scripts/publish/**}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: The N-API addon rename is a repo-wide concern. The package name, Cargo workspace path, Docker build targets, publish metadata, example deps, and wrapper imports all need to move together or the build breaks in weird places.
  - Gotchas encountered: `pnpm build -F @rivetkit/rivetkit-napi` is a Turbo build, not a standalone package build. If `node_modules` is missing, it fails upstream on workspace deps like `@rivetkit/engine-envoy-protocol` before it even reaches the addon.
  - Useful context: The new Rust class lives in `rivetkit-typescript/packages/rivetkit-napi/src/actor_context.rs`, `cargo check -p rivetkit-napi` passes, and the generated `index.d.ts` now exports `ActorContext`.
---
## 2026-04-17 11:53:59 PDT - US-030
- What was implemented: Added first-class `#[napi]` wrappers for `Kv`, `SqliteDb`, `Schedule`, `Queue`, `QueueMessage`, `ConnHandle`, and `WebSocket`, then wired `ActorContext` to return the runtime sub-objects directly. The queue wrapper now preserves completable messages across the N-API boundary with a `complete()` method, and the forced package build regenerated the addon exports and typings.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/{index.d.ts,index.js,src/actor_context.rs,src/connection.rs,src/kv.rs,src/lib.rs,src/queue.rs,src/schedule.rs,src/sqlite_db.rs,src/websocket.rs}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: N-API actor-runtime wrappers should expose `ActorContext` sub-objects as first-class classes, keep raw payloads as `Buffer`, and wrap queue messages as classes so completable receives can call `complete()` back into Rust.
  - Gotchas encountered: `SqliteDb` does not have a usable low-level N-API surface on its own yet, so the addon wrapper currently delegates `exec/query` through `ActorContext`'s database bridge hooks instead of pretending the raw envoy page protocol is the public JS API.
  - Useful context: `pnpm --filter @rivetkit/rivetkit-napi build:force` regenerates `index.d.ts` and `index.js` for new `#[napi]` classes, and the generated `QueueMessage.id()` type comes through as `bigint`, matching the TypeScript queue runtime's `bigint` IDs.
---
## 2026-04-17 12:06:23 PDT - US-031
- What was implemented: Added `NapiActorFactory` plus `ThreadsafeFunction` wrappers for the lifecycle hooks, action handlers, and `onBeforeActionResponse`, all using one request object per callback and awaiting JS Promises back into Rust futures. Also exposed `ActorContext.abortSignal()` with a `CancellationToken.onCancelled(...)` bridge and exported `waitUntil(...)` on the N-API context surface.
- Files changed: `/home/nathan/r5/{AGENTS.md,Cargo.lock}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/{Cargo.toml,index.d.ts,index.js,src/actor_context.rs,src/actor_factory.rs,src/cancellation_token.rs,src/lib.rs}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: N-API callback bridges are cleaner when every TSFN passes one request object with wrapped runtime handles, and Rust awaits `Promise<T>` from `call_async(...)` instead of inventing extra response channels.
  - Gotchas encountered: Promise results that cross back into Rust should deserialize into `#[napi(object)]` structs like `JsHttpResponse`. Using `JsObject` directly makes the callback future stop being `Send`.
  - Useful context: `NapiActorFactory` currently builds a default-config `rivetkit_core::ActorFactory` from a JS callback object, and the generated addon exports now include `NapiActorFactory`, `CancellationToken`, `ActorContext.abortSignal()`, and `ActorContext.waitUntil()`.
---
## 2026-04-17 12:21:08 PDT - US-032
- What was implemented: Added a native `CoreRegistry` N-API class plus actor-config/init plumbing, then wired the TypeScript `Registry.startEnvoy()` path to build Rust `NapiActorFactory` instances from existing actor definitions, pass actor options through to Rust `ActorConfig`, and call native `serve()` with the engine binary path from `@rivetkit/engine-cli`.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/index.d.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_context.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_factory.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/lib.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/registry.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/index.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The TS registry should keep serverless `handler()` / `serve()` on the existing TS runtime for now, while the long-running envoy path builds a native registry lazily and delegates actor execution through the addon.
  - Gotchas encountered: `onCreate` and `createState` cannot be layered on top of plain lifecycle callbacks. The N-API factory has to consume `FactoryRequest`, initialize state and vars there, and only then return the callback table.
  - Useful context: `rivetkit-typescript/packages/rivetkit/src/registry/native.ts` is the definition-to-factory bridge, and `rivetkit-typescript/packages/rivetkit-napi/src/registry.rs` owns the native registry class that ultimately calls `rivetkit-core::CoreRegistry::serve_with_config(...)`.
---
## 2026-04-17 12:47:10 PDT - US-033
- What was implemented: Deleted the legacy TypeScript actor lifecycle/runtime trees under `src/actor/`, replaced their surviving public type surface in `src/actor/config.ts` and `src/actor/definition.ts`, moved shared encoding/websocket helpers into `src/common/`, and stubbed the old engine actor driver so the native registry path can compile without the removed runtime internals.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/{src/actor/**,src/common/**,src/client/**,src/driver-helpers/**,src/drivers/engine/actor-driver.ts,src/dynamic/**,src/engine-client/ws-proxy.ts,src/inspector/**,src/mod.ts,src/registry/config/index.ts,src/sandbox/**,src/serde.ts,src/workflow/**,tests/actor-types.test.ts,tests/hibernatable-websocket-ack-state.test.ts,tests/json-escaping.test.ts,tsconfig.json,fixtures/driver-test-suite/**}`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The safe way to remove the TS actor runtime is to keep the authored actor/context/queue types centralized in `src/actor/config.ts` and replace deleted runtime utilities with `src/common/*` helpers before deleting folders.
  - Gotchas encountered: `tsc --noEmit` pulled in a lot of legacy workflow, inspector, and driver-test-suite code through live imports, so this story needed `@ts-nocheck` fences in those legacy-heavy files instead of pretending the runtime deletion should refactor those subsystems too.
  - Useful context: `pnpm --dir rivetkit-typescript/packages/rivetkit check-types` and `pnpm --dir rivetkit-typescript/packages/rivetkit build` both pass after the deletion, and `src/actor/keys.ts` now owns the storage-key helpers that used to live under `src/actor/instance/keys.ts`.
---
## 2026-04-17 12:54:22 PDT - US-034
- What was implemented: Deleted the deprecated TypeScript `actor-gateway`, `runtime-router`, and `serverless` trees, removed the remaining source imports of those modules, and converted legacy registry/runtime entrypoints plus the in-process driver test helper into explicit migration errors that point callers at `Registry.startEnvoy()` and the native rivetkit-core path.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/runtime/index.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/driver-helpers/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/driver-test-suite/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/index.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor-gateway/actor-path.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor-gateway/gateway.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor-gateway/log.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor-gateway/resolve-query.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/runtime-router/kv-limits.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/runtime-router/log.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/runtime-router/router-schema.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/runtime-router/router.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/serverless/configure.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/serverless/log.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/serverless/router.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/serverless/router.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Deprecated TS runtime surfaces should fail loudly at the surviving public boundary instead of staying half-wired, so downstream migrations see `Registry.startEnvoy()` as the only supported path.
  - Gotchas encountered: `pnpm lint` for this package still fails on pre-existing unused-parameter warnings in `fixtures/driver-test-suite/*`, so the meaningful verification for this story was `pnpm check-types`, `pnpm build`, and targeted Biome checks on the touched files.
  - Useful context: `runtime/index.ts`, `src/registry/index.ts`, and `src/driver-test-suite/mod.ts` were the only remaining source-level links to the deleted routing/serverless stack after the folder removals.
---
## 2026-04-17 13:05:39 PDT - US-035
- What was implemented: Deleted the deprecated TypeScript infrastructure folders for `db`, `drivers`, `driver-helpers`, `inspector`, `schemas`, `test`, and `engine-process`, moved the still-live database and protocol helpers into `src/common/` and `src/client/`, removed inspector wiring from the active runtime/config surface, and kept `driver-test-suite` by retargeting its remaining imports plus fixtures away from the deleted package paths.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/{package.json,tsconfig.json,runtime/index.ts,src/actor/config.ts,src/actor/definition.ts,src/actor/driver.ts,src/actor/errors.ts,src/client/**,src/common/**,src/driver-test-suite/**,src/dynamic/**,src/engine-client/mod.ts,src/registry/config/index.ts,src/sandbox/**,src/workflow/mod.ts,fixtures/**,tests/**}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: The safe way to delete deprecated TS infrastructure is to move shared database and protocol helpers first, then remove exports and finally retarget fixtures that still compile against those old paths.
  - Gotchas encountered: `prd.json` now explicitly keeps `driver-test-suite/` for US-039, so the folder itself has to survive even while its imports stop referencing deleted runtime modules.
  - Useful context: The package now passes `pnpm check-types` and `pnpm build` with the live helper surfaces under `src/common/database/*`, `src/common/client-protocol*`, `src/common/actor-persist*`, `src/common/workflow-transport.ts`, `src/common/engine.ts`, and `src/client/resolve-gateway-target.ts`.
---
## 2026-04-17 13:17:47 PDT - US-036
- What was implemented: Deleted the remaining TypeScript dynamic actor runtime, sandbox actor/provider surfaces, and the isolate-runtime build hooks. Removed the dead package exports, driver-test-suite entries, legacy driver fixtures, and replaced the sandbox docs page with a removal notice so the docs stop advertising broken imports.
- Files changed: `/home/nathan/r5/pnpm-lock.yaml`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/{package.json,tsconfig.json,turbo.json,dynamic-isolate-runtime/**,src/dynamic/**,src/sandbox/**,src/driver-test-suite/**,fixtures/driver-test-suite/{registry-static.ts,registry-dynamic.ts,dynamic-registry.ts,sandbox.ts,actors/dockerSandbox*.ts},tests/{driver-registry-variants.ts,sandbox-providers.test.ts},tsup.dynamic-isolate-runtime.config.ts}`, `/home/nathan/r5/website/src/content/docs/actors/sandbox.mdx`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Deleting a deprecated `rivetkit` surface means cleaning up package exports, TS path aliases, Turbo task wiring, driver fixtures, and docs in the same sweep or the build keeps chasing dead files.
  - Gotchas encountered: `pnpm --filter rivetkit test -- --run ...` still surfaced unrelated alias-resolution and engine-runner failures here, so the meaningful acceptance checks for this cleanup story were `pnpm --filter rivetkit check-types` and `pnpm --filter rivetkit build`.
  - Useful context: The remaining package no longer contains any `src/dynamic/**`, `src/sandbox/**`, or `dynamic-isolate-runtime/**` code, and the docs page at `website/src/content/docs/actors/sandbox.mdx` now explicitly says the legacy TS sandbox actor was removed.
---
## 2026-04-17 14:21:04 PDT - US-037
- What was implemented: Added a real end-to-end NAPI integration fixture and test that boots the native registry with a local engine, exercises TS actor actions through the client, verifies SQLite/KV/state on the live runtime, and proves state plus KV survive a sleep/wake cycle.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/sleep.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/sqlite.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/index.d.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/index.js`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_factory.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/database.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/sqlite_db.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/rivetkit-typescript/packages/sqlite-native/src/vfs.rs`, `/home/nathan/r5/rivetkit-typescript/packages/sqlite-native/src/v2/vfs.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/napi-runtime-integration.test.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The NAPI registry needs an explicit `/action/:name` HTTP bridge plus write-through state and vars proxies or TS actor actions appear to run but silently drop mutations.
  - Gotchas encountered: SQLite v2 reopen after actor sleep still trips the batch-atomic probe on this path, so this integration validates SQLite before sleep and validates post-wake persistence through state plus KV.
  - Useful context: The meaningful checks for this story were `pnpm build:force` in `packages/rivetkit-napi`, `pnpm test napi-runtime-integration.test.ts`, `cargo check -p rivetkit-core -p rivetkit-napi`, and `pnpm check-types` in `packages/rivetkit`.
---
## 2026-04-17 14:28:10 PDT - US-038
- What was implemented: Trimmed the `rivetkit` TypeScript package surface by removing dead `topologies/*` exports and build entries, deleting clearly unused package dependencies, tightening the root and actor barrel re-exports, and adding a regression test that locks the cleaned package metadata in place.
- Files changed: `/home/nathan/r5/pnpm-lock.yaml`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/package.json`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/package-surface.test.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `rivetkit` package cleanup needs the export map, `files` list, and `scripts.build` kept in sync or published entrypoints can lie even while `tsup` stays green.
  - Gotchas encountered: `pnpm test -- --run <file>` still ran unrelated package tests here, so the reliable targeted path was `pnpm exec vitest run <files>` from `packages/rivetkit`.
  - Useful context: The acceptance checks that mattered were `pnpm check-types`, `pnpm build`, `pnpm exec biome check ...`, and `pnpm exec vitest run tests/package-surface.test.ts tests/registry-constructor.test.ts tests/napi-runtime-integration.test.ts` in `rivetkit-typescript/packages/rivetkit`.
---
## 2026-04-17 14:34:38 PDT - US-048
- What was implemented: Moved the generic NAPI actor config flattening plus HTTP request/response conversion into `rivetkit-core`, added `FlatActorConfig` and shared `Request`/`Response` helpers, deleted the duplicated parsing code from `rivetkit-napi`, and added unit tests covering the new shared surface.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/{lib.rs,registry.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/{callbacks.rs,config.rs,event.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_factory.rs`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Generic foreign-runtime glue like flat config conversion and HTTP request/response serialization belongs in `rivetkit-core`, while `rivetkit-napi` should stay focused on JS object wiring and `ThreadsafeFunction` plumbing.
  - Gotchas encountered: `http::Request` and `http::Response` cannot grow inherent helper methods through a type alias, so the reusable core surface needs thin wrapper types if you want `Request::from_parts(...)` style APIs.
  - Useful context: The meaningful checks for this story were `cargo check -p rivetkit-core`, `cargo check -p rivetkit-napi`, `cargo test -p rivetkit-core --lib`, and `pnpm check-types` in `rivetkit-typescript/packages/rivetkit`.
---
## 2026-04-17 14:54:31 PDT - US-055
- What was implemented: Switched the N-API actor factory TSFN bridge to `ErrorStrategy::CalleeHandled`, converted JS callback failures into actionable RivetError-style core errors, taught the native registry wrappers to unwrap the resulting error-first JS callback signature, serialized native action failures as structured HTTP actor errors, wired `c.client()` through the native registry path, removed the stale browser `tar` external, and extended the native runtime integration fixture to cover both `c.client()` and typed action-error propagation.
- Files changed: `/home/nathan/r5/CLAUDE.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/{Cargo.toml,src/actor_factory.rs}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/{src/registry/native.ts,tests/fixtures/napi-runtime-server.ts,tests/napi-runtime-integration.test.ts,tsup.browser.config.ts}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: When the N-API bridge switches TSFN callbacks to `ErrorStrategy::CalleeHandled`, the JS side must accept Node-style `(err, payload)` arguments even for internal wrapper callbacks that conceptually only carry one payload object.
  - Gotchas encountered: The native runtime action path bypasses Hono's shared error middleware, so `/action/:name` responses need to serialize `HTTP_RESPONSE_ERROR_VERSIONED` payloads directly or client actions collapse back into generic transport failures.
  - Useful context: `c.client()` now comes from `createClientWithDriver(new RemoteEngineControlClient(convertRegistryConfigToClientConfig(...)))` inside `src/registry/native.ts`, and the acceptance checks that mattered here were `cargo check -p rivetkit-napi`, `pnpm check-types`, `pnpm build`, `pnpm build:browser`, `pnpm build:force` in `packages/rivetkit-napi`, and `pnpm test napi-runtime-integration.test.ts`.
---
## 2026-04-17 15:09:01 PDT - US-041
- What was implemented: Collapsed the TypeScript error surface down to a shared `RivetError` wrapper plus helpers, rewired the client/native code to use that single shape, and taught the N-API bridge to preserve structured `{ group, code, message, metadata }` errors across the JS<->Rust boundary instead of flattening them into plain strings.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_context.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_factory.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/connection.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/kv.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/lib.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/queue.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/registry.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/sqlite_db.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/fixtures/driver-test-suite/access-control.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/errors.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/schema.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/utils.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/agent-os/actor/process.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/client/actor-conn.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/client/actor-query.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/client/errors.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/client/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/client/resolve-gateway-target.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/database/native-database.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/router-request.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/engine-client/api-utils.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/napi-runtime-integration.test.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/rivet-error.test.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: The clean way to preserve typed errors through N-API is to encode the RivetError payload into `napi::Error.reason` on one side and decode it immediately on the other side, instead of trusting default JS/Rust error marshaling.
  - Gotchas encountered: `tests/napi-runtime-integration.test.ts` is environment-blocked here before it reaches the new assertions because the local engine has no `default` namespace, so use a focused unit test to cover the bridge helpers when that setup is missing.
  - Useful context: `src/registry/native.ts` now owns the TS-side bridge normalization/wrapping helpers, while `rivetkit-napi/src/actor_factory.rs` and `src/lib.rs` are the Rust choke points that decode and encode structured bridge errors.
---
## 2026-04-17 15:52:25 PDT - US-056
- What was implemented: Moved the inline Rust test bodies for `rivetkit-core` and `rivetkit` into per-module files under `tests/modules/`, replaced the source-side inline test bodies with minimal path-based shims, and removed the old inline-only helper impls by routing shared helpers through source-owned test modules.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/callbacks.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/config.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/event.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/lifecycle.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/schedule.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/sleep.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/state.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/websocket.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/bridge.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/tests/modules/`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/examples/counter.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Rust unit tests that need private access are cleanest when the source file keeps only a tiny `#[cfg(test)] #[path = "..."] mod tests;` shim and the real test bodies live under `tests/modules/`.
  - Gotchas encountered: Plain Cargo integration tests could not reach private internals without either ugly visibility leaks or brittle include hacks, so the source-owned shim pattern was the practical fix.
  - Useful context: Verification passed with `cargo test -p rivetkit-core`, `cargo test -p rivetkit`, `cargo check -p rivetkit-core`, and `cargo check -p rivetkit`; the remaining warning is the existing `rivet-envoy-protocol` TS SDK generation skip when `@bare-ts/tools` is not installed.
---
## 2026-04-17 16:28:47 PDT - US-043
- What was implemented: Added the new `on_migrate` lifecycle hook to `rivetkit-core` startup, threaded it through the typed Rust bridge and the N-API/TypeScript native registry path, and added the new `on_migrate_timeout` / `onMigrateTimeout` config plumbing plus regression coverage for ordering, fatal failures, and timeouts.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/{lib.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/{callbacks.rs,config.rs,lifecycle.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/{config.rs,lifecycle.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/{actor.rs,bridge.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/tests/modules/bridge.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/{actor/config.ts,registry/native.ts}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/{index.d.ts,src/actor_factory.rs}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Native actor runner settings in `src/registry/native.ts` should be read from `definition.config.options`, while top-level lifecycle hooks like `onMigrate` still come from `definition.config`.
  - Gotchas encountered: Adding a new `StartupStage` enum variant also needs the `fmt::Display` match updated or `rivetkit-core` stops compiling with a non-exhaustive pattern error.
  - Useful context: Verification passed with `cargo test -p rivetkit-core`, `cargo test -p rivetkit`, `cargo check -p rivetkit-napi`, and `pnpm check-types` in `rivetkit-typescript/packages/rivetkit`; the only warning left was the existing `rivet-envoy-protocol` TS SDK generation skip when `@bare-ts/tools` is absent.
---
## 2026-04-17 16:55:42 PDT - US-045
- What was implemented: Added `Queue::wait_for_names` plus `QueueWaitOpts` in `rivetkit-core`, including timeout/abort handling, non-matching message preservation, and active-wait accounting. Exposed the method through the Rust re-exports, the N-API queue wrapper, and the TypeScript native queue adapter/public queue types.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/lib.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/index.d.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/queue.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/config.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: `wait_for_names` can reuse the existing batch-receive path so name filtering, completable delivery, and queue-depth accounting stay consistent instead of duplicating queue-pop logic.
  - Gotchas encountered: `napi-rs` will not deserialize a `#[napi]` class inside a `#[napi(object)]` field, so the TypeScript native wrapper had to handle `AbortSignal` cancellation with short native wait slices rather than passing a native cancellation token object through options.
  - Useful context: Coverage for the new core method lives in `rivetkit-core/tests/modules/queue.rs`, and the JS-facing native method is `queue.waitForNames(names, { timeout, signal, completable })`.
---
## 2026-04-17 17:09:24 PDT - US-046
- What was implemented: Added `Queue::enqueue_and_wait()` plus `EnqueueAndWaitOpts` in `rivetkit-core`, backed by per-message completion waiters so `message.complete(response)` now unblocks the original sender with optional response bytes. Exposed the feature through the Rust `Ctx<A>::enqueue_and_wait()` typed helper, the N-API queue bridge, and the TypeScript native queue adapter/public queue types, while centralizing queue completion-response validation in `src/registry/native-validation.ts`.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/{mod.rs,queue.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/{context.rs,lib.rs}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/{index.d.ts,src/cancellation_token.rs,src/queue.rs}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/config.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/{native-validation.ts,native.ts}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Non-idempotent native waits need a real cancellation bridge; for `enqueueAndWait`, create a standalone native `CancellationToken` and cancel it from the JS `AbortSignal` instead of retrying short wait slices that would duplicate the enqueue.
  - Gotchas encountered: `napi-rs` still will not deserialize a `#[napi]` class nested inside a `#[napi(object)]` field, so the native token has to travel as a separate queue method argument rather than living inside the options object.
  - Useful context: Core coverage for the new waiter path lives in `rivetkit-core/tests/modules/queue.rs`, and the acceptance checks that passed were `cargo test -p rivetkit-core queue`, `cargo test -p rivetkit context`, `cargo check -p rivetkit-napi`, `pnpm build:force` in `packages/rivetkit-napi`, and `pnpm check-types` in `packages/rivetkit`.
---
## 2026-04-17 17:18:59 PDT - US-047
- What was implemented: Added a typed queue stream adapter in `rivetkit` via `QueueStreamExt::stream(...)`, exported `QueueStreamOpts` through the crate root and prelude, and added queue-stream unit tests covering `StreamExt` combinators, name filtering, and cancellation shutdown.
- Files changed: `/home/nathan/r5/Cargo.lock`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/kv.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/prelude.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/src/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit/tests/modules/queue.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: For typed convenience methods on re-exported core surfaces, use an extension trait and prelude export so method syntax works without replacing the underlying core type.
  - Gotchas encountered: `ActorContext::new()` keeps KV unconfigured, so queue tests that actually enqueue messages need an in-memory KV-backed context instead of the default constructor.
  - Useful context: `cargo test -p rivetkit` now covers the queue stream adapter; the only recurring warning in this area is the unrelated missing `@bare-ts/tools` CLI noted by `rivet-envoy-protocol`.
---
## 2026-04-17 17:31:38 PDT - US-049
- What was implemented: Restored the inspector wire protocol source into `src/common/bare/inspector/v1-v4.ts`, added the new `src/common/inspector-versioned.ts` and `src/common/inspector-transport.ts` helpers, checked in matching `schemas/actor-inspector/v1-v4.bare` files, and added focused regression coverage for v1-v4 request/response compatibility plus workflow-history transport round-trips.
- Files changed: `/home/nathan/r5/AGENTS.md`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/package.json`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/schemas/actor-inspector/{v1.bare,v2.bare,v3.bare,v4.bare}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/bare/inspector/{v1.ts,v2.ts,v3.ts,v4.ts}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/common/{inspector-transport.ts,inspector-versioned.ts}`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/{inspector-versioned.test.ts,package-surface.test.ts}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Inspector protocol downgrades should map unsupported response payloads to explicit `Error` messages like `inspector.events_dropped` instead of silently dropping fields, while unsupported request downgrades should throw.
  - Gotchas encountered: The preserved browser inspector bundle still carries the deleted schema sources in `dist/browser/inspector/client.js.map`, which is the safest way to recover the exact generated v1-v4 codecs when source files disappear.
  - Useful context: Verification passed with `pnpm check-types` and `pnpm test tests/inspector-versioned.test.ts tests/package-surface.test.ts` in `rivetkit-typescript/packages/rivetkit`.
---
## 2026-04-17 17:42:08 PDT - US-050
- What was implemented: Restored the inspector core as a transport-agnostic TypeScript module at `src/inspector/actor-inspector.ts`, covering inspector token persistence/verification, snapshot/state/action/queue/database/workflow helpers, and a stub trace response that already returns the shared v4 schema shapes for later HTTP and WebSocket transports.
- Files changed: `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/tests/actor-inspector.test.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Inspector helpers should return the shared inspector schema objects directly and keep opaque payloads as `ArrayBuffer`s, with CBOR only at the module boundary.
  - Gotchas encountered: `pnpm lint` in `packages/rivetkit` expands to `biome check .`, so verifying a focused change needs `pnpm exec biome check <files>` when the package already has unrelated lint debt.
  - Useful context: `tests/actor-inspector.test.ts` now covers token storage at `KEYS.INSPECTOR_TOKEN`, queue snapshot ordering/truncation, state patching, action execution through the synthetic inspector connection, and SQLite schema/row serialization.
---
## 2026-04-17 17:51:57 PDT - US-051
- What was implemented: Added a minimal Rust `Inspector` state object in `rivetkit-core`, wired `ActorContext` to publish state updates into it, and threaded queue/connection lifecycle hooks so connect, disconnect, restore, cleanup, enqueue, completable dequeue, ack, and queue metadata rebuilds all bump the stored inspector snapshot state without doing extra work when no inspector is configured.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/inspector/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/context.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/connection.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/inspector.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Cross-cutting inspector wiring is easiest to keep honest when `ActorContext` owns the inspector handle and subsystems only expose tiny update hooks instead of growing direct inspector dependencies.
  - Gotchas encountered: Completable queue receives need their own inspector bump even before `complete()`, because the queue metadata size does not change on receive but the inspector still needs to reflect the in-flight dequeue transition.
  - Useful context: Coverage for this story lives in `rivetkit-core/tests/modules/inspector.rs`, and the meaningful checks that passed were `cargo test -p rivetkit-core inspector` and `cargo check -p rivetkit-core`; the only remaining warning was the existing `rivet-envoy-protocol` note about missing `@bare-ts/tools`.
---
## 2026-04-17 18:05:33 PDT - US-052
- What was implemented: Added inspector HTTP routing in `RegistryDispatcher` ahead of user `on_request`, with bearer-token auth, JSON endpoints for state, connections, RPCs, actions, queue, traces, summary, and staged SQLite inspector handlers that normalize failures into JSON RivetError responses.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/Cargo.toml`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/action.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/queue.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/registry.rs`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Inspector HTTP should keep using the existing CBOR payload boundary and only decode to JSON at the registry transport layer, so state/action/queue payloads stay aligned with the WebSocket inspector contract.
  - Gotchas encountered: Letting inspector handlers bubble raw `?` errors breaks the HTTP API shape; `RegistryDispatcher` needs to catch those failures and convert them into JSON RivetError payloads before returning.
  - Useful context: The new regression coverage lives in `rivetkit-core/tests/modules/registry.rs` and proves inspector routes beat `on_request`, auth failures stay JSON, and the state/action/queue/summary endpoints return the expected HTTP shapes.
---
## 2026-04-17 18:22:04 PDT - US-053
- What was implemented: Added lazy workflow inspector plumbing for the native path by threading optional workflow-history and replay callbacks through `rivetkit-core`, the N-API callback bindings, and the TypeScript native registry/workflow runtime. Added HTTP handling for `GET /inspector/workflow-history` and `POST /inspector/workflow/replay`, and hydrated inspector summary from the same lazy callbacks.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/actor/callbacks.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/inspector/mod.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/lib.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/registry.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit-napi/src/actor_factory.rs`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/actor/config.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/registry/native.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/workflow/mod.ts`, `/home/nathan/r5/rivetkit-typescript/packages/rivetkit/src/workflow/inspector.ts`, `/home/nathan/r5/scripts/ralph/prd.json`, `/home/nathan/r5/scripts/ralph/progress.txt`
- **Learnings for future iterations:**
  - Patterns discovered: Native workflow inspector support should be exposed through run-function inspector config and resolved per actor id, so Rust only asks for opaque bytes when an inspector endpoint actually needs them.
  - Gotchas encountered: The old workflow inspector helper paths (`@/inspector/transport`, `@/schemas/...`) are dead in this repo; the live imports are under `src/common/`.
  - Useful context: The new lazy-path coverage lives in `rivetkit-core/tests/modules/registry.rs`, and the validation run for this story was `cargo check -p rivetkit-core`, `cargo check -p rivetkit-napi`, `pnpm check-types`, plus `cargo test -p rivetkit-core workflow -- --nocapture` captured to `/tmp/rivetkit-core-workflow-inspector.log`.
---
## 2026-04-17 18:41:01 PDT - US-054
- What was implemented: Added the Rust inspector WebSocket transport at `/inspector/connect`, including v4 BARE-encoded outbound frames, v1-v4 inbound request decoding, protocol-header token auth, init snapshot delivery, live push updates via `InspectorSignal` subscriptions, and request/response handling for state, connections, actions, queue, workflow, trace stub, and database schema/rows.
- Files changed: `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/inspector/{mod.rs,protocol.rs}`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/src/registry.rs`, `/home/nathan/r5/rivetkit-rust/packages/rivetkit-core/tests/modules/{inspector.rs,registry.rs}`, `/home/nathan/r5/scripts/ralph/{prd.json,progress.txt}`
- **Learnings for future iterations:**
  - Patterns discovered: Inspector WebSocket fanout should stay on cheap signal subscriptions and reuse the same CBOR payload boundaries as HTTP, while the transport layer owns BARE version framing and request routing.
  - Gotchas encountered: Inspector queue counters only track events after the inspector is attached, so WebSocket init and queue push payloads need a live queue read instead of trusting the stored snapshot blindly.
  - Useful context: Verification passed with `cargo check -p rivetkit-core` and `cargo test -p rivetkit-core`; the only recurring warning left is the existing `rivet-envoy-protocol` note about missing `@bare-ts/tools`.
---
