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