Não lançado
Em desenvolvimentoAdded
- Página `/conta` com DSAR self-service: download do export de dados (route handler com `Content-Disposition` e `no-store`) e exclusão de conta com confirmação digitada validada no servidor; link "Minha conta" no dashboard. E2E `dsar.spec.ts` + baseline a11y. (#11)
- Playback de vídeo Bunny Stream: `BunnyService` com Token Authentication HMAC-SHA256 (`node:crypto`, fixado em unit test com fixture), endpoint unificado `GET /videos/lesson/:lessonId/playback` com validação de matrícula, componente `VideoPlayer` com hls.js dinâmico, renovação de token e estados de processamento do `video_jobs`. (#9)
- Schema de billing agnóstico de provedor (ADR 0016): tabelas `plans`, `subscriptions`, `payments` com RLS FORCE, FK composta anti-cross-tenant, grants por coluna e testes de isolamento; decisão de provedor adiada. (#8)
- Testes de isolamento RLS para `badge_classes` e `certificates` — 100% das tabelas com `tenant_id` cobertas. (#5)
- E2E para fluxos críticos: quiz (aprovação/reprovação/tentativas), matrícula via catálogo e gestão de membros admin. (#10)
- Coverage v8 no CI com artifact e thresholds ratchet; unit tests em `packages/shared` (sanitizeContentRef, schemas Zod), `packages/auth` (CASL) e `apps/web` (PKCE/OIDC); job `ai-workers-lint` (ruff). (#6)
Fixed
- **`pnpm db:migrate` não aplicava nenhuma migration.** O script era `drizzle-kit migrate`, que lê `packages/db/migrations/meta/_journal.json` — e o journal estava com `entries: []`, então o comando concluía com sucesso sem executar nada. As migrations são SQL escrito à mão (políticas RLS, `DO $$`, triggers, grants por coluna) que `drizzle-kit generate` não produz nem registra. Substituído por runner próprio (`packages/db/src/migrate.ts`): aplica cada `NNNN_*.sql` uma vez em ordem numérica dentro de transação, registra nome + SHA-256 em `schema_migrations`, é idempotente, recusa migration já aplicada cujo conteúdo mudou, rejeita prefixos numéricos duplicados e serializa execuções por advisory lock.
- `docs/runbooks/deploy.md` afirmava que o workflow executava `db:migrate` antes de promover a imagem nova; nenhum step de migrate existe em `.github/workflows/`. Runbook corrigido com o procedimento manual real e o motivo (o Postgres não é exposto à internet, então o GitHub Actions não alcança o banco); automação registrada como Trigger Feature em `docs/backlog.md`.
- Guards do runner, todos verificados na leitura do diretório antes de tocar o banco (achados das revisões de banco e SRE): `.sql` fora do padrão de nome agora **lança** em vez de ser ignorado em silêncio — era a mesma classe do bug original, e `10000_x.sql` seria descartado; ordenação passou a ser numérica em vez de lexicográfica; migration com `BEGIN`/`COMMIT`/`ROLLBACK` próprios é rejeitada (um `COMMIT` interno fecharia a transação do runner e deixaria metade aplicada e não registrada); `CONCURRENTLY` é rejeitado por não rodar em transação. Cada migration roda com `lock_timeout` de 5s, para que uma migration aparentemente aditiva não enfileire atrás de query longa e bloqueie todos os leitores da tabela.
- Espera pelo advisory lock passou a ter deadline (`pg_try_advisory_lock` em laço): `pg_advisory_lock` bloquearia em silêncio até o socket de um par morto ser coletado — até 2h no default do Linux. O runner também detecta lock não liberado, sinal de conexão via pgBouncer em transaction pooling (armadilha nº 2 do `CLAUDE.md`), em que o lock vaza e toda execução seguinte trava.
- Erros ficaram diagnosticáveis: a migration que falhou é nomeada (o erro do driver diz o que quebrou, nunca onde), `ROLLBACK` e liberação de lock não mascaram mais o erro original, e o CLI imprime o erro inteiro — um `PostgresError` carrega `detail`, `hint`, `where` e `position`, que é a parte que explica a falha.
- `docs/runbooks/deploy.md` instruía `pnpm db:migrate` na VPS, o que não funciona: o Postgres não publica porta, e a imagem da API não tem `pnpm`, `tsx` nem os arquivos `.sql`. Substituído por procedimento executável com container efêmero na rede do stack. `infra/scripts/restore.sh` repetia a mesma instrução impossível — corrigido.
- `docs/runbooks/rollback.md` orientava "desenhar migrations reversíveis" sem alertar que reverter ou apagar o arquivo de uma migration aplicada agora trava toda execução seguinte por drift; documentado o procedimento correto e como descobrir onde o banco parou.
- `drizzle-kit generate` escrevia dentro de `packages/db/migrations/`, injetando SQL não revisado na sequência de aplicação; redirecionado para diretório gitignorado. `migrations/meta/` removido — o journal vazio era artefato morto que documentava uma mentira.
- `apps/api/test/rls/global-setup.ts` mantinha uma lista hardcoded das 15 migrations — segunda fonte da verdade que, ao divergir, faria os testes de RLS validarem um schema diferente do de produção. Agora usa o mesmo runner.
- Build standalone do Next.js não copiava `.next/static`/`public`: o servidor E2E respondia 404 para todos os chunks e nenhuma página hidratava — testes de cliente eram impossíveis. Alinhado ao comportamento do Dockerfile de produção. (#10)
- Job Semgrep quebrado silenciosamente: a imagem `latest` (1.164.0) falha na validação de regras do registry e o `continue-on-error` mascarava; imagem pinada em `semgrep/semgrep:1.116.0`. (#7)
- Duas violações sérias de contraste (WCAG AA) em `/app` e `/cursos`, detectáveis após a correção de hidratação. (#10)
- `PRIVACY.md` descrevia export/exclusão com comportamento que o código não tem (email assíncrono; anonimização com agregados); alinhado ao tratamento real e ao canal `/conta`. (#11)
Security
- `schema_migrations` nascia gravável pelo role de runtime `app`: `0012_app_role_grants.sql` concede DML sobre `ALL TABLES` e por `ALTER DEFAULT PRIVILEGES`. Um `app` comprometido poderia reescrever hashes do registro de migrations e mascarar drift de schema. O runner revoga todos os privilégios de `app` sobre a tabela ao final de cada execução, com teste de privilégio (e asserção de controle, para o teste não passar vacuamente).
- `.gitattributes` adicionado fixando `eol=lf` para `*.sql`: um checkout com CRLF alteraria o hash de toda migration e dispararia falso positivo de drift.
- Jobs Trivy e Semgrep do CI tornados bloqueantes após triagem completa (Trivy: 0 CVEs HIGH/CRITICAL; Semgrep: 2 findings resolvidos). (#7)
- `verify-publish-guards.cjs`: `execSync` com interpolação de path trocado por `execFileSync` sem shell (elimina injeção de comando). (#7)
- Endpoint unificado de playback valida matrícula ativa do aluno — o endpoint antigo validava apenas tenant. (#9)
Changed
- RELEASE_NOTES.md atualizado com backfill narrativo v0.1.0 → v1.0.0. (#6)
- Links de comparação do changelog apontam para o repositório real e incluem a v1.0.0.