{
    "name": "Systole — simulador de plantão de pronto-socorro",
    "short_name": "Systole",
    "start_url": ".",
    "display": "standalone",
    "_motivo_do_display": "[GTM, frente D, 28/10/2026 — VOLTOU A 'standalone', porque agora há cache de verdade] O texto da rodada 109 está logo abaixo, intacto: ele é a razão de este campo ter sido 'browser'. O dia que ele previa chegou — mobile/web/systole_sw.js é um service worker de cache do shell, registrado por mobile/web/index.html e carimbado por scripts/build_web.sh (o selo é o hash do conteúdo do pacote; o nome do cache leva o selo; build novo apaga o cache velho no activate). O service worker do Flutter deixou de ser registrado (index.html sem serviceWorkerSettings): ele se desregistra e, no mesmo escopo, levaria o nosso junto. MEDIDO agora num Chromium real (Playwright, headless, CanvasKit por swiftshader), contra pacotes construídos por scripts/build_web.sh e servidos por scripts/servir_web.py (brotli, ETag) atrás de um proxy contador de bytes: (1) navigator.serviceWorker.getRegistrations() passou de [] para um registro com systole_sw.js ativo e controlando a página; (2) perfil limpo, primeira abertura: o pacote SEM o worker custou 15 requisições e 3,56 MB na rede (11,98 MB descomprimidos no disco — o '10,43 MB em 12 arquivos' da rodada 109 era de um pacote menor e/ou sem compressão); COM o worker, 4,14 MB (os 3,56 mais cerca de 0,58 MB de áudio e ícones guardados em segundo plano depois do primeiro quadro); (3) recarga com o MESMO perfil e o cache HTTP do navegador esvaziado (o que um celular faz com o disco cheio): sem o worker, 3,56 MB de novo; com ele, 0,014 MB — só o documento; (4) recarga sem rede: sem o worker, net::ERR_INTERNET_DISCONNECTED; com ele, o jogo abre inteiro (a primeira tela, com o paciente, foi capturada em PNG offline); (5) o caminho da atualização, mesmo perfil: publicado um build novo (outro selo, outro main.dart.js) e recarregando COM rede, o V8 executou o main.dart.js NOVO já na primeira recarga (Debugger.scriptParsed: 5.142.732 caracteres antes, 5.142.599 depois), o cache velho foi apagado e o seguinte contém só o selo novo. CORREÇÃO DE UMA PREMISSA DA RODADA 109: 'custa isso TODA VEZ' vale para perfil limpo e para cache HTTP despejado; no mesmo perfil, com o cache HTTP vivo, a segunda abertura já custava 0 byte de corpo — mas 15 requisições condicionais (304), contra 2-4 com o worker, e sem rede o jogo não abria. O ganho do worker é o offline, o despejo e as idas e voltas, e não 'dez megabytes por abertura'. MODO DE FALHA QUE SOBRA: (a) o jogador com um worker VELHO ativo recebe o index novo pela rede e o worker, ao ver o selo diferente, passa a buscar os ativos pesados na rede até ser substituído — se a guia ficar aberta por dias sem recarregar, ela segue no build que carregou; (b) a lista do shell (precache) é gerada do pacote: um arquivo novo que o jogo peça e que não esteja em assets/, icons/ ou canvaskit/ não é guardado e some offline; (c) a API NUNCA é cacheada (outra origem, nunca GET com Authorization) — sem rede o jogo abre, mas conversar com o paciente e logar exigem a API, então 'offline' aqui é 'abre e mostra a primeira tela', não 'joga sem rede'; (d) o Safari/iOS despeja o armazenamento de sites pouco usados (~7 dias sem uso): lá o cache pode sumir e voltar à primeira abertura; (e) para desligar em emergência há receita no cabeçalho de systole_sw.js. === HISTÓRICO, TEXTO ORIGINAL DA RODADA 109 (mantido) === [QA rodada 109, frente N — achado G-A10] Era 'standalone', e essa palavra é uma PROMESSA: o navegador passa a oferecer 'instalar', a janela instalada perde a barra de endereços e o jogador espera algo que abra como aplicativo. Não havia nada atrás dela. MEDIDO num Chromium real contra um build deste commit, servido em 127.0.0.1:8123 (roteiro em scratchpad/N_a10_medicao.mjs e N_a10_medicao2.mjs): (1) depois de a página abrir, navigator.serviceWorker.getRegistrations() devolve [] — o flutter_service_worker.js que o Flutter 3.44.8 gera é o coto que se DESREGISTRA (install -> skipWaiting, activate -> registration.unregister()), então não existe camada de cache nenhuma; (2) abrir com o cache vazio custa 10,43 MB em 12 arquivos — canvaskit.wasm 5,49 MB e main.dart.js 4,13 MB —, e custa isso TODA VEZ; (3) abrir sem rede com o cache vazio dá net::ERR_INTERNET_DISCONNECTED, ou seja, a página de erro do próprio navegador — e num app instalado não há barra de endereços para sair dela; (4) tendo aberto uma vez, recarregar sem rede trava na tela estática de abertura ('PREPARANDO O PLANTÃO…'), porque o index.html vem do cache HTTP e o resto não vem. O público declarado deste jogo 'joga no ônibus'. As duas saídas honestas eram escrever um service worker de cache do shell ou tirar a promessa; a primeira exige mexer no web/index.html (registro do SW) e conviver com o modo de falha conhecido do versionamento ('o app não atualiza'), e o web/index.html não é desta frente. Fica a segunda. O manifesto CONTINUA valendo para o resto — nome, ícones, cor de tema, descrição —, que é o que um atalho de tela inicial usa. Trocar de volta para 'standalone' é uma linha, no dia em que houver cache de verdade. (Esta chave não é do padrão de Web App Manifest; membros desconhecidos são ignorados pelo analisador do navegador, e JSON não tem comentário.)",
    "background_color": "#0A111C",
    "theme_color": "#0A111C",
    "description": "Atenda o plantão brasileiro: fila de Manchester, consultório e box de choque. Converse com o paciente por IA, decida a conduta e leia o preceptor no fim.",
    "orientation": "portrait-primary",
    "prefer_related_applications": false,
    "icons": [
        {
            "src": "icons/Icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        },
        {
            "src": "icons/Icon-maskable-192.png",
            "sizes": "192x192",
            "type": "image/png",
            "purpose": "maskable"
        },
        {
            "src": "icons/Icon-maskable-512.png",
            "sizes": "512x512",
            "type": "image/png",
            "purpose": "maskable"
        }
    ]
}
