Ping — dados de conexão e auditoria

TLDR: Cada ping de presença na sala de atendimento deve registrar o contexto de conexão do participante (câmera, microfone, qualidade, rede) e ser logado na auditoria, permitindo investigar problemas de conectividade retroativamente.

Draft — não implementado. Asana: https://app.asana.com/1/1208104128529800/task/1214640670041059

A parte de contexto de conexão foi entregue por outro caminho, em meeting_ping_diagnostics: uma tabela meeting_participant_pings com diagnostics jsonb, em vez da coluna room_ping_events proposta aqui. O que continua pendente deste draft é a auditoria por ping (Branch 2) e a guarda de janela no endpoint.

Contexto

Ticket de suporte: uma cliente relatou que não conseguiu realizar a sessão do dia 18/03/2026 às 20h devido a instabilidade de conexão. Ao verificar no Avo, constam pings na sala das 20h às 21h12 (dentro da janela da sessão) e, no dia seguinte, das 12h14 às 12h15 — 15 horas após o fim da sessão.

Problema central: os pings não carregam nenhum dado de contexto (câmera, microfone, qualidade de conexão, tipo de rede), tornando impossível confirmar ou refutar o relato da cliente a partir dos dados do sistema.

Problema secundário: o endpoint de pings não verifica se a sessão ainda está dentro da janela permitida (can_join?), permitindo que pings sejam aceitos indefinidamente após joined: true ser setado.

Como funciona hoje

``` Cliente entra na sala → GET /api/v1/meetings/:id/credentials → MeetingsCredentialController#create → MeetingParticipant.update!(joined: true) ← verifica can_join?

Cliente envia heartbeat (a cada ~20s via frontend) → PATCH /api/v1/me/meetings/:meeting_id/pings → MeetingParticipants::CreatePingFlow → joined? → appenda Time.current.to_i em room_pings joined: false → appenda em wait_pings ← NENHUMA verificação de janela de tempo ← NENHUM dado de conexão capturado ```

Janela permitida (Meeting#can_join?): de start_at - 10min até start_at + 2h. Ignora sessões finished ou canceled.

Estrutura dos dados hoje:

meeting_participants room_pings string[] — array de Unix timestamps wait_pings string[] — array de Unix timestamps

Investigação em produção — resultado (11/05/2026)

Investigado via MeetingParticipant.find(547525) (meeting 274036, sessão 18/03/2026 20h–21h).

  Resultado
room_pings dia 18 393 pings — 20:00:39 até 21:12:38, presença contínua confirmada
room_pings dia 19 6 pings — 12:14:02 até 12:15:29, fora de qualquer janela de sessão
wait_pings 55 pings — cliente aguardou na sala de espera antes de entrar
Lacunas > 1min no dia 18 20:09:55→20:10:38 (43 s) e 20:25:53→20:27:16 (1min23s) — normais
Conclusão Cliente esteve presente na sala durante toda a sessão. Pings do dia 19 são artefato de app em background.

A sessão foi realizada. Não há evidência de queda de conexão nos dados disponíveis. Os pings fantasma do dia 19 confirmam o bug de ausência de guarda de janela no endpoint.

Objetivos

  • Registrar o contexto de conexão do participante a cada ping
  • Logar cada ping no audit trail, com action e metadata
  • Exibir os dados de conexão e o audit trail no Avo, sem necessidade de console
  • Rejeitar pings fora da janela da sessão

Fora de escopo

  • Alterar a semântica dos arrays legados room_pings/wait_pings

Mudanças

Branch 1 — feat/ping-connection-data

Adicionar uma nova coluna room_ping_events jsonb default '[]' em meeting_participants. Cada evento é um objeto JSON:

json { "t": 1742335836, "camera": true, "mic": false, "quality": "unstable", "network": "wifi" }

Campo Tipo Valores
t integer Unix timestamp
camera boolean true = câmera ativa
mic boolean true = microfone ativo
quality string good, unstable, poor
network string wifi, cellular, ethernet

Todos os campos são opcionais no request — o frontend envia o que tiver disponível. O t é sempre preenchido pelo servidor.

Endpoint atualizado:

PATCH /api/v1/me/meetings/:meeting_id/pings Body: { camera_active: true, microphone_active: false, connection_quality: "unstable", network_type: "wifi" }

Arquivos afetados: db/migrate/ (nova migration), app/models/meeting_participant.rb, app/controllers/api/v1/meetings/participant_pings_controller.rb, app/use_cases/meeting_participants/create_ping.rb, app/serializers/meeting_participant_ping_serializer.rb, spec/requests/api/v1/meetings/participant_pings_update_spec.rb.

Superado: entregue de outra forma em meeting_ping_diagnostics — tabela meeting_participant_pings com diagnostics jsonb, em vez de coluna room_ping_events no meeting_participants.

Branch 2 — feat/ping-audit-log

Logar cada tentativa de ping no audit trail usando a gem audited, já utilizada no modelo Meeting.

Verificar se MeetingParticipant já está auditado. Se não, adicionar audited com escopo restrito aos campos relevantes, ou criar um evento manual via Audited::Audit com os dados do ping.

Campo Valor
auditable MeetingParticipant
action "room_ping" ou "wait_ping"
user participante que enviou o ping
audited_changes { t:, camera:, mic:, quality:, network: }

Arquivos afetados: app/models/meeting_participant.rb, app/use_cases/meeting_participants/create_ping.rb, spec/requests/api/v1/meetings/participant_pings_update_spec.rb.

Branch 3 — feat/ping-avo-connection-display

Atualizar o Avo para exibir os dados de conexão por ping e o audit trail:

- 18 de março de 2026 às 21:10:36 — câmera: ✓ mic: ✗ conexão: instável rede: wifi - 18 de março de 2026 às 21:11:37 — câmera: ✓ mic: ✓ conexão: boa rede: wifi

Substituir ou complementar o painel atual de room_pings com o novo formato rico e adicionar painel de audit trail com eventos de ping.

Arquivos afetados: app/use_cases/meeting_participants/format_ping_dates.rb, app/avo/resources/meeting_participant.rb.

Superado: a exibição foi entregue em room_ping_network_quality_avo, com qualidade derivada de network.rtt e navegador via regex. O painel de audit trail continua pendente.

Melhoria adicional (separada)

Branch fix/ping-outside-session-window: adicionar verificação de meeting.can_join? no endpoint de pings para rejeitar pings fora da janela da sessão. Evita pings “fantasma” como os do dia 19 deste ticket. Continua pendente.

Como verificar

  • [ ] Branch 1: migration criada, endpoint aceita dados de conexão, room_ping_events populado
  • [ ] Branch 1: factory atualizada com dados de conexão
  • [ ] Branch 2: audit log criado a cada ping, com action e metadata corretos
  • [ ] Branch 3: Avo exibe dados de conexão por ping e audit trail
  • [ ] fix: guarda can_join? no endpoint de pings implementada e testada
  • [ ] make seed executa sem erro após as migrações

Documentação

Learning registrado em sessions_ghost_pings_and_missing_connection_data.