--- id: AGD-0035 ticket: studio-new-lsp-api-and-v1-boundary title: Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code status: accepted created: 2026-05-05 resolved: 2026-05-05 decision: DEC-0032 tags: [studio, lsp, vscode, protocol, api, boundary] --- ## Pain O stack legado de editor, `prometeu-lsp` e `prometeu-vfs` acabou de ser removido. O próximo passo natural é reconstruir o backend de linguagem, mas sem repetir o erro anterior de misturar engine semântica, transporte LSP, sessão editorial e host UI em um único desenho. Ao mesmo tempo, já existe uma extensão em `tools/vscode-extension` que hoje sabe conectar via TCP em `127.0.0.1:7777` e falar LSP como cliente real. Isso reduz incerteza no lado do FE, mas aumenta a exigência arquitetural do backend: o servidor novo precisa nascer com boundary correto. O risco central é reintroduzir acoplamento acidental entre: - contratos internos de linguagem; - transporte externo LSP; - implementação concreta baseada em `LSP4J`; - e necessidades específicas do VS Code. ## Context - O cleanup do stack legado foi fechado por `DEC-0031`. - A extensão VS Code já existe em `tools/vscode-extension`. - A extensão usa `vscode-languageclient` e conecta por socket TCP configurável. - O usuário quer explicitamente que `LSP4J` **não vaze** de `lsp-v1`. - A intenção geral do produto é que o Studio fale a própria língua internamente e exponha adapters externos para FEs, no mesmo espírito já discutido para integrações como Tiled. Hoje, a pergunta não é "como implementar o LSP inteiro", mas sim: - que módulos recriar; - qual boundary o `lsp-api` realmente deve carregar; - e qual responsabilidade cabe a `lsp-v1` como adapter concreto. Também já existe um direcionamento adicional do produto: - `lsp-v1` deve consumir serviços existentes de `compiler`; - `lsp-api` deve expor funcionalidades reutilizáveis por outras partes do sistema; - o bootstrap do servidor deve acontecer quando um projeto é aberto, e o shutdown quando o projeto é fechado; - a API pode começar mínima, por exemplo com `boot server` e `shutdown server`, e crescer só quando houver necessidade real. ## Open Questions - [x] O `lsp-api` deve expor contratos próprios de domínio/serviço ou DTOs alinhados diretamente ao protocolo LSP? R: o lsp-api deve expor somente servicos que serao usados por outras partes do sistema, e nao ser um espelho do protocolo. - [x] O `lsp-v1` deve ser somente um adapter `LSP4J` sobre serviços internos de `compiler`, ou também pode carregar parte da orquestração de sessão? R: O lsp-v1 deve conter o servidor e todo o necessario para falar LSP, incluindo DTOs e dependencias de LSP4J (e fazer uso de compiler quando necessario). o start e o stop do servidor podem ser chamados via lsp-api, mas a logica de orquestracao de sessao deve ficar dentro do lsp-v1. - [x] O lifecycle de bootstrap/shutdown do servidor por projeto pertence ao `lsp-api`, ao `lsp-v1`, ou a outro serviço do Studio? R: o contrato fica no lsp-api, mas a logica de orquestracao fica no lsp-v1. o lsp-api pode expor um contrato como `bootServer(project)` e `shutdownServer(project)`, mas a implementacao concreta e a logica de associar o servidor ao projeto fica no lsp-v1. - [x] O canal de automação (`compile/build/run/debug`) deve viver fora do novo `lsp-api` desde o início? R: o canal de automacao deve viver fora do lsp-api. nesse primeiro momento o foco principal deve ser o LSP e o comportamento editorial/semantico. o canal de automacao pode ser discutido e implementado separadamente, sem misturar responsabilidades. - [x] O `tools/vscode-extension` deve continuar falando LSP puro em socket TCP, ou o produto precisa reservar outra estratégia de transporte já nesta fase? R: o vscode-extension deve continuar falando LSP puro em socket TCP. o lsp-api deve ser agnóstico ao transporte, mas ainda aceito recomendacoes q sejam mais faceis ou performaticas. ## Options ### Option A - `lsp-api` como API diretamente moldada pelo protocolo LSP - **Approach:** recriar `lsp-api` com tipos e serviços muito próximos da superfície do protocolo, deixando `lsp-v1` apenas como bootstrap `LSP4J` e roteamento para `compiler`. - **Pro:** acelera integração com o cliente existente e reduz tradução entre camadas. - **Con:** tende a tornar o protocolo externo dono do desenho interno; muda mal quando surgir outro FE ou outro adapter. - **Maintainability:** fraca. O risco de vazamento conceitual do protocolo para dentro do domínio é alto, mesmo sem importar classes `LSP4J` fora de `lsp-v1`. ### Option B - `lsp-api` mínima e operacional; `lsp-v1` como adapter LSP4J que consome `compiler` - **Approach:** `lsp-api` expõe uma surface mínima e reutilizável para o sistema, começando com operações como `boot server(project)` e `shutdown server(project)` e, no máximo, contratos estáveis que outras partes do Studio precisem enxergar. `lsp-v1` concentra a implementação concreta do servidor, depende exclusivamente de `LSP4J`, e consome os serviços do `compiler` para responder às capacidades LSP. - **Pro:** respeita a regra de não vazamento de `LSP4J`, evita inflar a API cedo demais e alinha o lifecycle do servidor ao projeto aberto, não ao processo global do Studio. - **Con:** exige disciplina para não transformar `lsp-api` em um espelho parcial do protocolo nem `lsp-v1` em um segundo backend semântico solto do `compiler`. - **Maintainability:** forte. O backend nasce hexagonal e o custo de evolução fica mais previsível. ### Option C - Sem `lsp-api`; expor apenas um servidor LSP concreto em `lsp-v1` - **Approach:** pular a separação e concentrar tudo em um único módulo novo de servidor, com contratos locais apenas package-private ou internos. - **Pro:** menor custo inicial de scaffolding. - **Con:** repete exatamente o tipo de colapso arquitetural que gerou o legado descartado; dificulta teste, substituição de transporte e integração com outros FEs. - **Maintainability:** ruim. A velocidade inicial é comprada com dívida estrutural imediata. ## Discussion O dado novo mais importante é que o cliente VS Code já está funcional como cliente LSP puro. Isso elimina a necessidade de desenhar o protocolo "pensando no editor". O editor já sabe falar LSP; quem precisa de disciplina agora é o Studio. Isso empurra a arquitetura para uma separação bem objetiva: - `lsp-api` não deve ser "API pública do protocolo"; - `lsp-api` deve ser uma API interna e mínima do backend de linguagem do Studio; - `lsp-v1` deve ser o adapter LSP concreto e consumidor dos serviços de `compiler`; - `LSP4J` deve existir exclusivamente em `lsp-v1`. O ponto sensível é não confundir "API interna" com "engine concreta". Se `lsp-api` virar um lugar para pôr qualquer detalhe de sessão, thread, socket, `CompletableFuture` de transporte, ou estruturas do `LSP4J`, ele já nasce errado. Pelo direcionamento atual, a API deve começar deliberadamente estreita: - boot do servidor por projeto; - shutdown do servidor por projeto; - e só depois crescer quando outro consumidor real do sistema exigir isso. Também vale separar desde já o que não pertence ao LSP: - `compile/build/run/debug` não devem entrar no `lsp-api` por conveniência; - esses fluxos pertencem a um contrato de automação paralelo; - o LSP deve se limitar ao que é comportamento editorial/semântico. Outro ponto: o usuário já fixou que o Studio é o backend autoritativo e o cliente externo é só cliente. Isso reforça que o lifecycle do servidor precisa acompanhar o projeto aberto. Em outras palavras: - abrir Studio não implica subir servidor LSP global; - abrir projeto pode implicar subir o servidor LSP daquele projeto; - fechar projeto deve encerrar o servidor correspondente. As respostas atuais também fecharam um boundary operacional importante: - quem "fala LSP" de verdade é o `lsp-v1`; - `lsp-api` não é adapter nem espelho do protocolo; - `lsp-api` existe para expor um boundary interno consumível pelo Studio; - o contrato mínimo inicial pode ser pequeno, desde que suficiente para boot/shutdown por projeto; - DTOs, tipos e dependências do protocolo podem existir em `lsp-v1` sem contaminar a API interna. ## Resolution Direção recomendada para convergir: 1. recriar `lsp-api` como surface interna mínima e reutilizável do sistema, começando com bootstrap e shutdown por projeto; 2. recriar `lsp-v1` como adapter LSP/JSON-RPC concreto, com dependência exclusiva de `LSP4J`; 3. fazer `lsp-v1` consumir serviços existentes de `compiler`, em vez de reconstruir pipeline semântico próprio; 4. deixar a lógica concreta de orquestração de sessão e associação `projeto -> servidor` dentro de `lsp-v1`, mesmo quando o bootstrap/shutdown for disparado através de `lsp-api`; 5. tratar qualquer tipo `LSP4J` fora de `lsp-v1` como violação arquitetural; 6. manter `tools/vscode-extension` como cliente LSP puro em socket TCP; 7. discutir separadamente o contrato de automação para `compile/build/run/debug`, em vez de empurrá-lo para dentro do LSP. Neste ponto, a agenda já tem direção clara o bastante para virar `decision`. O que ainda resta fechar depois disso é implementação, não escolha arquitetural principal.