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

+146
View File
@@ -0,0 +1,146 @@
# Arquitetura — Gerador de Propostas
Estado: prompt 01. Baseline oficial: repositório standalone, sem código EloPro legado. Decisão em `adr/ADR-000-standalone-application.md`.
Não há feature de produto nesta fase: nem tela, nem migration, nem upload, nem PDF real, nem e-mail real.
## 1. Contexto
O Gerador de Propostas é um produto EloPro independente. O usuário autenticado opera propostas e, conforme o papel, modelos, dentro de um workspace. CRO, Contratos e Apresentação não são pré-requisito do fluxo.
```text
Pessoa de comercial / Admin / Gestor
|
v
Gerador de Propostas (este repositório)
|
+--> PostgreSQL (Supabase) estados formais, RLS
+--> Redis lock, claim de geração, job efêmero
+--> Storage privado arquivo de modelo e PDF formal
+--> Sentry erro, com scrub
+--> Provider de e-mail port; provider não escolhido (P-011)
+--> Motor de ingestão port; motor não escolhido (P-010)
+--> CRO port opcional; fonte de cliente aberta (P-002)
```
Auth, workspace, papéis e RLS nascem neste repositório. A UI usa os tokens EloPro V2.1 definidos na source of truth, em componentes locais, a partir do prompt 04.
## 2. Contêineres
| Contêiner | Papel nesta fase | Fonte de verdade |
| --- | --- | --- |
| Next.js 16 App Router | Ainda não criado. Entra no prompt 04. | — |
| PostgreSQL via Supabase | Ainda sem migration. Estados formais: modelo, proposta, versão, auditoria, e-mail. | Sim, quando existir |
| Redis (`ioredis`) | Ainda sem cliente. Lock de edição, claim de geração, estado efêmero de job. | Não |
| Storage | Ainda sem bucket. | O ponteiro formal fica no PostgreSQL |
| Sentry | Ainda sem SDK. A política de scrub já existe. | Não |
| Claude | Ainda sem SDK. Não lê marcadores. | Não |
PostgreSQL é a fonte dos estados formais. Redis pode perder dados. Se o processo cair depois do commit e antes de apagar o claim, o retry lê o PostgreSQL e devolve a mesma versão.
## 3. Camadas
```text
src/ui experiência. Vazia até o prompt 04.
src/application casos de uso. Hoje: coordenação da geração e o contrato de autorização.
src/domain regras e tipos. Não importa aplicação, ports, infra, UI nem integrações.
src/ports interfaces que a aplicação exige da infra.
src/infrastructure adapters. Hoje só duplos em memória, para teste.
src/integrations/elopro contratos com outros produtos. Sem banco nem sessão compartilhada.
src/observability scrub antes de log/Sentry.
```
Regra de dependência, também no ESLint:
- `domain` não importa as outras camadas.
- `ports` importa só `domain`.
- `application` não importa `infrastructure` nem `ui`. Teste pode importar duplo.
- `infrastructure` não importa `application` nem `ui`.
- `integrations` não importa `application`, `infrastructure` nem `ui`.
`app/` (rotas) fica para o prompt 04 e deve chamar aplicação, não o banco direto a partir de componente cliente.
## 4. Componentes de domínio
| Módulo | Responsabilidade | Fora dele |
| --- | --- | --- |
| Models | Modelos, limite 10, versão de modelo, ativação | Não inicia proposta |
| Proposals | Rascunho, status `Rascunho`/`Gerada`, versão formal | Não envia e-mail |
| Generation | Commit do PDF, número e versão na mesma transação | Não escolhe o escopo de `PROP` |
| Delivery | E-mail do `pdfRef` exibido | Não muda status |
| Audit | Eventos mínimos | Retenção é P-007 |
| Branding | Logo obrigatório, cores opcionais | Não é o design system da UI |
| CRO | Pré-preenchimento quando houver contexto | O standalone funciona com `no-context` |
Papéis previstos no tipo `WorkspaceRole`: `owner`, `admin`, `gestor`, `agent`. O `WorkspaceAuthorizer` não tem implementação. Não existe adapter que responda `allowed` por omissão. O prompt 03 implementa a matriz, no servidor, com `workspace_id` da sessão.
Lock de edição: `EDIT_LOCK_TTL_MS` = 10 minutos. A interface `DistributedLockService` não tem tomada forçada.
## 5. Geração: idempotência e transação
Entrada: workspace, proposta, rascunho, ator, chave de idempotência, hash do conteúdo, versão do modelo e, se já existir, o número comercial. A chave efetiva no store e no ledger inclui workspace + proposta + chave. Outro workspace não reaproveita o commit.
Ordem:
1. Se o PostgreSQL já tem commit da mesma chave e da mesma impressão digital (rascunho, versão do modelo, hash), devolver essa versão com `replayed: true`. Não renderiza de novo e não aloca número.
2. Se a mesma chave já tem outra impressão digital, responder `conflict`. Não cria versão.
3. Claim efêmero (Redis). Mesma impressão em andamento: `in-progress`. Outra impressão em andamento: `conflict`.
4. Renderizar para staging. Falha aqui não abre transação, não aloca número e não grava versão. O claim é liberado para o retry.
5. Commit único no store formal:
- promove o staging para `pdfRef`;
- se a proposta ainda não tem número e esta é a primeira versão, reserva o número dentro dessa mesma unidade;
- insere a versão imutável apontando para esse `pdfRef`;
- confirma a reserva.
6. Se qualquer passo do commit falha, a reserva sofre rollback, o `pdfRef` promovido é desfeito e o staging é descartado. `commercialNumberAssigned` e `formalVersionCreated` ficam falsos. A falha não é gravada como resultado da chave: o retry pode tentar de novo.
7. A segunda versão só segue se `existingCommercialNumber` já veio do commit anterior. O coordenador não pede outro número. Quem aloca é só a primeira versão, e só dentro do commit.
O duplo de teste emite `TEST-######` para deixar óbvio que não é a sequência de produção. O escopo real espera o ADR-001.
Não há método de update ou delete de versão formal. O objeto commitado é congelado no duplo. A garantia de produção será a ausência de `UPDATE`/`DELETE` na policy e o fluxo, no prompt 02.
O TTL do claim não é SLO. P-012 segue aberto. Quem ligar o coordinator no futuro passa `attemptTtlMs`.
## 6. Ports
| Port | Situação |
| --- | --- |
| `GenerationCoordinator` | Implementado em `src/application/generation/run-generation.ts` |
| `FormalGenerationStore` | Contrato. Duplo em memória nos testes |
| `CommercialNumberAllocator` | Contrato transacional (`reserve` / `commit` / `rollback`). Sem adapter de produção |
| `ProposalPdfRenderer` | Contrato. Adapter previsto: `@react-pdf/renderer` sobre representação normalizada. Não instalado |
| `StagingArtifactStore` | Promote só fica visível se o commit concluir |
| `GenerationAttemptLedger` | Contrato do estado efêmero |
| `DistributedLockService` | Contrato do lock de edição. Sem force takeover |
| `TemplateIngestionService` | Sempre bloqueado: P-006 e depois P-010 |
| `ProposalEmailProvider` | Sempre bloqueado: P-001 ou P-011 |
| `CroClientPrefillPort` | `no-context` ou `pending-source` |
| `WorkspaceAuthorizer` | Só a interface |
## 7. Sentry e correlação
`CorrelationContext` carrega `requestId`, `jobId`, `workspaceId` e `actorId`. O `jobId` nasce quando houver job de geração. O `requestId` nasce na borda HTTP se o chamador não enviar um.
Propagação: a borda cria o contexto, a aplicação repassa aos ports, o job carrega o mesmo `requestId` e preenche `jobId`. Tags previstas no Sentry: esses ids mais `proposalId` ou `modelId` quando a operação for sobre um registro.
`scrubForTelemetry` roda antes de `captureException` / breadcrumb. Remove, em qualquer profundidade, documento, template, snapshot, análise, e-mail, assunto, corpo, telefone, CPF, CNPJ, logo e nome de arquivo. Não envia o PDF nem o modelo. O SDK do Sentry entra junto com a primeira borda que possa falhar (prompt 14, ou antes se o prompt 04 já expuser erro de runtime). Até lá a função é a política, não um cliente.
## 8. Stack instalada agora
TypeScript 5, ESLint 9 e Vitest. O `tsconfig` usa `moduleResolution: Bundler` e strict, alinhado ao App Router que virá.
Ainda não instalados, de propósito: Next.js, React, Tailwind, Supabase, `ioredis`, Sentry, `@anthropic-ai/sdk`, `@react-pdf/renderer`. Não há uso para eles nesta fase.
## 9. Pendências que continuam Proposed
- ADR-001 / P-009 — escopo da sequência
- ADR-002 / P-010 — motor de ingestão
- ADR-003 / P-011 — provider de e-mail
- ADR-004 / P-006 — tamanho, antivírus, sandbox, extração parcial
P-001, P-002, P-003, P-004, P-005, P-007, P-008 e P-012 também seguem abertos e não têm ADR de decisão.
## 10. O que o prompt 02 pode fazer sem novas decisões
Pode criar o schema do workspace, papéis e das tabelas de domínio da source of truth, com RLS por `workspace_id`, desde que não feche os ADRs acima.
Não criar sequence, unique do número comercial, check de provider, limite de arquivo, job de antivírus nem enum que escolha o motor de ingestão. Detalhe em cada ADR.
+41
View File
@@ -0,0 +1,41 @@
# Ordem recomendada de execução
## Pacote A — Fundação
1. `prompts/00_repo_discovery_and_guardrails.md`
2. `prompts/01_architecture_and_adrs.md`
3. `prompts/02_database_schema_rls_migrations.md`
4. `prompts/03_auth_workspace_permissions.md`
5. `prompts/04_design_system_and_app_shell.md`
**Gate A:** typecheck/lint/test/build verdes; cross-tenant básico validado; ADRs críticos registrados.
## Pacote B — Modelos de Proposta
6. `prompts/05_models_domain_list_capacity.md`
7. `prompts/06_model_upload_analysis_pipeline.md`
8. `prompts/07_model_field_review_activation_versioning.md`
9. `prompts/08_model_lock_autosave_archive_delete.md`
**Gate B:** fluxo de Modelos P0/P1 funcional, incluindo 10/10, lock, reprocessamento, ativação e V2+.
## Pacote C — Propostas
10. `prompts/09_proposals_home_and_selection.md`
11. `prompts/10_proposal_editor_dynamic_fields_preview.md`
12. `prompts/11_proposal_autosave_lock_save_exit_model_switch.md`
13. `prompts/12_validate_generate_async_idempotency.md`
14. `prompts/13_versioning_history_download.md`
**Gate C:** 4.1 → 4.2 → 4.3 completo; falhas não criam versões parciais; retry/duplo clique é idempotente.
## Pacote D — Comunicação, observabilidade e integração
15. `prompts/14_email_audit_sentry.md`
16. `prompts/15_cro_prefill_integration.md`
**Gate D:** auditoria ponta a ponta; e-mail usa o PDF exibido; CRO não é dependência do fluxo standalone.
## Pacote E — Qualidade e release
17. `prompts/16_testing_security_accessibility.md`
18. `prompts/17_performance_resilience_hardening.md`
19. `prompts/18_release_ci_cd_runbook.md`
20. `prompts/19_final_gap_audit.md`
**Gate final:** Definition of Done do MVP atendida, pendências remanescentes explicitamente registradas e nenhum comportamento não aprovado inventado.
@@ -0,0 +1,23 @@
# Checklist humano entre os pacotes
## Antes do Pacote B
- ADR-001: escopo da sequência PROP decidido?
- ADR-002: engine real de ingestão PDF/PPTX/DOCX decidida?
- ADR-004: política de tamanho/segurança de arquivo decidida ao menos para staging?
## Antes do Pacote D
- ADR-003: provider de e-mail aprovado?
- P-001: comportamento quando workspace não tem Reply-To definido?
## Antes de produção
- P-006: tamanho máximo, antivírus/sandbox/zip-bomb/timeout definidos.
- P-007: retenção de auditoria definida.
- P-008: lifecycle de entitlements definido.
- P-009: sequência PROP aprovada.
- P-010: engine de ingestão validada com arquivos reais.
- P-011: provider e credenciais de e-mail formalizados.
- P-012: pelo menos thresholds operacionais/alertas de produção definidos, mesmo que SLO final venha depois.
- Cross-tenant e storage tests aprovados.
- Geração idempotente comprovada sob concorrência.
- PDF histórico baixado/enviado corresponde exatamente à versão exibida.
- QA P0/P1 sem blocker aberto.
@@ -0,0 +1,53 @@
# Mapa de implementação — Gerador de Propostas
Estado: prompt 01 concluído no papel. ADR-000 aceito: standalone neste repositório. P-006, P-009, P-010 e P-011 seguem Proposed.
## Layout
```text
app/ # prompt 04; ainda não existe
src/domain/{models,proposals,generation,delivery,audit,branding,cro,shared}
src/application/generation # coordinator da geração
src/application/access # interface; implementação no prompt 03
src/ports/
src/infrastructure/memory # somente duplos de teste
src/integrations/elopro # CRO e futuros contratos, sem código compartilhado
src/ui # prompt 04
src/observability
supabase/migrations/ # prompt 02; ainda não existe
docs/gerador-propostas/adr/
```
PostgreSQL será a fonte de verdade dos estados formais. Redis ficará com lock, claim de geração e job efêmero. Nenhum dos dois clientes está instalado.
## Mapa por prompt
| Prompt | Feature | Onde deve morar | Padrão existente |
| --- | --- | --- | --- |
| 01 | Arquitetura, ADRs P-006/P-009/P-010/P-011, ports | `docs/gerador-propostas/ARCHITECTURE.md`, `docs/gerador-propostas/adr/`, `src/ports/` | Feito. ADRs 001–004 em Proposed. Sem sequence, sem motor, sem provider. |
| 02 | Schema, constraints, RLS | `supabase/migrations/` | Nenhum. Toda tabela de domínio tenant-safe por `workspace_id` ou FK + RLS. |
| 03 | Auth, workspace, papéis | `src/application/access/` + guards nas actions | Interface `WorkspaceAuthorizer` sem adapter permissivo. `workspace_id` resolvido no servidor. |
| 04 | DS V2.1 e app shell | `app/`, estilos Tailwind v4, componentes locais do domínio | Nenhum token no repo. Lacunas de DS (Search, Table, Stepper, Empty, Pagination, Skeleton, Upload, Avatar) ficam locais, não globais. |
| 05 | Listagem de modelos e limite 10 | `src/domain/models/` + rota de Modelos | Contar draft/active/archived; versões não ocupam vaga. |
| 06 | Upload e análise `[campo]` | porta `TemplateIngestionService`; UI de upload local | P-010 aberto. Sem inferir campo por IA/heurística. |
| 07 | Revisão de campos, ativação, V1/V2+ | domínio de modelos | Versão ativada imutável no fluxo normal. Só modelo Ativo inicia proposta. |
| 08 | Lock, autosave, arquivar, excluir modelo | Redis lock + PostgreSQL estado | TTL 10 min, sem force takeover. |
| 09 | Home 4.1 e seleção | `app/` propostas | Abrir fluxo não cria rascunho vazio. |
| 10 | Editor 4.2, campos, preview | uma página, estados Preencher → Validar → Gerar | Tipos Texto e Número/moeda; default Texto; obrigatório = Não. |
| 11 | Autosave, lock, salvar, sair, trocar modelo | drafts + lock | Rascunho nasce no primeiro persist. Lock só libera após sucesso de salvar e sair. |
| 12 | Validar e gerar, idempotência | `GenerationCoordinator` + job | Falha não cria número, versão formal nem PDF parcial. |
| 13 | Versões, histórico, download | `proposal_versions` + storage privado | PDF servido é o da versão exibida. Histórico não é editável. |
| 14 | E-mail, auditoria, Sentry | porta de e-mail + `proposal_audit_events` + Sentry | P-011 e P-001 abertos. Sem documento integral nem PII desnecessária em log/Sentry. |
| 15 | Pré-preenchimento CRO | `src/domain/cro/` | Opcional. Standalone funciona sem CRO. P-002 aberto (fonte de Cliente). |
| 16 | Testes, segurança, a11y | Vitest + testes de RLS/cross-tenant | Ainda sem runner. |
| 17 | Performance e resiliência | sem módulo novo obrigatório | P-012 aberto; não inventar SLO. |
| 18 | CI/CD e runbook | pipeline no remoto Gitea (não há `.github/`) | Sem pipeline hoje. |
| 19 | Auditoria de lacunas | `docs/gerador-propostas/` | Confrontar com `SOURCE_OF_TRUTH.md` §7 e §8. |
## Fora do MVP (não mapear como entrega)
USD/i18n, editor rico, modelo do zero, duplicar modelo, assinatura, WhatsApp/CRM, status Enviada/Aceita/Rejeitada, rollback de checkpoint, app nativo, Contrato habilitado (P-005), Apresentação (P-004).
## Gates
Ordem e gates humanos: `EXECUTION_ORDER.md` e `HUMAN_REVIEW_CHECKLIST.md`. O próximo passo permitido é o prompt 01, depois de revisão desta discovery.
+107
View File
@@ -0,0 +1,107 @@
# Discovery do repositório — Gerador de Propostas EloPro
Data: 2026-10-02
Fase: prompt 00 (fundação; nenhuma feature de produto)
Raiz: `c:\Users\Usuário\Documents\gerador-proposta`
## Conclusão
Este repositório é greenfield. Não há aplicação EloPro pré-existente para reutilizar. A stack descrita no pacote de prompts e na especificação v1.0 ainda não está materializada aqui. O remoto aponta para Gitea (`origin` → `https://gitea.fatorxtech.com/cferraz/gerador-proposta.git`) e o branch `main` ainda não tem commits.
Decisão de 2026-10-02, registrada no ADR-000: o vazio deste checkout é o baseline oficial. Auth, workspace, papéis, RLS e design system serão criados aqui. Não há outro repositório a importar. A compatibilidade com o restante do EloPro é de produto e de contrato, não de código compartilhado.
## 1. Inventário da árvore
| Área pedida | Achado |
| --- | --- |
| `app/`, `src/`, componentes, `lib` | Ausentes |
| Server Actions, Route Handlers | Ausentes |
| Supabase (`supabase/`, clients, migrations) | Ausentes |
| Testes (`vitest`, `*.test.ts`) | Ausentes |
| CI (`.github/`, pipelines) | Ausente |
| Sentry (`sentry.*.config.*`, `@sentry/*`) | Ausente |
| `package.json`, lockfile, `tsconfig`, Tailwind, ESLint | Ausentes |
| `.cursor/rules/` | Criado nesta fase: `elopro-gerador-propostas.mdc` |
| `docs/gerador-propostas/` | Criado nesta fase |
Arquivos versionáveis antes desta fase: nenhum (working tree só com `.git`).
## 2. Workspace ativo e papéis
Não há código que resolva workspace ativo nem que represente `owner` / `admin` / `gestor` / `agent`.
Contrato documental (ainda não implementado), em `SOURCE_OF_TRUTH.md`:
- Tenant técnico = `workspace_id`, com RLS. CNPJ não é chave de tenant.
- Papéis: Superadmin (`profiles.is_superadmin`), Administrador (`owner`/`admin`), Gestor (`gestor`), Usuário (`agent`).
- `workspace_id` não pode vir do cliente como autoridade.
Não existe tabela `profiles`, sessão, helper de impersonação nem superadmin neste repo.
## 3. RLS, policies e autorização
Não há migrations, policies, helpers de autorização nem fluxo de impersonação. Nada a reutilizar. A regra do agente exige revalidação server-side e RLS; isso começa no prompt 02, não aqui.
## 4. Design system e Tailwind v4
Não há tokens, componentes EloPro, `tailwind` nem consumo de CSS. A especificação visual (EloPro V2.1) está só no texto: Cloud White `#FBFBFE`, Midnight Ink `#071B45`, Primary Blue `#1F6FFF`, Blue 600 `#225EDB`, Bright Blue `#2F8FFF`, Aqua Signal `#00C2A8`, Manrope, raios e motion descritos em `SOURCE_OF_TRUTH.md` §6. Ícones EloPro não estão no repositório; Lucide fica como fallback futuro, não como biblioteca da marca.
## 5. Redis / ioredis
Ausente. Não há cliente, URL nem uso de lock.
## 6. Storage privado e signed URLs
Ausente. Não há bucket, adapter nem padrão de signed URL.
## 7. Server Actions, Route Handlers, validação e erros
Ausentes. Não há convenção local de validação (Zod ou outra) nem de erro de domínio.
## 8. Sentry
Ausente nos três runtimes (client, server, edge). Não há DSN nem scrubbing configurado.
## 9. CI e comandos canônicos
Não há pipeline. Não há scripts `typecheck`, `lint`, `test` ou `build`.
### Baseline registrado (2026-10-02)
| Comando | Resultado |
| --- | --- |
| `typecheck` | Não existe. Sem `package.json` / `tsc`. |
| `lint` | Não existe. Sem ESLint. |
| `test` | Não existe. Sem Vitest. |
| `build` | Não existe. Sem Next.js. |
| Instalação | Não executada. Não há dependências declaradas e o prompt 00 proíbe introduzir dependências sem necessidade. |
Falhas preexistentes de toolchain: não se aplicam. O baseline é “repositório sem toolchain”. Regressão futura será qualquer falha introduzida depois que esses comandos passarem a existir.
## Divergências entre stack esperada e o repo
Stack obrigatória do pacote (ainda não instalada): Next.js 16 App Router, React 19, TypeScript 5, Tailwind CSS v4, Supabase Auth + PostgreSQL (`@supabase/supabase-js`, `@supabase/ssr`), Redis (`ioredis`), TanStack Query v5 só onde houver cache client-side real, `@react-pdf/renderer`, Sentry, `@anthropic-ai/sdk` só para IA aprovada, Lucide só como fallback, `@dnd-kit` só se drag-and-drop for necessário, Vitest, ESLint 9, `postgres` + `tsx` para migrations.
Premissa do prompt 00 (“integrar ao ecossistema EloPro existente e reutilizar helpers”) **não se confirma neste checkout**. O próximo prompt precisa tratar o app como novo, com boundaries explícitos, em vez de procurar pastas que não existem.
Entradas externas usadas nesta análise (não estão no Git):
- `Especificacao_Final_Gerador_de_Propostas_EloPro_v1.0.docx` (02/10/2026)
- `Prompt_Pack_Cursor_Gerador_Propostas_EloPro_v1.0.zip`
- `elopro-gerador-propostas.mdc`
## Riscos antes do prompt 01
1. **Plataforma EloPro ausente.** Resolvido pelo ADR-000: o produto segue standalone neste repositório. Entitlements (P-008) continuam sem lifecycle definido.
2. **Pendências P-001..P-012 permanecem abertas.** Não foram preenchidas. P-009, P-010, P-011 e P-006 são o alvo dos ADRs do prompt 01 e devem ficar `Proposed`.
3. **Sem baseline executável.** Gates A–E do `EXECUTION_ORDER.md` não podem ficar verdes até existir toolchain. O prompt 01 deve criar o mínimo de projeto só se o escopo dele exigir ports testáveis; o prompt 00 não cria app.
4. **Sem commits.** `main` não tem histórico. Convenção de commit ainda não existe no repo.
5. **Isolamento multi-tenant ainda é só documento.** Qualquer feature antes de RLS/policies (prompt 02) e auth server-side (prompt 03) não pode ser considerada segura.
6. **Spec longa fora do Git.** A fonte normativa operacional copiada é `SOURCE_OF_TRUTH.md`. O docx completo continua em Downloads; se ele mudar, este discovery não acompanha sozinho.
## O que esta fase não fez
- Nenhuma feature de negócio (modelos, propostas, PDF, e-mail, CRO).
- Nenhuma dependência nova.
- Nenhuma migration.
- Nenhuma escolha silenciosa sobre P-001..P-012.
+125
View File
@@ -0,0 +1,125 @@
# Source of Truth — Gerador de Propostas EloPro — MVP
## 1. Decisões consolidadas
- Produto standalone dentro do ecossistema EloPro.
- Integração com CRO é opcional e serve para pré-preenchimento quando houver contexto explícito de cliente.
- Tenant técnico = `workspace_id`; isolamento por RLS. CNPJ é dado comercial, não chave de tenant.
- Papéis funcionais: Superadmin, Administrador, Gestor e Usuário. Mapeamento atual EloPro: `profiles.is_superadmin`, `owner/admin`, `gestor`, `agent`.
- Modelos são importados por Admin/Gestor a partir de PDF, PPTX ou DOCX.
- Marcadores variáveis: `[campo]`. O sistema não infere trechos variáveis.
- Limite: 10 modelos por workspace; Rascunho, Ativo e Arquivado contam. Versões internas não ocupam nova vaga.
- Status de proposta: apenas `Rascunho` e `Gerada`.
- Estados de UI da criação: `Preencher → Validar → Gerar`; são estados da mesma experiência, não status persistidos independentes.
- Primeira geração bem-sucedida cria `PROP-######` e `V1`.
- Versões formais são imutáveis. Edição posterior cria/retoma o rascunho da próxima versão.
- Download e envio por e-mail usam exatamente o PDF da versão exibida.
- Lock: um editor por recurso, TTL funcional de 10 minutos, sem force takeover no MVP.
- Autosave obrigatório.
- Contrato fica visível/desabilitado nesta fase. Apresentação permanece pendente.
## 2. Regras de negócio
- RN-001 Standalone — fluxo principal funciona sem Calculadora, Contratos, Planilhas ou outros produtos.
- RN-002 Brasil/BRL — pt-BR, BRL; sem seletor obrigatório de moeda.
- RN-003 Segurança server-side — permissão, entitlement, limite e estado também no backend/RLS.
- RN-004 Autosave — autosave não substitui Salvar/Salvar e sair.
- RN-005 Limite de modelos — máximo 10 por workspace; draft/active/archived contam.
- RN-006 Modelo ativo — somente modelo Ativo inicia proposta.
- RN-007 Marcadores — somente `[campo]` explícito origina campo.
- RN-008 Tipos de campo — Texto e Número/moeda; default Texto, obrigatório=Não.
- RN-009 Estrutura do modelo — marcador não é criado/excluído/renomeado na tela de validação; correção estrutural exige reenvio.
- RN-010 Versão de modelo — primeira ativação V1; alterações V2+ preservam histórico.
- RN-011 Criação de rascunho — abrir 4.2 não cria rascunho; nasce no primeiro persist válido.
- RN-012 Status — apenas Rascunho/Gerada; edição em andamento é indicador.
- RN-013 Fluxo 4.2 — Preencher/Validar/Gerar são estados da mesma página.
- RN-014 Salvar — com pendências salva e fica em Preencher; sem pendências cria checkpoint e vai para Validar.
- RN-015 Salvar e sair — persiste/checkpoint; libera lock somente após sucesso.
- RN-016 Lock — um editor; 10 min; sem tomada forçada; observador vê último persistido.
- RN-017 Geração formal — só existe sucesso se PDF formal existir; falha não cria número/versão formal.
- RN-018 Numeração — primeira geração cria PROP-###### e V1; escopo da sequência ainda precisa de decisão técnica formal.
- RN-019 Imutabilidade — versão formal gerada é imutável.
- RN-020 Nova versão — somente com alteração de conteúdo + nova geração bem-sucedida.
- RN-021 Histórico — versões antigas podem ser vistas/baixadas/enviadas, nunca editadas/excluídas/usadas como base.
- RN-022 E-mail — usa exatamente PDF exibido; não muda status Gerada.
- RN-023 Auditoria — autoria e eventos críticos rastreáveis.
- RN-024 Logo — logo da empresa obrigatório para identidade da proposta; cores opcionais.
- RN-025 Erros — erros são estados das telas existentes, sem criar novas páginas numeradas.
## 3. Requisitos funcionais
RF-001 autenticar/resolver workspace; RF-002 listar propostas por escopo; RF-003 listar modelos ativos; RF-004 confirmar seleção sem criar draft; RF-005 abrir 4.2 sem draft vazio; RF-006 prefill CRO quando aplicável; RF-007 campos dinâmicos; RF-008 validação durante edição; RF-009 autosave; RF-010 draft no primeiro persist; RF-011 Salvar; RF-012 Salvar e sair; RF-013 trocar modelo; RF-014 lock; RF-015 validar antes de gerar; RF-016 gerar PDF; RF-017 reconciliar geração assíncrona; RF-018 número comercial; RF-019 versionar; RF-020 detectar ausência de alteração; RF-021 visualizar vigente/histórica; RF-022 baixar PDF; RF-023 enviar e-mail; RF-024 registrar envio; RF-025 excluir draft elegível; RF-026 listar modelos; RF-027 limite 10/10; RF-028 upload PDF/PPTX/DOCX; RF-029 analisar marcadores; RF-030 draft de modelo após upload+análise técnica; RF-031 reprocessar correção no mesmo draft; RF-032 configurar campos; RF-033 autosave/lock de modelo; RF-034 ativar V1; RF-035 versionar modelo V2+; RF-036 arquivar; RF-037 excluir modelo nunca usado (Admin); RF-038 auditoria; RF-039 RLS; RF-040 Sentry.
## 4. Requisitos não funcionais
- Segurança: RLS + autorização server-side; fail-closed.
- Privacidade: nenhum documento/template cross-tenant; Sentry/logs sem documento integral/PII desnecessária.
- Confiabilidade: geração/ativação idempotentes; sem efeitos parciais.
- Concorrência: lock TTL 10 min + renovação por atividade; sem force takeover.
- Recuperação: autosave e retomada respeitando lock.
- Performance: carregamento progressivo; paginação/virtualização quando necessário; metas numéricas ainda abertas.
- Acessibilidade: alvo WCAG 2.1 AA, teclado, foco, labels, contraste, status não apenas por cor.
- Responsividade: web adaptada; mobile não é mera redução proporcional.
- Observabilidade: Sentry client/server/edge + correlação request/job.
- Auditabilidade: eventos mínimos imutáveis; retenção ainda aberta.
- Manutenibilidade: TS strict recomendado, lint, testes, separação por domínio.
- Integridade documental: versão formal referencia versão exata do modelo e artefato.
- Segurança de arquivos: MIME/extensão/conteúdo; tamanho/antivírus/sandbox ainda abertos.
## 5. Modelo de dados proposto
- `proposal_models`: id, workspace_id, name, status, current_version, created_by, archived_at.
- `proposal_model_versions`: id, model_id, version_no, source_file_ref, normalized_template, analysis_json, activated_at, activated_by.
- `proposal_model_fields`: id, model_version_id, field_key, display_name, type, required, order.
- `proposals`: id, workspace_id, created_by, owner_user_id, status, commercial_number, current_version_no, model_id, client_ref?, created_at.
- `proposal_drafts`: id, proposal_id, based_on_version_no?, model_version_id, data_json, content_hash, lock_owner?, updated_at.
- `proposal_versions`: id, proposal_id, version_no, model_version_id, content_snapshot, content_hash, pdf_ref, generated_by, generated_at.
- `proposal_email_events`: id, proposal_version_id, actor_id, to_email, subject, result, provider_message_id?, created_at.
- `proposal_audit_events`: id, workspace_id, proposal_id?, model_id?, actor_id, event_type, metadata, created_at.
- locks preferencialmente no Redis.
- `workspace_branding`: workspace_id, logo_ref, brand colors opcionais, contact email.
## 6. Design System EloPro V2.1
- Base predominante: Cloud White `#FBFBFE`, 65–75% superfícies claras.
- Texto/âncora: Midnight Ink `#071B45`.
- Primary Blue `#1F6FFF`; botão primário deve usar token de contraste do DS, Blue 600 `#225EDB` quando aplicável.
- Bright Blue `#2F8FFF`; Aqua Signal `#00C2A8`; Soft Blue `#DCEBFF`; Warning `#F39A2D`; Error `#E63946`.
- Manrope para UI. Source Serif 4 apenas institucional/editorial.
- Spacing 4px base; radius input 10, button 18, card 14, panel 18, modal 24.
- Motion 120/200/320ms, easing `cubic-bezier(.2,.8,.2,1)`.
- Proibido: neon, cyberpunk, holograma, glassmorphism, dark page dominante, grid decorativo, robôs/cérebros futuristas.
- Estados sempre por cor + texto + ícone/semântica; nunca cor isolada.
- Componentes globais: Button, IconButton, Badge, Tag, Card, Input, Select, Checkbox, Radio, Switch, DateField/DateRangeField, Tabs, CapsuleTabs, ContextGroup, SectionPill, Toast, Tooltip, Dialog.
- Preservar quando fizer sentido: ContextLayers, JourneyTrail, OpenFramePanel, SemanticMarker.
- `JourneyTrail` não é Stepper genérico.
- Lacunas de DS: Search, Table, Stepper, Empty State, Pagination, Skeleton, File Upload, Avatar. Compor localmente usando tokens/primitivos e não promover como componente global sem decisão.
- Iconografia EloPro é prioridade; Lucide apenas fallback técnico.
## 7. Pendências que NÃO podem ser inventadas
- P-001 fallback de Reply-To sem e-mail comercial.
- P-002 fonte definitiva de Cliente no uso independente.
- P-003 Tipo no MVP e opções definitivas.
- P-004 Apresentação.
- P-005 Gerador de Contratos.
- P-006 tamanho máximo/antivírus/sandbox/extração parcial de arquivo.
- P-007 retenção da auditoria.
- P-008 fonte/lifecycle de entitlements.
- P-009 escopo definitivo da sequência PROP (recomendação técnica atual: workspace, mas não assumir sem ADR/aprovação).
- P-010 motor de ingestão/conversão PDF/PPTX/DOCX.
- P-011 provider de e-mail (EloPro documenta Resend, mas formalizar uso no módulo).
- P-012 metas quantitativas de performance/SLO/document size/generation time.
## 8. Critérios críticos de aceite
- Nenhum P0/P1 bloqueador aberto.
- Workspace A nunca acessa B por UI, URL manipulada, API/Action direta ou storage.
- Falha de geração não consome PROP nem versão.
- Duplo clique/retry não cria duas versões.
- Versão formal é imutável.
- Lock funcional expira em 10 min e impede edição concorrente.
- Usuário Final não acessa Modelos nem por rota direta.
- 10/10 bloqueia upload antes do processamento.
- PDF baixado/enviado é o mesmo da versão exibida.
- UI segue EloPro V2.1 sem inventar regra funcional.
@@ -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.