Initial Commit
This commit is contained in:
91
openspec/changes/enterprise-react-iphone/proposal.md
Normal file
91
openspec/changes/enterprise-react-iphone/proposal.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# OpenSpec Change: enterprise-react-iphone
|
||||
|
||||
- **Change ID**: enterprise-react-iphone
|
||||
- **Package**: `@suaveplan/react-iphone`
|
||||
- **Category**: react
|
||||
- **Target path**: `react/ui/react-iphone`
|
||||
- **Status**: PROPOSED
|
||||
- **Date**: 2026-04-24
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/react-iphone.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver `@suaveplan/react-iphone` on genesis from scratch at enterprise quality. A React iPhone-simulator SDK for WebRTC voice/video calling: on-device UI screens (`IncomingCallScreen`, `OutgoingCallScreen`, `ActiveCallScreen`, `EndedCallScreen`), video containers, call controls, plus the service layer (`CallManager`, `HookManager`, `SignalingClient`, `SignalingChannelAdapter`, `WebRTCProvider`), and hooks (`useCallManager`, `useCallTimer`). Genesis re-enables the commented-out enhanced features (`CallManagerEnhanced`, `CallManagerWebRTC`, `PhoneStateSerializer`, `ServerStateManager`), fills the hardcoded TODO stubs, adds Storybook stories for every screen, and reaches 100% test coverage.
|
||||
|
||||
---
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
Package does not exist on genesis — fresh build.
|
||||
|
||||
---
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| `@suaveplan/error` (`workspace:^`) | `CallError` base class |
|
||||
| `@suaveplan/events` (`workspace:^`) | Event bus for call state changes |
|
||||
| `@suaveplan/logger` (`workspace:^`) | Structured call logs |
|
||||
| `@suaveplan/react-translate` (`workspace:^`) | i18n |
|
||||
| `@suaveplan/react-ui` (`workspace:^`) | UI primitives |
|
||||
| `zod` (`catalog:forms`) | Call config schemas |
|
||||
| `@opentelemetry/api` (`catalog:observability`) | Call lifecycle spans |
|
||||
|
||||
External: `tslib catalog:build`. React 18 peer.
|
||||
|
||||
---
|
||||
|
||||
## Preserved public API surface
|
||||
|
||||
### Components
|
||||
|
||||
`CallButton`, call-state icons, `ActiveCallScreen`, `EndedCallScreen`, `IncomingCallScreen`, `OutgoingCallScreen`, `AudioOnlyContainer`, `CallVideoContainer`, `VideoStreamRenderer`
|
||||
|
||||
### Hooks
|
||||
|
||||
`useCallManager`, `useCallTimer`
|
||||
|
||||
### Services (all enabled — no commented exports)
|
||||
|
||||
`CallManager`, `CallManagerEnhanced`, `CallManagerWebRTC`, `HookManager`, `HookExecutionMode`, `SignalingChannelAdapter`, `SignalingClient`, `WebRTCProvider`, `PhoneStateSerializer`, `ServerStateManager`
|
||||
|
||||
---
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
1. **Re-enable enhanced services** — `CallManagerEnhanced`, `CallManagerWebRTC`, `PhoneStateSerializer`, `ServerStateManager` all exported and fully implemented (no TODO stubs).
|
||||
2. **`CallError`** — codes: `CALL_SIGNALING_FAILED`, `CALL_WEBRTC_FAILED`, `CALL_MANAGER_NOT_INITIALIZED`.
|
||||
3. **OTel call lifecycle spans** — `call.incoming`, `call.outgoing`, `call.connected`, `call.ended`.
|
||||
4. **Storybook** — every screen component with default + loading + error + playground stories.
|
||||
5. **Playwright E2E** — mock WebRTC + signaling; verify call state transitions render correct screens.
|
||||
|
||||
---
|
||||
|
||||
## File layout
|
||||
|
||||
```text
|
||||
react/ui/react-iphone/
|
||||
package.json
|
||||
tsconfig.json
|
||||
vitest.config.ts
|
||||
src/
|
||||
index.ts
|
||||
components/
|
||||
common/ (CallButton, icons)
|
||||
screens/ (ActiveCallScreen, EndedCallScreen, IncomingCallScreen, OutgoingCallScreen)
|
||||
video/ (VideoStreamRenderer, AudioOnlyContainer, CallVideoContainer)
|
||||
hooks/
|
||||
use-call-manager/ use-call-timer/
|
||||
services/
|
||||
call-manager/ call-manager-enhanced/
|
||||
call-manager-webrtc/ hook-manager/
|
||||
phone-state-serializer/ server-state-manager/
|
||||
signaling-channel-adapter/ signaling-client/
|
||||
webrtc-provider/
|
||||
types/
|
||||
e2e/
|
||||
call-flow.spec.ts
|
||||
```
|
||||
113
openspec/changes/enterprise-react-iphone/tasks.md
Normal file
113
openspec/changes/enterprise-react-iphone/tasks.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user