perf(ios): pace capture healthy-state watchdog polls

Merged
GI
4d ago
+752-47

What changed and why

iOS phone-mic capture was waking once a second while already healthy: the batch heartbeat crossed into Dart at 1Hz, and the Dart stall watchdog polled on the same cadence. Healthy batch capture now heartbeats every 4s (strictly under half the 10s batch deadline, so one lost or coalesced beat still lands ~8.5s in and cannot false-stall), and the iOS Dart watchdog waits that long only while liveness is fresh. Recovery from an interruption/rebuild preserves its pending ~1s fast beat before returning to the healthy cadence. The 3s stream and 10s batch stall deadlines are unchanged, and any interruption, rebuild, stale beat, or foreground return snaps back to the 1s poll. Android keeps the 1s poll.

Product invariants affected

none

How it was verified

  • flutter test test/unit/native_mic_recorder_service_test.dart — 29 passed, including the pacing cases (3s stream deadline, batch heartbeats without a false stall, stall at 10s, interruption/foreground snap-back, rebuild grace).
  • ruby app/ios/test/phone_mic_lifecycle_replay_test.rb — passed (14 assertions). The production controller stays quiet well inside the healthy 4s cadence, reports storage-full from the write path, snaps to a 1s beat on interruption, emits its preserved ~1s fast beat on resume before returning to the 4s cadence, and emits one fast beat after foreground.
  • No full iOS app build and no on-device capture run. DispatchSource timing was checked in the replay harness, not on a phone.

Tests

  • app/test/unit/native_mic_recorder_service_test.dart — iOS pacing group: deadline cap, healthy-cadence progress, interruption and foreground snap-back, rebuild grace. Existing unpaced cases still cover Android's 1s poll.
  • app/ios/test/phone_mic_lifecycle_replay_test.swift — batch heartbeat cadence and immediate storage-full reporting through the production controller.

UI evidence

UI-Evidence: none -- capture watchdog timing only; no UI change.

Failure class (fixes)

Failure-Class: none