Inicio do Projeto Piloto
ci / verify (push) Has been cancelled

This commit is contained in:
camilafagundes committed 2026-10-02 23:59:36 -03:00
1 parent 6e23f1533b
commit 419325db12
52 files changed
+1329 -110

No files matched your search

+2 -2
View File
@@ -1,5 +1,5 @@
# ADR 0004 — TTL de temporários
Status: aberta.
Status: política operacional definida; o número de produto continua configurável.
`SPREADSHEET_TEMP_TTL_SECONDS` é obrigatório em produção e aceita de 1 segundo a 7 dias para não gravar um número de produto no código. Fora de produção, a omissão usa 3600 segundos só para o ambiente local. A limpeza não apaga chaves que contenham `/models/`.
`SPREADSHEET_TEMP_TTL_SECONDS` é a política única. Em produção é obrigatório e o processo recusa subir sem um inteiro entre 1 e 604800. Fora de produção, a omissão usa 3600 segundos. A limpeza compara `expires_at` com o relógio injetado, é idempotente, só apaga chaves `workspaces/{workspaceId}/tmp/` e não interrompe o processamento se um delete falhar. O valor numérico aprovado pelo negócio ainda não foi fixado; o piloto define o número na configuração, não no código.
+12 -2
View File
@@ -2,8 +2,18 @@
## Azul de ação
O escopo fixa Bright Blue `#2F8FFF` e pede o azul primário do token oficial, que não estava no repositório. Uso provisório: `#0B3D91` em `--color-action`, com texto branco. Substituir quando o pacote do Design System Elo Pro for ligado.
O escopo fixa Bright Blue `#2F8FFF` e pede o azul primário do token oficial, que não estava no repositório. Uso provisório: `#0B3D91` em `--color-action`, com texto branco. Esse valor não é token oficial e não deve ser promovido por conveniência. Substituir somente quando o pacote do Design System Elo Pro for disponibilizado.
## Exclusão
Status: aberta. O schema marca `deleted` e preenche `deleted_at`, mantém o arquivo e o histórico. A exclusão física pode ser feita depois sem migração destrutiva. Enquanto houver job em `validating`, `ready`, `queued` ou `processing`, a exclusão é recusada.
Status: provisória e centralizada em `src/domain/spreadsheet-adapter/retention-policy.ts`.
| Entidade | Tratamento |
| --- | --- |
| Configuração de mapeamento | Permanece com o job |
| Modelo | Soft delete (`deleted` + `deleted_at`); arquivo retido |
| Fonte e resultado | Hard delete do blob após o TTL |
| Job | Metadados permanecem |
| Auditoria | Append-only; `app_user` não recebe update nem delete |
A exclusão física do modelo pode ser feita depois sem migração destrutiva. Enquanto houver job em `validating`, `ready`, `queued` ou `processing`, a exclusão do modelo é recusada. O prazo dos temporários continua em `SPREADSHEET_TEMP_TTL_SECONDS`.
@@ -1,5 +1,7 @@
# Revisão final contra o escopo V3
Status consolidado: **Pilot Ready in Code / Environment Validation Pending**. O gate, a ordem da validação no ambiente Elo Pro e a regra de manter a feature flag desligada estão em `18-pilot-final-gate.md`.
| Requisito | Status | Evidência |
| --- | --- | --- |
| Sem login, senha ou token próprio | OK | `src/server/auth`, ausência de tabela de senha |
@@ -0,0 +1,39 @@
# Auditoria de prontidão do piloto
Estado encontrado antes do hardening desta entrega. O resultado depois das mudanças está em `18-pilot-final-gate.md`.
Nenhum repositório real da Elo Pro estava disponível. Os nomes `profiles`, `workspaces` e `workspace_members` são o contrato local, não um schema confirmado.
| Item | Classificação | Estado | Evidência | Risco | Dependência externa | Decisão | Módulo | Ação | Severidade |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1. Autenticação Supabase | NEEDS INTEGRATION | `getUser` quando URL e anon key existem. Sem tabela de senha. | `src/server/auth/context.ts` | Piloto sem o projeto Supabase da Elo Pro não autentica de verdade | Projeto Supabase Elo Pro | Não criar identidade paralela | `src/server/auth` | Ligar as envs e validar login real | Alta |
| 2. Workspace | READY WITH CONFIGURATION | Cookie `elo_pro_workspace_id` validado por membership no servidor | `context.ts` | Cookie de outro ambiente aponta workspace errado | Cookie emitido pela Elo Pro | Confirmar o nome do cookie no host real | `context.ts` | Testar troca de workspace no host | Alta |
| 3. RLS | READY WITH CONFIGURATION | Policies e `FORCE RLS` no shim. Testes usam PGlite com `app_user` | `migrations/0003_rls.sql`, `tests/integration/rls.test.ts` | Postgres real pode divergir do PGlite | Postgres Elo Pro | Não aplicar o shim se as tabelas já existirem com outro formato | `migrations/` | Rodar o teste de isolamento no banco do piloto | Alta |
| 4. Papéis | READY | owner, admin, gestor, agent. Superadmin local não ignora o módulo | `permissions.ts` | Papéis reais da Elo Pro podem ter outros nomes | Membership real | Mapear papéis quando o schema for confirmado | `permissions.ts` | Manter o adaptador | Média |
| 5. Feature flag | READY WITH CONFIGURATION | `spreadsheet_adapter` nasce desligada | `workspace_module_flags`, `scripts/set-flag.ts` | Ligar para todos os workspaces | Operação | Só o workspace piloto | flag | Ativar no passo final do runbook | Alta |
| 6. Storage | NEEDS INTEGRATION | Interface privada. Produção exige bucket. Download passa pela rota autenticada | `src/infra/storage` | Bucket público ou URL permanente | Bucket Supabase | Manter privado | `storage/supabase.ts` | Criar bucket privado | Alta |
| 7. Jobs | READY WITH CONFIGURATION | Estados duráveis no Postgres. Inline só fora de produção | `service.ts`, `process-job.ts` | Piloto subir em inline | Worker | `ADAPTER_JOB_MODE=worker` | `scripts/worker.ts` | Subir o worker | Alta |
| 8. Redis | NEEDS INTEGRATION | Lock e fila de IDs. Sem Redis a produção recusa o processamento | `src/infra/redis` | Várias instâncias sem Redis processam em duplicidade | Redis | Obrigatório no piloto | `redis.ts` | Preencher `REDIS_URL` | Alta |
| 9. Locks | READY | Lock por usuário, TTL 120s, liberação no `finally` | `lockKey` | Lock em memória não atravessa processos | Redis no piloto | Não usar lock global | `redis.ts` | Usar Redis | Média |
| 10. TTL | READY WITH CONFIGURATION | Obrigatório em produção. Dev usa 3600 | `src/config/retention.ts`, ADR 0004 | Temporários eternos se a env faltar | Número operacional | Não gravar o número no código | `retention.ts` | Definir a env no piloto | Alta |
| 11. Limpeza | READY | `cleanupExpired` ignora `/models/` e é idempotente | `process-job.ts` | Falha de storage deixa blob órfão | Storage | Falha de limpeza não pode derrubar o job | worker | Observar `CLEANUP_FAILED` | Média |
| 12. Auditoria | READY | Eventos sem células. Append-only para `app_user` | `scrub.ts`, `0004_audit_append_only.sql` | Service role ainda escreve | Nenhuma | Não apagar auditoria no rollback | `audit_log` | Manter | Média |
| 13. Sentry | READY WITH CONFIGURATION | Sem DSN fica inerte. Payload descarta texto livre | `scrub.ts`, `capture.ts` | DSN ausente deixa o piloto cego | Projeto Sentry | Não anexar arquivo | `instrumentation.ts` | Configurar DSN | Média |
| 14. XLSX | NEEDS DECISION | ExcelJS 4.4.0 gera workbook novo. O modelo original não é reescrito | `parse.ts`, ADR 0005 | Formatação do modelo não é preservada | Nenhuma para o MVP | Manter ExcelJS no piloto | `13-xlsx-library-review.md` | Aceitar a recomendação | Média |
| 15. CSV | READY | UTF-8, delimitador, neutralização na saída | `csv.ts`, `csv-injection.ts` | Encoding legado é recusado | Nenhuma | UTF-8 apenas | `parse.ts` | Manter | Baixa |
| 16. PDF | READY | Gerado sob demanda, truncado em 200 linhas e 12 colunas | `pdf.tsx` | Usuário espera o arquivo inteiro | Nenhuma | Truncamento já documentado | `pdf.tsx` | Informar o limite | Baixa |
| 17. Exclusão | NEEDS DECISION | Soft delete de modelo. Blob temporário sai com o TTL | ADR 0006 | Reter planilha além do necessário | Negócio | Política provisória configurável | `retention-policy.ts` | Não fazer hard delete de auditoria | Média |
| 18. Migrations | READY WITH CONFIGURATION | 0001 é shim. 0002–0004 são do Adaptador | `migrations/` | Aplicar 0001 em cima do schema real da Elo Pro | Schema real | 0001 só se as tabelas não existirem | `identity-port.ts` | Conferir o banco antes do migrate | Alta |
| 19. Variáveis | NEEDS INTEGRATION | `.env.example` lista o conjunto. A validação recusa produção incompleta | `src/config/env.ts` | Build de CI não tem os segredos e não deve falhar | Secrets do piloto | Falhar no start, não no build | `15-production-environment.md` | Preencher no ambiente | Alta |
| 20. Dev versus produção | READY WITH CONFIGURATION | `ADAPTER_DEV_AUTH` exige não-produção | `dev-session.ts` | Segredo de dev vazado para produção | Nenhuma | Produção recusa a flag | `env.ts` | Não definir o segredo no piloto | Alta |
## Pilot Go-Live Blockers
Estes itens impedem ativar o primeiro workspace piloto. O restante é preparação já coberta no código ou decisão que não bloqueia se a recomendação for aceita.
1. O banco e o Auth reais da Elo Pro não estão ligados. O shim de identidade não pode ser aplicado se as tabelas já existirem com outro formato.
2. O bucket privado de storage não está criado nem configurado.
3. Redis e o worker não estão em execução no ambiente do piloto. Inline fica recusado em produção.
4. `SPREADSHEET_TEMP_TTL_SECONDS` ainda não tem valor no ambiente de piloto.
5. O smoke com Supabase, Redis e bucket reais não foi executado.
6. A feature flag do workspace piloto continua desligada, de propósito, até o fim do runbook.
@@ -0,0 +1,31 @@
# Integração de identidade Elo Pro
O Adaptador não cria usuário, senha, cadastro ou papel próprio. A sessão de produção é a do Supabase Auth. O workspace ativo chega no cookie `elo_pro_workspace_id` e é conferido no servidor contra a membership.
## Contexto autenticado
`resolveAuth` devolve `AuthenticatedContext`:
- `userId`
- `workspaceId`
- `role`
- `permissions` (`adapter.session`, `adapter.use`, `adapter.manage`, `adapter.upload`)
- `moduleEnabled`
APIs passam por `requireReady`. Serviços chamam `assertUse`, que relê a flag no banco. Um `workspaceId` no corpo não substitui o cookie validado. `rejectWorkspaceForgery` registra a tentativa.
## Contrato ainda não confirmado
`src/server/auth/identity-port.ts` lista as tabelas que o código lê hoje. O status é `unconfirmed_against_real_elo_pro`, exceto a flag do módulo, que é específica do Adaptador. Quando o repositório da Elo Pro estiver acessível, a adaptação acontece nesse ponto. Não há migration definitiva da Elo Pro neste repositório.
## Desenvolvimento
`ADAPTER_DEV_AUTH=1` só funciona com `NODE_ENV` diferente de `production` e com `ADAPTER_DEV_AUTH_SECRET`. Em produção a validação de ambiente recusa a flag, e `devAuthEnabled()` permanece falso.
## Testes
- Workspace de outro usuário não lista nem baixa o arquivo.
- `workspaceId` forjado é recusado.
- Usuário sem membership não opera.
- Módulo desligado bloqueia mesmo que o contexto do cliente diga o contrário.
- Produção ignora a sessão de desenvolvimento.
@@ -0,0 +1,26 @@
# Topologia de sessão
O Adaptador não implementa IdP nem OAuth próprio e não copia cookie entre domínios.
## Cenários
| Cenário | Comportamento |
| --- | --- |
| Mesmo domínio, subpaths | Cookie Supabase e `elo_pro_workspace_id` já visíveis. `/login` só redireciona se a sessão não existir |
| Subdomínios | Exige cookie no domínio pai, configurado na Elo Pro. O Adaptador não redefine o domínio do cookie |
| Domínios separados | O login central emite a sessão Supabase. O retorno usa `returnTo` apenas para host permitido |
| Redirect central | `ELO_PRO_LOGIN_URL` recebe `returnTo` |
| Sessão Supabase compartilhada | `getUser` lê os cookies `sb-*` já presentes |
## returnTo
`safeReturnPath` aceita somente caminho relativo interno. `https://`, `//` e host fora de `ELO_PRO_APP_URL` / `AUTH_ALLOWED_RETURN_HOSTS` voltam para `/adaptador`.
`/login` em produção redireciona para o login central. A tela local com identidades de desenvolvimento só aparece quando `ADAPTER_DEV_AUTH` está ligado fora de produção.
## Sessão e módulo
- Sem cookie de sessão: anônimo, 401 nas APIs.
- Cookie presente e usuário ausente: `session_expired`, mensagem para entrar de novo.
- Autenticado sem membership ou sem workspace: bloqueio, sem operar planilha.
- Módulo desligado: rotas voltam ao painel e APIs respondem `FEATURE_DISABLED`.
+19
View File
@@ -0,0 +1,19 @@
# Job runner
Postgres guarda o estado. Redis guarda o lock por usuário e uma fila de IDs. O payload da fila é somente o id do job.
Estados já existentes e suficientes: `draft`, `validating`, `ready`, `queued`, `processing`, `completed`, `failed`, `expired`, `cancelled`.
## Modo
`ADAPTER_JOB_MODE=worker` enfileira. `inline` só é aceito fora de produção. Produção com `inline` falha na validação de ambiente e também em `startProcess`.
Falha transitória (`5xx`, exceto timeout e erros de arquivo) volta para `queued` até 3 tentativas. Timeout, validação e modelo alterado não entram em loop. O lock é liberado no `finally`. Job sem lock em `processing` é recuperado por `recoverStaleJobs`.
## Subir o piloto
1. Redis acessível em `REDIS_URL`.
2. `npm run start` para a aplicação web, com as envs de produção.
3. `npm run worker` em outro processo, com as mesmas envs de banco, storage e Redis.
A limpeza de temporários roda no tick do worker. Se ela falhar, o tick seguinte continua e o erro segue para o capturador com o código `CLEANUP_FAILED`, sem conteúdo de planilha.
@@ -0,0 +1,35 @@
# Revisão da biblioteca XLSX
## Atual
| Campo | Valor |
| --- | --- |
| Biblioteca | ExcelJS |
| Versão declarada | 4.4.0 |
| Licença | MIT |
| Leitura | `workbook.xlsx.load`, valor exibido em `cell.text` |
| Escrita | Novo workbook, aba `Adaptado`, texto com formato `@` |
## Vantagens
MIT, já integrada, lê e escreve XLSX, não executa macro e não avalia fórmula. O arquivo modelo permanece o blob original; o processamento grava outra chave.
## Limitações
Não preserva estilo, mesclagem, largura de coluna nem as outras abas do modelo no arquivo de saída. Datas e números saem como texto exibido. PDF continua truncado à parte. Arquivo protegido por senha cai em erro de estrutura.
## Riscos
Trocar a biblioteca no piloto reabre a suíte de mapeamento sem ganho para o contrato atual: a saída é um artefato novo e o modelo é imutável.
## Alternativas
SheetJS Community tem restrição de licença que exigiria revisão jurídica. Manter ExcelJS atrás de `readSpreadsheet` e `buildXlsx` permite trocar depois sem mudar o domínio.
## Custo de migração
Alto para o piloto: fixtures, tipos, datas e o contrato de “não executar fórmula” teriam de ser revalidados. Não há blocker que obrigue a troca.
## Recomendação
Manter ExcelJS 4.4.0 no piloto. Não criar uma segunda engine. Fixtures em `tests/unit/xlsx-fixtures.test.ts` cobrem várias abas, fórmula como texto, data, caractere especial e a imutabilidade do modelo.
@@ -0,0 +1,17 @@
# Isolamento multi-tenant
Invariantes:
1. O workspace da operação é o do contexto resolvido no servidor.
2. RLS com `app_user` esconde linha de outro workspace e de outro usuário no mesmo workspace.
3. A flag do módulo é relida no banco em listagem, processamento e download.
4. Download só lê chave `workspaces/{workspaceId}/tmp/`.
5. Limpeza só apaga chave com o prefixo do workspace dono da linha.
6. Auditoria não é atualizada pelo papel da aplicação.
7. Texto de célula não entra em auditoria nem no payload enviado ao Sentry.
A suíte `tests/security/isolation.test.ts` e `tests/integration/rls.test.ts` cobre workspace A/B, admin com módulo desligado, agente sem o job de outro usuário, flag desligada depois do processamento, `workspaceId` de quem não é membro, lock concorrente e arquivo hostil.
URL assinada permanente não é emitida. O download é a rota autenticada. Reutilizar um id de outro workspace devolve `NOT_FOUND` ou `FEATURE_DISABLED`.
O que ainda depende do Postgres e do Storage reais está marcado no gate final.
@@ -0,0 +1,23 @@
# Ambiente de produção
A validação está em `src/config/env.ts`. Ela roda no start da aplicação e do worker. Durante `next build` ela não interrompe o CI, porque o build não tem os segredos do piloto.
| Variável | Obrigatória | Ambiente | Finalidade | Exemplo sem segredo | Risco se ausente |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_SUPABASE_URL | Sim | Produção | Auth Elo Pro | `https://<projeto>.supabase.co` | Ninguém autentica |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | Sim | Produção | Chave pública do Auth | `anon` | Sessão não resolve |
| SUPABASE_SERVICE_ROLE_KEY | Sim | Servidor | Storage e tarefas internas | `service-role` | Upload e worker param |
| SUPABASE_STORAGE_BUCKET | Sim | Produção | Bucket privado | `spreadsheet-adapter` | Produção recusa storage. Valor `public` é recusado |
| DATABASE_URL | Sim | Produção | Postgres | `postgres://host/db` | Aplicação não sobe |
| REDIS_URL | Sim | Produção | Lock e fila de ids | `redis://host:6379` | Processamento recusado |
| SENTRY_DSN | Sim | Produção | Erros sanitizados | `https://<key>@sentry/<id>` | Piloto sem alerta |
| SPREADSHEET_TEMP_TTL_SECONDS | Sim | Produção | Vida dos temporários | `86400` | Start recusado |
| ADAPTER_JOB_MODE | Sim, implícito | Produção | `worker` | `worker` | `inline` recusado |
| ELO_PRO_LOGIN_URL | Sim | Produção | Login central | `https://app.elo.example/login` | `/login` não encaminha |
| ELO_PRO_APP_URL | Recomendada | Produção | `returnTo` no mesmo host | `https://app.elo.example` | Retorno cai no caminho padrão |
| AUTH_ALLOWED_RETURN_HOSTS | Não | Produção | Hosts extras do retorno | `app.elo.example` | Host externo continua bloqueado |
| ADAPTER_DEV_AUTH | Deve ficar desligada | Produção | Sessão local | `0` | Valor `1` recusa o start |
| ADAPTER_DEV_AUTH_SECRET | Não | Só dev | Assina o cookie local | vazio em produção | Não é usado no piloto |
| SPREADSHEET_JOB_TIMEOUT_MS | Não | Todos | Teto do processamento | `120000` | Usa 120000 |
`ADAPTER_DEV_AUTH` não autentica em produção. Storage público não é aceito. TTL inválido falha cedo. Sem Redis, o modo worker não processa.
@@ -0,0 +1,32 @@
# Runbook de implantação do piloto
Execute nesta ordem. Não apague auditoria, banco ou arquivos como parte do rollback.
1. Preparar o projeto Supabase da Elo Pro e anotar URL, anon key e service role.
2. Validar que o login Elo Pro emite sessão Supabase e o cookie `elo_pro_workspace_id`.
3. Conferir se `profiles`, `workspaces` e `workspace_members` já existem. Se existirem com outro formato, não aplique `0001_platform_shim.sql`. Aplique só `0002`, `0003` e `0004` depois de adaptar `identity-port.ts`.
4. Validar RLS no papel `app_user`: um usuário não lê modelo de outro workspace.
5. Criar o bucket privado. Não gerar URL pública.
6. Subir Redis e preencher `REDIS_URL`.
7. Definir `ADAPTER_JOB_MODE=worker`.
8. Preencher os secrets da tabela em `15-production-environment.md`. Não definir `ADAPTER_DEV_AUTH` nem o segredo de desenvolvimento.
9. Definir `SPREADSHEET_TEMP_TTL_SECONDS` com o prazo operacional do piloto.
10. Preencher `SENTRY_DSN`. Confirmar que o evento de teste não contém célula.
11. Rodar `npm run migrate` no banco já conferido.
12. Iniciar a web com `npm run start`.
13. Iniciar `npm run worker` com as mesmas variáveis.
14. `GET /api/health/live` deve responder `live`. `GET /api/health/ready` deve responder `ready` sem connection string.
15. Executar o smoke do caminho completo no ambiente real (`SMOKE_REAL` ainda não está automatizado contra o host; percorrer a jornada na interface).
16. Ativar a flag só no workspace piloto: `npm run flag -- <workspace_id> on`.
17. Verificar logs do worker: nome do erro e código, sem planilha.
18. Processar um arquivo pequeno de ponta a ponta e baixar XLSX, CSV e PDF.
## Rollback
1. `npm run flag -- <workspace_id> off`.
2. Parar o processo do worker.
3. Manter `audit_log`, modelos e o banco.
4. Não rodar script que apague storage ou tabelas.
5. Se o deploy em si estiver incorreto, promover de volta o artefato anterior depois da flag desligada.
A última ativação do módulo fica no checklist de `18-pilot-final-gate.md`.
@@ -0,0 +1,21 @@
# Baseline de performance
Medição local em `tests/performance/baseline.test.ts`, CSV de 200 e de 5.000 linhas, duas colunas, nesta máquina de desenvolvimento. A amostra de 5.000 linhas, somando leitura, CSV e XLSX, ficou abaixo de 1 segundo. Não houve otimização em cima desse número.
Não foi medida a planilha XLSX de 50.000 linhas no ExcelJS. O limite de 50.000 linhas úteis continua o do domínio, exercitado no caminho CSV em `tests/performance/rows.test.ts`.
## Limites recomendados para o piloto
| Limite | Valor |
| --- | --- |
| Tamanho do arquivo | 10 MB |
| Linhas úteis | 50.000 |
| Colunas | 200 |
| Abas | 30 |
| Caracteres por célula | 8.000 |
| Fontes por operação | 10 |
| Modelos ativos | 10 |
| Tempo de processamento | 120 segundos, configurável |
| PDF | 200 linhas e 12 colunas, com aviso de truncamento |
Arquivo vazio, ZIP inválido, macro, CSV fora de UTF-8 e célula acima do teto são recusados antes do processamento. Fórmulas não são executadas. Macros não são abertas.
@@ -0,0 +1,86 @@
# Gate final do piloto
**Status: Pilot Ready in Code / Environment Validation Pending**
Não há novas alterações funcionais até a validação no ambiente real da Elo Pro. O Adaptador só entra no workspace do primeiro cliente depois que todos os itens externos deste gate passarem nesse ambiente.
| Frente | Estado |
| --- | --- |
| Código | Aprovado para piloto |
| Infraestrutura Elo Pro | Ainda não validada |
| Segurança multi-tenant | Aprovada na suíte disponível |
| Smoke real | Pendente |
| Rollback real | Pendente |
| Feature flag do cliente piloto | Desligada |
Pendências que não bloqueiam o código atual e bloqueiam a ativação: integração com o repositório real da Elo Pro e validação visual com os tokens oficiais. `#0B3D91` permanece provisório e não vira token oficial.
## Próximo pacote — somente validação no ambiente Elo Pro
1. Integrar e validar o login real no navegador.
2. Validar Postgres, migrations e RLS no banco Elo Pro.
3. Provisionar e testar o bucket privado.
4. Subir Redis e worker e confirmar processamento assíncrono real.
5. Provocar um erro controlado e confirmar sanitização no Sentry.
6. Executar o smoke end-to-end completo.
7. Executar o ensaio de rollback.
8. Integrar os tokens oficiais do Design System quando forem disponibilizados.
9. Somente então ativar a feature flag do workspace piloto.
Verificado nesta máquina em 2 de outubro de 2026:
- `npx eslint .` passou
- `npx tsc --noEmit` passou
- `npx vitest run` passou depois da correção do teste de auditoria (34 testes, 1 smoke real ignorado de propósito)
- `npx next build` passou
O smoke com Supabase, Redis e bucket reais não foi executado. `tests/smoke/real-environment.test.ts` permanece ignorado sem `SMOKE_REAL=1`.
Os prompts 21 e 22 não foram executados: não há repositório da Elo Pro nem tokens oficiais do Design System neste workspace. O azul `#0B3D91` continua provisório.
## Avaliação
| Área | Resultado | Teste que ainda falta |
| --- | --- | --- |
| Elo Pro Auth | PASS WITH EXTERNAL DEPENDENCY | Login real no navegador, sessão reconhecida, logout e sessão expirada no host Elo Pro |
| Workspace isolation | PASS | Suíte de aplicação executada |
| RLS | PASS WITH EXTERNAL DEPENDENCY | Repetir o isolamento no Postgres do piloto, não só no PGlite |
| Storage | PASS WITH EXTERNAL DEPENDENCY | Upload e download no bucket privado real |
| Redis | PASS WITH EXTERNAL DEPENDENCY | Lock e fila contra um Redis real |
| Worker | PASS WITH EXTERNAL DEPENDENCY | Subir `npm run worker` no ambiente do piloto e concluir um job enfileirado |
| TTL | PASS | Configuração inválida, arquivo vencido e limpeza repetida cobertos na suíte |
| XLSX | PASS | ExcelJS mantido. Fixtures e imutabilidade do modelo executadas |
| Observability | PASS WITH EXTERNAL DEPENDENCY | A sanitização foi testada com sentinela. Falta um evento real no projeto Sentry |
| Feature flag | PASS | Workspace desligado, flag alterada depois do job e contexto forjado cobertos. A ativação do workspace piloto é o último passo operacional |
| Audit trail | PASS | Eventos de upload, mapeamento, processo e download. Update pelo `app_user` é recusado |
| Upload security | PASS | Macro, CSV inválido, célula enorme, zip e tamanho cobertos |
| Concurrency | PASS | Duas chamadas do mesmo usuário terminam em um resultado |
| Production env | PASS WITH EXTERNAL DEPENDENCY | O validador foi testado. Falta subir web e worker com as envs reais do piloto |
| Smoke tests | PASS WITH EXTERNAL DEPENDENCY | A jornada local passou. A jornada no ambiente real não foi executada |
| Rollback | PASS WITH EXTERNAL DEPENDENCY | O procedimento está no runbook. Um ensaio de rollback não foi feito |
Nenhum item ficou FAIL no código desta entrega. O piloto não está liberado para o cliente enquanto os itens com dependência externa não forem executados no ambiente real.
## Congelamento
| Ocorrência | Classificação |
| --- | --- |
| `ADAPTER_DEV_AUTH` | Manter apenas em desenvolvimento |
| Jobs inline | Manter apenas em desenvolvimento |
| Shim `profiles` / `workspaces` / `workspace_members` | Manter até o schema real. Remover a dependência só na integração da Elo Pro |
| `console.log` dos scripts de migrate, seed, flag e cleanup | Aceitável no piloto. Não imprimem célula |
| `console.error` com o nome do erro | Aceitável no piloto |
| Azul `#0B3D91` | Pós-piloto, quando houver token oficial |
| Bucket público, senha própria, identidade paralela | Não há. Nada a remover |
| TODO / FIXME no código | Não há |
## Pilot Activation Checklist
- [ ] Confirmar que o schema Elo Pro já existente não receberá o shim de identidade
- [ ] Aplicar as migrations do Adaptador no banco conferido
- [ ] Criar o bucket privado
- [ ] Configurar Redis, worker, TTL, Sentry e o login central
- [ ] Subir web e worker e receber `ready` em `/api/health/ready`
- [ ] Executar o smoke real: modelo, fonte, aba, cabeçalho, mapeamento, prévia, processamento, XLSX, CSV e PDF
- [ ] Confirmar auditoria sem conteúdo de célula e rollback ensaiado com a flag desligada
- [ ] Enable Spreadsheet Adapter for pilot workspace