PWA privada para dois adultos coordenarem compromissos familiares e registros de medicação em uma única casa.
O produto está definido em PRD.md.
O projeto está na fase de especificação e decomposição em tickets.
- Hoje é a única navegação primária.
- Amanhã aparece inline a partir das 19h.
- Cada compromisso confirmável possui no máximo um responsável.
- O registro compartilhado é mais importante que notificações.
- O aplicativo não oferece orientação médica.
Requisitos: Node.js 22+, pnpm.
pnpm install
pnpm dev # http://localhost:3000Outros comandos:
pnpm run typecheck # tsc --noEmit
pnpm run test:unit # node --test, valida helpers de auth/push/household e o SW
pnpm run test:rls # Postgres: policies do household (CI; local skip se sem psql)
pnpm run build # next build → exporta site estático em out/, gera ícones e sw.js
pnpm run test:e2e # Playwright: registro do Service Worker, casca offline
pnpm run test:production-gate # gate PRD §21 (typecheck+unit+RLS; FULL=1 inclui build/e2e)
pnpm test # typecheck + test:unit + test:rls + build + test:e2eSomente variáveis prefixadas com NEXT_PUBLIC_ chegam ao cliente — nenhum segredo deve
usar esse prefixo. .env.development e .env.production versionam a configuração pública
não sensível de cada ambiente; .env.example documenta o que precisa existir. Segredos
(Supabase, VAPID, etc.) nunca são lidos pelo frontend estático e não pertencem a este
repositório — ver seção 15 do PRD.
NEXT_PUBLIC_SUPABASE_URL e NEXT_PUBLIC_SUPABASE_ANON_KEY são as únicas credenciais do
Supabase que o cliente recebe (públicas por design). Sem elas, o app mostra um estado
"não configurado" em vez de uma tela de login quebrada.
Login por OTP de seis dígitos enviado por Gmail SMTP dedicado, verificado dentro do PWA
instalado — sem cadastro público e sem Magic Link (ver seção 11 do PRD).
Procedimentos administrativos (provisionar os dois adultos, recuperar acesso sem Gmail,
trocar de adulto) estão em docs/runbook-auth.md.
O household singleton e as crianças usam RLS por membership —
docs/runbook-household.md.
A agenda de Hoje/Amanhã vem de household_agenda_snapshot (ocorrências derivadas).
Após o bootstrap, o adulto cadastra criança e rotina semanal em Configurações
(create_weekly_routine); seed_weekly_routine fica só para testes/service role
— ver o mesmo runbook.
Backup cifrado e restauração: docs/runbook-backup.md.
Operação e diagnóstico do stack Free (monitor, exclusão total, tabletop):
docs/runbook-ops.md.
Gate de entrada em produção (dois iPhones, evidência PRD §21):
docs/production-gate.md.
Notificações usam Web Push + VAPID (PRD §10). A chave pública VAPID entra no build
como NEXT_PUBLIC_VAPID_PUBLIC_KEY; a privada e o Cron ficam só no Supabase.
Gere o par com node scripts/generate-vapid-keys.mjs. Procedimentos (Function, Cron
Free, matriz nos iPhones, go/no-go) estão em docs/runbook-push.md.
O resultado do spike fica em docs/push-spike-result.md.
O app é exportado como site estático (next build com output: "export") e publicado no
Cloudflare Pages (plano Free), hostname estável *.pages.dev.
.github/workflows/ci.yml faz o deploy automaticamente: todo push em main que passa em
typecheck/testes/build roda wrangler pages deploy out --project-name=combinado. O build
do Actions embute NEXT_PUBLIC_* no bundle estático a partir de secrets do repositório —
variáveis do painel Cloudflare Pages não entram nesse caminho. Requer secrets no
GitHub (Settings → Secrets and variables → Actions):
CLOUDFLARE_API_TOKEN— token com permissão de "Cloudflare Pages: Edit" para a conta.CLOUDFLARE_ACCOUNT_ID— Account ID do Cloudflare.NEXT_PUBLIC_SUPABASE_URL— URL pública do projeto Supabase.NEXT_PUBLIC_SUPABASE_ANON_KEY— anon/publishable key (pública por design; ainda assim fica fora do git e só entra no build via CI).NEXT_PUBLIC_VAPID_PUBLIC_KEY— chave pública VAPID paraPushManager.subscribe.
Backup diário cifrado (PRD §16 / docs/runbook-backup.md)
usa secrets adicionais no Actions — nunca no frontend:
SUPABASE_DB_URL— connection string Postgres direta (dump/status).BACKUP_AGE_PUBLIC_KEY— recipientage1…. A chave privada permanece offline em duas cópias controladas.
Deploy manual (sem esperar o CI), com a Cloudflare CLI autenticada:
pnpm dlx wrangler@4 pages deploy out --project-name combinado.
Se o projeto Pages estiver ligado ao Git, use o preset Next.js (Static HTML
Export) (npx next build → diretório out). O preset Next.js padrão
(@cloudflare/next-on-pages) não é necessário para este export estático.
Nenhuma credencial do Cloudflare ou do Supabase é armazenada neste repositório.
public/manifest.webmanifestdefine nome, ícones,display: standaloneeidestável.scripts/generate-sw.mjsroda após o build e escreveout/sw.js: cache com nome versionado (hash do conteúdo exportado) contendo somente a casca pública do app. Cada deploy invalida o cache anterior. A nova versão baixa em segundo plano e só é oferecida quando não há confirmação/edição em andamento (PRD §18).- Após um primeiro carregamento com sucesso, a casca abre offline; login e dados seguem exigindo conexão, o que é sinalizado na tela.
- Checklist manual de acessibilidade/performance nos dois iPhones: docs/checklist-a11y-perf.md.