prometeu-studio/discussion/workflow/agendas/AGD-0011-compiler-analyze-compile-build-pipeline-split.md

195 lines
15 KiB
Markdown

---
id: AGD-0011
ticket: compiler-analyze-compile-build-pipeline-split
title: Desacoplar o pipeline do compiler em analyze, compile e build
status: accepted
created: 2026-03-30
resolved: 2026-03-30
decision: DEC-0007
tags:
- compiler
- pipeline
- artifacts
- build
- analysis
---
## Pain
Hoje o pipeline principal do compiler exposto por `BuilderPipelineService` e monolitico: ele resolve dependencias, carrega fontes, roda frontend, lowering, otimizacao, emissao, verificacao e sempre termina escrevendo `build/program.pbx`.
Isso acopla tres intencoes diferentes num unico fluxo:
- analisar codigo e produzir diagnosticos/estado semantico;
- compilar ate um resultado executavel em memoria;
- materializar artefato final em disco.
Esse acoplamento atrapalha diretamente o proximo wave do Studio editor e o futuro LSP:
- o editor precisa de `analyze` sem forcar emissao de PBX;
- um consumidor de semantica pode precisar de `compile` em memoria sem write side effects;
- `build` deve continuar existindo como fluxo de artefato, mas nao como unica forma de usar o compiler.
## Context
Domain owner: `compiler`
Owner surface: `docs/specs/compiler`
Cross-domain input: `studio`
Superficies relevantes hoje:
- `prometeu-compiler/prometeu-build-pipeline/.../BuilderPipelineService.java` registra uma lista fixa de stages e sempre inclui `WriteBytecodeArtifactPipelineStage` no fim;
- `EmitBytecodePipelineStage` ja produz `ctx.bytecodeModule` e `ctx.bytecodeBytes` em memoria antes de qualquer write em disco;
- `WriteBytecodeArtifactPipelineStage` apenas persiste `build/program.pbx`, o que mostra que a emissao de artefato ja e uma preocupacao separavel;
- `docs/specs/compiler/15. Bytecode and PBX Mapping Specification.md` normatiza a fronteira de emissao PBX/bytecode, mas nao exige que todo consumo do compiler materialize artefato em disco;
- na discussion do editor (`DSC-0010`), ja ficou aceito que `SourceProviderFactory` sera mantido como boundary de leitura, com providers mais especificos no futuro para sessao documental do editor.
- embora PBS seja hoje o frontend mais avancado nesse caminho, esta agenda pertence ao dominio `compiler` e precisa preservar um contrato de pipeline frontend-agnostico, reutilizavel por PBS e por futuros FEs sem regressao no comportamento de `compile`.
Boundary explicitamente fechado para esta agenda:
- `SourceProviderFactory` continua sendo a fronteira de leitura do compiler;
- esta agenda NAO vai discutir ainda o novo provider do editor;
- esta agenda vai discutir apenas como expor `analyze`, `compile` e `build` como entradas distintas do compiler.
- o split do pipeline nao pode introduzir semantica especial de PBS na API operacional do compiler; compatibilidade com o `compile` atual de PBS deve ser tratada como restricao obrigatoria.
## Open Questions
- [x] Qual deve ser o corte exato entre `analyze` e `compile`: `analyze` para no frontend/diagnostics, ou inclui tambem lowering/backend facts uteis para tooling?
Recomendacao: `analyze` deve terminar em `FrontendPhasePipelineStage`, isto e, `ResolveDeps + LoadSources + FrontendPhase`, expondo diagnosticos e facts semanticos orientados a tooling sem avancar para lowering ou emissao.
- [x] `compile` deve ir ate `bytecodeModule`/`bytecodeBytes` em memoria sem write, ou deve parar em `IRVM` e deixar emissao para `build`?
Recomendacao: `compile` deve terminar em `VerifyBytecodePipelineStage`, produzindo `bytecodeModule` e `bytecodeBytes` verificados em memoria sem write side effects; parar em `IRVM` deixaria a superficie incompleta para consumidores que precisam de resultado executavel sem persistencia.
- [x] A verificacao (`VerifyBytecodePipelineStage`) pertence a `compile`, a `build`, ou a ambos conforme perfil?
Recomendacao: `VerifyBytecodePipelineStage` e o stage terminal de `compile`, e `build` apenas adiciona `WriteBytecodeArtifactPipelineStage` depois disso; `compile` nao deve devolver bytes nao verificados.
- [x] Como representar esses tres modos: services separados, perfis de pipeline, ou uma composicao explicita de stage groups?
Recomendacao: expor tres chamadas canonicas no mesmo pipeline compartilhado, por exemplo `analyze`, `compile` e `build`, evitando tanto tres pipelines independentes quanto um unico `run` monolitico com semantica implicita.
- [x] O resultado de `analyze` deve expor um `AnalysisSnapshot` estavel para editor/LSP, ou apenas diagnosticos + facts minimos no primeiro wave?
Recomendacao: `analyze` deve expor um `AnalysisSnapshot` estavel ja no primeiro wave, mas com shape minimo e pragmatico, carregando apenas os facts e handles necessarios para editor/LSP.
- [x] O CLI atual e os callsites existentes devem continuar mapeando `build` como comportamento default?
Recomendacao: sim; a mudanca deve ampliar a superficie operacional do compiler sem quebrar o comportamento default atual de artefato em disco.
- [x] Como evitar que `build` e `compile` passem a divergir semanticamente por ordem de stages ou gates diferentes?
Recomendacao: `build` deve ser definido normativamente como o mesmo fluxo de `compile`, encerrado por `WriteBytecodeArtifactPipelineStage`, sem reorder ou gates semanticos exclusivos fora da persistencia terminal.
- [x] LSP/editor devem usar outro pipeline, ou o mesmo pipeline com chamadas e contextos diferentes?
Recomendacao: devem usar o mesmo pipeline canonico, mas atraves de chamadas diferentes e configs/contextos distintos por entrypoint; mudar inputs, sinks e shape de resultado e valido, mas mudar a semantica central dos stages canonicos nao.
- [x] O `run` atual deve continuar existindo como alias, ou deve ser absorvido por `build`?
Recomendacao: `run` nao deve permanecer como entrypoint publico; o comportamento default de hoje deve ser absorvido por `build`, com contexto/config de filesystem construido fora do pipeline em si.
- [x] Quais contratos ou specs precisam receber propagacao quando o pipeline deixar de ter uma unica entrada monolitica?
Recomendacao: a propagacao principal deve atingir a spec operacional do pipeline do compiler, os contratos de CLI/callsites e a documentacao de resultados intermediarios; a spec de PBX continua authority de emissao, nao de persistencia em disco.
## Options
### Option A - Manter `BuilderPipelineService` monolitico e adicionar flags
- **Approach:** Preservar o pipeline atual e usar flags condicionais para pular write de artefato ou parar em stages intermediarios.
- **Pro:** Menor mudanca inicial.
- **Con:** Esconde perfis diferentes dentro de um fluxo ad hoc, torna os cortes menos legiveis e aumenta risco de combinacoes invalidas.
- **Maintainability:** Fraca. O comportamento fica implicito e espalhado por condicionais.
### Option B - Definir tres entrypoints normativos sobre um pipeline composavel
- **Approach:** Modelar o pipeline como composicao reutilizavel de stage groups e expor tres modos canonicos: `analyze`, `compile` e `build`.
- **Pro:** Torna o contrato explicito, reaproveita as mesmas stages onde fizer sentido e separa claramente analise, compilacao em memoria e emissao de artefato.
- **Con:** Exige fechar com precisao o corte entre as fases e o shape dos resultados intermediarios.
- **Maintainability:** Forte. O compiler ganha uma superficie operacional clara para CLI, Studio, testes e LSP.
### Option C - Separar services independentes por responsabilidade
- **Approach:** Criar services distintos e quase autossuficientes para `AnalyzeService`, `CompileService` e `BuildService`, cada um com seu proprio encadeamento.
- **Pro:** As responsabilidades ficam bem nomeadas.
- **Con:** Se a composicao nao for rigorosamente compartilhada, o risco de drift entre os fluxos cresce rapido.
- **Maintainability:** Media. Pode funcionar, mas so se a infraestrutura comum ficar bem centralizada.
## Discussion
O codigo atual ja mostra o seam principal que queremos aproveitar:
1. primeiro o pipeline resolve e analisa a workspace;
2. depois produz artefatos executaveis em memoria;
3. por fim escreve `program.pbx` em disco.
O problema nao e falta de separacao conceitual.
O problema e que essa separacao ainda nao virou API normativa nem entrypoints distintos.
Para o editor e o LSP, isso importa muito:
- `analyze` precisa ser barato em side effects e orientado a snapshot;
- `compile` precisa produzir um resultado coerente em memoria sem obrigar write de artefato;
- `build` precisa continuar sendo o fluxo que materializa `program.pbx`.
Ao mesmo tempo, esta agenda nao deve misturar dois assuntos:
- como o compiler le fontes;
- e como ele expõe seus perfis de execucao.
O primeiro assunto fica travado: `SourceProviderFactory` continua sendo o boundary.
O segundo e o alvo desta discussion: desacoplar a intencao operacional do pipeline.
Tambem precisamos manter a classificacao correta do problema:
- PBS nao e o owner desta decisao; ele e um frontend consumidor importante;
- o split precisa servir ao compiler como plataforma multi-frontend;
- isso significa que a nova API de `analyze`/`compile`/`build` deve preservar o comportamento correto que o caminho atual ja entrega para PBS, sem acoplar o contrato a detalhes exclusivos desse frontend.
Tambem fica fechado que LSP/editor nao introduzem "outro pipeline" no dominio `compiler`.
O que muda sao as chamadas e os contextos de execucao:
- o pipeline canonico permanece unico;
- `analyze`, `compile` e `build` sao entrypoints distintos sobre esse mesmo pipeline;
- configs/contextos diferentes podem ajustar leitura, coletores, sinks e shape de retorno por caso de uso;
- esses contextos nao devem redefinir a semantica central dos stages canonicos.
- o comportamento default atual deixa de se chamar `run` e passa a ser o `build` construido com config/contexto de filesystem montado pelo callsite, nao pelo pipeline em si.
A direcao mais saudavel parece ser explicitar tres perfis canonicos:
1. `analyze`
termina em `FrontendPhasePipelineStage`, isto e, resolve deps, carrega fontes, roda frontend e produz `AnalysisSnapshot` com diagnosticos e facts semanticos sem executar lowering, emissao ou write de artefato.
2. `compile`
termina em `VerifyBytecodePipelineStage`, isto e, reaproveita o mesmo eixo semantico de `analyze`, avanca por lowering, otimizacao, emissao, link precheck e verificacao preload, e produz resultado executavel validado em memoria sem escrever arquivo.
3. `build`
termina em `WriteBytecodeArtifactPipelineStage`, isto e, e definido como `compile` seguido apenas da etapa terminal de materializacao do artefato em disco.
Isso preserva um unico eixo semantico e reduz o risco de o Studio ou o LSP precisarem inventar um "mini-compiler" paralelo.
Em termos de superficie de servico, o caminho recomendado elimina `run` como entrypoint normativo.
O servico deve passar a expor entrypoints explicitos, por exemplo:
- `analyze(config, logs)` ou equivalente;
- `compile(config, logs)` ou equivalente;
- `build(config, logs)` ou equivalente.
Cada chamada pode receber config/contexto apropriado ao caso de uso, mas continua percorrendo o mesmo pipeline canonico ate o stage terminal definido para aquele entrypoint.
O comportamento que hoje existe em `run` deve ser reexpresso como `build(filesystem-config, logs)` ou equivalente, com a construcao do contexto default acontecendo fora do pipeline itself.
Fechamentos recomendados para a decisao:
1. `analyze` termina em `FrontendPhasePipelineStage` e nao inclui lowering/backend execution artifacts.
2. `compile` termina em `VerifyBytecodePipelineStage` e vai ate `bytecodeModule` e `bytecodeBytes` em memoria.
3. `compile` inclui `LinkBytecode` e `VerifyBytecode` para que o resultado em memoria ja seja semanticamente confiavel.
4. `build` termina em `WriteBytecodeArtifactPipelineStage`; ele nao recompila por outro caminho e apenas reaproveita `compile` com a persistencia terminal.
5. a API publica deve expor snapshots/resultados estaveis por perfil, sem vazar `BuilderPipelineContext` como contrato externo.
6. o CLI e os callsites existentes continuam apontando para `build` como comportamento default.
7. a decisao deve preservar compatibilidade comportamental com o `compile` atual usado por PBS, tratando esse fluxo como baseline de corretude e nao como detalhe descartavel de um frontend especifico.
8. a superficie nova deve permanecer frontend-agnostica, para que futuros FEs possam reutilizar o mesmo contrato sem herdar semantica acidental de PBS.
9. `BuilderPipelineService` nao deve manter `run` como entrypoint publico; ele deve expor `analyze`, `compile` e `build` sobre o mesmo pipeline compartilhado.
10. o comportamento atual de `run` deve ser absorvido por `build` com config/contexto de filesystem construido fora do pipeline em si.
11. configs/contextos distintos por entrypoint sao permitidos para suportar CLI, editor e LSP, desde que nao alterem a semantica central dos stages canonicos.
## Resolution
Recommended direction: seguir com **Option B**.
A agenda deve convergir para uma decisao com os seguintes fechamentos:
1. `SourceProviderFactory` permanece como boundary de leitura do compiler;
2. o compiler deve expor tres entrypoints normativos: `analyze`, `compile` e `build`;
3. `build` nao e mais sinonimo de "todo uso do compiler", mas apenas o perfil que materializa artefato;
4. `analyze` deve ser definido como o perfil que termina em `FrontendPhasePipelineStage`, isto e, `ResolveDeps + LoadSources + FrontendPhase`, retornando `AnalysisSnapshot` estavel e sem write side effects;
5. `compile` deve ser definido como o perfil que termina em `VerifyBytecodePipelineStage`, isto e, `analyze + LowerToIRVM + OptimizeIRVM + EmitBytecode + LinkBytecode + VerifyBytecode`, retornando resultado validado em memoria;
6. `build` deve ser definido como o perfil que termina em `WriteBytecodeArtifactPipelineStage`, isto e, `compile + WriteBytecodeArtifact`, sem reorder semantico ou gates adicionais fora da persistencia terminal;
7. a API publica deve retornar resultados estaveis por perfil em vez de expor o `BuilderPipelineContext` diretamente;
8. o CLI/build atual deve continuar como comportamento default para compatibilidade;
9. a decisao deve preservar compatibilidade comportamental com o `compile` atual de PBS como baseline de corretude, sem transformar PBS em detalhe normativo do contrato publico;
10. a superficie `analyze`/`compile`/`build` deve permanecer frontend-agnostica para acomodar multiplos FEs no dominio `compiler`;
11. `BuilderPipelineService` deve remover `run` como entrypoint publico e expor `analyze`, `compile` e `build`, cada um com config/contexto apropriado ao seu caso de uso;
12. o comportamento default de hoje deve ser preservado como `build` com config/contexto de filesystem, construido fora do pipeline itself;
13. configs/contextos diferentes por entrypoint sao parte do modelo, mas nao podem redefinir a semantica central dos stages canonicos;
14. a propagacao deve atingir a spec operacional do pipeline, os contratos de CLI/callsites e a documentacao dos resultados intermediarios, preservando a spec de PBX como authority de emissao.
Next step suggestion: converter esta agenda em uma `decision` que feche o stage boundary de cada perfil, o contrato dos resultados intermediarios e a compatibilidade do CLI/build atual com o novo modelo.