|
162 | 162 | "priority": 10, |
163 | 163 | "passes": true, |
164 | 164 | "notes": "Investigated and documented the Env-bearing cleanup path plus constraints in docs-internal/engine/napi-bridge.md. Implementation deferred until a NAPI integration test can verify TSF drain and reference counts." |
| 165 | + }, |
| 166 | + { |
| 167 | + "id": "US-011", |
| 168 | + "title": "Drop message from wasm BRIDGE_RIVET_ERROR_SCHEMAS cache key", |
| 169 | + "description": "As an operator running a long-lived wasm worker, I want bridged JS error decoding to intern at most one schema per `(group, code)` so callers whose error messages vary per call (timestamps, request IDs, dynamic context) cannot grow the schema map and `Box::leak` arena unboundedly. The current US-002 fix keys on `(group, code, message)`, which still leaks one schema per unique message and re-introduces the same class of leak the PR is trying to close. The NAPI-side `STRUCTURED_TIMEOUT_SCHEMAS` already keys only on `(&'static str, &'static str)` (`rivetkit-typescript/packages/rivetkit-napi/src/napi_actor_events.rs:96-98`); wasm must match.", |
| 170 | + "acceptanceCriteria": [ |
| 171 | + "`BridgeRivetErrorSchemaKey` in `rivetkit-typescript/packages/rivetkit-wasm/src/lib.rs:2596` becomes `(String, String)` (or equivalent two-tuple) keyed only on `(group, code)`", |
| 172 | + "`bridge_rivet_error_schema` (`lib.rs:2599-2618`) inserts the first-seen `default_message` for a given `(group, code)` and reuses that schema for subsequent decodes regardless of payload message", |
| 173 | + "The actual per-error `message` field continues to flow through `RivetTransportError.message: Some(payload.message)` so callers still see the live message", |
| 174 | + "Update `parse_bridge_rivet_error_reuses_interned_schema` to assert that varying only the message for the same `(group, code)` reuses the schema (and therefore does NOT increment `BRIDGE_RIVET_ERROR_SCHEMAS.len()`)", |
| 175 | + "Add a second test (or extend the existing one) that varying `(group, code)` does still allocate a new schema", |
| 176 | + "`pnpm --filter @rivetkit/rivetkit-wasm run check:package` passes; `cargo test -p rivetkit-wasm` passes", |
| 177 | + "Typecheck passes", |
| 178 | + "Tests pass" |
| 179 | + ], |
| 180 | + "priority": 11, |
| 181 | + "passes": true, |
| 182 | + "notes": "Wasm bridged RivetError schemas now intern by `(group, code)` only while preserving each payload's live message on the transport error. Native `cargo test -p rivetkit-wasm` still hits the pre-existing host-target `wasm_bindgen_futures` compile issue; wasm-target cargo test compilation and package checks pass." |
| 183 | + }, |
| 184 | + { |
| 185 | + "id": "US-012", |
| 186 | + "title": "Bound register_task shutdown drain against shutdown deadline", |
| 187 | + "description": "As an operator, I want `ctx.registerTask(promise)` not to block actor shutdown indefinitely when the user-supplied JS promise never resolves. `WasmActorContext::register_task` (`rivetkit-typescript/packages/rivetkit-wasm/src/lib.rs:1345-1356`) and the analogous NAPI path route through `ActorContext::register_task` -> `track_shutdown_task`, so a never-resolving promise hangs shutdown drain. This contradicts the new CLAUDE.md rule added in this PR (\"Spawned futures that capture JS callbacks or other heavy resources must have a guaranteed completion path (e.g. a `CancellationToken` whose clones are guaranteed to drop)\").", |
| 188 | + "acceptanceCriteria": [ |
| 189 | + "`ActorContext::register_task` in `rivetkit-rust/packages/rivetkit-core/src/actor/context.rs:552-587` races the user future against `ctx.shutdown_deadline_token().cancelled()` (or equivalent) so registered tasks unblock shutdown when the grace deadline elapses", |
| 190 | + "Cancellation cause is logged at `tracing::warn!` with `actor_id` and a stable `reason` field so operators can find hanging registered tasks", |
| 191 | + "Behavior is identical for the `wasm-runtime` and native `cfg(not(feature = \"wasm-runtime\"))` variants of `register_task`", |
| 192 | + "Wasm `WasmActorContext::register_task` does not need a separate cancel path because core handles it", |
| 193 | + "Add a Rust integration test in `rivetkit-rust/packages/rivetkit-core/tests/` that registers a never-completing future, triggers shutdown, and asserts shutdown completes within a bounded deadline rather than hanging", |
| 194 | + "Typecheck passes", |
| 195 | + "Tests pass" |
| 196 | + ], |
| 197 | + "priority": 12, |
| 198 | + "passes": false, |
| 199 | + "notes": "" |
| 200 | + }, |
| 201 | + { |
| 202 | + "id": "US-013", |
| 203 | + "title": "Avoid panic on websocket_callback_regions ID overflow", |
| 204 | + "description": "As an operator running a long-lived wasm actor with frequent websocket callbacks, I do not want my actor to crash after ~4B handshakes because `next_websocket_callback_region_id` overflows `u32`. The current code (`rivetkit-typescript/packages/rivetkit-wasm/src/lib.rs:1351-1355`) uses `checked_add(1).expect(\"websocket callback region id overflow\")`, which crashes the actor process instead of degrading.", |
| 205 | + "acceptanceCriteria": [ |
| 206 | + "Either bump `next_websocket_callback_region_id` to `Cell<u64>` (and update the `begin_websocket_callback` return type plus any wasm-bindgen surface that exposes it) so overflow becomes operationally unreachable", |
| 207 | + "Or implement wraparound that skips IDs already present in `websocket_callback_regions` so reuse is safe while regions are in flight", |
| 208 | + "Either approach must keep IDs strictly monotonic for IDs currently in the map so no two live regions ever share an ID", |
| 209 | + "Add a wasm crate test that exercises the wraparound (or large-counter) path to confirm no panic and no ID collision with live regions", |
| 210 | + "If u32 is preserved, the test seeds `next_websocket_callback_region_id` near `u32::MAX` and verifies wraparound", |
| 211 | + "Typecheck passes", |
| 212 | + "Tests pass" |
| 213 | + ], |
| 214 | + "priority": 13, |
| 215 | + "passes": false, |
| 216 | + "notes": "" |
| 217 | + }, |
| 218 | + { |
| 219 | + "id": "US-014", |
| 220 | + "title": "Document global cache delta in parse_bridge_rivet_error test", |
| 221 | + "description": "As a future contributor adding tests under `rivetkit-typescript/packages/rivetkit-wasm/src/lib.rs`, I want a comment on `parse_bridge_rivet_error_reuses_interned_schema` (`lib.rs:2715-2737`) explaining that its `BRIDGE_RIVET_ERROR_SCHEMAS.len()` delta assertions only hold because the test owns a unique `(group, code)` namespace (`wasm_schema_cache_test`/`same_payload`). Without the comment, a future test that shares the global cache namespace will silently flip this assertion under parallel `cargo test`.", |
| 222 | + "acceptanceCriteria": [ |
| 223 | + "Add a one or two line comment immediately above `parse_bridge_rivet_error_reuses_interned_schema` (or above the `initial_count` capture) noting that the test relies on a dedicated `(group, code)` namespace so concurrent tests do not perturb the global `BRIDGE_RIVET_ERROR_SCHEMAS.len()` delta", |
| 224 | + "Comment names the namespace strings (`wasm_schema_cache_test`, `same_payload`) so future contributors know what to avoid", |
| 225 | + "If US-011 lands first and changes the cache key shape, update the comment to reflect the new key", |
| 226 | + "Typecheck passes", |
| 227 | + "Tests pass" |
| 228 | + ], |
| 229 | + "priority": 14, |
| 230 | + "passes": false, |
| 231 | + "notes": "" |
165 | 232 | } |
166 | 233 | ] |
167 | 234 | } |
0 commit comments