Implementação MVP

This commit is contained in:
camilafagundes committed 2026-10-03 00:00:45 -03:00
commit 807b3d72cd
54 files changed
+5241

No files matched your search

@@ -0,0 +1,23 @@
# ADR-000 — Aplicação standalone neste repositório
- Status: Accepted
- Data: 2026-10-02
- Decisor: produto / engenharia, nesta sessão
## Contexto
O discovery da fase 00 encontrou um repositório vazio. A premissa inicial do pacote era reutilizar auth, workspace, RLS e design system de outro checkout EloPro.
## Decisão
`gerador-proposta` é um produto standalone novo. O repositório vazio é o baseline oficial.
Auth, workspace, usuários, papéis e RLS serão implementados aqui, nas fases 02 e 03. O design system nasce aqui no prompt 04, com os tokens EloPro V2.1, pronto para convergir depois sem acoplar código agora.
A integração com outros componentes EloPro ocorre por ports. Este módulo não assume banco, sessão, componentes ou código compartilhado.
## Consequências
- Não há etapa de importar um repositório legado antes do prompt 02.
- Contratos externos ficam em `src/integrations/`. O CRO standalone devolve `no-context` ou `pending-source` (P-002), e a geração não depende dele.
- Stack alvo: Next.js 16, React 19, TypeScript 5, Tailwind CSS v4, Supabase, Redis, Sentry, Claude e Vitest. Nesta fase só entram TypeScript, ESLint e Vitest, porque ainda não há app, banco, fila nem chamada de modelo.
@@ -0,0 +1,49 @@
# ADR-001 — Escopo da sequência PROP
- Status: Proposed
- Data: 2026-10-02
- Pendência: P-009
- Decisão: nenhuma
## Contexto
A primeira geração bem-sucedida cria um número comercial no formato `PROP-######` e a versão `V1`. Falha de geração não pode consumir número nem versão. O que permanece aberto é o escopo do contador: o que faz duas propostas receberem números diferentes, e onde a unicidade vale.
A source of truth registra uma recomendação técnica de escopo por workspace. Esta recomendação não foi aprovada. Este ADR não a converte em regra.
## Alternativas
1. Sequência por `workspace_id`. É a recomendação técnica citada na especificação. Continua não adotada.
2. Sequência global da plataforma, única entre workspaces.
3. Sequência por documento comercial (CNPJ ou equivalente). CNPJ não é chave de tenant; esta alternativa entra só para ficar visível.
4. Sequência por usuário criador.
## Critérios para sair de Proposed
- Unicidade definida: quem pode ver o mesmo número e quem não pode.
- Comportamento em falha: a reserva volta ao contador. Pular número em rollback não atende RN-017.
- Concorrência: duas gerações simultâneas no mesmo escopo não recebem o mesmo número.
- Efeito em workspace novo, arquivado e reativado.
## Impacto no código desta fase
`CommercialNumberAllocator` só pode ser chamado de dentro do commit formal, depois do artefato válido, e só quando a proposta ainda não tem número. O duplo em memória usa o prefixo `TEST-` de propósito. Ele não é a sequência de produção e não escolhe a alternativa 1, 2, 3 ou 4.
O formato `PROP-######` já está na source of truth. O regex em `src/domain/generation/types.ts` descreve esse formato. Ele não aloca números.
## Impacto no prompt 02
A migration pode criar `proposals.commercial_number` como texto anulável.
Ainda não criar:
- `CREATE SEQUENCE` ou equivalente
- `UNIQUE (commercial_number)` global
- `UNIQUE (workspace_id, commercial_number)`
- trigger que preencha o número na inserção da proposta
Qualquer um desses objetos escolheria o escopo antes da aprovação deste ADR.
## O que aprovar este ADR não muda
Versão formal continua imutável. Retry com a mesma chave não cria outra versão. A segunda versão reutiliza o número já commitado; não pede outro.
@@ -0,0 +1,40 @@
# ADR-002 — Motor de ingestão de PDF, PPTX e DOCX
- Status: Proposed
- Data: 2026-10-02
- Pendência: P-010
- Decisão: nenhuma
## Contexto
Admin ou Gestor importa um modelo PDF, PPTX ou DOCX. O campo dinâmico nasce só de um marcador `[campo]` escrito no arquivo. O sistema não infere trecho variável. IA não substitui essa validação.
O motor que lê o arquivo e devolve texto ou estrutura ainda não foi escolhido. Sem essa escolha, não há parser real nesta fase.
## Alternativas
1. Bibliotecas locais por formato, com uma etapa posterior e determinística que só aceita `[campo]`.
2. Conversão por processo externo (por exemplo LibreOffice) para um formato intermediário, seguida da mesma detecção determinística.
3. Serviço externo de extração de documento, seguido da mesma detecção determinística.
4. Modelo de linguagem lendo o arquivo para achar campos. Esta alternativa conflita com a regra de marcador explícito e com a proibição de a IA substituir a validação. Está listada para não reaparecer como atalho. Não é candidata aceitável para descobrir campos.
As alternativas 1, 2 e 3 continuam abertas. Nenhuma foi escolhida. Claude permanece na stack para capacidades de IA que venham a ser aprovadas; não é o motor de marcadores.
## Critérios para sair de Proposed
- Fidelidade dos marcadores em PDF, PPTX e DOCX reais, inclusive repetidos e partidos por formatação.
- O que acontece com arquivo sem marcador, arquivo protegido e extração incompleta. O último ponto também depende de P-006.
- Isolamento do processo que abre o arquivo, tempo máximo e falha sem criar rascunho de modelo. O limite numérico continua em P-006 e P-012.
- O workspace só lê o próprio arquivo.
## Impacto no código desta fase
`TemplateIngestionService` devolve `blocked / P-006` enquanto a política de arquivo não existir, e `blocked / P-010` mesmo que um teste marque a política como aprovada. Não há adapter que leia bytes.
## Impacto no prompt 02
Pode existir coluna ou objeto para `source_file_ref`, `normalized_template` e `analysis_json`. O conteúdo desses JSON não fica preso a um motor. Não há worker de conversão nesta fase nem na migration.
## O que este ADR não decide
Tamanho máximo, antivírus, sandbox e extração parcial continuam no ADR-004.
@@ -0,0 +1,41 @@
# ADR-003 — Provider de e-mail
- Status: Proposed
- Data: 2026-10-02
- Pendência: P-011
- Relacionada: P-001
- Decisão: nenhuma
## Contexto
Download e e-mail usam o PDF da versão formal exibida. O envio não muda o status `Gerada`. A documentação de ecossistema cita Resend em outros contextos. Isso não formaliza o provider deste módulo.
P-001 também segue aberto: o que fazer quando o workspace não tem e-mail comercial para Reply-To.
## Alternativas
1. Resend. Citado fora deste módulo. Não adotado aqui.
2. Outro provider HTTP transacional.
3. SMTP operado por este produto.
4. Não enviar até haver provider e regra de Reply-To. É o comportamento do port hoje, não uma escolha de produto para o MVP.
## Critérios para sair de Proposed
- Provider nomeado, conta e ambiente definidos.
- O anexo é o `pdfRef` da versão exibida, não uma nova renderização.
- Falha de envio não altera status, número nem versão.
- Reply-To: ou o e-mail comercial do workspace, ou a regra explícita de P-001. O provider não inventa remetente.
- Evento em `proposal_email_events` com resultado e id do provider quando houver.
- Corpo, assunto e destinatário não vão para o Sentry.
## Impacto no código desta fase
`ProposalEmailProvider` devolve `blocked / P-001` sem Reply-To resolvido e `blocked / P-011` quando o Reply-To existe mas não há provider. Não há cliente HTTP.
## Impacto no prompt 02
A tabela `proposal_email_events` pode ser criada com `provider_message_id` anulável e `result` textual. Não gravar um check constraint com o nome de um provider.
## O que aprovar o provider não decide
P-001 continua uma pendência separada. Aprovar Resend, ou outro nome, não define o fallback de Reply-To.
@@ -0,0 +1,39 @@
# ADR-004 — Limites e segurança de arquivo
- Status: Proposed
- Data: 2026-10-02
- Pendência: P-006
- Decisão: nenhuma
## Contexto
O upload do modelo aceita PDF, PPTX e DOCX. A especificação deixa abertos tamanho máximo, antivírus, sandbox e o que fazer com extração parcial. P-012 também deixa abertas as metas numéricas de tempo e tamanho. Este ADR não preenche nenhum desses números.
## Alternativas
1. Allowlist de MIME e extensão, com teto de tamanho, sem antivírus no MVP.
2. Antivírus síncrono antes de persistir o arquivo.
3. Abertura do arquivo só em sandbox, com timeout.
4. Rejeitar qualquer extração parcial. A alternativa simétrica, aceitar documento parcial, também fica registrada e não foi escolhida.
Nenhuma combinação foi aprovada. Não há valor padrão de megabytes, timeout ou lista de assinaturas.
## Critérios para sair de Proposed
- Tamanho máximo por arquivo e por workspace, ou a decisão explícita de não ter teto.
- Varredura: obrigatória, adiada ou dispensada no MVP, com o risco aceito.
- Onde o arquivo é aberto e o que acontece se o conversor não terminar.
- Extração parcial: rejeita o upload ou segue com o que foi lido. Isso altera o rascunho de modelo e precisa estar explícito.
- Falha de política não cria rascunho de modelo nem dispara o motor do ADR-002.
## Impacto no código desta fase
`FileSecurityPolicy` só tem `unapproved` ou `approved` com uma referência de decisão. Não há campos `maxBytes` nem `timeoutMs`. A ingestão bloqueia em P-006 enquanto a política estiver `unapproved`.
## Impacto no prompt 02
Pode existir referência de storage privado para o arquivo de origem. Não criar check de tamanho, coluna de status de antivírus com fornecedor escolhido, nem job de varredura. Signed URL e bucket entram quando o fluxo de upload existir, com isolamento por workspace.
## Relação com os outros ADRs
P-010 escolhe como ler o arquivo. P-006 escolhe se ele pode ser aceito. Os dois precisam estar Approved antes do upload real do prompt 06. O checklist humano pede essa revisão antes do Pacote B.