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

15 KiB

id ticket title status created resolved decision tags
AGD-0011 compiler-analyze-compile-build-pipeline-split Desacoplar o pipeline do compiler em analyze, compile e build accepted 2026-03-30 2026-03-30 DEC-0007
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

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