Files
suaveplan/openspec/changes/enterprise-react-iphone/tasks.md
2026-05-27 23:29:53 +01:00

4.6 KiB

Tasks: enterprise-react-iphone

Change ID: enterprise-react-iphone Package: @suaveplan/react-iphone Target path: react/ui/react-iphone Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/events, @suaveplan/logger, @suaveplan/react-translate, @suaveplan/react-ui


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal layout.
  • Write package.json at 0.1.0 with "sideEffects": false, exports map.
  • Dependencies: @suaveplan/error workspace:^, @suaveplan/events workspace:^, @suaveplan/logger workspace:^, @suaveplan/react-translate workspace:^, @suaveplan/react-ui workspace:^, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build.
  • Write tsconfig.json, vitest.config.ts, scripts.

2. Error codes and types

  • Define CallError extending SuaveplanError; codes: CALL_SIGNALING_FAILED, CALL_WEBRTC_FAILED, CALL_MANAGER_NOT_INITIALIZED.
  • Define public types: CallState, CallDirection, CallInfo, SignalingMessage, WebRTCConfig, HookExecutionMode, ConnectionQuality, IceState, SignalingState.

3. Services

CallManager

  • Basic call state machine: idle → ringing → active → ended. startCall(), acceptCall(), rejectCall(), endCall(). Emits events via @suaveplan/events. OTel spans for each transition.
  • Write test + md.

CallManagerEnhanced

  • Extends CallManager with connectionQuality (derived from ICE stats — not hardcoded), iceState, signalingState — all real implementations.
  • Write test + md.

CallManagerWebRTC

  • Wraps WebRTCProvider, wires ICE + SDP negotiation into CallManager state machine.
  • Write test (mock RTCPeerConnection via DI) + md.

HookManager

  • Sync + async hook pipeline (HookExecutionMode). register(hook), execute(event): Promise<void>.
  • Write test + md.

SignalingClient + SignalingChannelAdapter

  • WebSocket-backed signaling; connect(), disconnect(), send(message), onMessage(handler).
  • Throws CallError(CALL_SIGNALING_FAILED) on connection failure.
  • Write test (real WebSocket via @suaveplan/testing mock server or real WS) + md.

WebRTCProvider

  • Manages RTCPeerConnection; createOffer(), setRemoteDescription(), addIceCandidate().
  • Throws CallError(CALL_WEBRTC_FAILED) on failure.
  • Write test (DI-injected RTCPeerConnection constructor) + md.

PhoneStateSerializer + ServerStateManager

  • PhoneStateSerializer — serializes/deserializes call state for server snapshot sync.
  • ServerStateManager — manages server-side state deltas.
  • Write test + md for each.

4. Hooks

  • useCallManager — wraps CallManager events via useSyncExternalStore, returns call state + actions.
  • useCallTimer — elapsed-time counter (seconds) while callState === 'active'.
  • Write tests + stories (playground for each) + md.

5. Components

For each screen (IncomingCallScreen, OutgoingCallScreen, ActiveCallScreen, EndedCallScreen):

  • Implement with i18n via useTranslation. OTel span on mount.
  • Write test + stories (default, loading/connecting, error, playground) + md.

For video components (VideoStreamRenderer, AudioOnlyContainer, CallVideoContainer):

  • Write test + stories + md.

For CallButton + icons:

  • Write test + stories + md.

6. E2E

  • e2e/call-flow.spec.ts — Playwright with injected mock WebRTCProvider + SignalingClient:
    • Render IncomingCallScreen, click accept → ActiveCallScreen shown.
    • Render ActiveCallScreen, click end → EndedCallScreen shown.
    • Timer increments during active call.

7. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/react-iphone — green.
  • bunx turbo test:coverage --filter=@suaveplan/react-iphone — 100%.
  • bun scripts/publish.ts --package @suaveplan/react-iphone.
  • Archive change folder.