Skip to content

feat(#717): consolidate the worker into a single lifecycle owner - #722

Closed
zgeoff wants to merge 3 commits into
mainfrom
717-worker-lifecycle
Closed

feat(#717): consolidate the worker into a single lifecycle owner#722
zgeoff wants to merge 3 commits into
mainfrom
717-worker-lifecycle

Conversation

@zgeoff

@zgeoff zgeoff commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Description

Closes #717

Three deletions of accidental complexity in the idle worker: continuations become durable start intents, lifecycle flows share one serialized chain, and the simulation handle is never null. Each pays for itself — net code shrinks while a held continuation now survives worker restarts.

  • Durable start intent replaces the in-memory pending continuation: held in the preferences store, delivered idempotently (start key) at resync entry before the snapshot fetch, cleared by a player stop or a fresh player-chosen start. planResync loses its continue kind; the continue apply path is deleted.
  • Lifecycle mailbox: start, resync, and continuation flows run one at a time on a shared chain; stops stay concurrent via the epoch. The per-await ownership captures and the per-flow chain plumbing collapse into one utility, with the stop-back at a single install boundary.
  • Never-null simulation deletes the lazy creations, null guards, and null-uninstalls.
  • Adversarially pre-reviewed; fixes landed for session-expiry intent durability and the park/stop write race.

Testing

  • bun run typecheck passes
  • bun run test passes
  • bun run lint passes
  • New tests added for new functionality

zgeoff added 2 commits July 20, 2026 23:06
Three deletions of accidental complexity in the idle worker, each
paying for itself:

- continuations become durable start intents: a same-row race or a
  transport failure holds a {avatarID, scope, startKey} record in the
  preferences store, delivered idempotently at reconnect and resync
  entry before the snapshot is fetched — the row it mints attaches
  like any other active row. planResync loses its continue plan and
  pending-continuation input, the continue apply path is deleted, and
  a held continuation now survives worker restarts, which the
  in-memory record never did. A player stop or a fresh player start
  clears the held intent.
- lifecycle mailbox: start, resync, and continuation flows run
  strictly one at a time on a shared chain; stops stay concurrent and
  their epoch bump is what queued flows re-check. The resync
  single-flight drop, the continuation's entry-state ownership
  capture, and the per-flow chain plumbing collapse into one utility,
  and a refused install under a landed stop stops the row back at the
  one install boundary instead of per caller.
- never-null simulation: the worker holds a simulation from boot —
  "no run" is an empty one — deleting the null guards, the lazy
  creation in initialize and start, and the null-uninstall in the
  continuation failure paths, which now swap in a fresh instance like
  every other clear.

Closes #717
Findings from an adversarial review of the branch:

- an expired or insufficient session holds the durable start intent instead of dropping it with a spurious fault — losing it there defeated the durability the intent exists for
- a park whose durable write races a concurrent stop's clear is removed by a post-park epoch re-check, keeping the promise that a stopped activity is never resumed
- the reconnect comment matches the activity-null guard's actual behavior
- an externally raised install takes a lifecycle-chain slot like every other flow, and cross-kind chain serialization is covered by a test
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c359abc4-0989-4c75-9a9b-9b4ca8be75f0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The worker replaces in-memory pending continuations with durable start intents, serializes lifecycle operations through one promise chain, keeps a non-null simulation, and updates resync and stop handling for continuation races and interrupted installations.

Changes

Worker lifecycle ownership

Layer / File(s) Summary
Durable start-intent contract and storage
libs/game/idle-client/src/submission/*, libs/game/idle-client/src/resync/*, docs/architecture/game/game-simulation.md
Adds persisted start-intent types and storage helpers, removes pending-continuation resync inputs and the continue plan, and updates coverage and architecture documentation.
Always-present simulation and lifecycle chain
libs/game/idle-client/src/worker/types.ts, libs/game/idle-client/src/worker/create-worker-runtime.ts, libs/game/idle-client/src/worker/run-on-lifecycle-chain.ts, libs/game/idle-client/src/worker/handle-*.ts, libs/game/idle-client/src/test-utils/*
Makes simulations eager and non-null, replaces start-flow state with a lifecycle chain, and routes lifecycle-affecting messages through serialized execution.
Pending-start delivery and outcomes
libs/game/idle-client/src/worker/flush-pending-start.ts, libs/game/idle-client/src/worker/flush-pending-start.test.ts
Flushes durable intents during resync and handles successful delivery, budget exhaustion, conflicts, service refusals, and transport failures.
Continuation parking and ownership checks
libs/game/idle-client/src/worker/run-continuation.ts, libs/game/idle-client/src/worker/run-continuation.test.ts
Parks continuations durably, clears replaced simulations, rechecks ownership, and removes parked intents when a stop intervenes.
Resync installation and stop-race handling
libs/game/idle-client/src/worker/handle-request-resync-message.ts, libs/game/idle-client/src/worker/handle-start-activity-message.ts, libs/game/idle-client/src/worker/handle-stop-activity-message.ts, libs/game/idle-client/src/worker/*test.ts
Extracts the resync flow, removes continuation plan handling, protects live installation with stop-back behavior, and updates start, stop, and resync tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • zgeoff/vers#716: Shares the worker-owned activity start and resync lifecycle changes.
  • zgeoff/vers#710: Shares stop/resync race handling changes.
  • zgeoff/vers#661: Covers the pending-continuation and continue resync behavior replaced here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: consolidating the worker around one lifecycle owner.
Description check ✅ Passed The description is directly about the same worker lifecycle, durable start-intent, and non-null simulation changes.
Linked Issues check ✅ Passed The changes cover durable start intents, lifecycle serialization, and never-null simulation as requested in #717.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes stand out beyond tests and docs supporting the stated worker refactor.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 717-worker-lifecycle

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/game/idle-client/src/resync/plan-resync.test.ts (1)

30-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the duplicate capped-rebase test.

Lines 7-20 already use the same input and assert the same result, so this block adds no coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/game/idle-client/src/resync/plan-resync.test.ts` around lines 30 - 47,
Remove the duplicate test case named “it rebases a capped activity” from
plan-resync.test.ts, keeping the existing equivalent capped-rebase test and its
coverage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@libs/game/idle-client/src/resync/plan-resync.test.ts`:
- Around line 30-47: Remove the duplicate test case named “it rebases a capped
activity” from plan-resync.test.ts, keeping the existing equivalent
capped-rebase test and its coverage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8d0ca734-08d1-4d1e-ab4d-f6d8dff9b51b

📥 Commits

Reviewing files that changed from the base of the PR and between 6058652 and b8122d7.

📒 Files selected for processing (35)
  • docs/architecture/game/game-simulation.md
  • libs/game/idle-client/src/resync/plan-resync.test.ts
  • libs/game/idle-client/src/resync/plan-resync.ts
  • libs/game/idle-client/src/resync/run-resync.ts
  • libs/game/idle-client/src/resync/types.ts
  • libs/game/idle-client/src/submission/constants.ts
  • libs/game/idle-client/src/submission/read-pending-start-intent.test.ts
  • libs/game/idle-client/src/submission/read-pending-start-intent.ts
  • libs/game/idle-client/src/submission/remove-pending-start-intent.test.ts
  • libs/game/idle-client/src/submission/remove-pending-start-intent.ts
  • libs/game/idle-client/src/submission/types.ts
  • libs/game/idle-client/src/submission/write-pending-start-intent.test.ts
  • libs/game/idle-client/src/submission/write-pending-start-intent.ts
  • libs/game/idle-client/src/test-utils/create-stub-worker-context.test.ts
  • libs/game/idle-client/src/test-utils/create-stub-worker-context.ts
  • libs/game/idle-client/src/worker/create-worker-runtime.ts
  • libs/game/idle-client/src/worker/flush-pending-start.test.ts
  • libs/game/idle-client/src/worker/flush-pending-start.ts
  • libs/game/idle-client/src/worker/handle-client-message.ts
  • libs/game/idle-client/src/worker/handle-initialize-message.ts
  • libs/game/idle-client/src/worker/handle-request-resync-message.test.ts
  • libs/game/idle-client/src/worker/handle-request-resync-message.ts
  • libs/game/idle-client/src/worker/handle-set-activity-message.test.ts
  • libs/game/idle-client/src/worker/handle-set-activity-message.ts
  • libs/game/idle-client/src/worker/handle-simulation-update.ts
  • libs/game/idle-client/src/worker/handle-start-activity-message.test.ts
  • libs/game/idle-client/src/worker/handle-start-activity-message.ts
  • libs/game/idle-client/src/worker/handle-stop-activity-message.test.ts
  • libs/game/idle-client/src/worker/handle-stop-activity-message.ts
  • libs/game/idle-client/src/worker/report-worker-fault.ts
  • libs/game/idle-client/src/worker/run-continuation.test.ts
  • libs/game/idle-client/src/worker/run-continuation.ts
  • libs/game/idle-client/src/worker/run-on-lifecycle-chain.test.ts
  • libs/game/idle-client/src/worker/run-on-lifecycle-chain.ts
  • libs/game/idle-client/src/worker/types.ts
💤 Files with no reviewable changes (5)
  • libs/game/idle-client/src/worker/handle-simulation-update.ts
  • libs/game/idle-client/src/worker/handle-set-activity-message.ts
  • libs/game/idle-client/src/resync/run-resync.ts
  • libs/game/idle-client/src/resync/types.ts
  • libs/game/idle-client/src/worker/handle-set-activity-message.test.ts

@zgeoff

zgeoff commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

CodeRabbit's one finding (duplicate capped-rebase test in plan-resync.test.ts) fixed in 8c15d78.

@zgeoff

zgeoff commented Jul 20, 2026

Copy link
Copy Markdown
Owner Author

Closing in favor of the parallel implementation on 717-worker-lifecycle-owner (PR incoming). A comparative adversarial review judged the two variants: this branch stranded a blocked continuation after a worker restart (the flush runs once at resync entry; the pass then closes the source row but nothing re-attempts delivery within the same resync), lacked the cross-avatar drain guard, and re-broadcast cap-halt forever for a stale intent under a spent budget. Its session-expiry intent fix, install-boundary stop-back, per-site fault tagging, and park naming were grafted into the surviving branch.

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.

consolidate the worker into a single-threaded lifecycle owner

1 participant