Skip to content

feat(remote-comms): cross-incarnation wake detection#822

Merged
sirtimid merged 6 commits intomainfrom
sirtimid/cross-incarnation-wake-detection
Feb 10, 2026
Merged

feat(remote-comms): cross-incarnation wake detection#822
sirtimid merged 6 commits intomainfrom
sirtimid/cross-incarnation-wake-detection

Conversation

@sirtimid
Copy link
Copy Markdown
Contributor

@sirtimid sirtimid commented Feb 10, 2026

Summary

Detects when the kernel process dies during system sleep and restarts after wake (#690). The runtime installWakeDetector can't catch this since the process wasn't running. This feature persists a lastActiveTime timestamp, compares it on next initRemoteComms(), and resets reconnection backoffs if the gap exceeds 1 hour.

How it works

  • store/methods/activity.ts: detectWake() reads the persisted lastActiveTime, checks if the gap exceeds 1 hour, writes a fresh timestamp. recordLastActiveTime() writes the current time (called from Kernel.stop()).
  • remote-comms.ts: initRemoteComms() calls detectWake() before transport init. If wake detected, calls resetAllBackoffs() after transport is up.
  • PlatformServices.resetAllBackoffs(): New method across the full stack (type, RPC spec, Node/browser implementations). Delegates to ReconnectionManager.resetAllBackoffs().

Design notes

  • False positives (e.g. long uptime → crash → restart) are harmless: resetAllBackoffs() on a fresh ReconnectionManager is a no-op.
  • If stop() never runs (crash), lastActiveTime is stale from the previous detectWake(), so the gap is larger — correct behavior.

Closes #690

🤖 Generated with Claude Code


Note

Medium Risk
Touches remote-communications initialization and transport reconnection behavior; incorrect wake detection or RPC wiring could change reconnect timing or introduce new startup-path failures, though changes are small and well-covered by tests.

Overview
Adds cross-incarnation wake detection by persisting a lastActiveTime timestamp in the kernel KV store (new activity store methods, recorded on Kernel.stop()), and checking it during initRemoteComms().

When the gap since the previous timestamp exceeds a 1-hour threshold, initRemoteComms() now logs the event and calls a new platform service, resetAllBackoffs(), which is plumbed through types + RPC (resetAllBackoffs method spec/handler) and implemented in both browser and Node platform services by delegating to transport’s reconnection manager.

Written by Cursor Bugbot for commit a219b85. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 10, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 78.23%
⬇️ -0.01%
6195 / 7918
🔵 Statements 78.21%
⬇️ -0.01%
6294 / 8047
🔵 Functions 76.41%
⬇️ -0.06%
1575 / 2061
🔵 Branches 78.22%
⬇️ -0.05%
2259 / 2888
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/kernel-browser-runtime/src/PlatformServicesClient.ts 90.9%
⬇️ -1.40%
75.86%
🟰 ±0%
80.95%
⬇️ -4.05%
90.9%
⬇️ -1.40%
110, 132, 290, 327-330
packages/kernel-browser-runtime/src/PlatformServicesServer.ts 89.24%
⬇️ -3.78%
82.75%
⬇️ -6.13%
70.83%
⬇️ -3.08%
89.24%
⬇️ -3.78%
143, 166, 197, 380-384, 427-444
packages/nodejs/src/kernel/PlatformServices.ts 91.01%
⬇️ -2.96%
82.6%
⬇️ -7.87%
83.33%
⬇️ -4.90%
91.01%
⬇️ -2.96%
146-149, 187, 217-222, 345-348
packages/ocap-kernel/src/Kernel.ts 91.25%
⬆️ +0.12%
80.95%
🟰 ±0%
87.8%
🟰 ±0%
91.25%
⬆️ +0.12%
117, 226-229, 411, 481, 491-492, 535
packages/ocap-kernel/src/types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/remotes/kernel/remote-comms.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/remotes/platform/transport.ts 82.74%
⬆️ +0.16%
80.72%
🟰 ±0%
76.66%
⬆️ +0.80%
82.74%
⬆️ +0.16%
114, 154-155, 160-164, 206-215, 248, 282-300, 324, 408, 452-455, 479, 503-508, 511-512, 516-519, 560, 590, 609-611, 620
packages/ocap-kernel/src/rpc/platform-services/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/rpc/platform-services/resetAllBackoffs.ts 100% 100% 100% 100%
packages/ocap-kernel/src/store/index.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
packages/ocap-kernel/src/store/methods/activity.ts 100% 100% 100% 100%
Generated in workflow #3633 for commit 47568fb by the Vitest Coverage Report Action

@sirtimid sirtimid force-pushed the sirtimid/cross-incarnation-wake-detection branch from c2815c8 to aa3187b Compare February 10, 2026 13:36
sirtimid and others added 5 commits February 10, 2026 16:43
Write a `lastActiveTime` timestamp to persistent storage on remote comms
initialization and compare it with the current time on the next startup.
When the gap exceeds 1 hour, treat it as a wake event and reset all
reconnection backoffs, complementing the existing runtime wake detector.

Writing at startup (rather than only on graceful shutdown) ensures the
timestamp is always recent, even after abrupt process termination.

Closes #690

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move wake detection from remote-comms (transport-specific) to
Kernel.initRemoteComms() for a cleaner architecture. The Kernel
now reads/writes `lastActiveTime` in KV and calls the new
`PlatformServices.resetAllBackoffs()` method when a wake is detected.

This removes `crossIncarnationWake` from RemoteCommsOptions and the
RPC initializeRemoteComms params, avoiding options pollution with
runtime state. Detection is now general-purpose at the Kernel level.

Design reasoning:
- `lastActiveTime` is written on both init and stop, covering both
  abrupt shutdowns (crash/kill) and graceful stops.
- No timer or crank hook is needed because false positives are harmless:
  `resetAllBackoffs()` on a fresh ReconnectionManager is a no-op.
- The 1-hour default threshold is generous enough to avoid false
  positives from normal restarts while catching actual sleep events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract lastActiveTime read/write/detect logic into
KernelStore.detectCrossIncarnationWake() and recordLastActiveTime()
methods, keeping the Kernel class clean. Detection now runs in
#init() (general startup) rather than initRemoteComms(), with the
result stored in a private field for later use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ove wake detection to remote-comms

Move the lastActiveTime persistence logic (detectWake, recordLastActiveTime) from
inline functions in the store index into a dedicated store/methods/activity.ts file,
following the existing store methods pattern. Move the cross-incarnation wake
detection and backoff reset logic from Kernel into remote-comms initRemoteComms where
it naturally belongs alongside transport initialization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove detectCrossIncarnationWake and DEFAULT_CROSS_INCARNATION_WAKE_THRESHOLD_MS
from kernel-utils — the one-liner check is now inlined directly in
store/methods/activity.ts. Add unit tests for activity.ts (detectWake,
recordLastActiveTime) and resetAllBackoffs.ts (RPC spec and handler).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sirtimid sirtimid force-pushed the sirtimid/cross-incarnation-wake-detection branch from 294b065 to a219b85 Compare February 10, 2026 15:45
@sirtimid sirtimid marked this pull request as ready for review February 10, 2026 15:46
@sirtimid sirtimid requested a review from a team as a code owner February 10, 2026 15:46
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

… tests, remove duplicate store tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@grypez grypez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jsdoc implies we need to await the subroutine

if (!this.#resetAllBackoffsFunc) {
return null;
}
this.#resetAllBackoffsFunc();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.#resetAllBackoffsFunc();
await this.#resetAllBackoffsFunc();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resetAllBackoffsFunc is typed as () => void (from initTransport), so it's not a thenable and doesn't need await. The async on the outer method is there to satisfy the PlatformServices interface which declares resetAllBackoffs(): Promise<void> (since it goes through RPC in the browser runtime).

@sirtimid sirtimid requested a review from grypez February 10, 2026 16:48
Copy link
Copy Markdown
Contributor

@grypez grypez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sirtimid sirtimid added this pull request to the merge queue Feb 10, 2026
Merged via the queue into main with commit 887f9dc Feb 10, 2026
36 checks passed
@sirtimid sirtimid deleted the sirtimid/cross-incarnation-wake-detection branch February 10, 2026 17:23
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.

Remote comms: Cross-incarnation wake detection

2 participants