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

129 lines
9.1 KiB
Markdown

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