Sessão interativa REST
Use GET /api/v1/runs/{runId}/session?expectedSequence=N para sincronização.
O cliente não precisa carregar o agregado completo após cada ação.
Capability: compact-interaction-sessions-v1. A versão das regras continua 31;
o launcher exige a capability para não reutilizar um host antigo sem /session.
Exemplos executáveis: interactive-session.http.
Fluxo
- Envie o comando canônico com commandId e versões esperadas.
- Aguarde o recibo durável; não é uma previsão otimista.
- Leia a sessão na sequência confirmada pelo recibo.
- Valide a versão e publique o bundle inteiro, nunca seção por seção.
A Godot solicita Prefer: return=minimal nos POSTs de comando. No gateway de run,
isso evita a cópia completa do agregado no recibo. Sem essa preferência, a
representação existente continua disponível. Não há outro executor conforme a
representação: regras, persistência e resultado são os mesmos.
Corpo
version: run, setting, sequência, steps, revisão, engineVersion e fingerprint;run: projeção de interação, sem encounters anteriores, manifesto ou regras completas;combat: read model do combate atual, ou null;commands,capabilities,cardZones,attributes;cardPresentations: base/origem das cartas necessárias à cena;modifierItems: inventário e compatibilidade;actions: candidatos canônicos com custos e aplicações de preview, sem traces completos;evaluations: inspeção detalhada da mão inteira não é requisito do refresh.
Somente enquanto o combate está ACTIVE, o mapa compacto contém o nó atual.
Em VICTORY, DEFEAT, DRAW ou ABANDONED, a sessão já publica o mapa completo,
mesmo que activeEncounterId ainda esteja presente aguardando confirmação do
resultado. Publicar os nós não resolve o combate nem libera viagens: os comandos
e legalNextNodeIds continuam sendo as únicas autoridades de progressão.
Sem um snapshot de combate ativo, a projeção também mantém o mapa completo.
Coleções leem /cards/presentations sob demanda. Inspeções continuam em
/combats/{id}/cards/{instanceId}/evaluation. Histórico e replay têm consultas
próprias, sem uma segunda autoridade.
Concorrência e cache
A consulta captura uma referência imutável e não faz HTTP interno. Sequência esperada incompatível retorna 409. Sem versão esperada, entrega um head inteiro. Nunca etiqueta o head atual como uma sequência passada.
ETag inclui estado, schema e permissões; If-None-Match compatível retorna 304.
O schema interno do ETag é 2 para invalidar respostas que compactavam indevidamente
o mapa de combates terminados; essa mudança de representação não altera a versão
de regras, o fingerprint do estado ou os saves.
O consumidor precisa já ter aquela representação. Não tratar 304 como snapshot
vazio. A Godot atualmente solicita o corpo compacto completo.
Após POST incerto, recupere o recibo pelo mesmo commandId. Não gere outro ID nem reenvie cegamente. Se outro cliente avançou depois do recibo, leia o novo head e descarte a apresentação antiga sem repetir a mutação.
Enquadramento do mapa na Godot
Ao trocar uma projeção compacta pelo mapa completo, a geometria pode mudar sem
alterar currentNodeId. MapView recentraliza o nó atual quando os retângulos ou
os limites calculados mudam, além da montagem inicial, progresso e resize.
Atualizações apenas de escolhas, recursos, texto ou abertura do inspetor flutuante
não recentralizam o mapa e preservam o arrasto manual.
Mudanças de nó usam interpolação visual de 0,28 segundo, configurável em
MapLayoutProfile.camera_transition_seconds e ajustada pela velocidade das
animações. O retorno de uma atividade parte do nó atual registrado na última
visita ao mapa, mesmo quando a tela foi recriada. O primeiro enquadramento,
correções de geometria sem progresso e movimento reduzido/instantâneo permanecem
imediatos. Arrastar o mapa cancela a interpolação; pausa congela seu andamento.
Isso não atrasa comandos nem altera a posição autoritativa do jogador.
Regressões: RunInteractionProjectionTests cobre todos os estados terminais;
WorldOneFoundationJourneyTests consulta a sessão antes de RESOLVE_COMBAT em dez
jornadas isoladas; InteractionSessionTests cobre cache novo e ETag antigo.
Na Godot, map_topology_refresh.gd cobre expansão/redução de geometria nas três
resoluções, e first_combat_map_transition.gd cobre a vitória com sessão inicialmente
compacta, entrada no mapa completo, conexões, confirmação pendente e câmera estável.
map_camera_motion.gd cobre interpolação, troca rápida de destino, refresh,
pausa, arrasto, retorno com tela recriada, movimento reduzido e descarte da tela.
Diagnóstico
Performance:DiagnosticsEnabled=true habilita spans e Server-Timing até envio dos
headers; o logger registra a duração completa do pipeline. Spans aninhados não
devem ser somados. Métricas não entram em commits, hashes ou RNG.
health/live é liveness. health/ready testa disponibilidade de I/O, não um
inventário/validação de todas as partidas. persistedRuns pode ser null.
Exporte o OpenAPI usando host e store isolados, sem a API do jogador:
dotnet run --project tools/ExportOpenApi -- .runtime/openapi-live.json
Se houver binários em uso, acrescente --artifacts-path .runtime/openapi-export
antes de -- para isolar a compilação.
O export é o documento gerado pelo host, usado para diagnóstico. O contrato
publicado em openapi/heroscript-v1.json também contém restrições e descrições
editoriais: atualize-o de forma aditiva e valide os testes de contrato; não o
substitua cegamente pelo export.