prompt inicial do projeto

This commit is contained in:
2026-07-22 15:55:55 -03:00
commit 6fb920d333
19 changed files with 2937 additions and 0 deletions

93
prompts/FASE-4-chamada.md Normal file
View File

@@ -0,0 +1,93 @@
# FASE 4 — Videochamada
## Objetivo
LiveKit integrado ponta a ponta: emissão de tokens, sala por visita, vídeo no navegador do visitante, e degradação graciosa quando o SFU falha.
## Pré-requisitos
FASES 03 concluídas. Ler `docs/05-INFRA-DOCKER.md` §3 e §4, e `docs/01-ARQUITETURA.md` §5.5.
> **Faça a POC antes de escrever código de produção.** Suba `livekit-server` + `coturn` e feche uma chamada entre um navegador móvel em 4G real e um Android, medindo o tempo até o primeiro frame. É o maior risco técnico do projeto e o mais barato de derrubar cedo. Se a POC falhar, o problema quase sempre é a faixa UDP fechada no firewall.
## Tarefas
### 1. Porta de vídeo
```kotlin
interface VideoCallProvider {
suspend fun criarSala(visitId: UUID): RoomInfo
suspend fun emitirToken(visitId: UUID, participante: Participant): String
suspend fun encerrarSala(visitId: UUID)
suspend fun saudavel(): Boolean
}
```
Adaptador `LiveKitProvider`. A porta existe para permitir trocar por serviço gerenciado sem tocar nos apps — decisão registrada em `01-ARQUITETURA.md` ADR-003.
### 2. Emissão de tokens
Token JWT do LiveKit gerado **pelo backend**, nunca pelo cliente. Escopo mínimo:
- `roomJoin` apenas na sala daquela visita
- `canPublish` para vídeo e áudio
- `canPublishData: false`
- TTL de 10 minutos, alinhado à expiração da visita
`room_name` derivado determinísticamente de `visitId`, gravado em `visits.room_name`.
**Um token nunca dá acesso a outra sala.** Verifique isso com teste explícito.
### 3. Ciclo de vida da sala
Sala criada na transição para `EM_CHAMADA`, não antes — criar em `PENDENTE` desperdiça recurso do SFU em visitas que nunca são atendidas.
Encerrada ao chegar em estado final. `empty_timeout: 120` no LiveKit é a rede de segurança para sala órfã.
### 4. Vídeo na web do visitante
SDK `livekit-client` carregado por **`import()` dinâmico**, apenas quando a visita entra em `EM_CHAMADA`. Ele sozinho pesa mais que todo o resto do app — carregá-lo no bundle inicial destruiria o orçamento de 200KB da FASE 3.
Vídeo remoto em tela cheia, auto-preview pequeno no canto, botão de mudo e de encerrar. Indicador de qualidade de conexão.
### 5. Degradação graciosa
Circuit breaker (Resilience4j) sobre o `VideoCallProvider`. Ao abrir:
```
LiveKit fora ─► AUDIO (mesma sala, sem vídeo, muito menos banda)
└─► FOTO_TEXTO (visitante manda foto, morador aprova sem chamada)
└─► FILA_OPERADOR (resolução por telefone)
```
O nível vigente vai em `visits.degraded_mode`, é emitido como `DEGRADED_MODE` no WebSocket, aparece em `/actuator/health` e no painel admin.
**Comunique a degradação, nunca a esconda.** Esconder faz o usuário achar que o produto quebrou; nomear faz o produto parecer resiliente — que é o que ele é.
### 6. Configuração de mídia
**Limite de resolução imposto no servidor**, não no cliente: 480p a ~600kbps. 480p basta para reconhecer alguém na portaria; 1080p multiplicaria o custo de banda por quatro sem melhorar nenhuma decisão. Ver o dimensionamento em `05-INFRA-DOCKER.md` §4.
Simulcast desligado (só dois participantes). `adaptiveStream` e `dynacast` ligados. TURN configurado com o segredo do coturn.
### 7. Métricas
`portaria.call.time_to_first_frame` · `portaria.call.duration` · `portaria.call.failed_ratio` · `portaria.call.turn_usage_ratio` (uso alto de TURN indica problema de rede na portaria) · `portaria.call.degraded_ratio`.
## Critérios de aceite
- [ ] POC validada em rede real antes do código de produção
- [ ] Chamada completa entre navegador móvel em 4G e Android
- [ ] Tempo até o primeiro frame < 3s em rede boa
- [ ] Teste: token de uma sala é rejeitado em outra sala
- [ ] Teste: com LiveKit derrubado, o sistema degrada para áudio e depois para foto+texto
- [ ] Teste: sala encerrada em todos os estados finais
- [ ] SDK do LiveKit **não** aparece no bundle inicial do visitante
- [ ] Bundle inicial continua ≤ 200KB gzip
- [ ] Chamada funciona atrás de NAT simétrico (valida o coturn)
## Não faça nesta fase
- Gravação (módulo `video_recording`, FASE 8)
- App do morador (FASE 5)
- Mais de 2 participantes por sala