Continuação direta, sem parar para autorização (a pedido do usuário).
2 subagentes eden-frontend em paralelo.
- Estoque: armazéns (CRUD), ativos com ações contextuais por status
(só oferece Reservar/Instalar/Devolver quando válido para o status
atual), entrada de ativo, detalhe com histórico de movimentos, ledger
só-leitura, reservas genéricas. Achado real documentado no código:
install/return gravam o movimento com reference_type='contract', não
'serialized_asset' — filtrar o ledger só pelo id do ativo nunca traz
esse histórico; contornado buscando também pelos movimentos do
contrato atual do ativo, registrado como pendência de backend.
- Faturamento: assinaturas (valor mensal pré-preenchido do contrato,
editável com aviso), fechamentos (aviso de idempotência ao regerar),
detalhe do fechamento com tabela de faturas + consolidação com
confirmação em duas etapas (irreversível), lista de faturas.
Reconciliação manual de App.tsx/AuthenticatedLayout.tsx entre os 2
agentes, confirmando typecheck limpo a cada mesclagem. Um dos agentes
restaurou a base do zero (sem git merge) — comparação byte a byte
(diff -rq) contra main confirmou zero desvio antes de aceitar qualquer
arquivo como genuinamente novo.
Verificado: pnpm turbo run typecheck build (28/28); containers
eden-api/eden-core reconstruídos; testado ao vivo através do proxy
same-origin (porta 3001); trilha de auditoria conferida no Postgres.
Os próprios agentes já haviam confirmado ao vivo contra a API real:
invariante de serial duplicado (409), ciclo completo de ativo, e o
fluxo idempotente de billing (gerar→regenerar→consolidar→409).
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Continuação direta da leva anterior, sem parar para autorização (a
pedido do usuário). 2 subagentes eden-frontend em paralelo.
- Cadastro de Cliente: lista, criação direta (PF/PJ + portabilidade),
detalhe rico editável por seção (status, endereço, contatos, dados
PF/PJ com CPF/CNPJ sempre somente-leitura, portabilidade condicional)
+ sub-seção de Sócios para PJ. Fecha a pendência da leva anterior:
QuoteDetailPage não pede mais UUID colado à mão para fechar negócio,
agora é um select real com atalho para criar cadastro novo.
- Contratos: lista, criação (itens produto+quantidade, partes
contratada/contratante), detalhe com resumo financeiro, aviso de
fidelidade sem efeito legal quando não aprovada, histórico de status,
e botões de transição limitados aos alvos válidos a partir do status
atual (servidor continua sendo a única fonte de verdade real).
QuoteDetailPage ganha link "Criar contrato a partir desta oferta"
quando a oferta está fechada.
Reconciliação manual de App.tsx/AuthenticatedLayout.tsx/QuoteDetailPage.tsx
entre os 2 agentes (mais o de Empresas/CRM/Ofertas da leva anterior),
um de cada vez, confirmando typecheck/build limpo a cada mesclagem.
Verificado: pnpm turbo run typecheck build (28/28); containers
eden-api/eden-core reconstruídos; fluxo completo testado ao vivo
ATRAVÉS DO PROXY same-origin (porta 3001, não direto na API) — login,
criação de cadastro, criação de contrato, transição de status inválida
corretamente rejeitada com 409.
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A API não tem domínio público próprio (decisão do operador — só IP
interno). O proxy externo que termina SSL nos 3 domínios públicos é
gerenciado fora deste repositório; a peça que este repo controla é o
nginx que já roda dentro de cada container de frontend.
infra/docker/nginx-spa.conf ganha um location /api/ com proxy_pass para
eden-api:8080 (mesma rede eden_net) — resolver 127.0.0.11 (DNS
embutido do Docker) + proxy_pass via variável, para não cachear o IP do
upstream permanentemente. Com isso o browser chamando /api/v1/... no
domínio público (já roteado para a porta 3001/3002/3003 pelo proxy
externo do operador) completa o fluxo same-origin sem precisar de CORS
nem de um domínio próprio para a API.
Testado ao vivo: login real via POST /api/v1/auth/login funcionando
através dos 3 containers reconstruídos (eden-core:3001,
eden-parceiros:3002, eden-assinante:3003).
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Prioriza a parte web a pedido do usuário. 2 subagentes eden-frontend em
paralelo sobre o shell autenticado já pronto:
- Empresas/Produtos/Revendas: CRUD completo (lista + criar/editar),
layout modelado nas páginas reais do tema DreamsERP comprado
(purchase/vendors.tsx, inventory/products.tsx) — máscara de moeda para
as 5 faixas de preço + implantação. Novo componente Table<T> genérico
em packages/ui.
- CRM: Leads (lista, criar/editar, converter em oportunidade) e
Oportunidades, modelados em crm/leads.tsx/dealsDetails.tsx.
- Ofertas/Quotes: o módulo mais rico — criação com lista dinâmica de
itens produto/quantidade, fidelidade reduzida opcional, desconto
opcional, totais calculados pelo servidor; detalhe com aprovar/rejeitar
e fechar negócio. Modelado em sales/salesQuotes*.tsx. Novo componente
Select em packages/ui.
Correção de instrução no meio da leva: usuário pediu para seguir de
perto o layout real do tema comprado, não só os tokens visuais já
extraídos — as páginas React relevantes do tema foram extraídas para
.tmp/theme-reference/ (nunca versionado) em cada worktree e os agentes
já em execução foram redirecionados via mensagem (não relançados).
Bug real do próprio agente, corrigindo meu brief: Leads/Opportunities
respondem em camelCase (mapLead/mapOpportunity), não snake_case como o
resto da API — verificado contra o código-fonte real antes de aceitar.
Integração manual: um dos 2 agentes não conseguiu git merge no próprio
sandbox e restaurou o shell existente por leitura+escrita — cada arquivo
restaurado foi comparado byte a byte contra main (diff -rq) antes de
aceitar. App.tsx/AuthenticatedLayout.tsx/packages/ui/index.ts
reconciliados manualmente juntando as rotas/exports dos dois agentes.
Verificado: pnpm turbo run typecheck build (28/28); containers
eden-api/eden-core reconstruídos; bundle de produção contém todas as
novas telas; login simulando Origin de browser real; listagens
retornando dados reais; criação de lead confirmando o formato camelCase.
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Continuação da leva "pelo menos mais 20 etapas" — 2 subagentes em paralelo.
## Documents (apps/api/src/documents/ + migration documents)
Metade "templates + geração" do eden.md módulo 4 (§1.1-1.3): document_templates,
document_template_versions (ciclo draft→publish, no máximo 1 rascunho por
template via índice único parcial, versão publicada imutável), document_generations
(idempotente por (template_version_id, origem), CHECK de origem única). Motor de
merge de variáveis ({{chave}}→valor, nunca lança exceção) como função pura
testável. A metade de Assinatura Eletrônica (envelopes, signatários, OTP,
cadeia de auditoria hash-chain — eden.md §1.4-1.9/§5-7, 9 tabelas satélite)
fica deliberadamente fora — grande e sensível demais para dividir espaço
com esta leva, fica para uma leva própria. PDF real via Chromium também
deferido; document_generations grava rendered_html, pdf_* ficam NULL.
## EDEN Parceiros / EDEN Assinante (apps/reseller-web, apps/subscriber-web)
Replicam o shell autenticado do core-web (leva anterior): login real com
suporte a MFA, sessão, AppShell do Design System — trocando só
applicationKey ("reseller"/"subscriber"), título e chave de localStorage
da sessão. As duas telas "Bootstrap da Fase 1" saem de circulação.
Testado ao vivo contra os containers reais: ciclo completo de template
(criar→draft→409 no segundo draft→editar→publicar→gerar documento com
merge de variáveis funcionando→regeneração idempotente reaproveita o
registro), e login via /v1/auth/login com applicationKey "reseller"/
"subscriber" retornando tokens válidos nos 2 portais reconstruídos.
pnpm turbo run typecheck build (28/28), pnpm --filter @eden/api test
(30/30, sem regressão), trilha de auditoria conferida no Postgres.
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Leva grande, pedida como "pelo menos mais 20 etapas, use subagentes":
fecha a pendência de MFA/TOTP da Fase 1 (feito diretamente, autenticação é
sensível demais para delegar) e lança 5 subagentes em paralelo em 2 ondas
(eden-inventory, eden-support, eden-hr-timeclock; depois eden-finance,
eden-fiscal, eden-frontend), cada um em worktree isolado, com integração
seletiva manual (barrel, app.module.ts, migrations renomeadas/aplicadas,
build/typecheck/teste ao vivo antes de cada merge).
## Backend
- MFA/TOTP (`apps/api/src/auth/mfa.service.ts`, `crypto/field-encryption.service.ts`):
login em duas etapas quando `mfa_enabled=true`, `FieldEncryptionService`
genérico (AES-256-GCM versionado, ADR-0005) reaproveitado por outros
segredos reversíveis (ex. credencial de equipamento Control iD).
- Inventory: warehouses, stock_movements (ledger append-only), serialized_assets,
asset_assignments, stock_reservations — invariante "serial/patrimônio/MAC
nunca duplicado entre ativos ativos" reforçada por índices únicos parciais
no banco (Master Prompt §15.3).
- Support/Service Desk: tickets com máquina de estados, SLA com pausa/retomada
em `waiting_customer`/`waiting_third_party` (funções puras testáveis).
- HR-Timeclock (fundação): dispositivos Control iD (senha cifrada), import de
AFD com parser + CRC-16/KERMIT, `afd_records` imutável sem exceção nem
para super_admin (sem válvula de escape, diferente de audit_log).
- Fiscal: NCM/CFOP/municípios/naturezas de operação (eden.md módulo 5, MVP de
5 das ~19 tabelas), product_fiscal_profiles com invariante de único
profile ativo por billing_component.
- Billing: subscription→billing_run→invoice idempotente (regenerar em draft
substitui, nunca duplica) e imutável após consolidar (ADR-0008: nunca
confunde faturamento/documento fiscal/recebimento).
- FKs cruzadas fechadas: quotes/client_registrations/contracts.
## Frontend — primeira leva
- `packages/ui`: Design System real extraído do tema DreamsERP comprado —
achado que mudou o escopo: o tema já vem em React+Tailwind (não só
Angular), tokens visuais reais portados (cores, Geist como fonte real,
divergindo da doc antiga que citava Nunito/Poppins).
- `apps/core-web`: login real (com suporte a MFA), sessão, shell autenticado
via `AppShell` — substitui a tela estática "Bootstrap da Fase 1".
## Bugs reais encontrados e corrigidos (todos via teste ao vivo, não leitura)
1. **Segurança**: o token de desafio MFA era aceito como access token normal
pelo `JwtAuthGuard` (mesma assinatura, nenhuma checagem da claim
`purpose`) — bypass total do segundo fator. Explorado e fechado ao vivo
contra o container real; teste de regressão em `test/mfa.spec.ts`.
2. Crase solta dentro de comentário SQL na migration de hr-timeclock quebrava
o template literal JS (`SyntaxError` ao rodar a migration).
3. CORS ausente por completo na API.
4. `turbo.json` não declarava `VITE_API_BASE_URL` como input de build — o
Turborepo cacheava o bundle do frontend ignorando mudança na env var.
5. `compose.yaml` usava `${VITE_API_BASE_URL:-/api/v1}`, que colidia com a
variável de mesmo nome do `.env` local — o default nunca era aplicado.
6. Não existia `.dockerignore` — todo build copiava node_modules/.git/
worktrees de subagentes (665MB) para dentro do contexto Docker.
Decisão do operador incorporada: a API nunca tem domínio público próprio
(só IP interno) — os 3 frontends chamam por caminho relativo `/api/v1`,
proxy externo roteia same-origin; CORS só é relevante para dev local.
Verificado: `pnpm turbo run typecheck build` (28/28), `pnpm --filter
@eden/api test` (30/30, cresceu de 15), todo o lote testado ao vivo via
curl contra os containers reais reconstruídos, incluindo os 6 bugs acima
já fechados e confirmados, e a trilha de auditoria conferida no Postgres.
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fecha o núcleo comercial da Fase 2/3: motor de precificação de Ofertas
(feito diretamente, dado o risco de erro de arredondamento/regra de
negócio), Cadastro de Cliente e esqueleto de Contratos (via 2 subagentes
em paralelo, salvos manualmente após ambos baterem no limite de gasto
mensal da conta — trabalho não commitado nos worktrees foi revisado
arquivo a arquivo e copiado para main, com as peças faltantes
completadas diretamente).
- Quotes: pricing.ts porta as fórmulas do legado (eden.md §7/§8) —
needsApproval, faixas de condição de pagamento, efeito legal da
fidelidade condicionado a approval_status==='approved'. Fluxo
criar→decidir aprovação→fechar negócio (vinculado a um cadastro de
cliente, com guarda contra fechamento duplo).
- Client Registration: cadastro direto (eden.md §3), MVP autenticado-only
(sem token público de autoatendimento, upload ou lookup CNPJ/CEP nesta
fase — documentado em comentário).
- Contracts: máquina de estados do ADR-0006 imposta via SELECT...FOR
UPDATE + tabela de transições, com signed_at/activated_at e histórico
de status; 4 das 11 tabelas do ADR-0006 (as demais adiadas).
2 bugs próprios corrigidos antes do commit: UpdateQuoteInput usava
COALESCE para campos financeiros (undefined do JS apagaria valor em
update parcial) — campos tornados obrigatórios; User.reseller_id/
can_approve_discount faltavam em types.ts apesar das colunas já
existirem no banco desde migrations anteriores.
Verificado: pnpm turbo run typecheck build (28/28), pnpm test (15/15),
e todo o fluxo testado ao vivo via curl contra a stack Docker real,
incluindo cenários de erro (transição de status inválida, FK de produto
inexistente, fechamento duplo, 401 sem token) e trilha de auditoria
conferida no Postgres.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Vitest + supertest + NestJS TestingModule contra o Postgres real (não
mockado, conforme Master Prompt §15.1). Bootstrap via SWC (não esbuild
puro) porque o esbuild não implementa emitDecoratorMetadata — sem isso,
toda injeção de dependência via decorator do Nest quebra silenciosamente
sob Vitest (achado real do agente, não bug de produto).
5 arquivos de teste, 15 casos, todos verdes contra a stack real:
- auth.spec.ts: anti-enumeração, rotação de refresh, detecção de replay
fim-a-fim, logout idempotente.
- role-weight.spec.ts: guarda a regressão do bug #1 (papel inexistente
→ 400) e o peso hierárquico (ator fraco não atribui papel mais forte).
- companies.spec.ts: CNPJ inválido rejeitado, CNPJ válido aceito.
- mass-assignment.spec.ts: whitelist rejeita campo inesperado.
- reseller-assignment.spec.ts: guarda a regressão do bug #2 (resellerId
do corpo não é mais aceito; :id da URL é a única fonte de verdade).
Cada fixture criada pela suíte é prefixada (qa-test-/qa_) e limpa em
afterAll (desativada onde não há endpoint DELETE, removida onde há) —
confirmado que nenhum dado pré-existente (super_admin seedado, Handix
Telecom LTDA, etc.) foi alterado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Auth (apps/api/src/auth/): Argon2id com parâmetros versionados
(ADR-0005), access token JWT de 15min, refresh token opaco de 256 bits
com rotação e detecção de replay (reuso de token já rotacionado revoga
toda a cadeia de sessões do usuário). Endpoints /api/v1/auth/
{login,refresh,logout,me}.
- Permissões resource+action+scope (ADR-0004) via PermissionGuard,
resolvidas do banco a cada request (nunca cacheadas no JWT) — mudança
de papel tem efeito imediato. super_admin com bypass hardcoded,
espelhando a trava já existente no banco.
- Companies (migration + CRUD): leitura aberta a qualquer autenticado,
escrita restrita a companies:manage; validação de CNPJ via
packages/domain-shared (dígito verificador).
- SeedService: cria o primeiro super_admin a partir de
EDEN_SUPERADMIN_EMAIL/PASSWORD só quando `users` está vazia.
- OpenAPI/Swagger em /api/docs; ValidationPipe global com
whitelist+forbidNonWhitelisted (proteção contra mass-assignment).
- packages/database: repositórios (roles/users/sessions/audit/companies)
reutilizáveis por api e worker.
Validado manualmente contra a stack real (não só typecheck): login com
senha errada → 401 genérico; refresh rotaciona e invalida o token
anterior; reuso do token antigo → replay detectado, cadeia revogada;
logout idempotente; audit log com a sequência completa de eventos;
CNPJ inválido rejeitado; mass-assignment rejeitado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fase 0 — descoberta e arquitetura:
- Inventário do projeto, glossário de domínio, arquitetura com bounded
contexts e topologia de containers, threat model inicial.
- 12 ADRs cobrindo modular monolith, topologia de containers (Postgres
isolado + eden-core/parceiros/assinante em containers e portas
distintos), auth/sessões, modelo de permissões, criptografia/segredos,
contrato first-class, stock ledger, separação billing/finance/fiscal,
outbox transacional, adapters SaperX e Focus NFe, e identidade
compartilhada entre as 3 apps.
- 14 subagentes e 7 skills especializados por domínio em .claude/.
- Hooks de segurança (PreToolUse/PostToolUse/Stop) testados via pipe.
Fase 1 — plataforma (em andamento):
- Monorepo pnpm workspaces + Turborepo: apps/{api,worker,core-web,
reseller-web,subscriber-web} + 9 packages compartilhados.
- apps/api: NestJS mínimo com /health/live e /health/ready (checando
Postgres real via @eden/database).
- 3 frontends Vite + React + TypeScript + Tailwind, com o favicon
oficial do EDEN.
- packages/database: migration baseline (node-pg-migrate) criando
roles/role_permissions/applications/users/user_applications/sessions/
audit_log — audit log append-only com hash-chain, testado ao vivo
(UPDATE/DELETE bloqueados pelo trigger).
- compose.yaml implementando a topologia da ADR-0002, validada de ponta
a ponta: os 6 containers sobem e ficam saudáveis com um único
`docker compose up`.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>