Skip to content

Fix async capture lifecycle and expose health diagnostics - #142

Open
JubaKitiashvili wants to merge 2 commits into
EvanBacon:mainfrom
JubaKitiashvili:codex/session-health-diagnostics
Open

JubaKitiashvili wants to merge 2 commits into
EvanBacon:mainfrom
JubaKitiashvili:codex/session-health-diagnostics

Conversation

@JubaKitiashvili

@JubaKitiashvili JubaKitiashvili commented Aug 12, 2026

Copy link
Copy Markdown

Summary

  • model the N-API capture start() / stop() methods as async and await their lifecycle
  • prevent a late capture startup from resurrecting a session that is already stopping
  • replace the unconditional health response with capture, frame, screen, and client diagnostics
  • retain failed sessions long enough to expose the native error and retry startup after the simulator boots

Background

#117 made the native capture lifecycle asynchronous, but the TypeScript handle still declared start() and stop() as synchronous void methods. Starting capture for a shutdown simulator could therefore leave a rejected Promise unobserved and terminate the Node process.

Teardown had a second race: Swift CaptureEngine.stop() launched framebuffer cleanup in a detached task and returned immediately. A concurrent startup could finish afterward and make a stopped session appear live again.

This PR is a focused follow-up to #117. It intentionally isolates the lifecycle and recovery concern that is currently bundled with the larger Xcode 27 / Device Hub work in #140.

Changes

  • serialize DeviceSession.start() and close() with shared Promises
  • await native startup, subscription creation, unsubscription, and teardown
  • make stop win over actor reentrancy in CaptureEngine
  • turn startup failures into HTTP 503 responses instead of unhandled rejections
  • allow a failed session to retry capture after its simulator is booted
  • report starting, ok, stalled, failed, and stopped through /health
  • include frame cadence, screen dimensions, codecs, active clients, and the latest native error in health snapshots
  • add deterministic unit and HTTP-level lifecycle regression tests

Verification

  • bun test packages/serve-sim/src/__tests__/session-health.test.ts packages/serve-sim/src/__tests__/device-session-health.test.ts — 9 passed
  • bun run typecheck
  • bun run lint
  • bun run packages/serve-sim/build.ts
  • manual cold cycle: shutdown produced a stable 503 failed diagnostic, then boot + stream retry recovered to 200 ok in the same server process
  • browser verification: live MJPEG stream, SpringBoard, controls, and tools panel all worked after recovery

The full local suite has one pre-existing/flaky camera-helper cleanup failure when run as a group (shm-probe.integration.test.ts reports a null child exit code). The same integration file passes 7/7 when rerun in isolation.

Summary by CodeRabbit

  • New Features
    • Added session health monitoring with readiness, status, diagnostics, frame, client, and uptime information.
    • Added a /health endpoint with meaningful HTTP status responses.
    • Sessions now start before streaming and report failures through appropriate responses.
  • Bug Fixes
    • Improved handling of startup, shutdown, retries, stalled streams, and cleanup.
    • Prevented lifecycle races from restoring or leaving sessions in an invalid state.
    • Capture failures are surfaced reliably, with cleanup completing before subsequent operations.
  • Tests
    • Added comprehensive coverage for session health, lifecycle failures, readiness, diagnostics, and teardown behavior.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 173e1b3a-386b-4cf1-b43a-33c9f09f9c4d

📥 Commits

Reviewing files that changed from the base of the PR and between c2182dd and fbb950d.

📒 Files selected for processing (4)
  • packages/serve-sim/src/__tests__/device-session-health.test.ts
  • packages/serve-sim/src/__tests__/session-health.test.ts
  • packages/serve-sim/src/device-session.ts
  • packages/serve-sim/src/session-health.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/serve-sim/src/tests/device-session-health.test.ts
  • packages/serve-sim/src/tests/session-health.test.ts
  • packages/serve-sim/src/session-health.ts
  • packages/serve-sim/src/device-session.ts

📝 Walkthrough

Walkthrough

The PR adds session health tracking, asynchronous native capture lifecycle APIs, stateful DeviceSession startup and shutdown, stream diagnostics, failure handling, and awaited simulator cleanup.

Changes

Capture session health

Layer / File(s) Summary
Session health model and validation
packages/serve-sim/src/session-health.ts, packages/serve-sim/src/__tests__/session-health.test.ts
SessionHealth tracks lifecycle, frames, clients, errors, timing, snapshots, and HTTP status codes. Tests cover startup, readiness, stalls, failures, retries, and stopped sessions.
Asynchronous native capture lifecycle
packages/serve-sim/src/native.ts, packages/serve-sim/Sources/SimNative/CaptureEngine.swift
Native capture startup and shutdown now return promises. Cleanup supports asynchronous callbacks. Swift startup failure and concurrent-stop paths now clean up and rethrow.
Device session lifecycle and streaming
packages/serve-sim/src/device-session.ts, packages/serve-sim/src/__tests__/device-session-health.test.ts
DeviceSession accepts injected dependencies, manages asynchronous lifecycle state, tracks stream clients, records frame diagnostics, exposes health responses, preserves failed sessions, and handles stream failures.
Awaited simulator cleanup
packages/serve-sim/src/middleware.ts
Stale-session cleanup and simulator shutdown now await closeDeviceSession.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant DeviceSession
  participant NativeCapture
  participant SessionHealth

  Client->>DeviceSession: Request stream or health
  DeviceSession->>NativeCapture: Await capture startup
  NativeCapture-->>DeviceSession: Frames or startup failure
  DeviceSession->>SessionHealth: Record lifecycle and frame state
  DeviceSession-->>Client: Stream or status response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two primary changes: asynchronous capture lifecycle handling and expanded session health diagnostics.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@packages/serve-sim/src/device-session.ts`:
- Around line 300-301: Update the MJPEG and AVCC stream cleanup around the
unsubscribe listeners to use one release function that removes both event
listeners and observes any promise returned by unsubscribe. Ensure cleanup is
triggered by either close or error without allowing rejected asynchronous native
cleanup to become an unhandled rejection, and apply the same behavior in both
stream paths.
- Around line 222-225: Update the cleanup flow around the mjpegClients and
avccClients sets to explicitly end or destroy every active HTTP response before
clearing its set; keep the existing hidSockets closure and perform the
client-set clearing only after both response collections have been terminated.

In `@packages/serve-sim/src/session-health.ts`:
- Around line 71-75: Update markStarting() so retries from the "failed" phase
reset startedAt using this.now() and clear any stale frame state before
transitioning to "starting"; preserve the existing initialization behavior for
other phases and continue clearing error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 159c71d7-7b06-4503-8de1-156faf88895d

📥 Commits

Reviewing files that changed from the base of the PR and between 14ad57f and c2182dd.

📒 Files selected for processing (7)
  • packages/serve-sim/Sources/SimNative/CaptureEngine.swift
  • packages/serve-sim/src/__tests__/device-session-health.test.ts
  • packages/serve-sim/src/__tests__/session-health.test.ts
  • packages/serve-sim/src/device-session.ts
  • packages/serve-sim/src/middleware.ts
  • packages/serve-sim/src/native.ts
  • packages/serve-sim/src/session-health.ts

Comment thread packages/serve-sim/src/device-session.ts
Comment thread packages/serve-sim/src/device-session.ts Outdated
Comment thread packages/serve-sim/src/session-health.ts
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