- register(): local metadata write failure -> LocalRecordFailed -> 503 Chinese
message with the cg- id (shared registration NOT rolled back, refresh list to
recover, never re-register); register_asr journaled BEFORE local persist so
failed registrations still count in the resource ledger
- write_worker_voice(): docstring now states the whole-file rewrite on
unreadable/corrupt state (other keys preserved only when parseable)
- capture->worker race closed with a session-immutable-snapshot equivalent:
_capture_voice holds the captured voice (no await gap); apply_selection
defers its worker-state write while any call is live; open_session releases
the hold on failure/cancel; VoiceSessionService.on_session_settled releases
on explicit end, end-into-debt and TTL expiry; response voice == the voice
the worker actually reads for that call, mid-call changes affect the next
call only; overlapping-call residual documented honestly
- tests: hel902 fault-injection case 1 rewritten for the new contract
(recovery + restart + 409 gate kept), 4 new deterministic Event-gated race
tests (worker read delayed past dispatch, concurrent apply, failure/cancel
cleanup, next-call voice), zero sleep-based assertions
- docs: contract §2.2/§2.5/§3 updated; r5 followup package tightened (head
milestones d167ba9/b09a907/pending-review, register ASR vs call ASR split,
call-ASR counting means + honest pending blocker, single-timebase frame
silence measurement, D1 female_soft reference audio pinned by sha256,
recycling checklist, shared-TTS snapshot/restore, git-revert is not
recycling, caps count failures/retries/outbound-no-response)
- evidence: pytest 380/380 (376 baseline + 4), verify chain 12/12, ruff clean,
requirement checks 4/4 (origin/main and b09a907, with/without handoff)
Co-authored-by: multica-agent <github@multica.ai>