Novo endpoint POST /documents/templates/:id/draft/preview: renderiza o
conteúdo atual (ainda não salvo) do editor com placeholders {{variavel}}
substituídos por [variavel], via Chromium real, sem persistir nada
(sem generation, sem upload S3) — permite validar visualmente o
resultado antes do "Publicar", que é uma ação permanente.
Corrige de passagem um bug real: css_content do template nunca chegava
ao renderPdf na geração real de documento (createGeneration), sendo
descartado silenciosamente.
No frontend, a pré-visualização aparece embutida num iframe na própria
tela (não em nova aba): abrir em nova aba se mostrou não-confiável ao
vivo — a navegação tardia de uma aba pré-aberta pode ser ignorada pelo
navegador quando o gesto do usuário que a autorizou já expirou no tempo
que o Chromium leva pra renderizar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Substitui a tabela de linhas com dropdown de produto por duas colunas,
no padrão do legado OrçaFácil: "Adicionar Produtos" à esquerda (busca,
ordenação, lista com preço da faixa do período de contrato, clique
adiciona ao carrinho) e "Resumo da Oferta" à direita, sticky (itens
com stepper, breakdown de implantação, implantação geral, condição de
pagamento, condição especial e observações — todos migrados pra dentro
do painel —, totais ao vivo com Total do Contrato em destaque).
Fórmula do preview espelha exatamente pricing.ts::computeQuotePricing
— nada muda no backend, é só a experiência de montar a oferta antes de
enviar; o servidor continua sendo a única fonte de verdade nos totais
persistidos.
pnpm turbo run typecheck build: 28/28. Suíte @eden/api: 113/113
(inalterada). Testado ao vivo ponta a ponta, incluindo o caso de
aprovação por desconto.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Usuário reportou "só tem nome/documento/e-mail" — era a tela de
criação (/resellers/new), que por design só mostra os campos mínimos
até a revenda ser salva (CNPJ-lookup/sócios/representante precisam de
um id existente), sem nenhuma explicação disso na tela. Adicionado
aviso claro dizendo que o resto do cadastro fica disponível assim que
salvar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Usuário: o cadastro de revenda precisa ficar completo porque essa
mesma base vai alimentar a criação de contrato com a revenda dentro
do ERP. resellers ganha rep_full_name/rep_cpf/rep_role/rep_email/
rep_mobile_phone/rep_authorization_ack, mesmo shape de
client_registrations.pj_rep_* — um contrato precisa de um signatário
nomeado e autorizado, não só a lista geral de sócios.
Divergência deliberada: ao contrário de pj_rep_cpf (bloqueado do PATCH
genérico em client_registrations, esperando um fluxo de submissão
pública que o EDEN ainda não construiu — hoje uma lacuna conhecida,
nunca preenchido de fato), rep_cpf aqui fica livremente editável —
revenda não tem esse fluxo de submissão pública pra travar atrás.
Nova tela: card "Representante legal" entre "Dados da revenda" e
"Sócios". Suíte: 107/107 (106→107). pnpm turbo run typecheck build:
28/28. Testado ao vivo em navegador real.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mesma UI construída pro Cadastro de Cliente, aplicada a Revenda, já
direto na ordem pedida (Dados -> Sócios -> Endereço -> Contatos), sem
precisar de uma segunda leva de reordenação. Criar uma revenda agora
redireciona pra edição dela (não pra lista) já que CNPJ-lookup/Sócios
só fazem sentido com um id existente. PartnersSection generalizada
para reuso entre Cliente e Revenda.
Corrige um bug real encontrado na verificação ao vivo: o rótulo do
botão de busca estava amarrado ao que estava sendo digitado, não ao
que já foi salvo — digitar um CNPJ válido trocava pra "Buscar
novamente" antes de qualquer busca real acontecer.
Suíte: 106/106 (inalterada). pnpm turbo run typecheck build: 28/28.
Testado ao vivo com CNPJ real (Petrobras).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ordem pedida pelo usuário. O <form> único virou dois (Sócios renderiza
seu próprio <form> interno entre eles, e form aninhado é inválido em
HTML) — ambos chamam o mesmo handleSubmit, que sempre lê o estado React
completo, então salvar continua gravando tudo de uma vez independente
de qual dos dois disparou. Testado ao vivo.
pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ao digitar/já ter um CNPJ num cadastro de cliente PJ, busca automática
contra a Receita Federal (cnpj.ws primário, BrasilAPI fallback) e
preenche razão social, endereço e inscrição estadual, além de criar os
sócios encontrados (sem duplicar em buscas repetidas) — pra usar depois
na confecção do contrato/assinatura.
Fecha uma lacuna real que já estava prevista mas nunca implementada:
pj_cnpj não tinha nenhuma forma de ser gravado (UpdateClientRegistrationDto
o excluía deliberadamente, esperando por "a dedicated re-verification
flow" — exatamente o que este endpoint é). O servidor sempre refaz a
própria consulta autoritativa a partir do CNPJ recebido, nunca confia
em dado "já encontrado" vindo do cliente.
Suíte: 100/100 (95→100). pnpm turbo run typecheck build: 28/28.
Testado ao vivo contra CNPJs reais (Petrobras, Banco do Brasil) e em
navegador real via Playwright.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Empresas, Produtos (com toda a árvore de submenus), Revendas e Clientes
saem do nível 1 do menu e passam a viver dentro de um novo item
"Cadastro". Isso exercita 3 níveis de submenu aninhado pela primeira
vez (Cadastro > Produtos > Grupos > Subgrupos) — a recursão do AppShell
já suportava isso estruturalmente, mas estava documentada como não
testada; verificado ao vivo via Playwright que funciona corretamente
(auto-expande só o caminho ativo em cada nível).
Suíte: 95/95. pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CatalogCreateForm/SimpleCatalogCard ganham nameLabel (default "Nome"),
propagado aos 5 catálogos: "Nome do tipo/grupo/subgrupo", "Nome da
marca/unidade".
Verificado desta vez em navegador real (Playwright headless, sem a
extensão claude-in-chrome disponível nesta sessão) em vez de só por
API — pegou um bug real que a verificação anterior não pegaria: o prop
nameLabel tinha sido adicionado à assinatura mas esquecido de usar no
JSX, então nenhum rótulo mudava apesar do build passar limpo. Corrigido
e reverificado.
Suíte: 95/95. pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mesma lógica do submenu "Produtos" já existente: "Grupos" agora expande
seu próprio submenu com "Subgrupos" (AppShell ganha suporte a um
segundo nível de accordion aninhado). Nova tela lista todos os
subgrupos de todos os grupos, com coluna Grupo e criação via dropdown
de grupo explícito — complementa (não substitui) o painel já existente
dentro da tela de um grupo específico.
listProductSubgroups passa a resolver group_name via JOIN.
Suíte: 95/95. pnpm turbo run typecheck build: 28/28 (core-web,
reseller-web e subscriber-web confirmados sem regressão no AppShell
compartilhado).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Menu Produtos ganha submenu (AppShell agora suporta itens aninhados) para
cadastrar Tipos/Grupos/Marcas/Unidades; Subgrupo é gerenciado a partir da
tela do Grupo (sempre pertence a um). O cadastro de item passa a usar
dropdowns desses catálogos em vez de texto livre — exatamente o que o
comentário original da migration de products já previa como trabalho
futuro.
Backend: 5 tabelas de catálogo simples (mesmo padrão dos catálogos
fiscais), products ganha as FKs correspondentes com backfill, e
endpoints CRUD (sem exclusão física, active=false é o "apagar").
Corrige também um buraco de integridade real encontrado na integração:
PATCH /v1/products/:id usava COALESCE para os campos de catálogo
opcionais, então não dava para distinguir "não mexer" de "limpar" —
trocar o Grupo de um produto sem tocar no Subgrupo deixava o vínculo
antigo órfão e inconsistente, sem nenhuma validação pegando isso. Agora
a atualização valida o estado efetivo resultante (bloqueia com 400 em
vez de corromper o vínculo) e productSubgroupId: null limpa de verdade.
Suíte: 95/95 verdes (92→95). pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fecha as duas pendências da leva anterior (módulo de Assinatura
Eletrônica): tela de backoffice para gerenciar envelopes (criação,
envio, cancelamento, checkpoint/finalização, auditoria) e as duas
páginas públicas (/assinar/:token, /verificar/:verificationId).
Backend: novos endpoints de streaming de PDF (documento congelado e
PDF final consolidado, autenticados e via token público) que faltavam
para a UI funcionar — o signatário não tinha como ver o próprio
documento antes de confirmar leitura. Rate limiting (guard próprio,
em memória, chaves por IP e por token) em todas as rotas sem sessão:
auth (login/mfa/forgot-password/reset-password/refresh/logout) e todo
o fluxo público de assinatura/verificação.
Suíte: 77/77 verdes. pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Porta o anexo regulatório do legado (Resolução Anatel 765/2023, RGC,
recorte STFC) com fidelidade total ao conteúdo legal, incluindo a
seção de tarifação — em vez do html2canvas+jsPDF client-side do
legado, usa o Chromium server-side já construído para Documents.
Schema: products ganha service_type/metered/tariff_rates/has_ldi
(migration 1788525270647) — campos que a migration original de
products tinha deliberadamente deferido. Snapshot no item da oferta
na criação (nunca referência viva ao produto). Novo pacote
compartilhado packages/domain-shared/src/telecom para o catálogo de
tipos e legendas de tarifação.
Endpoint GET /v1/quotes/:id/anatel-pdf (400 sem item STFC), sempre
renderizado ao vivo (nunca cacheado, já que o conteúdo deriva de
updated_at). Frontend: campos de produto/oferta necessários +
"Baixar PDF ANATEL" na tela de detalhe da oferta. Inclui também o
botão de download de PDF em Documentos (endpoint já existia, faltava
o link na UI).
Verificado ao vivo através do container real: PDF gerado com
Chromium de verdade, texto extraído confere byte a byte com os
valores computados (multa, datas, tarifas, resumo financeiro).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Liga o adapter SMTP existente (packages/integrations, já era fail-soft)
a um consumidor de verdade e adiciona o fluxo completo de recuperação
de senha, faltante desde a Fase 1.
- UsersService.invite() agora envia e-mail com a senha temporária
(mantém o retorno na resposta também — dupla entrega, eden.md §4.8).
- POST /v1/auth/forgot-password + POST /v1/auth/reset-password: token
de 256 bits, só o hash SHA-256 persistido (users.reset_token_hash/
reset_token_expires_at — colunas que já existiam desde a baseline da
Fase 1, nunca usadas até agora), validade de 1h, uso único, revoga
todas as sessões ativas do usuário ao concluir. Mesma disciplina
anti-enumeração do login: resposta idêntica para e-mail cadastrado
ou não.
- Frontend: link "Esqueci minha senha" no LoginPage, novas
ForgotPasswordPage/ResetPasswordPage.
Teste de integração novo (password-reset.spec.ts, 3 testes) usa
vi.mock para interceptar sendMail e recuperar o token real do corpo do
e-mail — única forma de testar o caminho feliz de ponta a ponta, já
que o token nunca é devolvido na resposta da API. Suíte completa:
33/33 (30→33). pnpm turbo run typecheck build (28/28). Testado ao vivo
através do proxy same-origin: anti-enumeração confirmada com curl
real, token gravado no Postgres, token bogus rejeitado (400).
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Última leva desta sequência, sem parar para autorização. 2 subagentes
eden-frontend em paralelo.
- Documentos: templates com ciclo draft→publish (edição por texto com
{{variavel}}, sem editor Tiptap real ainda), preview do HTML
publicado, geração de documento com extração automática de variáveis
via regex e aviso de missingVariables. Achado do agente corrigindo
meu brief: regra de key aceita minúsculas+dígitos+underscore, não só
minúsculas+underscore.
- Ponto Eletrônico (fundação): dispositivos Control iD (senha nunca
retornada, só hasCredential), import de AFD com decodificação
ISO-8859-1 explícita documentada no código, registros AFD só-leitura
(sem nenhuma ação de edição — imutabilidade legal respeitada também
na UI). 2 discrepâncias reais encontradas e corrigidas pelo agente:
reimportar retorna 409 estruturado (não 200), e a listagem de
registros é um envelope paginado (não array simples).
Reconciliação manual de App.tsx/AuthenticatedLayout.tsx entre os 2
agentes. 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) incluindo confirmação de que a senha do
dispositivo nunca aparece na resposta; trilha de auditoria conferida.
Com esta leva, o EDEN Core tem UI real para todo o backend construído
nesta sessão, exceto Assinatura Eletrônica e apuração/fechamento de
ponto (nenhum dos dois existe no backend ainda).
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Continuação direta, sem parar para autorização. 2 subagentes
eden-frontend em paralelo.
- Suporte: lista de tickets, criação, detalhe com painel de SLA
(prazos, cumprido/violado/pendente, aviso "SLA pausado"), thread de
comentários públicos/internos, histórico de status, transições
limitadas aos alvos válidos com campos condicionais de causa raiz/
resolução ao fechar.
- Fiscal: NCM/CFOP/Municípios (CRUD simples), Naturezas de Operação
(só leitura, seed fixo), nova seção "Fiscal" em ProductFormPage
(só ao editar) com aviso de que um novo perfil do mesmo componente
substitui automaticamente o anterior — invariante confirmada ao vivo
pelo próprio agente antes de entregar.
Reconciliação manual de App.tsx/AuthenticatedLayout.tsx/
ProductFormPage.tsx/format.ts entre os 2 agentes. Ambos restauraram a
base manualmente (sem git merge) — diff -rq contra main confirmou zero
desvio de restauração nos dois casos antes de aceitar qualquer arquivo
como 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.
Com esta leva, o EDEN Core tem UI real para praticamente todo o
backend construído até agora, exceto HR-Timeclock e Documentos.
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>
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>
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>