Repository navigation
feat: resolução de conflito de visita no ACS (#79) - #91
Merged
Merged
Conversation
…são vista (#79) O autor resolve o conflito de visita explicitamente: igual à versão vista grava o conteúdo do aparelho em existing.version + 1 e audita write/visit/conflict_resolved_local; versão vencida devolve um conflito novo com a cópia do servidor (read/visit_conflict), sem gravar. As checagens de identidade, território e autoria saem de _syncOne para _checkTarget, compartilhado com o sync, para as recusas serem as mesmas.
…servidor (#79) Conflito sai da retentativa (D3) e vive em _conflicts, persistido com a cópia do servidor; restore separa pendente/conflito/recusada; conflito sem versão é erro retentável (D7); forceConflict removido. resolveKeepServer (só local) e resolveKeepLocal (visits.resolveConflict com compare-and-set, espera sync em voo). BackendVisitSynchronizer também é o VisitConflictResolver; AcsBackend.resolveVisitConflict com a mesma checagem de dono de syncVisits. Envio diferido não manda nem apaga conflito (D6). Cópia do servidor ilegível no disco vira null em vez de derrubar o load. O app conta conflitos como visitas no aparelho (wipe, fila só em RAM, Sair).
…sita (#79) - VisitConflictListScreen e VisitConflictCompareScreen: painéis "Neste aparelho" / "No servidor (versão N)", marca "Diferente" (ícone + texto, accentOnSurface), "Manter a minha versão", "Editar e enviar" e "Manter a do servidor" com confirmação destrutiva; resultado em liveRegion e ações travadas com a chamada em voo. - VisitRegistrationScreen: botão "Resolver conflito(s)" e texto novo do resultado de conflito. - Contagens do "Sair"/"Limpar este aparelho" passam a incluir conflitos só em RAM, e conflito no disco conta como revisão (depende do ACS dono). - resolveKeepLocal recusado não recoloca nas recusadas a visita descartada durante a chamada.
…ste (#79) Novo testWidgets antes do da MFA: duas visitas registradas com o envio fora do ar, levadas à v3 por um 'outro aparelho' do mesmo ACS; pela tela, mantém a do aparelho numa (v4 no servidor) e a do servidor noutra (v3 intacta). O script confere versões, status, autoria, a trilha conflict_resolved_local e visit_conflict, e a cadeia de auditoria (audit_chain_check). Toques no fim da tela passam a centralizar o alvo (_tocarNoMeio): a barra de navegação e o SnackBar cobriam o botão encostado na borda, inclusive o wipe_button do teste da fila por dono.
…nflict (#79) O território era conferido contra o patientId declarado e nunca contra o da linha existente: um ACS movido de microárea recebia a cópia da área de origem no conflito ou a sobrescrevia pelo resolveConflict. Agora audita denied_patient_mismatch e responde como a recusa territorial, como no ramo legado. O teste da visita criada pelo resolveConflict passa a conferir a linha conflict_resolved_local.
…onta descartada (#79) O diálogo de Sair ganha a linha logout_conflict_visits e o "sobem sozinhas" deixa de contar as visitas em conflito, que o envio diferido não envia. O ramo synced de resolveKeepLocal não recoloca em enviadas a visita descartada durante a chamada, como o ramo rejected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #79
Resumo
O ACS passa a ver cada visita em conflito de sincronização, comparar lado a lado a versão do aparelho com a do servidor e decidir qual prevalece — ou editar e enviar uma nova versão (PRD §2.3.2, risco R1). Antes, a visita em conflito voltava à fila, era reenviada a cada "Sincronizar agora" (o contador crescia sem limite), não sobrevivia a um reinício do app e o ACS não tinha com o que comparar.
Plano:
docs/superpowers/plans/2026-10-16-issue-79-resolucao-de-conflito.md.Backend
VisitSyncResult.serverVisit: noconflict, o autor recebe a cópia do servidor (nunca nosyncLegacy); trilharead/visit_conflictsem conteúdo clínico. Sem migração de banco.visits.resolveConflict(accessToken, visit, expectedServerVersion): compare-and-set explícito — versão vista igual à atual grava comversion + 1e auditawrite/visit/conflict_resolved_local; vencida devolve conflito novo sem gravar. Mesmas recusas dosync, por um_checkTargetcompartilhado.sync/syncDeferred/resolveConflictrecusam umlocalIdexistente enviado com outropatientId(denied_patient_mismatch, mesma resposta da recusa territorial). Sem isso, um ACS movido de microárea leria as notas decifradas de uma visita antiga.App ACS
OfflineVisitQueue.resolveKeepServer(local, com confirmação destrutiva) eresolveKeepLocal(manter a minha / editar e enviar, viaresolveConflict), seguras com sync em voo.VisitConflictListScreeneVisitConflictCompareScreen(visit_conflict_screen.dart): lado a lado ≥ 600 dp, empilhadas no celular, marca "Diferente" com ícone + texto, alvos 48×52,liveRegion, testadas a 130%/200% de fonte.Testes
dart test test/unit933 passando; integração (visit_conflict_resolution_test.dartnovo + legado + território) 25 passando com-j 1.flutter test606 passando,flutter analyzelimpo.emulator-5554, bancosinalacs_e2e):scripts/qa/acs_full_e2e.shsaiu 0, 6/6 testWidgets; o banco confirma versão 4 para "manter a minha", versão 3 para "manter a do servidor", uma linhaconflict_resolved_locale cadeia de auditoria íntegra. Log emdocs/superpowers/plans/2026-10-16-issue-79-e2e.log. O e2e rodou antes da correção de território (coberta por testes de unidade e integração)._tocarNoMeio), porque a barra inferior cobria o alvo.Em aberto (registrado no
PROGRESS.md)resolveConflictlê e depois grava sem trava: dois aparelhos do mesmo ACS resolvendo ao mesmo tempo, o segundo vence sem aviso (osynctem a mesma lacuna).