prometeu-studio/discussion/workflow/agendas/AGD-0035-studio-new-lsp-api-and-v1-boundary.md
2026-05-05 15:50:55 +01:00

9.1 KiB

id ticket title status created resolved decision tags
AGD-0035 studio-new-lsp-api-and-v1-boundary Novo boundary entre lsp-api, lsp-v1 e a extensao VS Code accepted 2026-05-05 2026-05-05 DEC-0032
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

  • 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.
  • 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.
  • 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.
  • 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.
  • 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.