Files
amare/SPEC.md

2604 lines
67 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC.md — MVP da Plataforma de Assessoria de Eventos
> **Documento normativo para desenvolvimento orientado por especificação.**
> Este arquivo é a fonte de verdade para agentes como Cursor, Claude Code e outros agentes de implementação.
---
## 0. Metadados
| Campo | Valor |
|---|---|
| Produto | Plataforma de Assessoria de Eventos |
| Tipo | Aplicação web single-tenant |
| Estágio | MVP |
| Status da especificação | Aprovada para implementação |
| Idioma da interface | Português do Brasil (`pt-BR`) |
| Timezone padrão | `America/Sao_Paulo` |
| Cidade de atuação | São Paulo (capital) |
| Moeda | BRL, sem conversão entre moedas |
| Princípio principal | YAGNI — implementar somente o necessário para validar o produto |
| Arquitetura | Monólito modular Laravel |
| Área interna | Filament |
| Área pública | Livewire + Blade |
| Servidor de aplicação | FrankenPHP em modo regular |
| Banco de dados | PostgreSQL |
| Testes | Pest, Pest Browser/Playwright e testes visuais |
### 0.1 Vocabulário normativo
- **MUST / DEVE**: requisito obrigatório.
- **MUST NOT / NÃO DEVE**: comportamento proibido.
- **SHOULD / DEVERIA**: recomendação forte, dispensável apenas com justificativa registrada.
- **MAY / PODE**: opção permitida.
### 0.2 Ordem de precedência
Em caso de conflito, seguir esta ordem:
1. Instrução explícita mais recente do responsável pelo produto.
2. Este `SPEC.md`.
3. ADRs aceitos no repositório.
4. Testes automatizados existentes.
5. Convenções já consolidadas no código.
6. Preferência do agente ou da biblioteca.
O agente **NÃO DEVE** alterar silenciosamente uma decisão deste documento. Uma alteração de escopo ou arquitetura deve atualizar esta especificação ou criar uma ADR.
Mudanças incrementais são planejadas em `openspec/changes/` e, após arquivadas, este documento DEVE ser revalidado para incorporar decisões ratificadas (back-sync). Este arquivo permanece a fonte de verdade do produto.
---
## 1. Contrato de operação para agentes
Antes de implementar qualquer mudança, o agente DEVE:
1. Ler este arquivo integralmente.
2. Inspecionar a estrutura atual do repositório.
3. Identificar o requisito funcional pelo ID.
4. Listar os arquivos que pretende criar ou alterar.
5. Implementar uma fatia vertical pequena e funcional.
6. Criar ou atualizar os testes correspondentes.
7. Executar os gates de qualidade aplicáveis.
8. Informar o que foi concluído, o que permanece pendente e qualquer desvio da especificação.
### 1.1 Regras de comportamento do agente
O agente:
- DEVE priorizar a solução mais simples que satisfaça os critérios de aceite.
- DEVE reutilizar recursos nativos de Laravel, Filament e Livewire antes de adicionar dependências.
- DEVE manter Filament Resources e componentes Livewire finos.
- DEVE colocar regras de negócio em classes testáveis de domínio ou aplicação.
- DEVE adicionar testes antes de marcar um requisito como concluído.
- DEVE preservar compatibilidade com PostgreSQL e com o contêiner de produção.
- DEVE trabalhar em uma fase do backlog por vez, salvo instrução explícita.
- NÃO DEVE criar funcionalidades listadas em “Fora do MVP”.
- NÃO DEVE introduzir microserviços, uma SPA separada, API pública, Redis ou mensageria externa.
- NÃO DEVE criar abstrações genéricas sem ao menos dois usos concretos.
- NÃO DEVE criar `RepositoryInterface`, `BaseService`, `BaseAction` ou “helpers” genéricos por antecipação.
- NÃO DEVE colocar regras financeiras diretamente em views, Resources, Models observers ou callbacks de formulário.
- NÃO DEVE usar `float` para dinheiro.
- NÃO DEVE persistir status derivados que possam ser calculados corretamente a partir dos dados fonte.
- NÃO DEVE atualizar snapshots visuais apenas para fazer o CI passar sem revisar o diff.
### 1.2 Entrega incremental
Cada fatia vertical deve, quando aplicável, incluir:
- migration;
- enum ou regra de domínio;
- model e relacionamentos;
- Action/Query;
- Policy;
- interface Filament ou Livewire;
- factories e seed mínimo;
- testes unitários e/ou feature;
- teste browser quando a jornada for crítica;
- documentação curta de qualquer decisão não óbvia.
---
## 2. Visão do produto
O MVP é uma aplicação única para uma assessora de eventos ou uma equipe pequena. O produto possui duas superfícies integradas:
1. **Site público premium**, usado para apresentar a marca, construir confiança e converter visitantes em leads.
2. **Área interna operacional**, usada para gerenciar leads, eventos, tarefas, fornecedores, orçamento, pagamentos manuais, documentos e conteúdo do site.
### 2.1 Hipótese de produto
Uma assessora adotará o sistema quando ele:
- ajudar a vender melhor;
- reduzir planilhas e informações dispersas;
- mostrar claramente o que precisa de atenção;
- organizar o evento sem exigir uma mudança radical no fluxo de trabalho;
- transmitir profissionalismo aos possíveis clientes.
### 2.2 Resultados que o MVP precisa provar
- O site converte visitantes em leads qualificados.
- A assessora acompanha o pipeline sem planilhas externas.
- Um lead conquistado é convertido em evento sem redigitação.
- A assessora gerencia tarefas, fornecedores, orçamento e pagamentos manuais.
- O dashboard destaca pendências reais do dia.
- O site é visualmente forte, rápido, acessível e responsivo.
- A aplicação pode ser implantada e atualizada com segurança por pipeline automatizado.
### 2.3 Métricas iniciais
| Dimensão | Métrica | Meta inicial |
|---|---|---|
| Aquisição | Conclusão do formulário de briefing | Medir baseline e melhorar por iteração |
| Comercial | Leads ativos sem próxima ação | `0` ao fim de cada dia útil |
| Operação | Tarefas vencidas sem responsável | `0` |
| Financeiro | Itens de orçamento sem valor ou status | Menos de 5% por evento ativo |
| Qualidade | Jornadas E2E críticas passando | 100% antes de deploy |
| Visual | Snapshots aprovados | 100% |
| Confiabilidade | Erros não tratados | Alerta imediato e tendência decrescente |
---
## 3. Personas e acesso
### 3.1 Visitante
Objetivo: entender os serviços, confiar na assessora, ver eventos reais e solicitar contato.
Permissões:
- acessar apenas as rotas públicas;
- enviar o formulário de briefing;
- não possui conta nem área autenticada.
### 3.2 Assessora administradora
Objetivo: gerenciar toda a operação e o conteúdo do site.
Permissões:
- acesso integral ao Filament;
- gerenciar usuários internos;
- gerenciar configurações e conteúdos públicos;
- criar, atualizar e excluir entidades operacionais conforme Policies;
- visualizar auditoria.
### 3.3 Assistente
Objetivo: atualizar a rotina operacional dos eventos.
Permissões:
- acessar leads, eventos, tarefas, fornecedores, orçamento, pagamentos e documentos;
- não gerenciar usuários;
- não alterar configurações globais do site;
- não acessar ou apagar auditoria;
- não executar exclusões críticas sem autorização.
### 3.4 Papéis
Usar enum simples:
```php
enum UserRole: string
{
case Admin = 'admin';
case Assistant = 'assistant';
}
```
Não instalar sistema de permissões granular no MVP.
---
## 4. Escopo
### 4.1 Incluído no MVP
- site público;
- CMS interno do site;
- formulário de briefing;
- CRM de leads;
- conversão de lead em evento;
- cadastro e visão consolidada de eventos;
- checklist e tarefas;
- diretório e vínculo de fornecedores;
- orçamento por evento;
- pagamentos inseridos manualmente;
- documentos vinculados a leads e eventos;
- dashboard orientado a exceções;
- usuários internos e papéis simples;
- notificações internas e por e-mail para novos leads;
- auditoria de ações críticas;
- SEO básico;
- acessibilidade e testes visuais;
- CI/CD e deploy em contêiner com FrankenPHP.
### 4.2 Fora do MVP
Os itens abaixo **NÃO DEVEM** ser implementados:
- portal ou login para cliente final;
- convites digitais;
- RSVP;
- gestão de convidados;
- check-in ou QR Code;
- mapa de mesas;
- pagamento online;
- Pix, boleto, cartão ou gateway de pagamento;
- conciliação bancária;
- emissão fiscal;
- assinatura eletrônica;
- geração jurídica de contratos;
- cobrança automática;
- integração oficial com WhatsApp;
- chat interno;
- comunicação omnichannel;
- multi-tenancy;
- SaaS para múltiplas assessorias;
- aplicativo nativo;
- PWA offline avançada;
- push notifications;
- marketplace de fornecedores;
- avaliações públicas de fornecedores;
- IA generativa;
- transcrição ou resumo automático de reuniões;
- leitura automática de contratos;
- microserviços;
- event sourcing;
- CQRS arquitetural;
- Kafka, RabbitMQ, SQS ou Redis obrigatório;
- mecanismo externo de busca;
- API pública;
- editor visual de páginas semelhante a page builder;
- Kanban com drag-and-drop para leads;
- editor de templates de checklist.
### 4.3 Regra de mudança de escopo
Uma funcionalidade fora do MVP só poderá entrar quando:
1. desbloquear uma jornada já definida; ou
2. resolver um problema observado em uso real; e
3. tiver critério de aceite e impacto técnico documentados.
“Pode ser útil no futuro” não é justificativa suficiente.
---
## 5. Arquitetura de informação
### 5.1 Rotas públicas
| Método | Rota | Nome sugerido | Finalidade |
|---|---|---|---|
| GET | `/` | `home` | Home editorial |
| GET | `/servicos` | `services.index` | Lista de serviços publicados |
| GET | `/portfolio` | `portfolio.index` | Lista de casos publicados |
| GET | `/portfolio/{slug}` | `portfolio.show` | Detalhe de caso |
| GET | `/sobre` | `about` | História, método e credenciais |
| GET | `/contato` | `contact` | Briefing de contato |
| GET | `/privacidade` | `privacy` | Política de privacidade |
| GET | `/sitemap.xml` | `sitemap` | Sitemap público |
| GET | `/robots.txt` | `robots` | Política de crawling |
| GET | `/up` | `health` | Healthcheck sem autenticação |
### 5.2 Painel interno
Prefixo: `/admin`.
Navegação:
```text
Dashboard
Comercial
├── Leads
└── Atividades
Eventos
├── Eventos
├── Tarefas
├── Fornecedores
├── Orçamento e pagamentos
└── Documentos
Conteúdo do site
├── Serviços
├── Portfólio
├── Depoimentos
└── Configurações
Administração
├── Usuários
└── Auditoria
```
### 5.3 Páginas customizadas obrigatórias
- Dashboard: `Filament Page` customizada com widgets orientados a exceção.
- Detalhe do evento: página customizada do Resource com resumo operacional.
- Briefing público: componente Livewire próprio.
- Home: Blade/Livewire com componentes de design reutilizáveis.
### 5.4 Decisões de UX YAGNI
- Pipeline de leads será tabela com filtros e ações rápidas.
- Não criar Kanban no MVP.
- Conteúdo da home terá estrutura fixa; textos e itens serão editáveis.
- Não criar page builder genérico.
- Ações destrutivas precisam de confirmação.
- O dashboard deve priorizar pendências, não gráficos decorativos.
---
## 6. Design e experiência do site público
O site público é requisito funcional, não apenas acabamento visual.
### 6.1 Princípios visuais
- aparência editorial e premium;
- fotografia grande e bem tratada;
- tipografia com hierarquia clara;
- espaçamento generoso;
- poucos elementos simultâneos;
- transições sutis;
- contraste acessível;
- mobile-first;
- CTAs claros;
- sem aparência de template administrativo.
### 6.2 Estrutura da home
A home DEVE conter, nesta ordem aproximada:
1. header e navegação;
2. hero com proposta de valor e CTA;
3. manifesto da marca;
4. resumo dos serviços em destaque;
5. casos selecionados do portfólio;
6. método de trabalho (4 passos);
7. depoimentos;
8. posicionamento/perfil;
9. CTA final para briefing;
10. footer com contato, redes e links legais.
A ordem pode variar apenas se a revisão de UX justificar a mudança.
### 6.3 Design tokens mínimos
Centralizar tokens de:
- famílias tipográficas;
- escala de fonte;
- espaçamentos;
- raio de borda;
- largura de container;
- cores de fundo, texto, borda, destaque e estados;
- sombras;
- duração e easing de transições.
Não espalhar valores visuais arbitrários por componentes.
O MVP adota o sistema de design **Heritage Editorial** (ver DESIGN.md e ADR-013):
- tipografia serifada auto-hospedada (EB Garamond) com escala de display a label;
- paleta papel/oliva/sálvia/tinta com superfícies tonais; hierarquia sem sombras de card;
- raio de borda zero para superfícies interativas e de conteúdo;
- largura de container 1120px e ritmo de espaçamento de 8px;
- `prefers-reduced-motion` respeitado;
- contraste WCAG AA (tinta sobre papel e oliva sobre papel).
### 6.4 Requisitos de mídia
- Imagens públicas DEVERÃO possuir texto alternativo.
- A aplicação DEVERÁ validar extensão, MIME e tamanho.
- A aplicação DEVERÁ gerar ou servir variantes responsivas.
- Imagens fora da primeira dobra DEVERÃO usar lazy loading.
- Dimensões DEVERÃO ser reservadas para evitar layout shift.
- Não armazenar blobs de imagem no PostgreSQL.
- Não depender do disco efêmero do contêiner em produção.
- O logotipo da marca DEVE possuir texto alternativo e variantes claro/escuro; `site_settings.logo_path` sobrescreve o asset padrão quando preenchido.
### 6.5 Acessibilidade
- navegação completa por teclado;
- foco visível;
- labels associados aos campos;
- mensagens de erro ligadas ao campo correspondente;
- landmarks semânticos;
- um único `h1` por página;
- ordem coerente de headings;
- contraste compatível com WCAG AA;
- suporte a `prefers-reduced-motion`;
- nenhuma issue automatizada crítica ou séria nas jornadas cobertas.
### 6.6 Performance e SEO
Metas:
| Métrica | Meta |
|---|---|
| LCP | ≤ 2,5 s nas páginas principais |
| CLS | ≤ 0,1 |
| INP | ≤ 200 ms |
| Erros JS no console | 0 nas jornadas críticas |
SEO obrigatório:
- title e meta description por página;
- canonical;
- Open Graph;
- sitemap;
- robots;
- slug legível;
- conteúdo publicado somente quando `published_at` estiver preenchido;
- dados estruturados básicos quando aplicáveis;
- páginas de portfólio indexáveis.
---
## 7. Requisitos funcionais
### 7.1 Site público e CMS
#### WEB-01 — Home editorial
**Prioridade:** P0
A home deve apresentar proposta, serviços, casos, método, depoimentos e CTA.
**Aceite:**
```gherkin
Given que existem serviços, casos e depoimentos publicados
When um visitante acessa a home
Then o conteúdo publicado é exibido na ordem configurada
And a página funciona em viewport desktop e mobile
And não existem erros JavaScript no console
And o CTA leva ao briefing
```
#### WEB-02 — Serviços
**Prioridade:** P0
Campos mínimos:
- título;
- slug;
- resumo;
- descrição;
- imagem de capa opcional;
- texto alternativo;
- ordem;
- destaque;
- `published_at`.
**Regras:**
- slug deve ser único;
- serviço não publicado não aparece no site;
- exclusão deve exigir confirmação;
- admin pode gerenciar; assistant não pode.
#### WEB-03 — Portfólio
**Prioridade:** P0
Campos mínimos:
- título;
- slug;
- resumo;
- tipo de evento;
- cidade/local opcional;
- data do evento opcional;
- desafio;
- solução;
- resultado opcional;
- imagem de capa;
- galeria ordenada;
- destaque;
- `published_at`;
- metadados SEO opcionais.
**Aceite:**
```gherkin
Given um caso salvo como rascunho
When a assessora preenche os campos obrigatórios e publica o caso
Then a rota pública pelo slug passa a responder 200
And o caso aparece na listagem pública
And as imagens possuem dimensões reservadas e textos alternativos
```
#### WEB-04 — Depoimentos
**Prioridade:** P0
Campos:
- texto;
- nome da pessoa;
- contexto ou tipo de evento opcional;
- fotografia opcional;
- ordem;
- destaque;
- `published_at`.
#### WEB-05 — Briefing de contato
**Prioridade:** P0
Campos públicos:
- nome completo;
- e-mail;
- telefone/WhatsApp;
- tipo de evento;
- data ou período desejado opcional;
- cidade;
- número estimado de convidados opcional;
- serviço de interesse opcional;
- mensagem/principal preocupação;
- origem de marketing capturada quando disponível;
- aceite do aviso de privacidade.
**Regras:**
- validar no servidor;
- usar honeypot e rate limiting;
- impedir duplo envio acidental;
- enviar e-mail de confirmação quando o serviço de e-mail estiver configurado;
- exibir sucesso sem revelar dados internos.
> **Estado atual (Fases 01):** o formulário usa Blade + Controller e envia apenas e-mails informativos (para a assessoria e confirmação ao visitante), sem criar Lead. A criação de Lead (status `new`, origem `website`), a notificação aos administradores e o registro de aceite de privacidade entram na **Fase 2**, quando o formulário passa a criar o Lead via `CaptureWebsiteLead`. A falha de e-mail não pode apagar dados já criados.
**Aceite:**
```gherkin
Given um visitante no formulário de briefing
When ele envia dados válidos uma única vez
Then um lead é criado com status Novo
And a tela mostra confirmação
And a assessora recebe notificação interna
And uma nova submissão causada por duplo clique não cria lead duplicado
```
#### WEB-06 — Configurações do site
**Prioridade:** P0
Singleton gerenciável apenas por admin:
- nome da marca;
- texto e CTA do hero;
- resumo institucional;
- e-mail;
- telefone;
- endereço/cidade opcional;
- links sociais;
- title padrão;
- description padrão;
- imagem Open Graph padrão;
- scripts de analytics opcionais, desabilitados por padrão.
Não criar sistema genérico de chave/valor se um singleton tipado resolver.
#### WEB-07 — Páginas institucionais
**Prioridade:** P1
- Sobre;
- Política de privacidade;
- página 404 com identidade visual;
- página 500 segura e sem stack trace em produção.
---
### 7.2 Leads e CRM
#### CRM-01 — Cadastro de lead
**Prioridade:** P0
Lead pode ser criado:
- automaticamente pelo briefing;
- manualmente no Filament.
Campos:
- nome;
- e-mail;
- telefone;
- tipo de evento;
- data estimada;
- cidade;
- convidados estimados;
- mensagem;
- origem;
- status;
- próxima ação em;
- tipo da próxima ação;
- observação da próxima ação;
- motivo da perda;
- `converted_event_id` ou relacionamento equivalente;
- timestamps.
#### CRM-02 — Pipeline
**Prioridade:** P0
Estados permitidos:
```php
enum LeadStatus: string
{
case New = 'new';
case Contacted = 'contacted';
case Meeting = 'meeting';
case Proposal = 'proposal';
case Negotiation = 'negotiation';
case Won = 'won';
case Lost = 'lost';
}
```
Transições permitidas no MVP:
- qualquer estado ativo pode avançar ou retroceder para outro estado ativo;
- `won` somente por conversão bem-sucedida ou ação administrativa explícita;
- `lost` exige motivo;
- lead convertido não pode voltar para estado ativo sem operação manual administrativa documentada;
- lead perdido pode ser reaberto por admin, limpando motivo de perda e criando auditoria.
#### CRM-03 — Próxima ação
**Prioridade:** P0
- lead ativo pode possuir data, tipo e observação;
- lead ativo sem próxima ação deve aparecer no dashboard;
- próxima ação passada deve aparecer como atrasada;
- lead `won` ou `lost` não precisa de próxima ação.
#### CRM-04 — Histórico
**Prioridade:** P0
Registrar atividades:
- nota manual;
- alteração de status;
- alteração de próxima ação;
- criação pelo site;
- conversão em evento;
- marcação como perdido ou reaberto.
O histórico deve ser cronológico e imutável pela interface comum. Admin pode remover somente em operação excepcional auditada.
#### CRM-05 — Busca e filtros
**Prioridade:** P0
Filtrar ou buscar por:
- nome;
- e-mail;
- telefone;
- status;
- origem;
- tipo de evento;
- data estimada;
- próxima ação;
- leads sem próxima ação.
#### CRM-06 — Conversão em evento
**Prioridade:** P0
A conversão:
- DEVE ser idempotente;
- DEVE executar em transação;
- DEVE criar somente um evento;
- DEVE copiar dados relevantes do lead;
- DEVE vincular lead e evento;
- DEVE criar checklist padrão;
- DEVE alterar lead para `won`;
- DEVE criar atividade e auditoria;
- DEVE rejeitar uma segunda conversão.
**Aceite:**
```gherkin
Given um lead ainda não convertido
When a assessora executa Converter em evento
Then um único evento é criado
And o checklist padrão é criado
And o lead é marcado como Conquistado
And uma segunda execução não cria outro evento
```
#### CRM-07 — Perda do lead
**Prioridade:** P0
- motivo é obrigatório;
- próxima ação deve ser removida;
- mudança deve aparecer no histórico;
- motivo não é visível em área pública.
---
### 7.3 Eventos
#### EVT-01 — Cadastro de evento
**Prioridade:** P0
Campos:
- título;
- tipo;
- lead de origem opcional;
- nome do cliente;
- e-mail do cliente;
- telefone do cliente;
- data e hora de início opcionais durante planejamento;
- data e hora de fim opcionais;
- local;
- cidade;
- status;
- orçamento-alvo em centavos opcional;
- resumo;
- observações internas;
- responsável interno opcional.
#### EVT-02 — Estados do evento
```php
enum EventStatus: string
{
case Planning = 'planning';
case Confirmed = 'confirmed';
case InProgress = 'in_progress';
case Completed = 'completed';
case Cancelled = 'cancelled';
}
```
Regras:
- novo evento começa em `planning`;
- `confirmed` exige data de início;
- `in_progress` exige data de início;
- `completed` registra `completed_at`;
- `cancelled` registra motivo opcional e auditoria;
- evento cancelado não aparece entre próximos eventos ativos;
- reabertura de evento concluído ou cancelado exige admin e auditoria.
#### EVT-03 — Visão consolidada
**Prioridade:** P0
A página do evento deve mostrar:
- resumo e status;
- cliente e contatos;
- dias restantes;
- próximas tarefas;
- tarefas vencidas;
- fornecedores vinculados;
- totais financeiros;
- próximos pagamentos;
- documentos recentes;
- atalhos para criar tarefa, vínculo de fornecedor, item de orçamento, pagamento e documento.
#### EVT-04 — Listagem e filtros
**Prioridade:** P0
Filtros:
- status;
- tipo;
- período;
- responsável;
- eventos nos próximos 7, 30 e 90 dias;
- eventos sem data;
- busca por título, cliente, cidade ou local.
---
### 7.4 Tarefas
#### TSK-01 — Checklist do evento
**Prioridade:** P0
Campos:
- evento;
- título;
- descrição opcional;
- responsável opcional;
- prazo opcional;
- prioridade;
- status;
- posição/ordem;
- `completed_at`;
- `completed_by`.
Enums:
```php
enum TaskPriority: string
{
case Low = 'low';
case Medium = 'medium';
case High = 'high';
case Critical = 'critical';
}
enum TaskStatus: string
{
case Pending = 'pending';
case InProgress = 'in_progress';
case Completed = 'completed';
}
```
#### TSK-02 — Regras de conclusão
- concluir tarefa preenche `completed_at` e `completed_by`;
- reabrir tarefa limpa ambos;
- tarefa concluída não é considerada vencida;
- tarefa pendente com prazo anterior a hoje é vencida;
- tarefa sem responsável é explicitamente sinalizada;
- exclusão exige confirmação.
#### TSK-03 — Checklist padrão
**Prioridade:** P0
O MVP deve possuir um checklist padrão definido em seed ou configuração de código versionada.
- checklist deve ser criado na conversão de lead;
- tarefas podem possuir prazo relativo à data do evento quando esta existir;
- se a data do evento ainda não existir, tarefas relativas permanecem sem prazo;
- ao definir posteriormente a data do evento, a aplicação pode calcular prazos ausentes uma única vez;
- editor de templates fica fora do MVP.
Checklist inicial sugerido:
1. confirmar briefing e escopo;
2. definir data e local;
3. definir orçamento-alvo;
4. mapear fornecedores essenciais;
5. confirmar fornecedores selecionados;
6. revisar cronograma geral;
7. realizar reunião final;
8. confirmar contatos operacionais;
9. revisar pagamentos pendentes;
10. concluir evento e registrar aprendizados.
#### TSK-04 — Visão global de tarefas
**Prioridade:** P1
Listar tarefas de todos os eventos com filtros por:
- responsável;
- status;
- prioridade;
- evento;
- vencidas;
- hoje;
- próximos 7 dias;
- sem responsável.
---
### 7.5 Fornecedores
#### VEN-01 — Diretório
**Prioridade:** P0
Campos:
- nome;
- categoria;
- nome do contato opcional;
- e-mail opcional;
- telefone opcional;
- cidade/região;
- site ou rede social opcional;
- observações internas;
- ativo/inativo.
Categorias devem usar enum ou tabela fixa simples. Não criar CRUD de categorias no MVP.
Categorias iniciais:
- espaço;
- buffet;
- bebidas;
- decoração;
- fotografia;
- filmagem;
- música/DJ;
- som e iluminação;
- mobiliário;
- celebrante/cerimonial;
- segurança;
- limpeza;
- transporte;
- hospedagem;
- gráfica/comunicação;
- outros.
#### VEN-02 — Vínculo ao evento
**Prioridade:** P0
Campos:
- evento;
- fornecedor;
- status;
- contato específico opcional;
- observações;
- valor de referência opcional em centavos.
Status:
```php
enum EventVendorStatus: string
{
case Quoted = 'quoted';
case Selected = 'selected';
case Contracted = 'contracted';
case Discarded = 'discarded';
}
```
Um fornecedor não deve ser vinculado duas vezes ao mesmo evento na mesma categoria sem justificativa explícita. Usar constraint adequada conforme o modelo final.
---
### 7.6 Orçamento e pagamentos manuais
#### FIN-01 — Item de orçamento
**Prioridade:** P0
Campos:
- evento;
- categoria;
- descrição;
- fornecedor opcional;
- valor planejado em centavos opcional;
- valor acordado em centavos opcional;
- status;
- observações;
- posição/ordem.
Status:
```php
enum BudgetItemStatus: string
{
case Planned = 'planned';
case Quoted = 'quoted';
case Approved = 'approved';
case Contracted = 'contracted';
case Cancelled = 'cancelled';
}
```
#### FIN-02 — Dinheiro
Regras obrigatórias:
- todos os valores monetários são BRL;
- armazenar valores em centavos com `BIGINT`;
- nunca usar `float` em domínio, Action ou teste;
- formatar na UI como moeda brasileira;
- entradas de formulário devem ser convertidas de string decimal para centavos;
- valores não podem ser negativos;
- não implementar conversão de moeda.
Criar Value Object pequeno somente se ele reduzir duplicação e bugs reais:
```php
final readonly class Money
{
public function __construct(public int $cents) {}
}
```
#### FIN-03 — Pagamento manual
**Prioridade:** P0
Campos:
- evento;
- item de orçamento opcional;
- descrição;
- valor em centavos;
- vencimento;
- data de pagamento opcional;
- método opcional;
- comprovante opcional;
- observações;
- criado por;
- atualizado por.
Métodos permitidos:
- Pix;
- transferência;
- dinheiro;
- cartão;
- boleto;
- outro.
O método é apenas informativo. Nenhum pagamento é processado pelo sistema.
#### FIN-04 — Status derivado do pagamento
Não persistir status quando puder ser derivado:
```text
paid_at != null => paid
paid_at == null e due_date < hoje => overdue
paid_at == null e due_date >= hoje => pending
```
“Vencendo em breve” é uma apresentação calculada para vencimentos entre hoje e hoje + 7 dias.
#### FIN-05 — Totais do evento
Calcular:
- planejado: soma de `planned_amount_cents` não cancelados;
- acordado: soma de `agreed_amount_cents` não cancelados;
- pago: soma de pagamentos com `paid_at`;
- pendente: soma de pagamentos sem `paid_at`;
- vencido: soma de pagamentos sem `paid_at` e com vencimento passado;
- saldo do acordado: `max(acordado - pago, 0)`.
Não criar tabela de totais materializados no MVP. Usar Query dedicada e índices adequados.
#### FIN-06 — Consistência financeira
- pagamento não pode ter valor zero ou negativo;
- data de pagamento pode ser anterior, igual ou posterior ao vencimento;
- comprovante é opcional;
- alteração e exclusão de pagamento geram auditoria;
- exclusão deve ser restrita a admin ou possuir confirmação reforçada;
- a soma de pagamentos pode superar o valor acordado; a UI deve sinalizar, não bloquear, pois ajustes reais podem existir;
- nenhum módulo contábil ou fiscal será criado.
**Aceite:**
```gherkin
Given um evento com item acordado de R$ 3.000,00
And dois pagamentos manuais de R$ 1.000,00 e R$ 2.000,00
When ambos possuem data de pagamento
Then o total pago é R$ 3.000,00
And o saldo acordado é R$ 0,00
And nenhum pagamento aparece como pendente ou vencido
```
---
### 7.7 Documentos
#### DOC-01 — Upload vinculado
**Prioridade:** P1
Documentos podem pertencer a:
- lead; ou
- evento.
Campos:
- nome exibido;
- path privado;
- MIME;
- tamanho;
- categoria;
- observação;
- usuário que enviou;
- timestamps.
Regras:
- arquivo deve ser privado por padrão;
- download requer autenticação e Policy;
- validar allowlist de tipos;
- limite inicial configurável, padrão 10 MB;
- nome original não deve determinar o path físico;
- não armazenar arquivos no banco;
- exclusão gera auditoria.
Categorias iniciais:
- briefing;
- proposta;
- contrato;
- orçamento;
- comprovante;
- referência visual;
- cronograma;
- outro.
---
### 7.8 Dashboard e notificações
#### DASH-01 — Dashboard orientado a atenção
**Prioridade:** P0
O dashboard deve responder: “O que precisa da minha atenção hoje?”
Widgets obrigatórios:
- leads novos;
- leads ativos sem próxima ação;
- próximas ações vencidas;
- eventos nos próximos 7, 30 e 90 dias;
- tarefas vencidas;
- tarefas de hoje;
- tarefas sem responsável;
- pagamentos vencidos;
- pagamentos vencendo em até 7 dias;
- atalhos para novo lead, evento, fornecedor e caso de portfólio.
Cada item deve ser acionável e levar ao registro correspondente.
#### DASH-02 — Notificação de novo lead
**Prioridade:** P0
- criar database notification para admins;
- enviar e-mail assíncrono quando configurado;
- usar database queue;
- falha de notificação não pode reverter a criação do lead;
- job deve ser idempotente quando possível.
#### DASH-03 — Lembrete diário
**Prioridade:** P1
Scheduler diário pode criar notificação resumida com:
- próximas ações vencidas;
- tarefas vencidas;
- pagamentos vencidos.
Não enviar resumo se não houver pendências.
---
### 7.9 Administração e auditoria
#### ADM-01 — Usuários internos
**Prioridade:** P0
- admin cria e desativa usuários;
- e-mail único;
- papel `admin` ou `assistant`;
- usuário inativo não autentica;
- reset de senha seguro;
- acesso ao Filament restrito explicitamente.
#### ADM-02 — Auditoria
**Prioridade:** P0
Ações auditáveis:
- conversão de lead;
- marcação de lead perdido ou reaberto;
- mudança financeira;
- exclusão de pagamento;
- exclusão de documento;
- publicação ou despublicação de conteúdo;
- exclusão de entidade principal;
- mudança de papel ou ativação de usuário;
- reabertura de evento concluído/cancelado.
Campos mínimos:
- usuário opcional;
- ação;
- tipo da entidade;
- ID da entidade;
- metadados JSON sem dados sensíveis desnecessários;
- IP opcional;
- request ID;
- timestamp.
Auditoria não precisa ser um event sourcing. Registrar apenas operações críticas.
---
## 8. Modelo de domínio e banco de dados
### 8.1 Módulos
| Módulo | Responsabilidade |
|---|---|
| Marketing | Conteúdo público e conversão |
| CRM | Leads, atividades e conversão |
| Events | Eventos, tarefas e documentos |
| Vendors | Fornecedores e vínculos |
| Finance | Orçamento e pagamentos manuais |
| Identity | Usuários, acesso e auditoria |
### 8.2 Tabelas obrigatórias
#### `users`
- `id` bigint PK;
- `name` varchar;
- `email` varchar unique;
- `email_verified_at` timestamp nullable;
- `password` varchar;
- `role` varchar indexed;
- `is_active` boolean default true indexed;
- remember token;
- timestamps.
#### `site_settings`
Singleton:
- `id` bigint PK;
- `brand_name`;
- `logo_path` nullable;
- `logo_alt` nullable;
- `hero_eyebrow` nullable;
- `hero_title`;
- `hero_subtitle`;
- `hero_cta_label`;
- `hero_cta_secondary_label` nullable;
- `hero_note` nullable;
- `manifesto_title`;
- `manifesto_lead`;
- `manifesto_body`;
- `method_steps` jsonb (4 passos tipados);
- `principles` jsonb (lista tipada);
- `about_summary` nullable;
- `email`;
- `phone`;
- `city` nullable;
- `social_links` jsonb;
- `default_meta_title`;
- `default_meta_description`;
- `default_og_image_path` nullable;
- `default_og_image_alt` nullable;
- `analytics_enabled` boolean default false;
- timestamps.
#### `services`
- `id`;
- `title`;
- `slug` unique;
- `summary`;
- `description` text;
- `cover_image_path` nullable;
- `cover_image_alt` nullable;
- `sort_order` integer indexed;
- `is_featured` boolean indexed;
- `published_at` timestamp nullable indexed;
- timestamps;
- soft delete opcional somente se recuperação for necessária.
#### `portfolio_cases`
- `id`;
- `title`;
- `slug` unique;
- `summary`;
- `event_type`;
- `city` nullable;
- `venue` nullable;
- `event_date` date nullable;
- `challenge` text;
- `solution` text;
- `result` text nullable;
- `cover_image_path`;
- `cover_image_alt`;
- `is_featured` boolean indexed;
- `sort_order` integer indexed;
- `published_at` timestamp nullable indexed;
- `meta_title` nullable;
- `meta_description` nullable;
- timestamps.
#### `portfolio_images`
- `id`;
- `portfolio_case_id` FK cascade;
- `path`;
- `alt_text`;
- `caption` nullable;
- `sort_order` integer;
- timestamps;
- index composto por `portfolio_case_id, sort_order`.
#### `testimonials`
- `id`;
- `quote` text;
- `author_name`;
- `context` nullable;
- `photo_path` nullable;
- `photo_alt` nullable;
- `sort_order` integer;
- `is_featured` boolean indexed;
- `published_at` timestamp nullable indexed;
- timestamps.
#### `leads`
- `id`;
- `name`;
- `email` nullable indexed;
- `phone` nullable indexed;
- `event_type`;
- `estimated_event_date` date nullable indexed;
- `city` nullable;
- `estimated_guests` integer nullable;
- `message` text nullable;
- `source` varchar indexed;
- `status` varchar indexed;
- `next_action_at` timestamp nullable indexed;
- `next_action_type` nullable;
- `next_action_notes` text nullable;
- `lost_reason` text nullable;
- `converted_event_id` nullable unique;
- `privacy_notice_accepted_at` timestamp nullable;
- `marketing_metadata` jsonb nullable;
- timestamps;
- soft deletes recomendados para recuperação operacional.
#### `lead_activities`
- `id`;
- `lead_id` FK cascade;
- `user_id` FK nullable set null;
- `type` varchar indexed;
- `description` text;
- `metadata` jsonb nullable;
- `created_at`;
- sem `updated_at` quando possível, pois atividade é append-only.
#### `events`
- `id`;
- `lead_id` FK nullable unique;
- `title`;
- `event_type`;
- `client_name`;
- `client_email` nullable;
- `client_phone` nullable;
- `starts_at` timestamp nullable indexed;
- `ends_at` timestamp nullable;
- `venue` nullable;
- `city` nullable;
- `status` varchar indexed;
- `target_budget_cents` bigint nullable;
- `summary` text nullable;
- `internal_notes` text nullable;
- `owner_user_id` FK nullable indexed;
- `completed_at` timestamp nullable;
- timestamps;
- soft deletes recomendados.
#### `event_tasks`
- `id`;
- `event_id` FK cascade indexed;
- `title`;
- `description` text nullable;
- `assigned_user_id` FK nullable indexed;
- `due_at` timestamp nullable indexed;
- `priority` varchar indexed;
- `status` varchar indexed;
- `sort_order` integer;
- `relative_due_days` integer nullable;
- `completed_at` timestamp nullable;
- `completed_by_user_id` FK nullable;
- timestamps;
- index composto por `event_id, status, due_at`.
#### `vendors`
- `id`;
- `name` indexed;
- `category` varchar indexed;
- `contact_name` nullable;
- `email` nullable;
- `phone` nullable;
- `city_region` nullable indexed;
- `website_url` nullable;
- `notes` text nullable;
- `is_active` boolean default true indexed;
- timestamps;
- soft deletes opcionais.
#### `event_vendors`
- `id`;
- `event_id` FK cascade indexed;
- `vendor_id` FK restrict indexed;
- `category` varchar indexed;
- `status` varchar indexed;
- `contact_override` nullable;
- `reference_amount_cents` bigint nullable;
- `notes` text nullable;
- timestamps;
- índice composto por `event_id, category, status`.
#### `budget_items`
- `id`;
- `event_id` FK cascade indexed;
- `vendor_id` FK nullable set null;
- `category` varchar indexed;
- `description`;
- `planned_amount_cents` bigint nullable;
- `agreed_amount_cents` bigint nullable;
- `status` varchar indexed;
- `notes` text nullable;
- `sort_order` integer;
- timestamps;
- checks de valores não negativos quando suportado.
#### `payments`
- `id`;
- `event_id` FK cascade indexed;
- `budget_item_id` FK nullable set null indexed;
- `description`;
- `amount_cents` bigint;
- `due_date` date indexed;
- `paid_at` timestamp nullable indexed;
- `method` varchar nullable;
- `proof_path` nullable;
- `notes` text nullable;
- `created_by_user_id` FK;
- `updated_by_user_id` FK;
- timestamps;
- check `amount_cents > 0`.
#### `documents`
- `id`;
- `lead_id` FK nullable cascade;
- `event_id` FK nullable cascade;
- `display_name`;
- `path`;
- `mime_type`;
- `size_bytes` bigint;
- `category` varchar indexed;
- `notes` text nullable;
- `uploaded_by_user_id` FK;
- timestamps;
- constraint: exatamente um entre `lead_id` e `event_id` deve ser preenchido.
#### `audit_logs`
- `id`;
- `user_id` FK nullable set null indexed;
- `action` varchar indexed;
- `auditable_type` varchar indexed;
- `auditable_id` bigint indexed;
- `metadata` jsonb nullable;
- `ip_address` nullable;
- `request_id` nullable indexed;
- `created_at`.
#### Infraestrutura Laravel
Usar tabelas padrão conforme recursos habilitados:
- notifications;
- jobs;
- job_batches, somente se necessário;
- failed_jobs;
- password reset tokens;
- sessions, caso sessão em banco seja escolhida.
### 8.3 Integridade e transações
Usar transação em:
- conversão de lead em evento;
- criação de evento com checklist;
- alterações financeiras que gerem múltiplas gravações;
- exclusões críticas com auditoria;
- operações que atualizam estado e histórico juntos.
Constraints de banco devem proteger:
- e-mail único de usuário;
- slug único;
- lead convertido uma única vez;
- evento originado de um único lead;
- valores financeiros válidos;
- relacionamentos obrigatórios;
- documento pertencendo a um único owner lógico.
---
## 9. Arquitetura técnica
### 9.1 Stack
| Camada | Tecnologia |
|---|---|
| Runtime | PHP com versão minor fixada no Docker |
| Framework | Laravel 13 |
| Admin | Filament 5 |
| UI pública | Livewire 4 + Blade + Alpine + Tailwind |
| Banco | PostgreSQL |
| Servidor | FrankenPHP + Caddy |
| Assets | Vite |
| Testes | Pest 4 + Pest Browser/Playwright |
| Arquivos | Laravel Filesystem + Cloudflare R2 (S3-compatible) em produção |
| Fila | Database queue |
| Scheduler | Laravel Scheduler em processo separado |
Caso o projeto seja criado com versões posteriores, o agente deve manter compatibilidade entre as versões instaladas e registrar a decisão. Não fazer upgrade de major version no meio da implementação sem ADR.
### 9.2 Estilo arquitetural
Monólito modular com dependências orientadas:
```text
Interface (Filament / Livewire / HTTP)
Application (Actions / Queries / Data)
Domain (Enums / Value Objects / Rules)
Infrastructure (Eloquent / Mail / Filesystem / Queue)
```
### 9.3 Regras arquiteturais
- Domain NÃO DEVE depender de Filament ou Livewire.
- Filament e Livewire chamam Actions e Queries.
- Eloquent Models podem existir em `app/Models` e ser usados por Actions/Queries.
- Não criar repositórios genéricos.
- Queries de leitura podem usar Eloquent diretamente.
- Actions devem representar casos de uso, não CRUD trivial.
- Enums representam estados estáveis.
- Value Objects somente para conceitos com regra real, como dinheiro.
- Eventos Laravel somente para efeitos colaterais claros.
- Policies controlam autorização.
- Observers devem ser evitados para regras críticas por esconderem fluxo.
- Soft delete deve ser aplicado seletivamente.
- Todo arquivo PHP próprio deve usar `declare(strict_types=1);` quando compatível.
### 9.4 Estrutura sugerida
```text
app/
├── Application/
│ ├── Actions/
│ │ ├── CRM/
│ │ ├── Events/
│ │ ├── Finance/
│ │ ├── Marketing/
│ │ └── Vendors/
│ ├── Data/
│ └── Queries/
├── Domain/
│ ├── CRM/
│ ├── Events/
│ ├── Finance/
│ ├── Marketing/
│ ├── Vendors/
│ └── Identity/
├── Filament/
│ ├── Pages/
│ ├── Resources/
│ └── Widgets/
├── Livewire/
│ └── PublicSite/
├── Models/
├── Notifications/
├── Policies/
└── Support/
resources/views/
├── components/
├── layouts/
├── livewire/public-site/
└── pages/
tests/
├── Architecture/
├── Browser/
├── Feature/
└── Unit/
```
Não criar diretórios vazios antecipadamente. Criar quando o primeiro uso existir.
### 9.5 Actions mínimas esperadas
- `CaptureWebsiteLead`;
- `CreateManualLead` somente se CRUD padrão não for suficiente;
- `UpdateLeadStatus`;
- `ScheduleLeadNextAction`;
- `MarkLeadAsLost`;
- `ReopenLead`;
- `ConvertLeadToEvent`;
- `CreateDefaultEventChecklist`;
- `CompleteEventTask`;
- `ReopenEventTask`;
- `RecordManualPayment`;
- `UpdateManualPayment`;
- `DeleteManualPayment`;
- `PublishPortfolioCase`;
- `UnpublishPortfolioCase`;
- `StorePrivateDocument`;
- `WriteAuditLog` ou serviço equivalente, sem transformar auditoria em event sourcing.
Não criar Action para todo `create/update` se o Filament Resource puder salvar de forma segura sem regra adicional.
### 9.6 Queries mínimas esperadas
- `GetDashboardAttentionItems` ou queries menores por widget;
- `GetEventFinancialSummary`;
- `GetUpcomingEvents`;
- `GetOverdueTasks`;
- `GetLeadsWithoutNextAction`;
- `GetOverduePayments`;
- `GetDueSoonPayments`.
### 9.7 Fluxos técnicos
#### Briefing
```text
Livewire valida
→ CaptureWebsiteLead
→ Lead salvo em transação
→ LeadActivity criada
→ NewLeadCaptured disparado após commit
→ Database notification e e-mail em fila
→ confirmação exibida
```
#### Conversão
```text
Filament Action autoriza
→ ConvertLeadToEvent
→ lock/checagem idempotente
→ cria Event
→ cria checklist
→ atualiza Lead
→ cria atividade e auditoria
→ commit
```
#### Pagamento
```text
Filament form valida
→ RecordManualPayment
→ converte string monetária para centavos
→ salva Payment
→ cria auditoria
→ Query financeira recalcula leitura
```
#### Publicação de caso
```text
Filament salva conteúdo
→ valida imagem e alt text
→ publica por published_at
→ rota pública passa a exibir
→ sitemap inclui slug
```
---
## 10. Interface Filament
### 10.1 Resources obrigatórios
- `LeadResource`;
- `EventResource`;
- `EventTaskResource` ou relation manager + visão global;
- `VendorResource`;
- `BudgetItemResource` preferencialmente dentro do evento;
- `PaymentResource` preferencialmente dentro do evento + visão global;
- `DocumentResource` ou managers vinculados;
- `ServiceResource`;
- `PortfolioCaseResource`;
- `TestimonialResource`;
- `SiteSettingResource` ou página singleton;
- `UserResource`;
- `AuditLogResource` somente leitura.
### 10.2 Regras de Resource
- tabelas sempre paginadas;
- busca somente em campos úteis;
- filtros indexados;
- eager loading explícito para evitar N+1;
- ações de estado devem chamar Actions;
- labels e mensagens em pt-BR;
- valores monetários formatados em BRL;
- datas apresentadas no timezone do produto;
- ações destrutivas confirmadas;
- Policies aplicadas e testadas;
- campos internos nunca expostos em rotas públicas.
### 10.3 Dashboard
Widgets devem executar queries pequenas e independentes. Não fazer uma consulta gigante que carregue todos os registros.
### 10.4 Detalhe do evento
A página deve combinar, sem virar uma única classe monolítica:
- header do evento;
- cards de atenção;
- resumo financeiro;
- tarefas;
- fornecedores;
- documentos;
- ações rápidas.
Usar componentes/Widgets menores e testáveis.
---
## 11. Livewire e site público
### 11.1 Componentes sugeridos
- `ContactBriefingForm`;
- `FeaturedPortfolioCases` se houver necessidade de consulta dinâmica;
- `PublishedServices` se houver necessidade de consulta dinâmica.
Não transformar todas as seções estáticas em componentes Livewire. Usar Blade quando não houver estado ou interação.
### 11.2 Formulário de briefing
> **Estado atual (Fases 01):** o formulário é implementado em Blade + Controller (`POST /contato`, `ContactBriefingRequest`), conforme WEB-05. Se a Fase 2 mantiver Blade + Controller, os requisitos abaixo valem para o formulário e seus testes independentemente da tecnologia; a criação de Lead segue para a Fase 2.
O componente deve:
- ter estado tipado ou Form Object quando útil;
- validar no servidor;
- possuir loading state;
- desabilitar botão durante submissão;
- preservar acessibilidade;
- limpar dados após sucesso;
- evitar exposição de exceção;
- suportar teste Livewire sem browser;
- suportar jornada E2E em navegador real.
### 11.3 JavaScript
- usar Alpine apenas para interações pequenas;
- não introduzir framework SPA;
- não usar dependência JS quando CSS/HTML/Livewire resolverem;
- toda interação crítica deve funcionar sem estado global complexo.
---
## 12. Segurança e privacidade
### 12.1 Autenticação
- login interno;
- e-mail verificado;
- reset seguro;
- senhas tratadas pelo Laravel;
- cookie `secure`, `httpOnly` e `sameSite` em produção;
- usuário inativo bloqueado;
- acesso Filament validado por `canAccessPanel` ou mecanismo equivalente.
2FA pode ser adicionado apenas se suportado de forma simples pelo stack escolhido. Não bloquear o MVP por isso.
### 12.2 Autorização
Policies obrigatórias para entidades internas.
Admin:
- acesso total.
Assistant:
- operação cotidiana;
- sem usuários, configurações globais e auditoria;
- exclusões críticas restritas.
### 12.3 Formulário público
- CSRF;
- rate limit;
- honeypot;
- validação server-side;
- limite de tamanho de campos;
- normalização de e-mail e telefone;
- proteção contra mass assignment;
- logs sem conteúdo integral da mensagem do visitante.
### 12.4 Uploads
- allowlist de MIME e extensão;
- limite de tamanho;
- path aleatório;
- arquivos privados por padrão;
- imagens públicas com validação e alt text;
- não confiar no nome original;
- download autorizado por Policy;
- proteção contra path traversal.
### 12.5 LGPD
- coletar apenas dados necessários;
- exibir aviso de privacidade no briefing;
- registrar aceite do aviso;
- não tratar consentimento como base universal;
- definir rotina administrativa para correção e exclusão quando cabível;
- evitar dados sensíveis no MVP;
- não registrar dados pessoais desnecessários em logs ou auditoria;
- documentar retenção de leads perdidos antes do lançamento.
### 12.6 Dependências
- `composer.lock` e lock de frontend versionados;
- `composer audit` no CI;
- `npm audit` conforme política definida no projeto;
- nenhuma dependência abandonada ou sem uso;
- justificar dependências grandes em PR/ADR.
---
## 13. Estratégia de testes
### 13.1 Objetivo
A pirâmide deve possuir:
1. muitos testes unitários rápidos;
2. testes feature/integration suficientes para Laravel, PostgreSQL, Livewire e Filament;
3. poucos testes E2E cobrindo jornadas críticas;
4. testes visuais determinísticos nas telas mais importantes.
A distribuição é por intenção, não por percentual rígido.
### 13.2 Unit tests
Não inicializam Laravel nem banco quando possível.
Cobertura obrigatória:
- conversão e formatação de dinheiro;
- rejeição de valores negativos;
- status derivado de pagamento;
- cálculo de totais financeiros;
- transições de lead;
- invariantes de conversão;
- conclusão e reabertura de tarefa;
- cálculo de vencimento;
- regras de publicação quando extraídas para domínio.
Exemplo esperado:
```php
it('marks an unpaid past-due payment as overdue', function () {
$payment = PaymentSchedule::from(
amount: new Money(150000),
dueDate: new DateTimeImmutable('2026-07-01'),
paidAt: null,
);
expect($payment->statusAt(new DateTimeImmutable('2026-07-27')))
->toBe(PaymentStatus::Overdue);
});
```
### 13.3 Feature e integration tests
Devem inicializar Laravel e usar PostgreSQL real no CI.
Cobertura obrigatória:
- migrations e constraints;
- Actions;
- Policies;
- conversão transacional e idempotente;
- criação de checklist;
- validação do briefing;
- notificações;
- upload e autorização de download;
- queries financeiras;
- filtros principais;
- Resources e Actions Filament;
- componentes Livewire;
- publicação e despublicação de conteúdo;
- autenticação e papéis.
Usar `Mail::fake`, `Notification::fake`, `Storage::fake` e `Queue::fake` quando o objetivo não for testar a integração externa.
### 13.4 E2E browser
Executar com Chromium headless contra a aplicação servida por FrankenPHP.
Jornadas obrigatórias:
| ID | Jornada | Resultado esperado |
|---|---|---|
| E2E-01 | Visitante envia briefing | Lead criado, sucesso visível, sem erro de console |
| E2E-02 | Assessora autentica e trata lead | Login, filtro, status e próxima ação funcionam |
| E2E-03 | Assessora converte lead | Evento e checklist criados uma única vez |
| E2E-04 | Assessora registra orçamento e pagamento | Totais atualizados e pagamento visível |
| E2E-05 | Assessora publica caso | Conteúdo aparece na rota pública |
Não duplicar todas as combinações no browser. Casos de borda ficam em unit/feature.
Em falha, CI deve publicar:
- screenshot;
- trace ou vídeo quando suportado;
- logs da aplicação;
- logs do browser;
- HTML report quando disponível.
### 13.5 Regressão visual
Usar `assertScreenshotMatches()` ou API equivalente do Pest Browser.
Snapshots obrigatórios:
| Tela | Desktop | Mobile |
|---|---:|---:|
| Home | 1440×1000 | 390×844 |
| Serviços | 1440×1000 | 390×844 |
| Portfólio | 1440×1000 | 390×844 |
| Detalhe do portfólio | 1440×1000 | 390×844 |
| Briefing vazio | 1280×900 | 390×844 |
| Briefing com erros | 1280×900 | 390×844 |
| Briefing sucesso | 1280×900 | 390×844 |
| Login | 1280×900 | Opcional |
| Dashboard | 1440×1000 | Não obrigatório |
| Detalhe do evento | 1440×1000 | Não obrigatório |
Determinismo obrigatório:
- Chromium e imagem Linux fixos;
- viewport fixo;
- timezone `America/Sao_Paulo`;
- locale `pt-BR`;
- fontes instaladas na imagem;
- relógio congelado;
- seed determinístico;
- animações e transições desabilitadas;
- dados dinâmicos mascarados quando necessário.
Atualização de baseline deve usar comando explícito e revisão humana do diff.
### 13.6 Testes de arquitetura
Criar regras Pest Architecture:
```php
arch()
->expect('App\\Domain')
->toUseStrictTypes()
->not->toUse(['dd', 'dump', 'die']);
arch()
->expect('App\\Domain')
->not->toDependOn(['App\\Filament', 'App\\Livewire']);
```
Adicionar regras conforme a estrutura real, sem tornar a suíte excessivamente frágil.
### 13.7 Cobertura
- meta de 80% para `Domain` e `Application`;
- não medir views, migrations, código gerado e framework;
- cobertura não substitui critérios de aceite;
- queda de cobertura em módulo crítico deve bloquear merge.
### 13.8 Acessibilidade automatizada
Smoke/browser tests devem verificar:
- ausência de issues críticas ou sérias;
- foco inicial coerente quando modal abrir;
- labels de formulário;
- landmarks e headings básicos;
- ausência de erro de console.
Revisão manual mínima antes do lançamento:
- navegação por teclado;
- foco visível;
- contraste;
- zoom a 200%;
- leitor de tela nos fluxos de briefing e login.
### 13.9 Comandos padronizados
O projeto deve expor scripts equivalentes:
```bash
composer test:unit
composer test:feature
composer test:browser
composer test
composer quality
composer visual:update
```
Composição esperada:
```text
test:unit → Unit + Architecture
test:feature → Feature + Livewire + Filament
test:browser → E2E + visual + acessibilidade + smoke
quality → Pint check + PHPStan/Larastan + audits + testes
```
---
## 14. CI/CD
### 14.1 Gates de pull request
| Job | Responsabilidade | Bloqueia merge |
|---|---|---:|
| `static` | Pint, PHPStan/Larastan, Composer validate, Composer e npm audit | Sim |
| `unit` | Unitários, arquitetura e cobertura | Sim |
| `feature` | PostgreSQL, migrations, Livewire, Filament e integração | Sim |
| `browser` | Vite, FrankenPHP, E2E, smoke, acessibilidade e visual | Sim |
| `container` | Build da imagem final e healthcheck | Sim |
### 14.2 Regras do pipeline
- todo PR executa todos os gates;
- usar cache de Composer, npm e browser de forma segura;
- banco de integração deve ser PostgreSQL;
- CI não deve usar SQLite como substituto;
- browser tests usam o mesmo artefato ou imagem próxima da produção;
- falhas devem publicar artefatos diagnósticos;
- migrations devem ser executadas em banco vazio;
- seed de demonstração deve ser executável;
- secrets nunca ficam no repositório ou na imagem.
### 14.3 Branches e ambientes
- PR: testes e preview opcional;
- `main`: build imutável por SHA publicado no GHCR e deploy automático em staging via Dokploy;
- staging: Dokploy Compose executa migração, healthcheck `/up` e smoke pós-deploy (`/up`, `/`, `/admin/login`);
- produção: promoção da mesma imagem aprovada, sem rebuild (retag do digest em `:production`);
- produção requer aprovação humana explícita no MVP (`workflow_dispatch` com confirmação);
- rollback usa imagem anterior (SHA anterior, sem rebuild);
- migrations devem ser backward-compatible quando possível.
### 14.4 Definition of Done
Uma história só está concluída quando:
- critérios de aceite estão atendidos;
- código passa por Pint;
- PHPStan/Larastan está verde;
- unit e feature tests cobrem regras relevantes;
- E2E foi criado ou atualizado para fluxo crítico;
- snapshot foi criado ou revisado para UI coberta;
- acessibilidade automatizada não possui issues críticas/sérias;
- migration foi testada;
- autorização foi verificada;
- logs não expõem PII desnecessária;
- factories e seed foram atualizados;
- staging e smoke pós-deploy estão verdes;
- documentação afetada foi atualizada.
---
## 15. Deploy com FrankenPHP
### 15.1 Decisão
Usar FrankenPHP em modo regular no MVP.
**NÃO ativar worker mode inicialmente.**
Worker mode somente poderá ser ativado após:
- profiling demonstrar benefício relevante;
- testes de persistência de estado;
- testes de memória;
- suíte de regressão verde;
- ADR aprovada.
### 15.2 Processos
| Processo | Imagem | Comando/função |
|---|---|---|
| web | imagem da aplicação | FrankenPHP/Caddy servindo `public/` |
| queue | mesma imagem | `php artisan queue:work --sleep=2 --tries=3` |
| scheduler | mesma imagem | `php artisan schedule:work` ou cron `schedule:run` |
| database | serviço gerenciado | PostgreSQL com backup |
| files | serviço gerenciado | bucket S3-compatible |
### 15.3 Dockerfile
Usar multi-stage build:
1. Composer dependencies;
2. frontend assets;
3. runtime FrankenPHP.
Requisitos:
- versão PHP fixada;
- extensões PHP explícitas;
- `composer install --no-dev --prefer-dist --no-interaction --classmap-authoritative` em produção;
- `npm ci`;
- build Vite;
- usuário não-root quando suportado;
- nenhum secret em layer;
- healthcheck;
- imagem reproduzível;
- caches Laravel criados no entrypoint/deploy quando dependem do ambiente.
Não executar `php artisan config:cache` em uma stage que não possui as variáveis finais quando isso congelar configuração incorreta.
### 15.4 Variáveis de ambiente mínimas
```text
APP_NAME
APP_ENV
APP_KEY
APP_DEBUG=false
APP_URL
APP_LOCALE=pt_BR
APP_FALLBACK_LOCALE=pt_BR
APP_TIMEZONE=America/Sao_Paulo
DB_CONNECTION=pgsql
DB_HOST
DB_PORT
DB_DATABASE
DB_USERNAME
DB_PASSWORD
CACHE_STORE=database ou file conforme ambiente
QUEUE_CONNECTION=database
SESSION_DRIVER=database ou cookie conforme decisão
FILESYSTEM_DISK=r2 em produção
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET
R2_ENDPOINT
R2_URL (domínio próprio opcional)
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION, AWS_BUCKET, AWS_ENDPOINT, AWS_USE_PATH_STYLE_ENDPOINT — opcionais, apenas se o disk s3 for usado
MAIL_MAILER=resend em produção (mailer nativo Laravel)
RESEND_API_KEY
MAIL_FROM_ADDRESS
MAIL_FROM_NAME
```
### 15.5 Healthcheck
`GET /up` deve:
- responder rapidamente;
- não exigir autenticação;
- não expor segredo;
- retornar falha quando a aplicação não inicializar;
- opcionalmente validar conexão simples com banco em check separado, sem tornar o endpoint caro.
---
## 16. Observabilidade, backup e operação
### 16.1 Logs
Logs estruturados devem incluir quando disponível:
- request ID;
- user ID;
- rota;
- ação;
- tipo e ID de entidade;
- duração;
- nível e exceção.
Não registrar:
- senha;
- token;
- conteúdo integral de comprovante;
- conteúdo integral de mensagem pessoal;
- dados pessoais sem necessidade.
### 16.2 Monitoramento mínimo
- disponibilidade da home;
- disponibilidade do briefing;
- disponibilidade do login;
- erros não tratados;
- falhas repetidas de queue;
- falha de backup;
- smoke pós-deploy.
Não construir stack própria de observabilidade distribuída no MVP.
### 16.3 Backup
| Item | Política inicial |
|---|---|
| PostgreSQL | backup diário automático |
| Retenção | mínimo 14 dias |
| Arquivos | versionamento ou lifecycle do bucket |
| RPO | até 24 horas |
| RTO | até 4 horas |
| Teste de restauração | trimestral |
O procedimento de restauração deve ser documentado antes da produção.
---
## 17. Seeds, factories e dados determinísticos
### 17.1 Factory obrigatória
Criar factories para:
- User;
- Lead;
- LeadActivity;
- Event;
- EventTask;
- Vendor;
- EventVendor;
- BudgetItem;
- Payment;
- Service;
- PortfolioCase;
- Testimonial;
- Document, quando viável sem arquivo real.
### 17.2 Seed de demonstração
O seed deve criar:
- um admin;
- um assistant;
- configurações do site;
- 3 serviços;
- 3 casos de portfólio;
- depoimentos reais: os 5 casais de `depoimentos.md` (Jeniffer e Maick, Quesia e Jhonata, Milena e Weslley, Raquel e Pedro, Victoria e Pedro), preservando texto e datas; autores fictícios de demonstração removidos; em produção permanecem não publicados até autorização explícita de publicação;
- leads em estados variados;
- 2 eventos futuros e 1 concluído;
- tarefas vencidas e futuras;
- fornecedores;
- itens de orçamento;
- pagamentos pagos, pendentes e vencidos.
As credenciais de desenvolvimento devem ser documentadas apenas em ambiente local e nunca usadas em produção.
### 17.3 Determinismo
Para visual e browser tests:
- usar datas fixas;
- usar imagens fixture versionadas ou placeholders estáveis;
- não depender de rede externa;
- não usar conteúdo randômico não seedado;
- congelar horário.
---
## 18. Backlog de implementação
O agente deve implementar na sequência, salvo instrução explícita.
### Fase 0 — Fundação
**Entregas:**
- [x] projeto Laravel;
- [x] PostgreSQL local e CI;
- [x] Filament instalado e autenticado;
- [x] Livewire configurado;
- [x] Tailwind/Vite;
- [~] FrankenPHP e Docker Compose local (imagem pronta; serviço de aplicação local pendente);
- [x] papéis admin/assistant;
- [x] Pint;
- [x] PHPStan/Larastan;
- [x] Pest;
- [x] Pest Browser;
- [x] Architecture tests;
- [x] pipeline CI inicial;
- [x] design tokens mínimos;
- [x] healthcheck;
- [x] seed de admin local.
- [ ] verificação de e-mail e reset seguro (MustVerifyEmail);
- [ ] npm audit no `composer quality` e no job `static`;
- [ ] gate de cobertura `Domain`/`Application` ≥ 80%;
- [ ] serviço de aplicação FrankenPHP no Compose local;
- [ ] hello-world implantado em staging (critério de saída).
> Os itens pendentes acima são tratados pela mudança OpenSpec `complete-foundation-parity`; o critério de saída da fase só é atingido com staging implantado.
**Critério de saída:** pipeline verde e hello-world implantado em staging.
### Fase 1 — Site e CMS
- [x] `site_settings`;
- [x] serviços;
- [x] portfólio e galeria;
- [x] depoimentos;
- [x] home;
- [x] listagem e detalhe de serviços;
- [x] listagem e detalhe de portfólio;
- [x] sobre;
- [x] privacidade;
- [x] SEO;
- [x] mídia otimizada;
- [x] snapshots desktop/mobile;
- [x] testes de acessibilidade.
**Critério de saída:** conteúdo gerenciável no Filament e site público aprovado visualmente (baselines em `tests/.pest/snapshots/`; aprovação humana do diff visual no PR).
### Fase 2 — Leads
- [ ] migration/model/factory de Lead;
- [ ] LeadActivity;
- [ ] briefing Livewire;
- [ ] proteção contra abuso;
- [ ] notificação de novo lead;
- [ ] LeadResource;
- [ ] filtros;
- [ ] próxima ação;
- [ ] histórico;
- [ ] perda e reabertura;
- [ ] testes unit/feature/browser;
- [ ] E2E-01 e E2E-02.
**Critério de saída:** jornada visitante → lead → tratamento interna totalmente verde.
### Fase 3 — Eventos e tarefas
- [ ] Event;
- [ ] EventTask;
- [ ] conversão idempotente;
- [ ] checklist padrão;
- [ ] EventResource;
- [ ] detalhe customizado;
- [ ] tarefas por evento;
- [ ] visão global de tarefas;
- [ ] dashboard operacional inicial;
- [ ] E2E-03.
**Critério de saída:** lead é convertido e evento pode ser administrado.
### Fase 4 — Fornecedores e financeiro
- [ ] Vendor;
- [ ] EventVendor;
- [ ] BudgetItem;
- [ ] Payment;
- [ ] Money/conversão para centavos;
- [ ] resumo financeiro;
- [ ] pagamentos pagos, pendentes e vencidos;
- [ ] comprovante opcional;
- [ ] auditoria financeira;
- [ ] widgets financeiros;
- [ ] E2E-04.
**Critério de saída:** assessora acompanha orçamento e pagamentos sem integração bancária.
### Fase 5 — Documentos, hardening e lançamento
- [ ] documentos privados;
- [ ] auditoria completa;
- [ ] scheduler de resumo;
- [ ] performance e índices;
- [ ] revisão N+1;
- [ ] revisão de Policies;
- [ ] revisão LGPD;
- [ ] backup e restore documentados;
- [ ] monitoramento;
- [ ] smoke pós-deploy;
- [ ] revisão manual de acessibilidade;
- [ ] E2E-05;
- [ ] seed de demonstração completo;
- [ ] documentação operacional.
**Critério de saída:** Definition of Done integral e produção aprovada.
---
## 19. Critérios de aceite transversais
Toda tela interna deve:
- exigir autenticação;
- aplicar Policy;
- estar em pt-BR;
- exibir estados vazios úteis;
- possuir validação server-side;
- indicar sucesso e falha;
- ser navegável por teclado;
- não executar N+1 conhecido;
- paginar listas de crescimento aberto;
- possuir teste proporcional ao risco.
Toda rota pública deve:
- funcionar sem autenticação;
- não expor conteúdo não publicado;
- possuir title e description;
- usar layout responsivo;
- não emitir erro de console;
- respeitar acessibilidade básica;
- ter comportamento de erro seguro.
Toda operação financeira deve:
- usar centavos inteiros;
- ser validada;
- ser autorizada;
- gerar auditoria quando crítica;
- possuir testes unitários e feature.
---
## 20. Riscos e mitigação
| Risco | Mitigação obrigatória |
|---|---|
| Filament concentrar domínio | Resources finos, Actions e Queries testáveis |
| Site parecer genérico | design tokens, conteúdo real, fotografia e visual tests |
| Snapshots instáveis | contêiner fixo, relógio/dados/fontes determinísticos |
| Escopo crescer | lista de não objetivos e mudança somente com hipótese real |
| Financeiro virar contabilidade | limitar a orçamento e pagamentos manuais |
| Worker mode vazar estado | manter modo regular até benchmark e ADR |
| Upload degradar performance | limites, variantes, storage externo |
| SQLite esconder diferenças | PostgreSQL em integração e CI |
| Regras escondidas em callbacks | Actions explícitas e testes de caso de uso |
| Dashboard ficar lento | queries independentes, índices e agregações pequenas |
---
## 21. ADRs iniciais
| ADR | Decisão | Status |
|---|---|---|
| ADR-001 | Monólito modular Laravel, sem microserviços | Aceita |
| ADR-002 | Filament para área interna e Livewire/Blade para área pública | Aceita |
| ADR-003 | PostgreSQL como único banco transacional | Aceita |
| ADR-004 | Pagamentos somente manuais | Aceita |
| ADR-005 | Pest unifica unit, feature, browser e visual | Aceita |
| ADR-006 | FrankenPHP regular mode; worker mode adiado | Aceita |
| ADR-007 | Single-tenant; SaaS e portal do cliente adiados | Aceita |
| ADR-008 | Database queue; Redis adiado | Aceita |
| ADR-009 | Dinheiro em BRL armazenado como centavos inteiros | Aceita |
| ADR-010 | Home com estrutura fixa e CMS tipado, sem page builder | Aceita |
| ADR-011 | Cloudflare R2 (S3-compatible) como storage de objetos em produção | Aceita |
| ADR-012 | E-mail transacional via Resend (mailer nativo Laravel) | Aceita |
| ADR-013 | Design system Heritage Editorial para o site público | Aceita |
| ADR-014 | Deploy via Dokploy Compose com imagem imutável por SHA no GHCR | Aceita |
---
## 22. Gatilhos objetivos para evolução
| Evolução | Gatilho |
|---|---|
| Portal do cliente | clientes pedem repetidamente visibilidade e aprovação fora do WhatsApp |
| RSVP | volume relevante de eventos exige gestão de convidados |
| Pagamento online | cobrança manual causa esforço ou inadimplência mensurável |
| Multi-tenancy | uma segunda assessoria aceita pagar e usar o produto |
| Redis | database queue não atende volume ou confiabilidade |
| FrankenPHP worker | benchmark prova ganho e testes de estado/memória passam |
| API pública | existe consumidor real e contrato de integração |
| Kanban | tabela de leads demonstra limitação frequente observada |
| Editor de checklist | diferentes tipos de evento exigem manutenção frequente do seed |
---
## 23. Condições de conclusão do MVP
O MVP está concluído somente quando:
- site público está publicado e visualmente aprovado;
- assessora edita os conteúdos essenciais sem desenvolvedor;
- briefing cria leads de forma segura;
- pipeline e próxima ação funcionam;
- lead é convertido uma única vez em evento;
- checklist é criado automaticamente;
- evento possui visão consolidada;
- fornecedores podem ser cadastrados e vinculados;
- orçamento e pagamentos manuais possuem totais consistentes;
- dashboard mostra pendências do dia;
- usuários e Policies estão corretos;
- auditoria registra operações críticas;
- testes unit, feature, E2E, visuais e arquitetura estão verdes;
- CI bloqueia regressões;
- imagem FrankenPHP é reproduzível;
- staging e produção usam a mesma imagem promovida;
- backup, restauração e monitoramento estão documentados;
- nenhum item explicitamente fora do MVP foi introduzido.
---
## 24. Formato esperado de reporte do agente
Ao finalizar uma tarefa, o agente deve responder no formato:
```text
Requisito implementado: <ID e nome>
Alterações:
- <arquivo ou módulo>
- <arquivo ou módulo>
Testes adicionados/atualizados:
- <teste>
- <teste>
Comandos executados:
- <comando e resultado>
Critérios de aceite:
- [x] <critério>
- [x] <critério>
Pendências ou desvios:
- Nenhum
```
Caso exista desvio:
```text
Desvio da SPEC:
- Decisão afetada:
- Motivo:
- Alternativa escolhida:
- Impacto:
- ADR criada/atualizada:
```
---
## 25. Primeira instrução recomendada ao agente
```text
Leia integralmente o SPEC.md e inspecione o repositório. Implemente apenas a Fase 0 — Fundação. Antes de alterar arquivos, apresente um plano curto relacionando cada mudança aos requisitos da fase. Não avance para a Fase 1. Ao terminar, execute todos os gates disponíveis, reporte os resultados e marque somente os itens comprovadamente concluídos.
```