Skip to content

docs(rivetkit): document keepAwake/waitUntil counter-arm race and surface bridge errors#4759

Draft
NathanFlurry wants to merge 1 commit intosleep-cleanup/13-alarm-test-deterministicfrom
sleep-cleanup/14-document-counter-arm-race
Draft

docs(rivetkit): document keepAwake/waitUntil counter-arm race and surface bridge errors#4759
NathanFlurry wants to merge 1 commit intosleep-cleanup/13-alarm-test-deterministicfrom
sleep-cleanup/14-document-counter-arm-race

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

NathanFlurry commented Apr 24, 2026

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown

claude Bot commented Apr 24, 2026

Code Review: PR #4759

PR state: Draft | Files changed: 1 (native.ts) | Net diff: +40/-8


Summary

This PR (1) documents the pre-existing counter-arm race in keepAwake and waitUntil, and (2) surfaces previously-silent bridge rejections by replacing void callNative(...) with explicit .catch() handlers that log warnings for unexpected failures and swallow the expected teardown-time errors.


Correctness

The changes are correct. The .catch() pattern matches what is already used for internalKeepAwake (sync path) and aligns with the CLAUDE.md "fail-by-default" invariant. stringifyError is appropriate as it handles all realistic callNative rejection shapes.


Issues and Observations

1. Comment style deviates from CLAUDE.md conventions (minor)

CLAUDE.md: "Write comments as normal, complete sentences. Avoid fragmented structures with parentheticals and dashes."

Several new comments use fragmented constructions, e.g.:

// for the duration of the promise (blocks both idle sleep and grace finalize).
// Counter-arm race (acceptable): the NAPI `keep_awake` call is async,

Suggested complete-sentence rewrites:

// Forward to core keep_awake. The counter blocks both idle sleep and grace finalize.
// Counter-arm race note: the NAPI keep_awake call is async, so the Rust
// keep_awake_guard increment happens on first poll, not when JS calls this method.

2. Race comment conflates can_arm_sleep_timer vs. can_finalize_sleep (non-blocking)

The comment says: "In practice the idle timer runs on sleep_timeout (default 30s), so the next poll always observes the counter before the timer fires."

This conflates two predicate paths:

  • can_arm_sleep_timer: arms the deadline, not immediate sleep. A missed count defers arming by one tick (harmless).
  • can_finalize_sleep: actively blocks grace teardown. The waitUntil comment correctly identifies this path.

The keepAwake comment should clarify the risk is arming the idle deadline incorrectly, not triggering sleep prematurely.

3. Other void callNative calls remain (follow-up opportunity)

At least one other fire-and-forget void callNative(...) exists in the same file (the WebSocket close path) with the same silent-failure characteristic. Worth a follow-up to apply the same .catch() pattern for consistency.

4. No test coverage for isClosedTaskRegistrationError matching

The check relies on a regex against a RivetError metadata string (/actor task registration is (closed|not configured)/). If the underlying Rust error message changes, this will silently stop suppressing teardown errors and they will appear as warnings. A small unit test asserting the classifier correctly identifies known teardown errors would prevent silent regression.


Verdict

The fix is correct and safe. Main recommendations before marking ready:

  1. Fix comment style to match CLAUDE.md conventions (complete sentences, no parenthetical fragments).
  2. Add a test that validates isClosedTaskRegistrationError so the regex does not silently break on future Rust message changes.
  3. Track remaining void callNative fire-and-forget calls as a follow-up.

@NathanFlurry NathanFlurry force-pushed the sleep-cleanup/14-document-counter-arm-race branch from c76a37f to 3d62102 Compare April 24, 2026 13:16
@NathanFlurry NathanFlurry force-pushed the sleep-cleanup/13-alarm-test-deterministic branch from 3f88401 to 0e7355d Compare April 24, 2026 13:16
@github-actions
Copy link
Copy Markdown
Contributor

Preview packages published to npm

Install with:

npm install rivetkit@pr-4759

All packages published as 0.0.0-pr.4759.e2b1650 with tag pr-4759.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-e2b1650
docker pull rivetdev/engine:full-e2b1650
Individual packages
npm install rivetkit@pr-4759
npm install @rivetkit/react@pr-4759
npm install @rivetkit/rivetkit-napi@pr-4759
npm install @rivetkit/workflow-engine@pr-4759

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant